From 65d9a8f5e32421c49a13bc643816524cf09f9cce Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:44:08 +0530 Subject: [PATCH 01/33] feat: add current VRF transcript primitives --- dot_ring/curve/curve.py | 9 +- .../short_weierstrass/sw_affine_point.py | 5 + dot_ring/curve/specs/baby_jubjub.py | 22 ++- dot_ring/curve/specs/bandersnatch.py | 70 +++++++- dot_ring/curve/specs/bandersnatch_sw.py | 33 +++- dot_ring/curve/specs/ed25519.py | 14 +- dot_ring/curve/specs/jubjub.py | 22 ++- dot_ring/curve/specs/p256.py | 81 ++++++++- .../curve/twisted_edwards/te_affine_point.py | 5 + dot_ring/keygen.py | 45 +---- dot_ring/vrf/ark_transcript.py | 74 ++++++++ dot_ring/vrf/delinearize.py | 24 +++ dot_ring/vrf/domain.py | 17 ++ dot_ring/vrf/transcript.py | 169 ++++++++++++++++++ dot_ring/vrf/transcript_constants.py | 2 + dot_ring/vrf/vrf.py | 41 +---- dot_ring/vrf/vrf_io.py | 14 ++ dot_ring/vrf/vrf_protocol.py | 34 ++++ 18 files changed, 577 insertions(+), 104 deletions(-) create mode 100644 dot_ring/vrf/ark_transcript.py create mode 100644 dot_ring/vrf/delinearize.py create mode 100644 dot_ring/vrf/domain.py create mode 100644 dot_ring/vrf/transcript.py create mode 100644 dot_ring/vrf/transcript_constants.py create mode 100644 dot_ring/vrf/vrf_io.py create mode 100644 dot_ring/vrf/vrf_protocol.py diff --git a/dot_ring/curve/curve.py b/dot_ring/curve/curve.py index 80db784..5956eb7 100644 --- a/dot_ring/curve/curve.py +++ b/dot_ring/curve/curve.py @@ -2,7 +2,7 @@ import hashlib import math -from dataclasses import dataclass +from dataclasses import dataclass, field from typing import TYPE_CHECKING, Any, cast from dot_ring.curve.e2c import E2C_Variant @@ -62,6 +62,13 @@ class Curve: BBy: int | tuple[int, int] | None UNCOMPRESSED: bool POINT_LEN: int + SUITE_ID: bytes | None = field(default=None, kw_only=True) + TRANSCRIPT_HASH: str = field(default="sha512", kw_only=True) + HASH_TO_CURVE: str | None = field(default=None, kw_only=True) + ACCUMULATOR_BASE_X: int | tuple[int, int] | None = field(default=None, kw_only=True) + ACCUMULATOR_BASE_Y: int | tuple[int, int] | None = field(default=None, kw_only=True) + PADDING_X: int | tuple[int, int] | None = field(default=None, kw_only=True) + PADDING_Y: int | tuple[int, int] | None = field(default=None, kw_only=True) def __post_init__(self) -> None: """Validate curve parameters after initialization.""" diff --git a/dot_ring/curve/short_weierstrass/sw_affine_point.py b/dot_ring/curve/short_weierstrass/sw_affine_point.py index 18e47ac..0377657 100644 --- a/dot_ring/curve/short_weierstrass/sw_affine_point.py +++ b/dot_ring/curve/short_weierstrass/sw_affine_point.py @@ -556,6 +556,11 @@ def encode_to_curve( if not isinstance(salt, bytes): salt = bytes.fromhex(salt) + if cls.curve.HASH_TO_CURVE == "tai": + from dot_ring.vrf.transcript import hash_to_curve_tai + + return cast(Self, hash_to_curve_tai(cls, salt + alpha_string)) + if cls.curve.E2C in [E2C_Variant.SSWU, E2C_Variant.SSWU_NU]: if cls.curve.E2C.value.endswith("_NU_"): return cls.sswu_hash2_curve_nu(alpha_string, salt, General_Check) diff --git a/dot_ring/curve/specs/baby_jubjub.py b/dot_ring/curve/specs/baby_jubjub.py index 165cbe9..dc3caba 100644 --- a/dot_ring/curve/specs/baby_jubjub.py +++ b/dot_ring/curve/specs/baby_jubjub.py @@ -19,7 +19,8 @@ class BabyJubJubParams: Specification of the Baby JubJub curve in Twisted Edwards form. """ - SUITE_STRING = b"Baby-JubJub_SHA-512_TAI" # "Babyjubjub_XMD:SHA-512_ELL2_RO_" + SUITE_STRING = b"BabyJubJub-SHA512-TAI-v1" + SUITE_ID = b"BabyJubJub-SHA512-TAI-v1" DST = b"" # Curve parameters @@ -45,13 +46,19 @@ class BabyJubJubParams: Requires_Isogeny: Final[bool] = False Isogeny_Coeffs = None # Challenge length in bytes for VRF (aligned with 128-bit security level) - CHALLENGE_LENGTH: Final[int] = 32 # 128 bits + CHALLENGE_LENGTH: Final[int] = 16 # Blinding Base For Pedersen - BBx: Final[int] = 8170247200255741810297410022472365370979789984587637609570347196251706043122 - BBy: Final[int] = 16313972569917201570489077828713531620741538540099917729994937953803219324220 + BBx: Final[int] = 15549380791300914366206471199568039679131690710803662429646809536753521087193 + BBy: Final[int] = 15218614024055502695611547593111691164731001864276292210438920202280814188379 + ACCUMULATOR_BASE_X: Final[int] = 6402374321243162085389111671722843560682527921646684137786768606010797479351 + ACCUMULATOR_BASE_Y: Final[int] = 9735581299071570006712034490635195155689931359428941496570758703259384062170 + PADDING_X: Final[int] = 11167490195257431015694161063225325511805242064780376648595733691987293447528 + PADDING_Y: Final[int] = 18403369502642103292159933062507105566469227524991433735553439433605496057425 UNCOMPRESSED = False POINT_LEN: Final[int] = 32 + TRANSCRIPT_HASH = "sha512" + HASH_TO_CURVE = "tai" BabyJubJub_TE_Curve: Final[TECurve] = TECurve( @@ -79,6 +86,13 @@ class BabyJubJubParams: ENDIAN=BabyJubJubParams.ENDIAN, POINT_LEN=BabyJubJubParams.POINT_LEN, CHALLENGE_LENGTH=BabyJubJubParams.CHALLENGE_LENGTH, + SUITE_ID=BabyJubJubParams.SUITE_ID, + TRANSCRIPT_HASH=BabyJubJubParams.TRANSCRIPT_HASH, + HASH_TO_CURVE=BabyJubJubParams.HASH_TO_CURVE, + ACCUMULATOR_BASE_X=BabyJubJubParams.ACCUMULATOR_BASE_X, + ACCUMULATOR_BASE_Y=BabyJubJubParams.ACCUMULATOR_BASE_Y, + PADDING_X=BabyJubJubParams.PADDING_X, + PADDING_Y=BabyJubJubParams.PADDING_Y, ) diff --git a/dot_ring/curve/specs/bandersnatch.py b/dot_ring/curve/specs/bandersnatch.py index f6a5b2e..183a7de 100644 --- a/dot_ring/curve/specs/bandersnatch.py +++ b/dot_ring/curve/specs/bandersnatch.py @@ -22,8 +22,9 @@ class BandersnatchParams: implementation of zero-knowledge proofs and VRFs. """ - SUITE_STRING = b"Bandersnatch_SHA-512_ELL2" - DST = b"ECVRF_Bandersnatch_XMD:SHA-512_ELL2_RO_Bandersnatch_SHA-512_ELL2" + SUITE_STRING = b"Bandersnatch-SHA512-ELL2-v1" + SUITE_ID = b"Bandersnatch-SHA512-ELL2-v1" + DST = SUITE_ID + b"\x60" # Curve parameters PRIME_FIELD: Final[int] = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 @@ -44,7 +45,7 @@ class BandersnatchParams: GLV_C: Final[int] = 0x6CC624CF865457C3A97C6EFD6C17D1078456ABCFFF36F4E9515C806CDF650B3D # Challenge length in bytes for VRF (aligned with 256-bit security level) - CHALLENGE_LENGTH: Final[int] = 32 # 256 bits + CHALLENGE_LENGTH: Final[int] = 16 # Z Z: Final[int] = 5 @@ -57,10 +58,16 @@ class BandersnatchParams: Requires_Isogeny: Final[bool] = False Isogeny_Coeffs = None - BBx: Final[int] = 6150229251051246713677296363717454238956877613358614224171740096471278798312 - BBy: Final[int] = 28442734166467795856797249030329035618871580593056783094884474814923353898473 + BBx: Final[int] = 23335687741101763108036518445642207119627658113885888016488710494487028845889 + BBy: Final[int] = 5552214580375038693022409684979828600325210968745774080859660443337357929963 + ACCUMULATOR_BASE_X: Final[int] = 14056632001415368875257708737821299882600475929746323097150942355715730684350 + ACCUMULATOR_BASE_Y: Final[int] = 10322661992765989500407719465917595459409463902187386706652408883505670839210 + PADDING_X: Final[int] = 26913883415342152801331916189968962157924271221160514298872262294143390094043 + PADDING_Y: Final[int] = 30874728313203001508631936119690348239461579770372782660098261717479009115354 UNCOMPRESSED = False POINT_LEN: Final[int] = 32 + TRANSCRIPT_HASH = "sha512" + HASH_TO_CURVE = "ell2-xmd" """GLV endomorphism parameters for Bandersnatch curve.""" @@ -95,6 +102,13 @@ class BandersnatchParams: ENDIAN=BandersnatchParams.ENDIAN, POINT_LEN=BandersnatchParams.POINT_LEN, CHALLENGE_LENGTH=BandersnatchParams.CHALLENGE_LENGTH, + SUITE_ID=BandersnatchParams.SUITE_ID, + TRANSCRIPT_HASH=BandersnatchParams.TRANSCRIPT_HASH, + HASH_TO_CURVE=BandersnatchParams.HASH_TO_CURVE, + ACCUMULATOR_BASE_X=BandersnatchParams.ACCUMULATOR_BASE_X, + ACCUMULATOR_BASE_Y=BandersnatchParams.ACCUMULATOR_BASE_Y, + PADDING_X=BandersnatchParams.PADDING_X, + PADDING_Y=BandersnatchParams.PADDING_Y, ) @@ -188,3 +202,49 @@ def msm(cls, points: list[Self], scalars: list[int]) -> Self: curve=Bandersnatch_TE_Curve, point=BandersnatchPoint, ) + + +Bandersnatch_SHAKE128_TE_Curve: Final[TECurve] = TECurve( + PRIME_FIELD=BandersnatchParams.PRIME_FIELD, + ORDER=BandersnatchParams.ORDER, + GENERATOR_X=BandersnatchParams.GENERATOR_X, + GENERATOR_Y=BandersnatchParams.GENERATOR_Y, + COFACTOR=BandersnatchParams.COFACTOR, + Z=BandersnatchParams.Z, + EdwardsA=BandersnatchParams.EDWARDS_A, + EdwardsD=BandersnatchParams.EDWARDS_D, + SUITE_STRING=b"Bandersnatch-SHAKE128-ELL2-v1", + DST=b"Bandersnatch-SHAKE128-ELL2-v1\x60", + E2C=E2C_Variant.ELL2, + BBx=6153734995852631824944342602386415873379775188383988340041079006556670120775, + BBy=27204351599954061630605768787803524395123895650061061132592995395630473050754, + M=BandersnatchParams.M, + K=BandersnatchParams.K, + L=BandersnatchParams.L, + S_in_bytes=None, + H_A=hashlib.shake_128, + Requires_Isogeny=BandersnatchParams.Requires_Isogeny, + Isogeny_Coeffs=BandersnatchParams.Isogeny_Coeffs, + UNCOMPRESSED=BandersnatchParams.UNCOMPRESSED, + ENDIAN=BandersnatchParams.ENDIAN, + POINT_LEN=BandersnatchParams.POINT_LEN, + CHALLENGE_LENGTH=BandersnatchParams.CHALLENGE_LENGTH, + SUITE_ID=b"Bandersnatch-SHAKE128-ELL2-v1", + TRANSCRIPT_HASH="shake128", + HASH_TO_CURVE="ell2-xof", + ACCUMULATOR_BASE_X=27631238720955528589004064829276283990465032040945349648037876197995278250917, + ACCUMULATOR_BASE_Y=37605358688136619817560700742505556266961225274493904038881144193539047100140, + PADDING_X=1834402953989431481748983728202937234471322740714585873803966488035889514523, + PADDING_Y=52100941849053769665273763352270294131006971127418863694682093199651869272752, +) + + +class BandersnatchSHAKE128Point(BandersnatchPoint): + curve: TECurve = Bandersnatch_SHAKE128_TE_Curve + + +Bandersnatch_SHAKE128 = CurveVariant( + name="Bandersnatch_SHAKE128", + curve=Bandersnatch_SHAKE128_TE_Curve, + point=BandersnatchSHAKE128Point, +) diff --git a/dot_ring/curve/specs/bandersnatch_sw.py b/dot_ring/curve/specs/bandersnatch_sw.py index c2d8744..c0b240d 100644 --- a/dot_ring/curve/specs/bandersnatch_sw.py +++ b/dot_ring/curve/specs/bandersnatch_sw.py @@ -13,8 +13,9 @@ @dataclass(frozen=True) class BandersnatchSWParams: - SUITE_STRING = b"Bandersnatch_SW_SHA-512_TAI" - DST = b"ECVRF_Bandersnatch_XMD:SHA-512_TAI_RO_Bandersnatch_SW_SHA-512_TAI" + SUITE_STRING = b"Bandersnatch-SW-SHA512-TAI-v1" + SUITE_ID = b"Bandersnatch-SW-SHA512-TAI-v1" + DST = b"" PRIME_FIELD: Final[int] = 52435875175126190479447740508185965837690552500527637822603658699938581184513 ORDER: Final[int] = 0x1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E1 @@ -33,14 +34,20 @@ class BandersnatchSWParams: S_in_bytes: Final[int] = 64 H_A = hashlib.sha512 ENDIAN = "little" - BBx: Final[int] = 43295201540795761503961631609120105078472641399392666499799525033203881929458 - BBy: Final[int] = 47295792057744344182638225978402781315571475472700428341116949953237551542374 - - CHALLENGE_LENGTH: Final[int] = 32 + BBx: Final[int] = 28115362618644671219696075022370511395136332234538034358311199318506963235315 + BBy: Final[int] = 3900851469868158154936962463930962496000252801946757953905982128670530185313 + ACCUMULATOR_BASE_X: Final[int] = 13189182432637108534251278524663360416811744717379968387043749958796254980045 + ACCUMULATOR_BASE_Y: Final[int] = 14483286006782706188671626508232161325054303360192563232232823772738911894793 + PADDING_X: Final[int] = 20496180070424734470560955314776462366297546779079302509428101119888111900885 + PADDING_Y: Final[int] = 8839106592405352067483360946162273985142890146060814748321063063028225641813 + + CHALLENGE_LENGTH: Final[int] = 16 Requires_Isogeny: Final[bool] = False Isogeny_Coeffs = None UNCOMPRESSED = False POINT_LEN: Final[int] = 33 + TRANSCRIPT_HASH = "sha512" + HASH_TO_CURVE = "tai" Bandersnatch_SW_SW_Curve: Final[SWCurve] = SWCurve( @@ -68,6 +75,13 @@ class BandersnatchSWParams: ENDIAN=BandersnatchSWParams.ENDIAN, POINT_LEN=BandersnatchSWParams.POINT_LEN, CHALLENGE_LENGTH=BandersnatchSWParams.CHALLENGE_LENGTH, + SUITE_ID=BandersnatchSWParams.SUITE_ID, + TRANSCRIPT_HASH=BandersnatchSWParams.TRANSCRIPT_HASH, + HASH_TO_CURVE=BandersnatchSWParams.HASH_TO_CURVE, + ACCUMULATOR_BASE_X=BandersnatchSWParams.ACCUMULATOR_BASE_X, + ACCUMULATOR_BASE_Y=BandersnatchSWParams.ACCUMULATOR_BASE_Y, + PADDING_X=BandersnatchSWParams.PADDING_X, + PADDING_Y=BandersnatchSWParams.PADDING_Y, ) @@ -159,8 +173,11 @@ def string_to_point(cls, data: str | bytes) -> Self: raise ValueError("INVALID point: no y-coordinate found for x") y, y_neg = y_candidates - is_negative = (data[-1] >> 7) & 1 - is_infinity = (data[-1] >> 6) & 1 + flag = data[-1] + if flag & 0x3F: + raise ValueError("INVALID: invalid canonical point flags") + is_negative = (flag >> 7) & 1 + is_infinity = (flag >> 6) & 1 if is_infinity: if is_negative: diff --git a/dot_ring/curve/specs/ed25519.py b/dot_ring/curve/specs/ed25519.py index 3547421..be992e4 100644 --- a/dot_ring/curve/specs/ed25519.py +++ b/dot_ring/curve/specs/ed25519.py @@ -19,7 +19,8 @@ class Ed25519Params: Specification of the JubJub curve in Twisted Edwards form. """ - SUITE_STRING = b"edwards25519_XMD:SHA-512_ELL2_RO_" + SUITE_STRING = b"Ed25519-SHA512-TAI-v1" + SUITE_ID = b"Ed25519-SHA512-TAI-v1" DST = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_" # Curve parameters @@ -46,11 +47,13 @@ class Ed25519Params: # Challenge length in bytes for VRF (from RFC 9381) CHALLENGE_LENGTH: Final[int] = 16 # 128 bits - BBx: Final[int] = 52417091031015867055192825304177001039906336859819158874861527659737645967040 - BBy: Final[int] = 24364467899048426341436922427697710961180476432856951893648702734568269272170 + BBx: Final[int] = 45003173884697328536089278691112838614164406922820087464913813433380838325453 + BBy: Final[int] = 31256014272390301975555524011230972931324093235775711248505761870355310252869 UNCOMPRESSED = False POINT_LEN: Final[int] = 32 + TRANSCRIPT_HASH = "sha512" + HASH_TO_CURVE = "tai" class Ed25519Curve(TECurve): @@ -71,7 +74,7 @@ def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") DST = DST.replace(b"_RO_", b"_NU_") if e2c_variant.value == "TryAndIncrement": - SUITE_STRING = b"Ed25519_SHA-512_TAI" # as per davxy + SUITE_STRING = Ed25519Params.SUITE_ID DST = b"" super().__init__( PRIME_FIELD=Ed25519Params.PRIME_FIELD, @@ -98,6 +101,9 @@ def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: ENDIAN=Ed25519Params.ENDIAN, POINT_LEN=Ed25519Params.POINT_LEN, CHALLENGE_LENGTH=Ed25519Params.CHALLENGE_LENGTH, + SUITE_ID=Ed25519Params.SUITE_ID if e2c_variant == E2C_Variant.TAI else None, + TRANSCRIPT_HASH=Ed25519Params.TRANSCRIPT_HASH, + HASH_TO_CURVE=Ed25519Params.HASH_TO_CURVE if e2c_variant == E2C_Variant.TAI else None, ) def calculate_j_k(self) -> tuple[int, int]: diff --git a/dot_ring/curve/specs/jubjub.py b/dot_ring/curve/specs/jubjub.py index 6d5a6da..aeb96a4 100644 --- a/dot_ring/curve/specs/jubjub.py +++ b/dot_ring/curve/specs/jubjub.py @@ -19,7 +19,8 @@ class JubJubParams: Specification of the JubJub curve in Twisted Edwards form. """ - SUITE_STRING = b"JubJub_SHA-512_TAI" # "Jubjub_XMD:SHA-512_ELL2_RO_" + SUITE_STRING = b"JubJub-SHA512-TAI-v1" + SUITE_ID = b"JubJub-SHA512-TAI-v1" DST = b"" # f_len=q_len=32 # Curve parameters @@ -45,13 +46,19 @@ class JubJubParams: ENDIAN = "little" Requires_Isogeny: Final[bool] = False Isogeny_Coeffs = None - CHALLENGE_LENGTH: Final[int] = 32 + CHALLENGE_LENGTH: Final[int] = 16 # Blinding Base For Pedersen - BBx: Final[int] = 42257337814662035284373945156525735092765968053982822992704750832078779438788 - BBy: Final[int] = 47476395315228831116309413527962830333178159651930104661512857647213254194102 + BBx: Final[int] = 38206460563694846719174258613922853630278999941532690543235578292520143148532 + BBy: Final[int] = 34254498978062207918041301829525626783549813531091321004550549786528984401675 + ACCUMULATOR_BASE_X: Final[int] = 48142684311216766702182564801462043940571084233680216669499475549492432046964 + ACCUMULATOR_BASE_Y: Final[int] = 34380560660182334518990118617091967209302636551264477863958902286043397647879 + PADDING_X: Final[int] = 17348704025397475127937572481155408456556065464328870407269802701696798733683 + PADDING_Y: Final[int] = 24318278422173803457621119807961883607097742387673491974779969503617097905596 UNCOMPRESSED = False POINT_LEN: Final[int] = 32 + TRANSCRIPT_HASH = "sha512" + HASH_TO_CURVE = "tai" JubJub_TE_Curve: Final[TECurve] = TECurve( @@ -79,6 +86,13 @@ class JubJubParams: ENDIAN=JubJubParams.ENDIAN, POINT_LEN=JubJubParams.POINT_LEN, CHALLENGE_LENGTH=JubJubParams.CHALLENGE_LENGTH, + SUITE_ID=JubJubParams.SUITE_ID, + TRANSCRIPT_HASH=JubJubParams.TRANSCRIPT_HASH, + HASH_TO_CURVE=JubJubParams.HASH_TO_CURVE, + ACCUMULATOR_BASE_X=JubJubParams.ACCUMULATOR_BASE_X, + ACCUMULATOR_BASE_Y=JubJubParams.ACCUMULATOR_BASE_Y, + PADDING_X=JubJubParams.PADDING_X, + PADDING_Y=JubJubParams.PADDING_Y, ) diff --git a/dot_ring/curve/specs/p256.py b/dot_ring/curve/specs/p256.py index c3f748d..40b86ac 100644 --- a/dot_ring/curve/specs/p256.py +++ b/dot_ring/curve/specs/p256.py @@ -20,8 +20,8 @@ class P256Params: in TLS, digital signatures, and other cryptographic applications. """ - # From RFC 9380 Section 8.1: P-256_XMD:SHA-256_SSWU_RO_ - SUITE_STRING = b"P256_XMD:SHA-256_SSWU_RO_" + SUITE_STRING = b"Secp256r1-SHA256-TAI-v1" + SUITE_ID = b"Secp256r1-SHA256-TAI-v1" DST = b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_" # Default DST is the same as SUITE_STRING # Curve parameters for y² = x³ - 3x + b @@ -48,14 +48,16 @@ class P256Params: ENDIAN = "big" # Blinding Base For Pedersen VRF # These are arbitrary points on the curve for blinding - BBx: Final[int] = 55516455597544811540149985232155473070193196202193483189274003004283034832642 - BBy: Final[int] = 48580550536742846740990228707183741745344724157532839324866819111997786854582 + BBx: Final[int] = 100063053743935619201936855760019111820847755970243670581468062459849338000 + BBy: Final[int] = 113675507039234898358330549589155441528265243038226986303017485279501143145422 # Challenge length in bytes for VRF (from RFC 9381) CHALLENGE_LENGTH: Final[int] = 16 # 128 bits Requires_Isogeny: Final[bool] = False Isogeny_Coeffs = None UNCOMPRESSED = False POINT_LEN: Final[int] = 33 + TRANSCRIPT_HASH = "sha256" + HASH_TO_CURVE = "tai" class P256Curve(SWCurve): @@ -76,8 +78,9 @@ def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") DST = DST.replace(b"_RO_", b"_NU_") elif e2c_variant.value == "TryAndIncrement": - SUITE_STRING = b"\x01" # as per davxy + SUITE_STRING = P256Params.SUITE_ID DST = b"" + ENDIAN = "little" if e2c_variant == E2C_Variant.TAI else P256Params.ENDIAN super().__init__( PRIME_FIELD=P256Params.PRIME_FIELD, @@ -101,9 +104,12 @@ def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: Requires_Isogeny=P256Params.Requires_Isogeny, Isogeny_Coeffs=P256Params.Isogeny_Coeffs, UNCOMPRESSED=P256Params.UNCOMPRESSED, - ENDIAN=P256Params.ENDIAN, + ENDIAN=ENDIAN, POINT_LEN=P256Params.POINT_LEN, CHALLENGE_LENGTH=P256Params.CHALLENGE_LENGTH, + SUITE_ID=P256Params.SUITE_ID if e2c_variant == E2C_Variant.TAI else None, + TRANSCRIPT_HASH=P256Params.TRANSCRIPT_HASH, + HASH_TO_CURVE=P256Params.HASH_TO_CURVE if e2c_variant == E2C_Variant.TAI else None, ) @@ -133,6 +139,69 @@ def identity_point(cls) -> None: # The identity point return None + def point_to_string(self, compressed: bool = True) -> bytes: + if getattr(self.curve, "HASH_TO_CURVE", None) != "tai": + return super().point_to_string(compressed) + + if self.x is None and self.y is None: + return bytes([0] * 32 + [0x40]) + + p = self.curve.PRIME_FIELD + x_bytes = int(self.x).to_bytes(32, "little") + flag = 0x80 if int(self.y) > (-int(self.y) % p) else 0x00 + return x_bytes + bytes([flag]) + + @classmethod + def string_to_point(cls, data: str | bytes): + if isinstance(data, str): + data = bytes.fromhex(data) + + if getattr(cls.curve, "HASH_TO_CURVE", None) != "tai": + return super().string_to_point(data) + elif len(data) == 33 and data[0] in (0x02, 0x03): + # Canonical SW encodings put flags in the final byte, so external + # vectors can coincidentally start with SEC1 marker bytes. + try: + return cls._string_to_canonical_point(data) + except ValueError: + return super().string_to_point(data) + + if len(data) != 33: + raise ValueError(f"Invalid compressed point length: expected 33, got {len(data)}") + return cls._string_to_canonical_point(data) + + @classmethod + def _string_to_canonical_point(cls, data: bytes): + flag = data[-1] + is_negative = (flag >> 7) & 1 + is_infinity = (flag >> 6) & 1 + if flag & 0x3F: + raise ValueError("Invalid canonical point flags") + if is_infinity: + if is_negative or any(data[:-1]): + raise ValueError("Invalid infinity encoding") + return cls.identity() + + x = int.from_bytes(data[:-1], "little") + if x >= cls.curve.PRIME_FIELD: + raise ValueError("x-coordinate is not in field") + y_candidates = cls._y_recover(x) + if y_candidates is None: + raise ValueError("INVALID point") + y, y_neg = y_candidates + return cls(x, y_neg if is_negative else y) + + @classmethod + def _y_recover(cls, x: int) -> tuple[int, int] | None: + p = cls.curve.PRIME_FIELD + y_square = (pow(x, 3, p) + cls.curve.WeierstrassA * x + cls.curve.WeierstrassB) % p + try: + y = cls.curve.mod_sqrt(y_square) + except ValueError: + return None + neg_y = -y % p + return (y, neg_y) if y <= neg_y else (neg_y, y) + P256_RO = CurveVariant( name="P256_RO", diff --git a/dot_ring/curve/twisted_edwards/te_affine_point.py b/dot_ring/curve/twisted_edwards/te_affine_point.py index e8f7f70..c1dc4ff 100644 --- a/dot_ring/curve/twisted_edwards/te_affine_point.py +++ b/dot_ring/curve/twisted_edwards/te_affine_point.py @@ -213,6 +213,11 @@ def encode_to_curve( if not isinstance(salt, bytes): salt = bytes.fromhex(salt) + if cls.curve.HASH_TO_CURVE == "tai": + from dot_ring.vrf.transcript import hash_to_curve_tai + + return cast(Self, hash_to_curve_tai(cls, salt + alpha_string)) + if cls.curve.E2C in [E2C_Variant.ELL2, E2C_Variant.ELL2_NU]: if cls.curve.E2C.value.endswith("_NU_"): return cls.encode_to_curve_hash2_suite_nu(alpha_string, salt, General_Check) diff --git a/dot_ring/keygen.py b/dot_ring/keygen.py index 3f96c58..d851bb1 100644 --- a/dot_ring/keygen.py +++ b/dot_ring/keygen.py @@ -1,33 +1,15 @@ from __future__ import annotations -from typing import Literal, cast - from dot_ring.curve.curve import CurveVariant from dot_ring.curve.specs.bandersnatch import Bandersnatch -from dot_ring.ring_proof.helpers import Helpers -from dot_ring.vrf.ietf.ietf import IETF_VRF - - -def _hash_seed(curve: CurveVariant, seed: bytes, counter: int) -> bytes: - hasher = curve.curve.H_A() - hasher.update(seed) - if counter: - hasher.update(bytes([counter])) - if curve.curve._uses_xof(): - length = curve.curve._default_xof_len() - return cast(bytes, hasher.digest(length)) - return cast(bytes, hasher.digest()) +from dot_ring.vrf.ietf import TinyVRF +from dot_ring.vrf.transcript import scalar_encode, secret_from_seed_scalar def secret_from_seed(seed: bytes, curve: CurveVariant = Bandersnatch) -> tuple[bytes, bytes]: """ Deterministically derive a secret scalar and public key from a seed. - Mirrors ark-vrf's Secret::from_seed: - - Hash seed with curve's hash function - - Interpret hash output as little-endian integer, reduce modulo curve order - - If zero, append a counter byte and rehash - Returns: (public_key_bytes, secret_scalar_bytes) """ @@ -36,24 +18,7 @@ def secret_from_seed(seed: bytes, curve: CurveVariant = Bandersnatch) -> tuple[b if not isinstance(curve, CurveVariant): raise TypeError("curve must be a CurveVariant") - seed_bytes = bytes(seed) - order = curve.curve.ORDER - scalar_len = (order.bit_length() + 7) // 8 - - counter = 0 - while True: - digest = _hash_seed(curve, seed_bytes, counter) - sk_int = int.from_bytes(digest, "little") % order - if sk_int != 0: - break - counter = (counter + 1) & 0xFF - if counter == 0: - raise RuntimeError("failed to derive non-zero secret scalar") - - sk_bytes = Helpers.int_to_str( - sk_int, - cast(Literal["little", "big"], curve.curve.ENDIAN), - scalar_len, - ) - pk_bytes = IETF_VRF[curve].get_public_key(sk_bytes) + sk_int = secret_from_seed_scalar(curve, bytes(seed)) + sk_bytes = scalar_encode(curve, sk_int) + pk_bytes = TinyVRF[curve].get_public_key(sk_bytes) return pk_bytes, sk_bytes diff --git a/dot_ring/vrf/ark_transcript.py b/dot_ring/vrf/ark_transcript.py new file mode 100644 index 0000000..4c44eab --- /dev/null +++ b/dot_ring/vrf/ark_transcript.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +import hashlib + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint + + +def _scalar_len(curve: CurveVariant) -> int: + return (curve.curve.ORDER.bit_length() + 7) // 8 + + +class ArkTranscript: + def __init__(self, label: bytes, hash_name: str = "sha512") -> None: + self.hash_name = hash_name + self._absorbed = bytearray(label) + self._seed: bytes | None = None + self._squeeze_offset = 0 + + def clone(self) -> ArkTranscript: + other = ArkTranscript(b"", self.hash_name) + other._absorbed = bytearray(self._absorbed) + other._seed = self._seed + other._squeeze_offset = self._squeeze_offset + return other + + def absorb_raw(self, data: bytes) -> None: + if self._seed is not None: + raise ValueError("cannot absorb after squeeze") + self._absorbed.extend(data) + + def absorb_point(self, point: CurvePoint) -> None: + self.absorb_raw(point.point_to_string()) + + def absorb_scalar(self, curve: CurveVariant, value: int) -> None: + self.absorb_raw(int(value % curve.curve.ORDER).to_bytes(_scalar_len(curve), "little")) + + def squeeze_raw(self, size: int) -> bytes: + if self.hash_name == "shake128": + return self._squeeze_shake128(size) + return self._squeeze_digest_xof(size) + + def _digest(self, data: bytes) -> bytes: + if self.hash_name == "sha256": + return hashlib.sha256(data).digest() + if self.hash_name == "sha512": + return hashlib.sha512(data).digest() + raise ValueError(f"unsupported transcript hash {self.hash_name!r}") + + def _squeeze_digest_xof(self, size: int) -> bytes: + if self._seed is None: + self._seed = self._digest(bytes(self._absorbed)) + start = self._squeeze_offset + end = start + size + block_size = len(self._seed) + first_block = start // block_size + last_block = (end + block_size - 1) // block_size + stream = b"".join( + self._digest(self._seed + counter.to_bytes(8, "little")) + for counter in range(first_block, last_block) + ) + self._squeeze_offset = end + return stream[start % block_size : start % block_size + size] + + def _squeeze_shake128(self, size: int) -> bytes: + if self._seed is None: + self._seed = bytes(self._absorbed) + start = self._squeeze_offset + end = start + size + xof = hashlib.shake_128() + xof.update(self._seed) + out = xof.digest(end) + self._squeeze_offset = end + return out[start:end] diff --git a/dot_ring/vrf/delinearize.py b/dot_ring/vrf/delinearize.py new file mode 100644 index 0000000..699d5c9 --- /dev/null +++ b/dot_ring/vrf/delinearize.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from dot_ring.curve.curve import CurveVariant + +from .ark_transcript import ArkTranscript +from .domain import DomSep +from .transcript_constants import CHALLENGE_LEN + + +class DelinearizeScalars: + def __init__(self, curve: CurveVariant, transcript: ArkTranscript) -> None: + self.curve = curve + self.transcript = transcript.clone() + self.transcript.absorb_raw(bytes([DomSep.DELINEARIZE])) + self.first = True + + def next(self) -> int: + if self.first: + self.first = False + return 1 + return int.from_bytes(self.transcript.squeeze_raw(CHALLENGE_LEN), "little") % self.curve.curve.ORDER + + def take(self, n: int) -> list[int]: + return [self.next() for _ in range(n)] diff --git a/dot_ring/vrf/domain.py b/dot_ring/vrf/domain.py new file mode 100644 index 0000000..5cfbee2 --- /dev/null +++ b/dot_ring/vrf/domain.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from enum import IntEnum + + +class DomSep(IntEnum): + TINY_VRF = 0x00 + THIN_VRF = 0x01 + PEDERSEN_VRF = 0x02 + NONCE_EXPAND = 0x10 + NONCE = 0x11 + PEDERSEN_BLINDING = 0x12 + POINT_TO_HASH = 0x20 + DELINEARIZE = 0x30 + CHALLENGE = 0x40 + BATCH_VERIFY = 0x50 + HASH_TO_CURVE = 0x60 diff --git a/dot_ring/vrf/transcript.py b/dot_ring/vrf/transcript.py new file mode 100644 index 0000000..35f3b7a --- /dev/null +++ b/dot_ring/vrf/transcript.py @@ -0,0 +1,169 @@ +from __future__ import annotations + +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.ark_transcript import ArkTranscript +from dot_ring.vrf.delinearize import DelinearizeScalars +from dot_ring.vrf.domain import DomSep +from dot_ring.vrf.transcript_constants import CHALLENGE_LEN, SECURITY_PARAMETER +from dot_ring.vrf.vrf_io import VrfIo + + +def suite_id(curve: CurveVariant) -> bytes: + return curve.curve.SUITE_ID or curve.curve.SUITE_STRING + + +def scalar_len(curve: CurveVariant) -> int: + return (curve.curve.ORDER.bit_length() + 7) // 8 + + +def scalar_encode(curve: CurveVariant, value: int) -> bytes: + return int(value % curve.curve.ORDER).to_bytes(scalar_len(curve), "little") + + +def scalar_decode(curve: CurveVariant, value: bytes) -> int: + return int.from_bytes(value, "little") + + +def expanded_scalar_len(curve: CurveVariant, sec_bits: int = SECURITY_PARAMETER) -> int: + return (curve.curve.ORDER.bit_length() + sec_bits + 7) // 8 + + +def transcript_for(curve: CurveVariant) -> ArkTranscript: + return ArkTranscript(suite_id(curve), curve.curve.TRANSCRIPT_HASH) + + +def nonce_scalar(curve: CurveVariant, transcript: ArkTranscript) -> int: + return int.from_bytes(transcript.squeeze_raw(expanded_scalar_len(curve)), "little") % curve.curve.ORDER + + +def challenge_scalar(curve: CurveVariant, transcript: ArkTranscript) -> int: + return int.from_bytes(transcript.squeeze_raw(CHALLENGE_LEN), "little") % curve.curve.ORDER + + +def nonce(curve: CurveVariant, secret_scalar: int, transcript: ArkTranscript | None = None) -> int: + t = transcript.clone() if transcript is not None else transcript_for(curve) + + t_exp = t.clone() + t_exp.absorb_raw(bytes([DomSep.NONCE_EXPAND])) + t_exp.absorb_scalar(curve, secret_scalar) + secret_hash = t_exp.squeeze_raw(64) + + t.absorb_raw(bytes([DomSep.NONCE])) + t.absorb_raw(secret_hash) + return nonce_scalar(curve, t) + + +def challenge(curve: CurveVariant, points: list[CurvePoint], transcript: ArkTranscript | None = None) -> int: + t = transcript.clone() if transcript is not None else transcript_for(curve) + t.absorb_raw(bytes([DomSep.CHALLENGE])) + for point in points: + t.absorb_point(point) + return challenge_scalar(curve, t) + + +def point_to_hash(curve: CurveVariant, point: CurvePoint, size: int = 32) -> bytes: + t = transcript_for(curve) + t.absorb_raw(bytes([DomSep.POINT_TO_HASH])) + t.absorb_point(point) + return t.squeeze_raw(size) + + +def vrf_transcript( + curve: CurveVariant, + scheme: DomSep, + ios: list[VrfIo], + ad: bytes, +) -> tuple[ArkTranscript, VrfIo]: + t, scalars = vrf_transcript_scalars(curve, scheme, ios, ad) + if not ios: + zero = curve.point.identity() + return t, VrfIo(zero, zero) + if len(ios) == 1: + return t, ios[0] + + input_acc = curve.point.identity() + output_acc = curve.point.identity() + for io in ios: + z = scalars.next() + input_acc = input_acc + io.input * z + output_acc = output_acc + io.output * z + return t, VrfIo(input_acc, output_acc) + + +def vrf_transcript_scalars( + curve: CurveVariant, + scheme: DomSep, + ios: list[VrfIo], + ad: bytes, +) -> tuple[ArkTranscript, DelinearizeScalars]: + t = transcript_for(curve) + t.absorb_raw(bytes([scheme])) + t.absorb_raw(len(ios).to_bytes(8, "little")) + for io in ios: + t.absorb_raw(io.to_bytes()) + t.absorb_raw(len(ad).to_bytes(8, "little")) + t.absorb_raw(ad) + return t, DelinearizeScalars(curve, t) + + +def schnorr_ios(curve: CurveVariant, public_key: CurvePoint, ios: list[VrfIo]) -> list[VrfIo]: + generator = curve.point.generator_point() + return [VrfIo(generator, public_key), *ios] + + +def secret_from_seed_scalar(curve: CurveVariant, seed: bytes) -> int: + if len(seed) != 32: + raise ValueError("seed must be exactly 32 bytes") + base_secret = int.from_bytes(seed, "little") % curve.curve.ORDER + counter = 0 + while True: + t = transcript_for(curve) + t.absorb_raw(seed) + if counter: + t.absorb_raw(bytes([counter])) + secret = nonce(curve, base_secret, t) + if secret != 0: + return secret + counter += 1 + if counter > 255: + raise RuntimeError("failed to derive non-zero secret scalar") + + +def hash_to_curve_tai(point_cls: Any, data: bytes) -> CurvePoint: + curve = point_cls.curve + field_len = (curve.PRIME_FIELD.bit_length() + 7) // 8 + + prefix = ArkTranscript(curve.SUITE_ID or curve.SUITE_STRING, curve.TRANSCRIPT_HASH) + prefix.absorb_raw(bytes([DomSep.HASH_TO_CURVE])) + prefix.absorb_raw(len(data).to_bytes(8, "little")) + prefix.absorb_raw(data) + + for counter in range(256): + t = prefix.clone() + t.absorb_raw(bytes([counter])) + candidate = bytearray(t.squeeze_raw(field_len)) + if hasattr(curve, "WeierstrassA"): + shave = field_len * 8 - curve.PRIME_FIELD.bit_length() + if shave: + candidate[-1] &= (1 << (8 - shave)) - 1 + candidate = bytearray(candidate + b"\x80") + else: + sign = candidate[-1] & 0x80 + shave = field_len * 8 - curve.PRIME_FIELD.bit_length() + if shave: + candidate[-1] &= (1 << (8 - shave)) - 1 + candidate[-1] |= sign + try: + point = point_cls.string_to_point(bytes(candidate)) + except ValueError: + continue + if isinstance(point, str): + continue + if point.curve.COFACTOR > 1: + point = point * point.curve.COFACTOR + if not point.is_identity(): + return point + raise ValueError("hash_to_curve_tai failed") diff --git a/dot_ring/vrf/transcript_constants.py b/dot_ring/vrf/transcript_constants.py new file mode 100644 index 0000000..49a1889 --- /dev/null +++ b/dot_ring/vrf/transcript_constants.py @@ -0,0 +1,2 @@ +SECURITY_PARAMETER = 128 +CHALLENGE_LEN = SECURITY_PARAMETER // 8 diff --git a/dot_ring/vrf/vrf.py b/dot_ring/vrf/vrf.py index f668e38..dd94b83 100644 --- a/dot_ring/vrf/vrf.py +++ b/dot_ring/vrf/vrf.py @@ -2,39 +2,14 @@ import hashlib import hmac -from abc import abstractmethod -from typing import Any, ClassVar, Generic, Literal, Protocol, Self, TypeVar, cast +from typing import Any, ClassVar, Generic, Literal, Self, TypeVar, cast from ..curve.curve import CurveVariant -from ..curve.point import CurvePoint, Point +from ..curve.point import CurvePoint from ..ring_proof.helpers import Helpers +from .vrf_protocol import VRFProtocol C = TypeVar("C", bound=CurveVariant) -P = TypeVar("P", bound=Point) - - -class VRFProtocol(Protocol[C, P]): - """Protocol defining the interface for VRF implementations.""" - - curve: C - point_type: type[P] - - @abstractmethod - def proof(self, alpha: bytes, secret_key: int, additional_data: bytes) -> tuple[P, tuple[int, int]]: - """Generate VRF proof.""" - ... - - @abstractmethod - def verify( - self, - public_key: P, - input_point: P, - additional_data: bytes, - output_point: P, - proof: tuple[int, int], - ) -> bool: - """Verify VRF proof.""" - ... class VRF(Generic[C]): @@ -42,12 +17,12 @@ class VRF(Generic[C]): Base VRF (Verifiable Random Function) implementation. This class provides the core functionality for VRF operations, - following the IETF specification. + following the shared dot-ring VRF interface. Usage with subscript syntax: >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.ietf.ietf import IETF_VRF - >>> proof = IETF_VRF[Bandersnatch].prove(alpha, secret_key, additional_data) + >>> from dot_ring.vrf.ietf import TinyVRF + >>> proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, additional_data) """ cv: ClassVar[CurveVariant] @@ -258,7 +233,9 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: @classmethod def get_public_key(cls, secret_key: bytes) -> bytes: """Take the Secret_Key and return Public Key""" - secret_key_int = Helpers.str_to_int(secret_key, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) % cls.cv.curve.ORDER + from dot_ring.vrf.transcript import scalar_decode + + secret_key_int = scalar_decode(cls.cv, secret_key) # Create generator point generator = cls.cv.point.generator_point() public_key: CurvePoint = cast(Any, generator) * secret_key_int diff --git a/dot_ring/vrf/vrf_io.py b/dot_ring/vrf/vrf_io.py new file mode 100644 index 0000000..89f8bad --- /dev/null +++ b/dot_ring/vrf/vrf_io.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from dot_ring.curve.point import CurvePoint + + +@dataclass(frozen=True) +class VrfIo: + input: CurvePoint + output: CurvePoint + + def to_bytes(self) -> bytes: + return self.input.point_to_string() + self.output.point_to_string() diff --git a/dot_ring/vrf/vrf_protocol.py b/dot_ring/vrf/vrf_protocol.py new file mode 100644 index 0000000..1e95e72 --- /dev/null +++ b/dot_ring/vrf/vrf_protocol.py @@ -0,0 +1,34 @@ +from __future__ import annotations + +from abc import abstractmethod +from typing import Protocol, TypeVar + +from ..curve.curve import CurveVariant +from ..curve.point import Point + +C = TypeVar("C", bound=CurveVariant) +P = TypeVar("P", bound=Point) + + +class VRFProtocol(Protocol[C, P]): + """Protocol defining the interface for VRF implementations.""" + + curve: C + point_type: type[P] + + @abstractmethod + def proof(self, alpha: bytes, secret_key: int, additional_data: bytes) -> tuple[P, tuple[int, int]]: + """Generate VRF proof.""" + ... + + @abstractmethod + def verify( + self, + public_key: P, + input_point: P, + additional_data: bytes, + output_point: P, + proof: tuple[int, int], + ) -> bool: + """Verify VRF proof.""" + ... From d91e71e74f7e66a2d75682985137aa53b30ed872 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:44:32 +0530 Subject: [PATCH 02/33] feat: implement Tiny and Thin VRF schemes --- dot_ring/vrf/ietf/__init__.py | 3 + dot_ring/vrf/ietf/ietf.py | 155 +---------------------- dot_ring/vrf/ietf/ietf_vrf.py | 7 + dot_ring/vrf/ietf/thin_batch_item.py | 14 ++ dot_ring/vrf/ietf/thin_batch_verifier.py | 54 ++++++++ dot_ring/vrf/ietf/thin_vrf.py | 97 ++++++++++++++ dot_ring/vrf/ietf/tiny_vrf.py | 125 ++++++++++++++++++ 7 files changed, 306 insertions(+), 149 deletions(-) create mode 100644 dot_ring/vrf/ietf/ietf_vrf.py create mode 100644 dot_ring/vrf/ietf/thin_batch_item.py create mode 100644 dot_ring/vrf/ietf/thin_batch_verifier.py create mode 100644 dot_ring/vrf/ietf/thin_vrf.py create mode 100644 dot_ring/vrf/ietf/tiny_vrf.py diff --git a/dot_ring/vrf/ietf/__init__.py b/dot_ring/vrf/ietf/__init__.py index e69de29..9c88d21 100644 --- a/dot_ring/vrf/ietf/__init__.py +++ b/dot_ring/vrf/ietf/__init__.py @@ -0,0 +1,3 @@ +from .ietf import IETF_VRF, ThinBatchItem, ThinBatchVerifier, ThinVRF, TinyVRF + +__all__ = ["IETF_VRF", "ThinBatchItem", "ThinBatchVerifier", "ThinVRF", "TinyVRF"] diff --git a/dot_ring/vrf/ietf/ietf.py b/dot_ring/vrf/ietf/ietf.py index c9a87ab..8d7f25e 100644 --- a/dot_ring/vrf/ietf/ietf.py +++ b/dot_ring/vrf/ietf/ietf.py @@ -1,153 +1,10 @@ from __future__ import annotations -from dataclasses import dataclass -from typing import Any, Literal, cast +from .thin_batch_item import ThinBatchItem +from .thin_batch_verifier import ThinBatchVerifier +from .thin_vrf import ThinVRF +from .tiny_vrf import TinyVRF -from ...curve.point import CurvePoint -from ...ring_proof.helpers import Helpers -from ..vrf import VRF +IETF_VRF = TinyVRF - -@dataclass -class IETF_VRF(VRF[Any]): - """ - IETF specification compliant VRF implementation. - - This implementation follows the IETF draft specification - for VRFs using the Bandersnatch curve. - - Usage: - >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.ietf.ietf import IETF_VRF - >>> proof: IETF_VRF = IETF_VRF[Bandersnatch].prove(alpha, secret_key, additional_data) - >>> verified = proof.verify(public_key, input_point, additional_data) - """ - - output_point: CurvePoint - c: int - s: int - - @classmethod - def from_bytes(cls, proof_bytes: bytes) -> IETF_VRF: - """ - Deserialize proof from bytes. - - Args: - proof_bytes: Bytes representation of the proof - Returns: - IETF_VRF: Deserialized proof object - """ - challenge_len = cls.cv.curve.CHALLENGE_LENGTH - output_point_end = cls.cv.curve.POINT_LEN - # Calculate positions in the proof - if cls.cv.curve.UNCOMPRESSED: - output_point_end *= 2 - - c_end = output_point_end + challenge_len - # Extract components - output_point = cls.cv.point.string_to_point(proof_bytes[:output_point_end]) - if isinstance(output_point, str): - raise ValueError("Invalid output point") - c = Helpers.str_to_int( - proof_bytes[output_point_end:c_end], - cast(Literal["little", "big"], cls.cv.curve.ENDIAN), - ) - s = Helpers.str_to_int(proof_bytes[c_end:], cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) - if s >= cls.cv.curve.ORDER: - raise ValueError("Response scalar s is not less than the curve order") - return cls(output_point, c, s) - - def to_bytes(self) -> bytes: - """ - Serialize proof to bytes. - - Returns: - bytes: Bytes representation of the proof - """ - scalar_len = (self.cv.curve.PRIME_FIELD.bit_length() + 7) // 8 - proof = ( - self.output_point.point_to_string() - + Helpers.int_to_str( - self.c, - cast(Literal["little", "big"], self.cv.curve.ENDIAN), - self.cv.curve.CHALLENGE_LENGTH, - ) - + Helpers.int_to_str(self.s, cast(Literal["little", "big"], self.cv.curve.ENDIAN), scalar_len) - ) - return proof - - @classmethod - def prove( - cls, - alpha: bytes, - secret_key: bytes, - additional_data: bytes, - salt: bytes = b"", - ) -> IETF_VRF: - """ - Generate IETF VRF proof. - - Args: - alpha: Input message - secret_key: Secret key - additional_data: Additional data for challenge - salt: Optional salt for encoding - - Returns: - Tuple[BandersnatchPoint, Tuple[int, int]]: (output_point, (c, s)) - """ - secret_key_int = Helpers.str_to_int(secret_key, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) % cls.cv.curve.ORDER - - # Create generator point - generator = cls.cv.point.generator_point() - # Encode input to curve point - input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) - # Compute output point and public key - output_point = input_point * secret_key_int - public_key = generator * secret_key_int - - if cls.cv.point.__name__ == "P256PointVariant": - input_point_octet = input_point.point_to_string() - nonce = cls.ecvrf_nonce_rfc6979(secret_key_int, input_point_octet) - else: - # Generate nonce and compute proof points - nonce = cls.generate_nonce(secret_key_int, input_point) - - U = generator * nonce - V = input_point * nonce - - # Generate challenge - c = cls.challenge([public_key, input_point, output_point, U, V], additional_data) - s = (nonce + (c * secret_key_int)) % cls.cv.curve.ORDER - return cls(output_point, c, s) - - def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: - """ - Verify IETF VRF proof. - - Args: - public_key: Public key point - input_point: Input point - additional_data: Additional data used in proof - proof: Proof bytes - salt: Optional salt for encoding - - Returns: - bool: True if proof is valid - """ - input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) - public_key_pt = self.cv.point.string_to_point(public_key) - if isinstance(public_key_pt, str): - raise ValueError("Invalid public key") - generator = self.cv.point.generator_point() - n = self.cv.curve.ORDER - neg_c = (-self.c) % n - - # MSM for U = G*s + pk*(-c) and V = H*s + O*(-c) - U = self.cv.point.msm([generator, public_key_pt], [self.s, neg_c]) - V = self.cv.point.msm([input_point, self.output_point], [self.s, neg_c]) - - # Verify challenge - expected_c = self.challenge([public_key_pt, input_point, self.output_point, U, V], additional_data) - - return self.c == expected_c +__all__ = ["IETF_VRF", "ThinBatchItem", "ThinBatchVerifier", "ThinVRF", "TinyVRF"] diff --git a/dot_ring/vrf/ietf/ietf_vrf.py b/dot_ring/vrf/ietf/ietf_vrf.py new file mode 100644 index 0000000..2e5b546 --- /dev/null +++ b/dot_ring/vrf/ietf/ietf_vrf.py @@ -0,0 +1,7 @@ +from __future__ import annotations + +from .tiny_vrf import TinyVRF + +IETF_VRF = TinyVRF + +__all__ = ["IETF_VRF"] diff --git a/dot_ring/vrf/ietf/thin_batch_item.py b/dot_ring/vrf/ietf/thin_batch_item.py new file mode 100644 index 0000000..618c85a --- /dev/null +++ b/dot_ring/vrf/ietf/thin_batch_item.py @@ -0,0 +1,14 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import VrfIo + + +@dataclass +class ThinBatchItem: + c: int + merged: VrfIo + r: CurvePoint + s: int diff --git a/dot_ring/vrf/ietf/thin_batch_verifier.py b/dot_ring/vrf/ietf/thin_batch_verifier.py new file mode 100644 index 0000000..9348f6d --- /dev/null +++ b/dot_ring/vrf/ietf/thin_batch_verifier.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +import secrets +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, schnorr_ios, vrf_transcript + +from .thin_batch_item import ThinBatchItem +from .thin_vrf import ThinVRF + + +def _batch_coefficients(count: int, order: int) -> list[int]: + if count == 0: + return [] + coefficients = [1] + for _ in range(count - 1): + coefficient = 0 + while coefficient == 0: + coefficient = secrets.randbelow(order) + coefficients.append(coefficient) + return coefficients + + +class ThinBatchVerifier: + def __init__(self, curve: CurveVariant): + self.cv = curve + self.items: list[ThinBatchItem] = [] + + @classmethod + def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[ThinBatchVerifier] | Any: + if not isinstance(curve_variant, CurveVariant): + return cls + + class _SpecializedThinBatchVerifier(cls): + def __init__(self) -> None: + super().__init__(curve_variant) + + _SpecializedThinBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" + return _SpecializedThinBatchVerifier + + def push(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes, proof: ThinVRF) -> None: + transcript, merged = vrf_transcript(self.cv, DomSep.THIN_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) + c = challenge(self.cv, [proof.r], transcript) + self.items.append(ThinBatchItem(c, merged, proof.r, proof.s)) + + def verify(self) -> bool: + points = [] + scalars = [] + for coefficient, item in zip(_batch_coefficients(len(self.items), self.cv.curve.ORDER), self.items, strict=False): + points.extend([item.merged.input, item.merged.output, item.r]) + scalars.extend([coefficient * item.s, -coefficient * item.c, -coefficient]) + return self.cv.point.msm(points, scalars).is_identity() diff --git a/dot_ring/vrf/ietf/thin_vrf.py b/dot_ring/vrf/ietf/thin_vrf.py new file mode 100644 index 0000000..e662ee9 --- /dev/null +++ b/dot_ring/vrf/ietf/thin_vrf.py @@ -0,0 +1,97 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, cast + +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import ( + DomSep, + VrfIo, + challenge, + nonce, + point_to_hash, + scalar_decode, + scalar_encode, + scalar_len, + schnorr_ios, + vrf_transcript, +) + +from ..vrf import VRF + + +@dataclass +class ThinVRF(VRF[Any]): + """Batch-friendly VRF-AD proof storing the nonce commitment.""" + + output_point: CurvePoint + r: CurvePoint + s: int + + @classmethod + def from_bytes(cls, proof_bytes: bytes) -> ThinVRF: + point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + scalar_size = scalar_len(cls.cv) + expected = 2 * point_len + scalar_size + if len(proof_bytes) != expected: + raise ValueError(f"invalid Thin VRF proof length: expected {expected}, got {len(proof_bytes)}") + output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) + r = cls.cv.point.string_to_point(proof_bytes[point_len : 2 * point_len]) + if isinstance(output_point, str) or isinstance(r, str): + raise ValueError("Invalid point in proof") + s = scalar_decode(cls.cv, proof_bytes[2 * point_len :]) + if s >= cls.cv.curve.ORDER: + raise ValueError("Response scalar s is not less than the curve order") + return cls(output_point, r, s) + + def to_bytes(self) -> bytes: + return self.output_point.point_to_string() + self.r.point_to_string() + scalar_encode(self.cv, self.s) + + @classmethod + def prove( + cls, + alpha: bytes, + secret_key: bytes, + additional_data: bytes, + salt: bytes = b"", + ) -> ThinVRF: + secret_scalar = scalar_decode(cls.cv, secret_key) + public_key = cls.cv.point.generator_point() * secret_scalar + input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) + output_point = input_point * secret_scalar + return cls.prove_ios([VrfIo(input_point, output_point)], secret_scalar, public_key, additional_data) + + @classmethod + def prove_ios( + cls, + ios: list[VrfIo], + secret_scalar: int, + public_key: CurvePoint, + additional_data: bytes, + ) -> ThinVRF: + transcript, merged = vrf_transcript(cls.cv, DomSep.THIN_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) + k = nonce(cls.cv, secret_scalar, transcript) + r = merged.input * k + c = challenge(cls.cv, [r], transcript) + s = (k + c * secret_scalar) % cls.cv.curve.ORDER + output_point = ios[0].output if len(ios) == 1 else merged.output + return cls(output_point, r, s) + + def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: + input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) + public_key_pt = self.cv.point.string_to_point(public_key) + if isinstance(public_key_pt, str): + raise ValueError("Invalid public key") + return self.verify_ios(public_key_pt, [VrfIo(input_point, self.output_point)], additional_data) + + def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: + transcript, merged = vrf_transcript(self.cv, DomSep.THIN_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) + c = challenge(self.cv, [self.r], transcript) + lhs = self.cv.point.msm([merged.input, merged.output], [self.s, -c]) + return lhs == self.r + + @classmethod + def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: + if mul_cofactor: + gamma = gamma * cls.cv.curve.COFACTOR + return point_to_hash(cls.cv, gamma) diff --git a/dot_ring/vrf/ietf/tiny_vrf.py b/dot_ring/vrf/ietf/tiny_vrf.py new file mode 100644 index 0000000..fd86e8a --- /dev/null +++ b/dot_ring/vrf/ietf/tiny_vrf.py @@ -0,0 +1,125 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, cast + +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import ( + CHALLENGE_LEN, + DomSep, + VrfIo, + challenge, + nonce, + point_to_hash, + scalar_decode, + scalar_encode, + scalar_len, + schnorr_ios, + vrf_transcript, +) + +from ..vrf import VRF + + +@dataclass +class TinyVRF(VRF[Any]): + """ + Tiny VRF proof. + + Compact VRF-AD proof using transcript-derived challenges and nonces, + additional data, and multi-input delinearization. + + Usage: + >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch + >>> from dot_ring.vrf.ietf.ietf import TinyVRF + >>> proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, additional_data) + >>> verified = proof.verify(public_key, input_point, additional_data) + """ + + output_point: CurvePoint + c: int + s: int + + @classmethod + def from_bytes(cls, proof_bytes: bytes) -> TinyVRF: + point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + scalar_size = scalar_len(cls.cv) + expected = point_len + CHALLENGE_LEN + scalar_size + if len(proof_bytes) != expected: + raise ValueError(f"invalid Tiny VRF proof length: expected {expected}, got {len(proof_bytes)}") + output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) + if isinstance(output_point, str): + raise ValueError("Invalid output point") + c = int.from_bytes(proof_bytes[point_len : point_len + CHALLENGE_LEN], "little") % cls.cv.curve.ORDER + s = scalar_decode(cls.cv, proof_bytes[point_len + CHALLENGE_LEN :]) + if s >= cls.cv.curve.ORDER: + raise ValueError("Response scalar s is not less than the curve order") + return cls(output_point, c, s) + + def to_bytes(self) -> bytes: + return ( + self.output_point.point_to_string() + + self.c.to_bytes(CHALLENGE_LEN, "little") + + scalar_encode(self.cv, self.s) + ) + + @classmethod + def _io_from_alpha(cls, alpha: bytes, secret_scalar: int, salt: bytes = b"") -> VrfIo: + input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) + output_point = input_point * secret_scalar + return VrfIo(input_point, output_point) + + @classmethod + def prove( + cls, + alpha: bytes, + secret_key: bytes, + additional_data: bytes, + salt: bytes = b"", + ) -> TinyVRF: + secret_scalar = scalar_decode(cls.cv, secret_key) + public_key = cls.cv.point.generator_point() * secret_scalar + io = cls._io_from_alpha(alpha, secret_scalar, salt) + return cls.prove_ios([io], secret_scalar, public_key, additional_data) + + @classmethod + def prove_ios( + cls, + ios: list[VrfIo], + secret_scalar: int, + public_key: CurvePoint, + additional_data: bytes, + ) -> TinyVRF: + transcript, merged = vrf_transcript(cls.cv, DomSep.TINY_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) + k = cls.generate_nonce(secret_scalar, transcript) + r = merged.input * k + c = challenge(cls.cv, [r], transcript) + s = (k + c * secret_scalar) % cls.cv.curve.ORDER + output_point = ios[0].output if len(ios) == 1 else merged.output + return cls(output_point, c, s) + + @classmethod + def generate_nonce(cls, secret_scalar: int, transcript: Any) -> int: + if not hasattr(transcript, "clone"): + return super().generate_nonce(secret_scalar, transcript) + return nonce(cls.cv, secret_scalar, transcript) + + def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: + input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) + public_key_pt = self.cv.point.string_to_point(public_key) + if isinstance(public_key_pt, str): + raise ValueError("Invalid public key") + io = VrfIo(input_point, self.output_point) + return self.verify_ios(public_key_pt, [io], additional_data) + + def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: + transcript, merged = vrf_transcript(self.cv, DomSep.TINY_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) + r = self.cv.point.msm([merged.input, merged.output], [self.s, -self.c]) + expected_c = challenge(self.cv, [r], transcript) + return self.c == expected_c + + @classmethod + def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: + if mul_cofactor: + gamma = gamma * cls.cv.curve.COFACTOR + return point_to_hash(cls.cv, gamma) From 8ede12da21fa22edf6b9fa334c4e6644f0f1850a Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:45:05 +0530 Subject: [PATCH 03/33] feat: update Pedersen VRF proofs and batching --- dot_ring/vrf/pedersen/__init__.py | 3 + dot_ring/vrf/pedersen/pedersen.py | 229 +----------------- dot_ring/vrf/pedersen/pedersen_batch_item.py | 17 ++ .../vrf/pedersen/pedersen_batch_verifier.py | 75 ++++++ dot_ring/vrf/pedersen/pedersen_vrf.py | 187 ++++++++++++++ 5 files changed, 286 insertions(+), 225 deletions(-) create mode 100644 dot_ring/vrf/pedersen/pedersen_batch_item.py create mode 100644 dot_ring/vrf/pedersen/pedersen_batch_verifier.py create mode 100644 dot_ring/vrf/pedersen/pedersen_vrf.py diff --git a/dot_ring/vrf/pedersen/__init__.py b/dot_ring/vrf/pedersen/__init__.py index e69de29..ea89805 100644 --- a/dot_ring/vrf/pedersen/__init__.py +++ b/dot_ring/vrf/pedersen/__init__.py @@ -0,0 +1,3 @@ +from .pedersen import PedersenBatchItem, PedersenBatchVerifier, PedersenVRF + +__all__ = ["PedersenBatchItem", "PedersenBatchVerifier", "PedersenVRF"] diff --git a/dot_ring/vrf/pedersen/pedersen.py b/dot_ring/vrf/pedersen/pedersen.py index 40bef83..a0cba76 100644 --- a/dot_ring/vrf/pedersen/pedersen.py +++ b/dot_ring/vrf/pedersen/pedersen.py @@ -1,228 +1,7 @@ from __future__ import annotations -from dataclasses import dataclass -from typing import Any, Literal, TypeVar, cast +from .pedersen_batch_item import PedersenBatchItem +from .pedersen_batch_verifier import PedersenBatchVerifier +from .pedersen_vrf import PedersenVRF -from dot_ring.curve.curve import CurveVariant -from dot_ring.curve.point import CurvePoint - -from ...ring_proof.helpers import Helpers -from ..vrf import VRF - -C = TypeVar("C", bound=CurveVariant) - - -@dataclass -class PedersenVRF(VRF[C]): - """ - Pedersen VRF implementation. - - This implementation provides Pedersen-style VRF operations - with blinding support. - - Usage: - >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.pedersen.pedersen import PedersenVRF - >>> proof = PedersenVRF[Bandersnatch].prove(alpha, secret_key, additional_data) - >>> verified = PedersenVRF[Bandersnatch].verify(input_point, additional_data, proof) - """ - - output_point: CurvePoint - blinded_pk: CurvePoint - result_point: CurvePoint - ok: CurvePoint - s: int - sb: int - - # Blinding factor used in proof generation - _blinding_factor: int - - @classmethod - def from_bytes(cls, proof: bytes) -> PedersenVRF: - scalar_len = (cls.cv.curve.PRIME_FIELD.bit_length() + 7) // 8 - - point_length = cls.cv.curve.POINT_LEN - if cls.cv.curve.UNCOMPRESSED: - point_length *= 2 - - output_point = cls.cv.point.string_to_point(proof[point_length * 0 : point_length * 1]) - - public_key_cp = cls.cv.point.string_to_point(proof[point_length * 1 : point_length * 2]) - R = cls.cv.point.string_to_point(proof[point_length * 2 : point_length * 3]) - Ok = cls.cv.point.string_to_point(proof[point_length * 3 : point_length * 4]) - s = Helpers.str_to_int( - proof[-scalar_len * 2 : -scalar_len], - cast(Literal["little", "big"], cls.cv.curve.ENDIAN), - ) - Sb = Helpers.str_to_int(proof[-scalar_len:], cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) - - if isinstance(output_point, str) or isinstance(public_key_cp, str) or isinstance(R, str) or isinstance(Ok, str): - raise ValueError("Invalid point in proof") - - return cls( - output_point=output_point, - blinded_pk=public_key_cp, - result_point=R, - ok=Ok, - s=s, - sb=Sb, - _blinding_factor=0, # Blinding factor is not needed to verify - ) - - def to_bytes(self) -> bytes: - """ - Serialize proof to bytes. - - Returns: - bytes: Serialized proof - """ - scalar_len = (self.cv.curve.PRIME_FIELD.bit_length() + 7) // 8 - point_length = self.cv.curve.POINT_LEN - if self.cv.curve.UNCOMPRESSED: - point_length *= 2 - - proof = ( - self.output_point.point_to_string() - + self.blinded_pk.point_to_string() - + self.result_point.point_to_string() - + self.ok.point_to_string() - + Helpers.int_to_str(self.s, cast(Literal["little", "big"], self.cv.curve.ENDIAN), scalar_len) - + Helpers.int_to_str( - self.sb, - cast(Literal["little", "big"], self.cv.curve.ENDIAN), - scalar_len, - ) - ) - return proof - - @classmethod - def prove( - cls, - alpha: bytes, - secret_key: bytes, - additional_data: bytes, - salt: bytes = b"", - ) -> PedersenVRF: - """ - Generate Pedersen VRF proof. - - Args: - alpha: Input message - secret_key: Secret key - additional_data: Additional data for challenge - need_blinding: Whether to return blinding factor - salt: Optional salt for encoding - - Returns: - bytes: Proof bytes, or tuple of (proof, blinding) if need_blinding=True - """ - - scalar_len = (cls.cv.curve.PRIME_FIELD.bit_length() + 7) // 8 - secret_key_int = Helpers.str_to_int(secret_key, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) % cls.cv.curve.ORDER - - # Create generator point - generator = cls.cv.point.generator_point() - - b_base = cls.cv.point(cast(int, cls.cv.curve.BBx), cast(int, cls.cv.curve.BBy)) - input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) - # Use curve's endianness for secret key serialization - secret_key_bytes = secret_key_int.to_bytes(scalar_len, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) - blinding_factor = cls.blinding(secret_key_bytes, input_point.point_to_string(), additional_data) - - output_point = input_point * secret_key_int - - if cls.cv.point.__name__ == "P256PointVariant": - input_point_octet = input_point.point_to_string() - k = cls.ecvrf_nonce_rfc6979(secret_key_int, input_point_octet) - Kb = cls.ecvrf_nonce_rfc6979(blinding_factor, input_point_octet) - else: - k = cls.generate_nonce(secret_key_int, input_point) - Kb = cls.generate_nonce(blinding_factor, input_point) - - public_key_cp = cast(Any, generator) * secret_key_int + b_base * blinding_factor - R = cast(Any, generator) * k + b_base * Kb - Ok = input_point * k - c = cls.challenge([public_key_cp, input_point, output_point, R, Ok], additional_data) - s = (k + c * secret_key_int) % cls.cv.curve.ORDER - Sb = (Kb + c * blinding_factor) % cls.cv.curve.ORDER - - return cls( - output_point=output_point, - blinded_pk=public_key_cp, - result_point=R, - ok=Ok, - s=s, - sb=Sb, - _blinding_factor=blinding_factor, - ) - - def verify(self, input: bytes, additional_data: bytes) -> bool: - """ - Verify Pedersen VRF proof. - - Args: - input: Input message bytes - additional_data: Additional data used in proof - - Returns: - bool: True if proof is valid - """ - generator = self.cv.point.generator_point() - input_point = cast(Any, self.cv.point).encode_to_curve(input) - b_base = self.cv.point(cast(int, self.cv.curve.BBx), cast(int, self.cv.curve.BBy)) - - c = self.challenge( - [ - self.blinded_pk, - input_point, - self.output_point, - self.result_point, - self.ok, - ], - additional_data, - ) - - # Check 1: ok + c * output_point - s * input_point == 0 - # 1*ok + c*output_point + (-s)*input_point == identity - check1 = self.cv.point.msm([self.ok, self.output_point, input_point], [1, c, -self.s]) - Theta0 = check1.is_identity() - - # Check 2: result_point + c * blinded_pk - s * generator - sb * b_base == 0 - # 1*result_point + c*blinded_pk + (-s)*generator + (-sb)*b_base == identity - check2 = self.cv.point.msm( - [self.result_point, self.blinded_pk, generator, b_base], - [1, c, -self.s, -self.sb], - ) - Theta1 = check2.is_identity() - - return Theta0 and Theta1 - - @classmethod - def blinding(cls, secret: bytes, input_point: bytes, add: bytes) -> int: - DOM_SEP_START = b"\xcc" - DOM_SEP_END = b"\x00" - buf = cls.cv.curve.SUITE_STRING + DOM_SEP_START - buf += secret - buf += input_point - buf += add - buf += DOM_SEP_END - scalar_len = (cls.cv.curve.ORDER.bit_length() + 7) // 8 - hashed = cls.cv.curve.hash(buf, 2 * scalar_len) - return int.from_bytes(hashed, "big") % cls.cv.curve.ORDER - - @classmethod - def ecvrf_proof_to_hash(cls, output_point_bytes: bytes | str) -> bytes: - """Convert VRF output point to hash. - - Args: - output_point_bytes: VRF output point bytes - - Returns: - bytes: Hash of VRF output - """ - if not isinstance(output_point_bytes, bytes): - output_point_bytes = bytes.fromhex(output_point_bytes) - output_point = cls.cv.point.string_to_point(output_point_bytes) - if isinstance(output_point, str): - raise ValueError("Invalid output point") - return cls.proof_to_hash(output_point) +__all__ = ["PedersenBatchItem", "PedersenBatchVerifier", "PedersenVRF"] diff --git a/dot_ring/vrf/pedersen/pedersen_batch_item.py b/dot_ring/vrf/pedersen/pedersen_batch_item.py new file mode 100644 index 0000000..79e2c4f --- /dev/null +++ b/dot_ring/vrf/pedersen/pedersen_batch_item.py @@ -0,0 +1,17 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from dot_ring.curve.point import CurvePoint + + +@dataclass +class PedersenBatchItem: + c: int + input: CurvePoint + output: CurvePoint + pk_com: CurvePoint + r: CurvePoint + ok: CurvePoint + s: int + sb: int diff --git a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py new file mode 100644 index 0000000..a12dedd --- /dev/null +++ b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py @@ -0,0 +1,75 @@ +from __future__ import annotations + +import secrets +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, vrf_transcript + +from .pedersen_batch_item import PedersenBatchItem +from .pedersen_vrf import PedersenVRF, _blinding_base + + +def _batch_coefficients(count: int, order: int) -> list[int]: + if count == 0: + return [] + coefficients = [1] + for _ in range(count - 1): + coefficient = 0 + while coefficient == 0: + coefficient = secrets.randbelow(order) + coefficients.append(coefficient) + return coefficients + + +class PedersenBatchVerifier: + def __init__(self, curve: CurveVariant): + self.cv = curve + self.items: list[PedersenBatchItem] = [] + + @classmethod + def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[PedersenBatchVerifier] | Any: + if not isinstance(curve_variant, CurveVariant): + return cls + + class _SpecializedPedersenBatchVerifier(cls): + def __init__(self) -> None: + super().__init__(curve_variant) + + _SpecializedPedersenBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" + return _SpecializedPedersenBatchVerifier + + def push(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> None: + transcript, merged = vrf_transcript(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) + transcript.absorb_point(proof.blinded_pk) + c = challenge(self.cv, [proof.result_point, proof.ok], transcript) + self.items.append( + PedersenBatchItem( + c=c, + input=merged.input, + output=merged.output, + pk_com=proof.blinded_pk, + r=proof.result_point, + ok=proof.ok, + s=proof.s, + sb=proof.sb, + ) + ) + + def verify(self) -> bool: + blinding_base = _blinding_base(self.cv) + generator = self.cv.point.generator_point() + input_points: list[CurvePoint] = [] + input_scalars: list[int] = [] + commitment_points: list[CurvePoint] = [] + commitment_scalars: list[int] = [] + for coefficient, item in zip(_batch_coefficients(len(self.items), self.cv.curve.ORDER), self.items, strict=False): + input_points.extend([item.input, item.output, item.ok]) + input_scalars.extend([coefficient * item.s, -coefficient * item.c, -coefficient]) + commitment_points.extend([generator, blinding_base, item.pk_com, item.r]) + commitment_scalars.extend([coefficient * item.s, coefficient * item.sb, -coefficient * item.c, -coefficient]) + return self.cv.point.msm(input_points, input_scalars).is_identity() and self.cv.point.msm( + commitment_points, + commitment_scalars, + ).is_identity() diff --git a/dot_ring/vrf/pedersen/pedersen_vrf.py b/dot_ring/vrf/pedersen/pedersen_vrf.py new file mode 100644 index 0000000..d27563e --- /dev/null +++ b/dot_ring/vrf/pedersen/pedersen_vrf.py @@ -0,0 +1,187 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, TypeVar, cast + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import ( + DomSep, + VrfIo, + challenge, + nonce, + point_to_hash, + scalar_decode, + scalar_encode, + scalar_len, + vrf_transcript, +) + +from ..vrf import VRF + +C = TypeVar("C", bound=CurveVariant) + + +def _blinding_base(curve: CurveVariant) -> CurvePoint: + if curve.curve.BBx is None or curve.curve.BBy is None: + raise ValueError(f"{curve.name} does not define a Pedersen blinding base") + return curve.point(cast(int, curve.curve.BBx), cast(int, curve.curve.BBy)) + + +@dataclass +class PedersenVRF(VRF[C]): + """ + Pedersen VRF implementation. + + This implementation provides Pedersen-style VRF operations with blinding + support. + + Usage: + >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch + >>> from dot_ring.vrf.pedersen.pedersen import PedersenVRF + >>> proof = PedersenVRF[Bandersnatch].prove(alpha, secret_key, additional_data) + >>> verified = PedersenVRF[Bandersnatch].verify(input_point, additional_data, proof) + """ + + output_point: CurvePoint + blinded_pk: CurvePoint + result_point: CurvePoint + ok: CurvePoint + s: int + sb: int + _blinding_factor: int = 0 + + @classmethod + def from_bytes(cls, proof: bytes) -> PedersenVRF: + point_length = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + scalar_size = scalar_len(cls.cv) + expected = 4 * point_length + 2 * scalar_size + if len(proof) != expected: + raise ValueError(f"invalid Pedersen VRF proof length: expected {expected}, got {len(proof)}") + + output_point = cls.cv.point.string_to_point(proof[0:point_length]) + public_key_cp = cls.cv.point.string_to_point(proof[point_length : 2 * point_length]) + r = cls.cv.point.string_to_point(proof[2 * point_length : 3 * point_length]) + ok = cls.cv.point.string_to_point(proof[3 * point_length : 4 * point_length]) + s = scalar_decode(cls.cv, proof[4 * point_length : 4 * point_length + scalar_size]) + sb = scalar_decode(cls.cv, proof[4 * point_length + scalar_size :]) + + if isinstance(output_point, str) or isinstance(public_key_cp, str) or isinstance(r, str) or isinstance(ok, str): + raise ValueError("Invalid point in proof") + + return cls( + output_point=output_point, + blinded_pk=public_key_cp, + result_point=r, + ok=ok, + s=s, + sb=sb, + ) + + def to_bytes(self) -> bytes: + return ( + self.output_point.point_to_string() + + self.blinded_pk.point_to_string() + + self.result_point.point_to_string() + + self.ok.point_to_string() + + scalar_encode(self.cv, self.s) + + scalar_encode(self.cv, self.sb) + ) + + @classmethod + def _io_from_alpha(cls, alpha: bytes, secret_scalar: int, salt: bytes = b"") -> VrfIo: + input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) + output_point = input_point * secret_scalar + return VrfIo(input_point, output_point) + + @classmethod + def prove( + cls, + alpha: bytes, + secret_key: bytes, + additional_data: bytes, + salt: bytes = b"", + ) -> PedersenVRF: + secret_scalar = scalar_decode(cls.cv, secret_key) + io = cls._io_from_alpha(alpha, secret_scalar, salt) + public_key = cls.cv.point.generator_point() * secret_scalar + return cls.prove_ios([io], secret_scalar, public_key, additional_data) + + @classmethod + def prove_ios( + cls, + ios: list[VrfIo], + secret_scalar: int, + public_key: CurvePoint, + additional_data: bytes, + ) -> PedersenVRF: + transcript, merged = vrf_transcript(cls.cv, DomSep.PEDERSEN_VRF, ios, additional_data) + blinding_factor = cls.blinding_scalar(secret_scalar, transcript) + blinding_base = _blinding_base(cls.cv) + generator = cls.cv.point.generator_point() + + blinded_pk = public_key + blinding_base * blinding_factor + transcript.absorb_point(blinded_pk) + + k = nonce(cls.cv, secret_scalar, transcript) + kb = nonce(cls.cv, blinding_factor, transcript) + result_point = generator * k + blinding_base * kb + ok = merged.input * k + c = challenge(cls.cv, [result_point, ok], transcript) + s = (k + c * secret_scalar) % cls.cv.curve.ORDER + sb = (kb + c * blinding_factor) % cls.cv.curve.ORDER + output_point = ios[0].output if len(ios) == 1 else merged.output + + return cls( + output_point=output_point, + blinded_pk=blinded_pk, + result_point=result_point, + ok=ok, + s=s, + sb=sb, + _blinding_factor=blinding_factor, + ) + + def verify(self, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: + input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) + return self.verify_ios([VrfIo(input_point, self.output_point)], additional_data) + + def verify_ios(self, ios: list[VrfIo], additional_data: bytes) -> bool: + transcript, merged = vrf_transcript(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) + transcript.absorb_point(self.blinded_pk) + c = challenge(self.cv, [self.result_point, self.ok], transcript) + blinding_base = _blinding_base(self.cv) + generator = self.cv.point.generator_point() + + lhs1 = self.cv.point.msm([merged.input, merged.output], [self.s, -c]) + if lhs1 != self.ok: + return False + + lhs2 = self.cv.point.msm([generator, blinding_base, self.blinded_pk], [self.s, self.sb, -c]) + return lhs2 == self.result_point + + @classmethod + def blinding_scalar(cls, secret_scalar: int, transcript: Any) -> int: + t = transcript.clone() + t.absorb_raw(bytes([DomSep.PEDERSEN_BLINDING])) + return nonce(cls.cv, secret_scalar, t) + + @classmethod + def blinding(cls, secret: bytes, input_point: bytes, add: bytes) -> int: + del input_point, add + return cls.blinding_scalar(scalar_decode(cls.cv, secret), vrf_transcript(cls.cv, DomSep.PEDERSEN_VRF, [], b"")[0]) + + @classmethod + def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: + if mul_cofactor: + gamma = gamma * cls.cv.curve.COFACTOR + return point_to_hash(cls.cv, gamma) + + @classmethod + def ecvrf_proof_to_hash(cls, output_point_bytes: bytes | str) -> bytes: + if not isinstance(output_point_bytes, bytes): + output_point_bytes = bytes.fromhex(output_point_bytes) + output_point = cls.cv.point.string_to_point(output_point_bytes) + if isinstance(output_point, str): + raise ValueError("Invalid output point") + return cls.proof_to_hash(output_point) From 0153ca8209d960371feb3e78ce1ae155181b738f Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:45:35 +0530 Subject: [PATCH 04/33] feat: generalize Ring VRF setup and PCS --- dot_ring/ring_proof/columns/columns.py | 33 ++- dot_ring/ring_proof/constants.py | 8 +- .../ring_proof/constraints/aggregation.py | 11 +- .../ring_proof/constraints/constraints.py | 153 ++++++++----- dot_ring/ring_proof/params.py | 111 +++++++++- dot_ring/ring_proof/pcs/__init__.py | 6 + dot_ring/ring_proof/pcs/bn254_kzg.py | 163 ++++++++++++++ .../ring_proof/pcs/bn254_serialization.py | 70 ++++++ dot_ring/ring_proof/pcs/bn254_srs.py | 87 ++++++++ dot_ring/ring_proof/pcs/kzg.py | 26 ++- dot_ring/ring_proof/pcs/opening.py | 12 ++ dot_ring/ring_proof/polynomial/fft.py | 22 ++ dot_ring/ring_proof/polynomial/ops.py | 14 +- dot_ring/ring_proof/proof/aggregation_poly.py | 12 +- .../ring_proof/proof/linearization_poly.py | 64 +++--- dot_ring/ring_proof/proof/quotient_poly.py | 5 +- dot_ring/ring_proof/verify.py | 172 ++++++++++++--- dot_ring/scripts/export_python_proof.py | 6 +- .../data/bn254-testing-2-9-uncompressed.bin | Bin 0 -> 98640 bytes dot_ring/vrf/ring/__init__.py | 18 ++ dot_ring/vrf/ring/ring.py | 48 +++++ dot_ring/vrf/ring/ring_batch_item.py | 19 ++ dot_ring/vrf/ring/ring_batch_verifier.py | 53 +++++ dot_ring/vrf/ring/ring_context.py | 48 +++++ dot_ring/vrf/ring/ring_keys.py | 10 + dot_ring/vrf/ring/ring_root.py | 102 +++------ dot_ring/vrf/ring/ring_serialization.py | 44 ++++ .../vrf/ring/ring_verifier_key_builder.py | 24 +++ dot_ring/vrf/ring/ring_vrf.py | 203 ++++++++++-------- 29 files changed, 1247 insertions(+), 297 deletions(-) create mode 100644 dot_ring/ring_proof/pcs/bn254_kzg.py create mode 100644 dot_ring/ring_proof/pcs/bn254_serialization.py create mode 100644 dot_ring/ring_proof/pcs/bn254_srs.py create mode 100644 dot_ring/ring_proof/pcs/opening.py create mode 100644 dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin create mode 100644 dot_ring/vrf/ring/ring.py create mode 100644 dot_ring/vrf/ring/ring_batch_item.py create mode 100644 dot_ring/vrf/ring/ring_batch_verifier.py create mode 100644 dot_ring/vrf/ring/ring_context.py create mode 100644 dot_ring/vrf/ring/ring_keys.py create mode 100644 dot_ring/vrf/ring/ring_serialization.py create mode 100644 dot_ring/vrf/ring/ring_verifier_key_builder.py diff --git a/dot_ring/ring_proof/columns/columns.py b/dot_ring/ring_proof/columns/columns.py index a669454..a7ddb32 100644 --- a/dot_ring/ring_proof/columns/columns.py +++ b/dot_ring/ring_proof/columns/columns.py @@ -4,10 +4,11 @@ import os import secrets from dataclasses import dataclass -from typing import cast +from typing import Any, cast +from dot_ring.curve.point import CurvePoint +from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.constants import DEFAULT_SIZE, MAX_RING_SIZE, OMEGAS, S_PRIME, ZK_ROWS, SeedPoint -from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve as TE from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG @@ -54,11 +55,11 @@ def interpolate( self.evals += [0] * (self.size - len(self.evals)) self.coeffs = poly_interpolate_fft(self.evals, domain_omega, prime) - def commit(self) -> None: + def commit(self, pcs: Any = KZG) -> None: if self.coeffs is None: raise ValueError("call interpolate() first") if self.commitment is None: - self.commitment = KZG.commit(self.coeffs) + self.commitment = pcs.commit(self.coeffs) @dataclass(slots=True) @@ -73,6 +74,9 @@ class WitnessColumnBuilder: max_ring_size: int = MAX_RING_SIZE padding_rows: int = 4 test_vectors: bool = False + seed_point: tuple[int, int] = SeedPoint + point_cls: type[CurvePoint] = Bandersnatch.point + pcs: Any = KZG @classmethod def from_params( @@ -94,8 +98,19 @@ def from_params( max_ring_size=params.max_ring_size, padding_rows=params.padding_rows, test_vectors=params.test_vectors, + seed_point=params.seed_point, + point_cls=params.ring_point_cls, + pcs=params.pcs, ) + def _add(self, point1: tuple[int, int], point2: tuple[int, int]) -> tuple[int, int]: + result = self.point_cls(point1[0], point1[1]) + self.point_cls(point2[0], point2[1]) + return int(result.x), int(result.y) + + def _mul(self, scalar: int, point: tuple[int, int]) -> tuple[int, int]: + result = self.point_cls(point[0], point[1]) * scalar + return int(result.x), int(result.y) + def _bits_vector(self) -> list[int]: bv = [1 if i == self.producer_index else 0 for i in range(self.max_ring_size)] t_bits = bin(self.secret_t)[2:][::-1] @@ -113,12 +128,12 @@ def _bits_vector(self) -> list[int]: return bv def _conditional_sum_accumulator(self, b_vector: list[int]) -> tuple[list[int], list[int]]: - seed_sw = SeedPoint + seed_sw = self.seed_point acc = [seed_sw] acc_len = self.size - self.padding_rows + 1 for i in range(1, acc_len): - next_pt = acc[i - 1] if b_vector[i - 1] == 0 else cast(tuple[int, int], TE.add(acc[i - 1], self.ring_pk[i - 1])) + next_pt = acc[i - 1] if b_vector[i - 1] == 0 else self._add(acc[i - 1], self.ring_pk[i - 1]) acc.append(next_pt) return H.unzip(acc) @@ -142,7 +157,7 @@ def build(self) -> tuple[Column, Column, Column, Column]: ] for col in columns: col.interpolate(self.omega, self.prime, hidden=True, test_vectors=self.test_vectors) - col.commit() + col.commit(self.pcs) return (columns[0], columns[1], columns[2], columns[3]) def result(self, Blinding_point: tuple[int, int]) -> tuple[int, int]: @@ -155,12 +170,12 @@ def result(self, Blinding_point: tuple[int, int]) -> tuple[int, int]: PK_k = self.ring_pk[self.producer_index] Result_point = cast( tuple[int, int], - TE.add(PK_k, cast(tuple[int, int], TE.mul(self.secret_t, sw_H))), + self._add(PK_k, self._mul(self.secret_t, sw_H)), ) return Result_point def result_p_seed(self, result: tuple[int, int]) -> tuple[int, int]: """result plus seed""" # res=sw.add(result, sw.from_twisted_edwards(SeedPoint)) - res = cast(tuple[int, int], TE.add(result, SeedPoint)) + res = cast(tuple[int, int], self._add(result, self.seed_point)) return res diff --git a/dot_ring/ring_proof/constants.py b/dot_ring/ring_proof/constants.py index 04ecf39..635f0a0 100644 --- a/dot_ring/ring_proof/constants.py +++ b/dot_ring/ring_proof/constants.py @@ -17,8 +17,8 @@ # new seed point SeedPoint: tuple[int, int] = ( - 37805570861274048643170021838972902516980894313648523898085159469000338764576, - 14738305321141000190236674389841754997202271418876976886494444739226156422510, + BandersnatchParams.ACCUMULATOR_BASE_X, + BandersnatchParams.ACCUMULATOR_BASE_Y, ) @@ -29,8 +29,8 @@ # new padding point PaddingPoint: tuple[int, int] = ( - 26287722405578650394504321825321286533153045350760430979437739593351290020913, - 19058981610000167534379068105702216971787064146691007947119244515951752366738, + BandersnatchParams.PADDING_X, + BandersnatchParams.PADDING_Y, ) diff --git a/dot_ring/ring_proof/constraints/aggregation.py b/dot_ring/ring_proof/constraints/aggregation.py index c740915..17c9b80 100644 --- a/dot_ring/ring_proof/constraints/aggregation.py +++ b/dot_ring/ring_proof/constraints/aggregation.py @@ -1,8 +1,9 @@ from __future__ import annotations from collections.abc import Sequence +from typing import Any, cast -from dot_ring.curve.native_field.vector_ops import vect_add +from dot_ring.curve.native_field.vector_ops import vect_add as _native_vect_add from dot_ring.ring_proof.constants import S_PRIME from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft @@ -24,6 +25,14 @@ def vanishing_poly(domain: list[int], k: int = 3, prime: int = S_PRIME) -> list[ return vanishing_term +def vect_add(a: Any, b: Any, prime: int) -> list[int]: + if prime == S_PRIME: + return cast(list[int], _native_vect_add(a, b, prime)) + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] + + def aggregate_constraints( polys: Sequence[Sequence[int]], alphas: Sequence[int], diff --git a/dot_ring/ring_proof/constraints/constraints.py b/dot_ring/ring_proof/constraints/constraints.py index daa8f8c..fcebe33 100644 --- a/dot_ring/ring_proof/constraints/constraints.py +++ b/dot_ring/ring_proof/constraints/constraints.py @@ -5,17 +5,64 @@ from functools import lru_cache from typing import Any, cast -from dot_ring.curve.native_field.vector_ops import vect_add, vect_mul, vect_sub -from dot_ring.curve.specs.bandersnatch import BandersnatchParams +from dot_ring.curve.native_field.vector_ops import vect_add as _native_vect_add +from dot_ring.curve.native_field.vector_ops import vect_mul as _native_vect_mul +from dot_ring.curve.native_field.vector_ops import vect_sub as _native_vect_sub from dot_ring.ring_proof.constants import S_PRIME, SeedPoint from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.polynomial.fft import evaluate_poly_fft from dot_ring.ring_proof.polynomial.ops import lagrange_basis_polynomial -def _to_radix(vec: Sequence[int], radix_domain_size: int, radix_omega: int) -> list[int]: +def _is_list(value: Any) -> bool: + return isinstance(value, list) + + +def vect_add(a: Any, b: Any, prime: int) -> list[int]: + if prime == S_PRIME: + return cast(list[int], _native_vect_add(a, b, prime)) + if _is_list(a) and _is_list(b): + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] + if _is_list(a): + return [(int(x) + int(b)) % prime for x in a] + if _is_list(b): + return [(int(a) + int(y)) % prime for y in b] + return [(int(a) + int(b)) % prime] + + +def vect_sub(a: Any, b: Any, prime: int) -> list[int]: + if prime == S_PRIME: + return cast(list[int], _native_vect_sub(a, b, prime)) + if _is_list(a) and _is_list(b): + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) - int(y)) % prime for x, y in zip(a, b, strict=True)] + if _is_list(a): + return [(int(x) - int(b)) % prime for x in a] + if _is_list(b): + return [(int(a) - int(y)) % prime for y in b] + return [(int(a) - int(b)) % prime] + + +def vect_mul(a: Any, b: Any, prime: int) -> list[int]: + if prime == S_PRIME: + return cast(list[int], _native_vect_mul(a, b, prime)) + if _is_list(a) and _is_list(b): + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) * int(y)) % prime for x, y in zip(a, b, strict=True)] + if _is_list(a): + return [(int(x) * int(b)) % prime for x in a] + if _is_list(b): + return [(int(a) * int(y)) % prime for y in b] + return [(int(a) * int(b)) % prime] + + +def _to_radix(vec: Sequence[int], radix_domain_size: int, radix_omega: int, prime: int) -> list[int]: """Convert a radix‑2 evaluation vector to radix‑domain evaluations.""" - return cast(list[int], evaluate_poly_fft(list(vec), radix_domain_size, radix_omega, S_PRIME)) + return cast(list[int], evaluate_poly_fft(list(vec), radix_domain_size, radix_omega, prime)) def _shift(vec: Sequence[int], shift: int) -> list[int]: @@ -28,11 +75,11 @@ def _constraint_context(params: RingProofParams) -> dict[str, Any]: domain = params.domain radix_domain = params.radix_domain last_index = params.last_index - not_last = vect_sub(radix_domain, pow(params.omega, last_index, S_PRIME), S_PRIME) - l0_coeffs = lagrange_basis_polynomial(domain, 0, S_PRIME) - ln_coeffs = lagrange_basis_polynomial(domain, last_index, S_PRIME) - l0_radix = cast(list[int], evaluate_poly_fft(l0_coeffs, params.radix_domain_size, params.radix_omega, S_PRIME)) - ln_radix = cast(list[int], evaluate_poly_fft(ln_coeffs, params.radix_domain_size, params.radix_omega, S_PRIME)) + not_last = vect_sub(radix_domain, pow(params.omega, last_index, params.prime), params.prime) + l0_coeffs = lagrange_basis_polynomial(domain, 0, params.prime) + ln_coeffs = lagrange_basis_polynomial(domain, last_index, params.prime) + l0_radix = cast(list[int], evaluate_poly_fft(l0_coeffs, params.radix_domain_size, params.radix_omega, params.prime)) + ln_radix = cast(list[int], evaluate_poly_fft(ln_coeffs, params.radix_domain_size, params.radix_omega, params.prime)) return { "radix_domain": radix_domain, "shift": params.radix_shift, @@ -56,6 +103,7 @@ class RingConstraintBuilder: acc_y: Sequence[int] acc_ip: Sequence[int] params: RingProofParams = field(default_factory=RingProofParams, repr=False) + seed_point: tuple[int, int] = SeedPoint _px4: list[int] = field(init=False, repr=False) _py4: list[int] = field(init=False, repr=False) @@ -69,13 +117,13 @@ class RingConstraintBuilder: def __post_init__(self) -> None: self._ctx = _constraint_context(self.params) radix_domain = self._ctx["radix_domain"] - self._px4 = _to_radix(self.px, len(radix_domain), self.params.radix_omega) - self._py4 = _to_radix(self.py, len(radix_domain), self.params.radix_omega) - self._s4 = _to_radix(self.s, len(radix_domain), self.params.radix_omega) - self._b4 = _to_radix(self.b, len(radix_domain), self.params.radix_omega) - self._accx4 = _to_radix(self.acc_x, len(radix_domain), self.params.radix_omega) - self._accy4 = _to_radix(self.acc_y, len(radix_domain), self.params.radix_omega) - self._accip4 = _to_radix(self.acc_ip, len(radix_domain), self.params.radix_omega) + self._px4 = _to_radix(self.px, len(radix_domain), self.params.radix_omega, self.params.prime) + self._py4 = _to_radix(self.py, len(radix_domain), self.params.radix_omega, self.params.prime) + self._s4 = _to_radix(self.s, len(radix_domain), self.params.radix_omega, self.params.prime) + self._b4 = _to_radix(self.b, len(radix_domain), self.params.radix_omega, self.params.prime) + self._accx4 = _to_radix(self.acc_x, len(radix_domain), self.params.radix_omega, self.params.prime) + self._accy4 = _to_radix(self.acc_y, len(radix_domain), self.params.radix_omega, self.params.prime) + self._accip4 = _to_radix(self.acc_ip, len(radix_domain), self.params.radix_omega, self.params.prime) # convenient classmethod for Column builders @classmethod @@ -105,38 +153,40 @@ def compute(self) -> dict[str, list[int]]: compute_all = compute # alias def _c1(self) -> list[int]: + p = self.params.prime accip_w = _shift(self._accip4, self._ctx["shift"]) constraint = vect_sub( - vect_sub(accip_w, self._accip4, S_PRIME), - vect_mul(self._b4, self._s4, S_PRIME), - S_PRIME, + vect_sub(accip_w, self._accip4, p), + vect_mul(self._b4, self._s4, p), + p, ) - c1x = vect_mul(constraint, self._ctx["not_last"], S_PRIME) + c1x = vect_mul(constraint, self._ctx["not_last"], p) return c1x def _c2(self) -> list[list[int]]: + p = self.params.prime bx = self._b4 accx_w = _shift(self._accx4, self._ctx["shift"]) accy_w = _shift(self._accy4, self._ctx["shift"]) - te_coeff_a = BandersnatchParams.EDWARDS_A # BandersnatchParams.EDWARDS_A % S_PRIME + te_coeff_a = self.params.ring_edwards_a b = bx x1, x2, x3 = self._accx4, self._px4, accx_w y1, y2, y3 = self._accy4, self._py4, accy_w # b(x_3(y_1. y_2 + ax_1 .x_2) - x_1.y_1 - y_2.x_2) + (1 - b)(x_3 - x_1) = 0 # => b(x_3(y_1. y_2 + ax_1 .x_2) - (x_1.y_1 + y_2.x_2)) + (1 - b)(x_3 - x_1) = 0 - y1_y2 = vect_mul(y1, y2, S_PRIME) - a_x1_x2 = vect_mul(te_coeff_a, vect_mul(x1, x2, S_PRIME), S_PRIME) - x1_y1 = vect_mul(x1, y1, S_PRIME) - y2_x2 = vect_mul(y2, x2, S_PRIME) - one_m_b = vect_sub(1, b, S_PRIME) - x3_m_x1 = vect_sub(x3, x1, S_PRIME) + y1_y2 = vect_mul(y1, y2, p) + a_x1_x2 = vect_mul(te_coeff_a, vect_mul(x1, x2, p), p) + x1_y1 = vect_mul(x1, y1, p) + y2_x2 = vect_mul(y2, x2, p) + one_m_b = vect_sub(1, b, p) + x3_m_x1 = vect_sub(x3, x1, p) - term1 = vect_mul(x3, vect_add(y1_y2, a_x1_x2, S_PRIME), S_PRIME) - term2 = vect_mul(b, vect_sub(term1, vect_add(x1_y1, y2_x2, S_PRIME), S_PRIME), S_PRIME) - term3 = vect_add(term2, vect_mul(one_m_b, x3_m_x1, S_PRIME), S_PRIME) - c2x = vect_mul(term3, self._ctx["not_last"], S_PRIME) + term1 = vect_mul(x3, vect_add(y1_y2, a_x1_x2, p), p) + term2 = vect_mul(b, vect_sub(term1, vect_add(x1_y1, y2_x2, p), p), p) + term3 = vect_add(term2, vect_mul(one_m_b, x3_m_x1, p), p) + c2x = vect_mul(term3, self._ctx["not_last"], p) # print("c2x here", c2x) # accx_m_px = vect_sub(self._accx4, self._px4, S_PRIME) @@ -175,24 +225,25 @@ def _c2(self) -> list[list[int]]: # // b(y_3(x_1.y_2 - x_2.y_1) - x_1.y_1 + x_2.y_2) + (1 - b)(y_3 - y_1) = 0 # => b(y_3(x_1.y_2 - x_2.y_1) - (x_1.y_1 - x_2.y_2) + (1 - b)(y_3 - y_1) = 0 - x1_y2 = vect_mul(x1, y2, S_PRIME) - x2_y1 = vect_mul(x2, y1, S_PRIME) - y3_m_y1 = vect_sub(y3, y1, S_PRIME) + x1_y2 = vect_mul(x1, y2, p) + x2_y1 = vect_mul(x2, y1, p) + y3_m_y1 = vect_sub(y3, y1, p) - term1 = vect_mul(y3, vect_sub(x1_y2, x2_y1, S_PRIME), S_PRIME) - term2 = vect_mul(b, vect_sub(term1, vect_sub(x1_y1, y2_x2, S_PRIME), S_PRIME), S_PRIME) - term3 = vect_add(term2, vect_mul(one_m_b, y3_m_y1, S_PRIME), S_PRIME) - c3x = vect_mul(term3, self._ctx["not_last"], S_PRIME) + term1 = vect_mul(y3, vect_sub(x1_y2, x2_y1, p), p) + term2 = vect_mul(b, vect_sub(term1, vect_sub(x1_y1, y2_x2, p), p), p) + term3 = vect_add(term2, vect_mul(one_m_b, y3_m_y1, p), p) + c3x = vect_mul(term3, self._ctx["not_last"], p) return [c2x, c3x] def _c4(self) -> list[int]: - one_m_bx = vect_sub(1, self._b4, S_PRIME) - c4x = vect_mul(self._b4, one_m_bx, S_PRIME) + p = self.params.prime + one_m_bx = vect_sub(1, self._b4, p) + c4x = vect_mul(self._b4, one_m_bx, p) return c4x def _c5(self) -> list[list[int]]: # seed_x, seed_y = sw.from_twisted_edwards(SeedPoint) - seed_x, seed_y = SeedPoint + seed_x, seed_y = self.seed_point # print("seed_here",(seed_x, seed_y)) @@ -203,16 +254,17 @@ def _c5(self) -> list[list[int]]: l0_rad4 = self._ctx["l0_radix"] ln4_rad4 = self._ctx["ln_radix"] + p = self.params.prime c5x = vect_add( - vect_mul(vect_sub(self._accx4, seed_x, S_PRIME), l0_rad4, S_PRIME), - vect_mul(vect_sub(self._accx4, rx_psx, S_PRIME), ln4_rad4, S_PRIME), - S_PRIME, + vect_mul(vect_sub(self._accx4, seed_x, p), l0_rad4, p), + vect_mul(vect_sub(self._accx4, rx_psx, p), ln4_rad4, p), + p, ) c6x = vect_add( - vect_mul(vect_sub(self._accy4, seed_y, S_PRIME), l0_rad4, S_PRIME), - vect_mul(vect_sub(self._accy4, ry_psy, S_PRIME), ln4_rad4, S_PRIME), - S_PRIME, + vect_mul(vect_sub(self._accy4, seed_y, p), l0_rad4, p), + vect_mul(vect_sub(self._accy4, ry_psy, p), ln4_rad4, p), + p, ) return [c5x, c6x] @@ -221,10 +273,11 @@ def _c7(self) -> list[int]: l0_rad4 = self._ctx["l0_radix"] ln4_rad4 = self._ctx["ln_radix"] + p = self.params.prime c7x = vect_add( - vect_mul(self._accip4, l0_rad4, S_PRIME), - vect_mul(vect_sub(self._accip4, 1, S_PRIME), ln4_rad4, S_PRIME), - S_PRIME, + vect_mul(self._accip4, l0_rad4, p), + vect_mul(vect_sub(self._accip4, 1, p), ln4_rad4, p), + p, ) return c7x diff --git a/dot_ring/ring_proof/params.py b/dot_ring/ring_proof/params.py index d1357be..4d5bee6 100644 --- a/dot_ring/ring_proof/params.py +++ b/dot_ring/ring_proof/params.py @@ -1,12 +1,14 @@ from __future__ import annotations -from dataclasses import dataclass +from dataclasses import dataclass, field from functools import lru_cache -from typing import ClassVar from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.constants import D_2048, DEFAULT_SIZE, MAX_RING_SIZE, OMEGA_2048, S_PRIME +from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG +from dot_ring.ring_proof.pcs.kzg import KZG def _is_power_of_two(n: int) -> bool: @@ -28,6 +30,21 @@ def _omega_for_domain(domain_size: int, prime: int = S_PRIME, base_root: int = O return pow(base_root, base_size // domain_size, prime) +@lru_cache(maxsize=16) +def _primitive_root_of_unity(size: int, prime: int) -> int: + if not _is_power_of_two(size): + raise ValueError(f"root size must be a power of two, got {size}") + if (prime - 1) % size != 0: + raise ValueError(f"root size {size} does not divide prime - 1") + exponent = (prime - 1) // size + candidate = 2 + while True: + root = pow(candidate, exponent, prime) + if root != 1 and pow(root, size, prime) == 1 and pow(root, size // 2, prime) != 1: + return root + candidate += 1 + + @lru_cache(maxsize=32) def _domain_for_size( domain_size: int, @@ -82,6 +99,20 @@ def _sqrt_mod_prime(n: int, prime: int) -> int: return x +_BANDERSNATCH_MONT_A_OVER_THREE = 9992940898322946442093665462003920523391277922024982836398934612730118446984 +_BANDERSNATCH_MONT_B = 25465760566081946422412445027709227188579564747101592991722834452325077642517 + + +def _bandersnatch_sw_to_te(point: tuple[int, int]) -> tuple[int, int]: + prime = S_PRIME + sw_x, sw_y = point + mont_x = (_BANDERSNATCH_MONT_B * sw_x - _BANDERSNATCH_MONT_A_OVER_THREE) % prime + mont_y = (_BANDERSNATCH_MONT_B * sw_y) % prime + v = mont_x * pow(mont_y, -1, prime) + w = (mont_x - 1) * pow((mont_x + 1) % prime, -1, prime) + return v % prime, w % prime + + @lru_cache(maxsize=8) def _extend_root_to_size(base_root: int, base_size: int, target_size: int, prime: int) -> tuple[int, int]: """Extend a root of unity by repeated square roots until reaching target size.""" @@ -102,10 +133,18 @@ class RingProofParams: prime: int = S_PRIME base_root: int = OMEGA_2048 base_root_size: int = 2048 + pcs: object = field(default=KZG, compare=False, hash=False, repr=False) test_vectors: bool = False - cv: ClassVar[CurveVariant] = Bandersnatch + cv: CurveVariant = field(default_factory=lambda: Bandersnatch, compare=False, hash=False) def __post_init__(self) -> None: + if self.cv.name == "BabyJubJub": + if self.prime == S_PRIME: + object.__setattr__(self, "prime", int(self.cv.curve.PRIME_FIELD)) + if self.base_root == OMEGA_2048 and self.base_root_size == 2048: + object.__setattr__(self, "base_root", _primitive_root_of_unity(self.base_root_size, int(self.cv.curve.PRIME_FIELD))) + if self.pcs is KZG: + object.__setattr__(self, "pcs", BN254KZG) radix_domain_size = self.radix_domain_size if radix_domain_size is None: radix_domain_size = self.domain_size * 4 @@ -128,6 +167,15 @@ def __post_init__(self) -> None: raise ValueError("padding_rows must be less than domain_size") if self.max_ring_size > self.domain_size - self.padding_rows: raise ValueError(f"max_ring_size {self.max_ring_size} exceeds supported size {self.domain_size - self.padding_rows}") + default_max_ring_size = self.domain_size - self.padding_rows - self.cv.curve.ORDER.bit_length() + if self.max_ring_size == MAX_RING_SIZE: + if default_max_ring_size <= 0: + raise ValueError( + "domain_size is too small for the suite scalar bit length: " + f"domain_size={self.domain_size}, padding_rows={self.padding_rows}, " + f"scalar_bits={self.cv.curve.ORDER.bit_length()}" + ) + object.__setattr__(self, "max_ring_size", default_max_ring_size) @property def omega(self) -> int: @@ -159,6 +207,54 @@ def last_index(self) -> int: def max_effective_ring_size(self) -> int: return self.domain_size - self.padding_rows + @property + def blinding_base(self) -> tuple[int, int]: + if self.cv.curve.BBx is None or self.cv.curve.BBy is None: + raise ValueError(f"{self.cv.name} does not define a ring blinding base") + return self.affine_to_ring_point((int(self.cv.curve.BBx), int(self.cv.curve.BBy))) + + @property + def seed_point(self) -> tuple[int, int]: + if self.cv.curve.ACCUMULATOR_BASE_X is None or self.cv.curve.ACCUMULATOR_BASE_Y is None: + raise ValueError(f"{self.cv.name} does not define a ring accumulator base") + return self.affine_to_ring_point((int(self.cv.curve.ACCUMULATOR_BASE_X), int(self.cv.curve.ACCUMULATOR_BASE_Y))) + + @property + def padding_point(self) -> tuple[int, int]: + if self.cv.curve.PADDING_X is None or self.cv.curve.PADDING_Y is None: + raise ValueError(f"{self.cv.name} does not define a ring padding point") + return self.affine_to_ring_point((int(self.cv.curve.PADDING_X), int(self.cv.curve.PADDING_Y))) + + def affine_to_ring_point(self, point: tuple[int, int]) -> tuple[int, int]: + if self.cv.name == "Bandersnatch_SW": + return _bandersnatch_sw_to_te(point) + return point + + def point_to_ring_point(self, point: CurvePoint) -> tuple[int, int]: + return self.affine_to_ring_point((int(point.x), int(point.y))) + + @property + def ring_point_cls(self) -> type[CurvePoint]: + if self.cv.name == "Bandersnatch_SW": + return Bandersnatch.point + return self.cv.point + + @property + def ring_edwards_a(self) -> int: + if self.cv.name == "Bandersnatch_SW": + return int(Bandersnatch.curve.EdwardsA) + return int(self.cv.curve.EdwardsA) + + def add_points(self, point1: tuple[int, int], point2: tuple[int, int]) -> tuple[int, int]: + p1 = self.ring_point_cls(point1[0], point1[1]) + p2 = self.ring_point_cls(point2[0], point2[1]) + result = p1 + p2 + return int(result.x), int(result.y) + + def mul_point(self, scalar: int, point: tuple[int, int]) -> tuple[int, int]: + result = self.ring_point_cls(point[0], point[1]) * scalar + return int(result.x), int(result.y) + @classmethod def from_ring_size( cls, @@ -168,6 +264,7 @@ def from_ring_size( base_root: int = OMEGA_2048, base_root_size: int = 2048, test_vectors: bool = False, + cv: CurveVariant = Bandersnatch, ) -> RingProofParams: """ Automatically construct RingProofParams based on ring size. @@ -189,8 +286,9 @@ def from_ring_size( raise ValueError(f"ring_size must be positive, got {ring_size}") # Calculate minimum domain size needed: - # domain_size >= ring_size + padding_rows - min_domain_size = ring_size + padding_rows + # domain_size >= ring_size + padding_rows + scalar bit decomposition + scalar_bits = cv.curve.ORDER.bit_length() + min_domain_size = ring_size + padding_rows + scalar_bits # Round up to next power of 2 domain_size = _next_power_of_two(min_domain_size) @@ -207,10 +305,11 @@ def from_ring_size( return cls( domain_size=domain_size, - max_ring_size=ring_size, + max_ring_size=domain_size - padding_rows - scalar_bits, padding_rows=padding_rows, prime=prime, base_root=base_root, base_root_size=base_root_size, test_vectors=test_vectors, + cv=cv, ) diff --git a/dot_ring/ring_proof/pcs/__init__.py b/dot_ring/ring_proof/pcs/__init__.py index e69de29..435e977 100644 --- a/dot_ring/ring_proof/pcs/__init__.py +++ b/dot_ring/ring_proof/pcs/__init__.py @@ -0,0 +1,6 @@ +from .bn254_kzg import BN254KZG +from .bn254_srs import BN254SRS +from .kzg import KZG +from .opening import Opening + +__all__ = ["BN254KZG", "BN254SRS", "KZG", "Opening"] diff --git a/dot_ring/ring_proof/pcs/bn254_kzg.py b/dot_ring/ring_proof/pcs/bn254_kzg.py new file mode 100644 index 0000000..3c68867 --- /dev/null +++ b/dot_ring/ring_proof/pcs/bn254_kzg.py @@ -0,0 +1,163 @@ +from __future__ import annotations + +import secrets +from typing import Any + +import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] + +from .bn254_serialization import read_field, sqrt_mod_prime, write_field, y_flag +from .bn254_srs import BN254SRS +from .opening import Opening +from .utils import CoeffVector, Scalar + +BN254_G1Point = Any + + +def _poly_evaluate_mod(coeffs: CoeffVector, x: int, modulus: int) -> int: + result = 0 + for coeff in reversed(coeffs): + result = (result * x + int(coeff)) % modulus + return result + + +def _synthetic_div_mod(coeffs: CoeffVector, x: int, y: int, modulus: int) -> list[int]: + if len(coeffs) < 2: + return [0] + q = [0] * (len(coeffs) - 1) + carry = int(coeffs[-1]) % modulus + q[-1] = carry + for i in range(len(coeffs) - 2, 0, -1): + carry = (int(coeffs[i]) + carry * x) % modulus + q[i - 1] = carry + remainder = (int(coeffs[0]) + carry * x) % modulus + if remainder != y % modulus: + raise ValueError("inconsistent polynomial opening") + return q + + +class BN254KZG: + commitment_size = 32 + scalar_modulus = bn254.curve_order + srs = BN254SRS.default() + + @staticmethod + def normalize_g1(point: BN254_G1Point) -> tuple[int, int]: + if bn254.is_inf(point): + return (0, 0) + x, y = bn254.normalize(point) + return int(x), int(y) + + @classmethod + def compress_g1(cls, point: BN254_G1Point) -> bytes: + if bn254.is_inf(point): + return write_field(0, 0x40) + x, y = cls.normalize_g1(point) + return write_field(x, y_flag(y)) + + @classmethod + def serialize_g1_uncompressed(cls, point: BN254_G1Point) -> bytes: + if bn254.is_inf(point): + return write_field(0) + write_field(0, 0x40) + x, y = cls.normalize_g1(point) + return write_field(x) + write_field(y, y_flag(y)) + + @classmethod + def decompress_g1(cls, data: bytes) -> BN254_G1Point: + x, flag = read_field(data, flags=True) + if flag == 0x40: + if x != 0: + raise ValueError("invalid BN254 point at infinity") + return bn254.Z1 + rhs = (pow(x, 3, bn254.field_modulus) + 3) % bn254.field_modulus + y = sqrt_mod_prime(rhs, bn254.field_modulus) + if y_flag(y) != flag: + y = (-y) % bn254.field_modulus + point = (bn254.FQ(x), bn254.FQ(y), bn254.FQ(1)) + if not bn254.is_on_curve(point, bn254.b): + raise ValueError("invalid BN254 G1 point") + return point + + @classmethod + def commit(cls, coeffs: CoeffVector) -> BN254_G1Point: + if len(coeffs) > len(cls.srs.g1): + raise ValueError("polynomial degree exceeds SRS size") + acc = bn254.Z1 + for coeff, point in zip(coeffs, cls.srs.g1, strict=False): + scalar = int(coeff) % cls.scalar_modulus + if scalar: + acc = bn254.add(acc, bn254.multiply(point, scalar)) + return acc + + @classmethod + def msm_g1(cls, points: list[BN254_G1Point], scalars: list[int]) -> BN254_G1Point: + acc = bn254.Z1 + for point, scalar in zip(points, scalars, strict=False): + scalar = int(scalar) % cls.scalar_modulus + if scalar: + acc = bn254.add(acc, bn254.multiply(point, scalar)) + return acc + + @classmethod + def open(cls, coeffs: CoeffVector, x: Scalar) -> Opening: + point = int(x) % cls.scalar_modulus + y = _poly_evaluate_mod(coeffs, point, cls.scalar_modulus) + q = _synthetic_div_mod(coeffs, point, y, cls.scalar_modulus) + return Opening(cls.commit(q), y) + + @classmethod + def verify(cls, commitment: BN254_G1Point, proof: BN254_G1Point, point: Scalar, value: Scalar) -> bool: + z = int(point) % cls.scalar_modulus + y = int(value) % cls.scalar_modulus + g1_gen = cls.srs.g1[0] + g2_gen = cls.srs.g2[0] + g2_tau = cls.srs.g2[1] + commitment_term = bn254.add(commitment, bn254.neg(bn254.multiply(g1_gen, y))) + tau_term = bn254.add(g2_tau, bn254.neg(bn254.multiply(g2_gen, z))) + lhs = bn254.final_exponentiate(bn254.pairing(g2_gen, commitment_term, final_exponentiate=False)) + rhs = bn254.final_exponentiate(bn254.pairing(tau_term, proof, final_exponentiate=False)) + return bool(lhs == rhs) + + @classmethod + def batch_verify(cls, verifications: list[tuple[BN254_G1Point, BN254_G1Point, Scalar, Scalar]]) -> bool: + if not verifications: + return True + + if len(verifications) == 1: + return cls.verify(*verifications[0]) + + order = cls.scalar_modulus + coeffs = [1] + for _ in range(len(verifications) - 1): + coeff = 0 + while coeff == 0: + coeff = secrets.randbelow(order) + coeffs.append(coeff) + + g1_gen = cls.srs.g1[0] + g2_gen = cls.srs.g2[0] + g2_tau = cls.srs.g2[1] + lhs_points: list[BN254_G1Point] = [] + lhs_scalars: list[int] = [] + rhs_points: list[BN254_G1Point] = [] + rhs_scalars: list[int] = [] + sum_v = 0 + + for coeff, (commitment, proof, point, value) in zip(coeffs, verifications, strict=False): + z = int(point) % order + y = int(value) % order + lhs_points.append(commitment) + lhs_scalars.append(coeff) + lhs_points.append(proof) + lhs_scalars.append((coeff * z) % order) + rhs_points.append(proof) + rhs_scalars.append(coeff) + sum_v = (sum_v + coeff * y) % order + + lhs_points.append(g1_gen) + lhs_scalars.append((-sum_v) % order) + + lhs_point = cls.msm_g1(lhs_points, lhs_scalars) + rhs_point = cls.msm_g1(rhs_points, rhs_scalars) + lhs = bn254.final_exponentiate(bn254.pairing(g2_gen, lhs_point, final_exponentiate=False)) + rhs = bn254.final_exponentiate(bn254.pairing(g2_tau, rhs_point, final_exponentiate=False)) + return bool(lhs == rhs) diff --git a/dot_ring/ring_proof/pcs/bn254_serialization.py b/dot_ring/ring_proof/pcs/bn254_serialization.py new file mode 100644 index 0000000..0c4a843 --- /dev/null +++ b/dot_ring/ring_proof/pcs/bn254_serialization.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] + + +def sqrt_mod_prime(n: int, prime: int) -> int: + if n == 0: + return 0 + if pow(n, (prime - 1) // 2, prime) != 1: + raise ValueError("point is not on BN254 G1") + if prime % 4 == 3: + return pow(n, (prime + 1) // 4, prime) + q = prime - 1 + s = 0 + while q % 2 == 0: + s += 1 + q //= 2 + z = 2 + while pow(z, (prime - 1) // 2, prime) != prime - 1: + z += 1 + m = s + c = pow(z, q, prime) + x = pow(n, (q + 1) // 2, prime) + t = pow(n, q, prime) + while t != 1: + i = 1 + t2i = (t * t) % prime + while i < m: + if t2i == 1: + break + t2i = (t2i * t2i) % prime + i += 1 + b = pow(c, 1 << (m - i - 1), prime) + x = (x * b) % prime + t = (t * b * b) % prime + c = (b * b) % prime + m = i + return x + + +def read_field(data: bytes, *, flags: bool = False) -> tuple[int, int]: + if len(data) != 32: + raise ValueError(f"invalid BN254 field length: expected 32, got {len(data)}") + masked = bytearray(data) + flag = 0 + if flags: + flag = masked[-1] & 0xC0 + masked[-1] &= 0x3F + if flag == 0xC0: + raise ValueError("invalid BN254 point flags") + value = int.from_bytes(masked, "little") + if value >= bn254.field_modulus: + raise ValueError("invalid BN254 field element") + return value, flag + + +def write_field(value: int, flag: int = 0) -> bytes: + if value < 0 or value >= bn254.field_modulus: + raise ValueError("invalid BN254 field element") + if flag not in (0, 0x40, 0x80): + raise ValueError("invalid BN254 point flags") + data = bytearray(value.to_bytes(32, "little")) + if data[-1] & 0xC0: + raise ValueError("BN254 field element overlaps point flags") + data[-1] |= flag + return bytes(data) + + +def y_flag(y: int) -> int: + return 0 if y <= (-y % bn254.field_modulus) else 0x80 diff --git a/dot_ring/ring_proof/pcs/bn254_srs.py b/dot_ring/ring_proof/pcs/bn254_srs.py new file mode 100644 index 0000000..91b70ec --- /dev/null +++ b/dot_ring/ring_proof/pcs/bn254_srs.py @@ -0,0 +1,87 @@ +from __future__ import annotations + +from dataclasses import dataclass +from functools import lru_cache +from pathlib import Path +from typing import Any + +import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] + +from .bn254_serialization import read_field + +BN254_G1Point = Any +BN254_G2Point = Any + + +@dataclass() +class BN254SRS: + g1: list[BN254_G1Point] + g2: list[BN254_G2Point] + g1_points: list[tuple[int, int]] + g2_points: list[tuple[tuple[int, int], tuple[int, int]]] + g1_uncompressed: list[bytes] + g2_uncompressed: list[bytes] + + @classmethod + def from_file(cls) -> BN254SRS: + filename = Path(__file__).resolve().parent.parent.parent / "vrf" / "data" / "bn254-testing-2-9-uncompressed.bin" + data = filename.read_bytes() + if len(data) < 8: + raise ValueError("BN254 SRS file too short") + g1_count = int.from_bytes(data[:8], "little") + offset = 8 + + g1: list[BN254_G1Point] = [] + g1_points: list[tuple[int, int]] = [] + g1_uncompressed: list[bytes] = [] + for i in range(g1_count): + raw = data[offset : offset + 64] + if len(raw) != 64: + raise ValueError(f"unexpected end of BN254 SRS while reading G1 point {i}") + offset += 64 + x, _ = read_field(raw[:32]) + y, _ = read_field(raw[32:], flags=True) + point = (bn254.FQ(x), bn254.FQ(y), bn254.FQ(1)) + if not bn254.is_on_curve(point, bn254.b): + raise ValueError(f"invalid BN254 SRS G1 point {i}") + g1.append(point) + g1_points.append((x, y)) + g1_uncompressed.append(raw) + + if len(data) < offset + 8: + raise ValueError("BN254 SRS file missing G2 count") + g2_count = int.from_bytes(data[offset : offset + 8], "little") + offset += 8 + + g2: list[BN254_G2Point] = [] + g2_points: list[tuple[tuple[int, int], tuple[int, int]]] = [] + g2_uncompressed: list[bytes] = [] + for i in range(g2_count): + raw = data[offset : offset + 128] + if len(raw) != 128: + raise ValueError(f"unexpected end of BN254 SRS while reading G2 point {i}") + offset += 128 + x0, _ = read_field(raw[0:32]) + x1, _ = read_field(raw[32:64]) + y0, _ = read_field(raw[64:96]) + y1, _ = read_field(raw[96:128], flags=True) + point = (bn254.FQ2([x0, x1]), bn254.FQ2([y0, y1]), bn254.FQ2([1, 0])) + if not bn254.is_on_curve(point, bn254.b2): + raise ValueError(f"invalid BN254 SRS G2 point {i}") + g2.append(point) + g2_points.append(((x1, x0), (y1, y0))) + g2_uncompressed.append(raw) + + return cls( + g1=g1, + g2=g2[:2], + g1_points=g1_points, + g2_points=g2_points, + g1_uncompressed=g1_uncompressed, + g2_uncompressed=g2_uncompressed[:2], + ) + + @staticmethod + @lru_cache(maxsize=1) + def default() -> BN254SRS: + return BN254SRS.from_file() diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index 279cbe9..aa14283 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -1,7 +1,6 @@ from __future__ import annotations import secrets -from dataclasses import dataclass from typing import Any, cast import py_ecc.optimized_bls12_381 as bls # type: ignore[import-untyped] @@ -9,6 +8,7 @@ import dot_ring.blst as _blst # type: ignore[import-untyped] from ..polynomial.poly_ops import poly_evaluate_single +from .opening import Opening from .pairing import blst_final_verify, blst_miller_loop from .srs import G1Point, srs from .utils import CoeffVector, Scalar, blst_p1_to_fq_tuple, g1_to_blst, synthetic_div @@ -48,13 +48,23 @@ def p2_neg(p: Any) -> Any: return p.dup().neg() -@dataclass(slots=True, frozen=True) -class Opening: - proof: G1Point # commitment to the quotient polynomial - y: Scalar # claimed evaluation f(x) +class KZG: + commitment_size = 48 + scalar_modulus = bls.curve_order + srs = srs + @staticmethod + def normalize_g1(point: Point_G1) -> tuple[int, int]: + if isinstance(point, blst.P1): + point = blst_p1_to_fq_tuple(point) + x, y = bls.normalize(point) + return int(x), int(y) + + @classmethod + def msm_g1(cls, points: list[Any], scalars: list[int]) -> Any: + blst_points = [point if isinstance(point, blst.P1) else g1_to_blst(point) for point in points] + return blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(blst_points), scalars) -class KZG: @classmethod def commit(cls, coeffs: CoeffVector) -> G1Point: """ @@ -227,3 +237,7 @@ def batch_verify( rhs = blst_miller_loop(rhs_point, g2_tau) return bool(blst_final_verify(lhs, rhs)) +from .bn254_kzg import BN254KZG +from .bn254_srs import BN254SRS + +__all__ = ["BN254KZG", "BN254SRS", "KZG", "Opening"] diff --git a/dot_ring/ring_proof/pcs/opening.py b/dot_ring/ring_proof/pcs/opening.py new file mode 100644 index 0000000..667db25 --- /dev/null +++ b/dot_ring/ring_proof/pcs/opening.py @@ -0,0 +1,12 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from .utils import Scalar + + +@dataclass(slots=True, frozen=True) +class Opening: + proof: Any + y: Scalar diff --git a/dot_ring/ring_proof/polynomial/fft.py b/dot_ring/ring_proof/polynomial/fft.py index f8f6d66..85dcce3 100644 --- a/dot_ring/ring_proof/polynomial/fft.py +++ b/dot_ring/ring_proof/polynomial/fft.py @@ -8,6 +8,7 @@ from functools import lru_cache # Cython-based FTT +from dot_ring.ring_proof.constants import S_PRIME from dot_ring.ring_proof.polynomial.ntt import ntt_in_place @@ -80,6 +81,27 @@ def _fft_in_place(coeffs: list[int], omega: int, prime: int) -> None: if n == 1: return + if prime != S_PRIME: + rev = _get_bit_reverse(n) + for i, j in enumerate(rev): + if i < j: + coeffs[i], coeffs[j] = coeffs[j], coeffs[i] + + m = 2 + while m <= n: + half_m = m >> 1 + w_step = pow(omega, n // m, prime) + for start in range(0, n, m): + w = 1 + for j in range(half_m): + u = coeffs[start + j] + v = coeffs[start + j + half_m] * w % prime + coeffs[start + j] = (u + v) % prime + coeffs[start + j + half_m] = (u - v) % prime + w = w * w_step % prime + m <<= 1 + return + rev = _get_bit_reverse(n) twiddles = _get_twiddle_factors(n, omega, prime) diff --git a/dot_ring/ring_proof/polynomial/ops.py b/dot_ring/ring_proof/polynomial/ops.py index 534fd82..6bd8316 100644 --- a/dot_ring/ring_proof/polynomial/ops.py +++ b/dot_ring/ring_proof/polynomial/ops.py @@ -31,10 +31,18 @@ def get_root_of_unity(n: int, prime: int) -> int: if key in _root_of_unity_cache: return _root_of_unity_cache[key] - # We need n to be a power of 2 and divide prime-1 - # prime-1 is divisible by 2^32, so any power of 2 <= 2^32 works + # We need n to be a power of 2 and divide prime-1. + if n <= 0 or n & (n - 1): + raise ValueError(f"n must be a power of two, got {n}") + if (prime - 1) % n != 0: + raise ValueError(f"n={n} does not divide prime-1") exponent = (prime - 1) // n - root = pow(GENERATOR, exponent, prime) + candidate = GENERATOR + while True: + root = pow(candidate, exponent, prime) + if root != 1 and pow(root, n, prime) == 1 and (n == 1 or pow(root, n // 2, prime) != 1): + break + candidate += 1 _root_of_unity_cache[key] = root return root diff --git a/dot_ring/ring_proof/proof/aggregation_poly.py b/dot_ring/ring_proof/proof/aggregation_poly.py index 904b878..e471146 100644 --- a/dot_ring/ring_proof/proof/aggregation_poly.py +++ b/dot_ring/ring_proof/proof/aggregation_poly.py @@ -6,7 +6,7 @@ class AggPoly: # get the aggregated poly @classmethod - def aggregated_poly(cls, fixed_cols: list, witness_cols: list, Q_p: list[int], cf_vectors: list[int]) -> list[int]: + def aggregated_poly(cls, fixed_cols: list, witness_cols: list, Q_p: list[int], cf_vectors: list[int], prime: int = S_PRIME) -> list[int]: poly_I = [ fixed_cols[0].coeffs, fixed_cols[1].coeffs, @@ -20,7 +20,7 @@ def aggregated_poly(cls, fixed_cols: list, witness_cols: list, Q_p: list[int], c V_list = cf_vectors agg_poly = [0] for i in range(len(poly_I)): - agg_poly = poly_add(agg_poly, poly_scalar_mul(poly_I[i], V_list[i], S_PRIME), S_PRIME) + agg_poly = poly_add(agg_poly, poly_scalar_mul(poly_I[i], V_list[i], prime), prime) return agg_poly # two proof openings @@ -34,12 +34,14 @@ def proof_contents_phi( witness_cols: list, Q_p: list[int], cf_vectors: list[int], + prime: int = S_PRIME, + pcs: object = KZG, ) -> tuple: """ input:agg_poly, liner_poly, zeta, zeta_omega output: Phi_zeta, phi_zeta_omega """ - agg_p = cls.aggregated_poly(fixed_cols, witness_cols, Q_p, cf_vectors) - phi_z_opening = KZG.open(agg_p, zeta) # take only proof - phi_zw_opening = KZG.open(l_agg, zeta_omega) # take only proof + agg_p = cls.aggregated_poly(fixed_cols, witness_cols, Q_p, cf_vectors, prime) + phi_z_opening = pcs.open(agg_p, zeta) # take only proof + phi_zw_opening = pcs.open(l_agg, zeta_omega) # take only proof return phi_z_opening, phi_zw_opening, phi_z_opening, phi_zw_opening diff --git a/dot_ring/ring_proof/proof/linearization_poly.py b/dot_ring/ring_proof/proof/linearization_poly.py index b531dc3..83a1164 100644 --- a/dot_ring/ring_proof/proof/linearization_poly.py +++ b/dot_ring/ring_proof/proof/linearization_poly.py @@ -1,7 +1,6 @@ -from typing import cast +from typing import Any, cast -from dot_ring.curve.native_field.vector_ops import vect_mul -from dot_ring.curve.specs.bandersnatch import BandersnatchParams +from dot_ring.curve.native_field.vector_ops import vect_mul as _native_vect_mul from dot_ring.ring_proof.columns.columns import Column from dot_ring.ring_proof.constants import S_PRIME from dot_ring.ring_proof.polynomial.ops import poly_evaluate @@ -11,6 +10,20 @@ from dot_ring.ring_proof.transcript.transcript import Transcript +def _vect_mul(a: Any, b: Any, prime: int) -> list[int]: + if prime == S_PRIME: + return _native_vect_mul(a, b, prime) + if isinstance(a, list) and isinstance(b, list): + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) * int(y)) % prime for x, y in zip(a, b, strict=True)] + if isinstance(a, list): + return [(int(x) * int(b)) % prime for x in a] + if isinstance(b, list): + return [(int(a) * int(y)) % prime for y in b] + return [(int(a) * int(b)) % prime] + + class LAggPoly: def __init__( self, @@ -21,16 +34,19 @@ def __init__( alphas: list[int], domain: list[int], omega: int, + prime: int = S_PRIME, padding_rows: int = 4, + edwards_a: int = -5, ) -> None: self.t, self.zeta = phase2_eval_point(cur_t, C_q) - self.zeta_omega = (self.zeta * omega) % S_PRIME + self.prime = prime + self.zeta_omega = (self.zeta * omega) % self.prime last_index = len(domain) - padding_rows - self.scalar_term = (self.zeta - domain[last_index]) % S_PRIME + self.scalar_term = (self.zeta - domain[last_index]) % self.prime self.fs = fixed_cols self.wts = witness_res self.alphas = alphas - self.a = BandersnatchParams.EDWARDS_A + self.a = edwards_a def evaluate_polys_at_zeta(self) -> None: if self.fs[0].coeffs is None or self.fs[1].coeffs is None or self.fs[2].coeffs is None: @@ -38,18 +54,18 @@ def evaluate_polys_at_zeta(self) -> None: if self.wts[0].coeffs is None or self.wts[1].coeffs is None or self.wts[2].coeffs is None or self.wts[3].coeffs is None: raise ValueError("Witness columns not interpolated") - self.P_x_zeta = cast(list[int], poly_evaluate(self.fs[0].coeffs, [self.zeta], S_PRIME))[0] - self.P_y_zeta = cast(list[int], poly_evaluate(self.fs[1].coeffs, [self.zeta], S_PRIME))[0] - self.s_zeta = cast(list[int], poly_evaluate(self.fs[2].coeffs, [self.zeta], S_PRIME))[0] - self.b_zeta = cast(list[int], poly_evaluate(self.wts[0].coeffs, [self.zeta], S_PRIME))[0] - self.acc_ip_zeta = cast(list[int], poly_evaluate(self.wts[3].coeffs, [self.zeta], S_PRIME))[0] - self.acc_x_zeta = cast(list[int], poly_evaluate(self.wts[1].coeffs, [self.zeta], S_PRIME))[0] - self.acc_y_zeta = cast(list[int], poly_evaluate(self.wts[2].coeffs, [self.zeta], S_PRIME))[0] + self.P_x_zeta = cast(list[int], poly_evaluate(self.fs[0].coeffs, [self.zeta], self.prime))[0] + self.P_y_zeta = cast(list[int], poly_evaluate(self.fs[1].coeffs, [self.zeta], self.prime))[0] + self.s_zeta = cast(list[int], poly_evaluate(self.fs[2].coeffs, [self.zeta], self.prime))[0] + self.b_zeta = cast(list[int], poly_evaluate(self.wts[0].coeffs, [self.zeta], self.prime))[0] + self.acc_ip_zeta = cast(list[int], poly_evaluate(self.wts[3].coeffs, [self.zeta], self.prime))[0] + self.acc_x_zeta = cast(list[int], poly_evaluate(self.wts[1].coeffs, [self.zeta], self.prime))[0] + self.acc_y_zeta = cast(list[int], poly_evaluate(self.wts[2].coeffs, [self.zeta], self.prime))[0] def compute_l1(self) -> list[int]: if self.wts[3].coeffs is None: raise ValueError("Witness column 3 not interpolated") - return poly_scalar(self.wts[3].coeffs, self.scalar_term, S_PRIME) + return poly_scalar(self.wts[3].coeffs, self.scalar_term, self.prime) def compute_l2(self) -> list[int]: x1, y1 = self.acc_x_zeta, self.acc_y_zeta @@ -57,16 +73,16 @@ def compute_l2(self) -> list[int]: b = self.b_zeta coeff_a = self.a - C_acc_x = (b * (y1 * y2 + (coeff_a * x1 * x2)) % S_PRIME + (1 - b) % S_PRIME) % S_PRIME + C_acc_x = (b * (y1 * y2 + (coeff_a * x1 * x2)) % self.prime + (1 - b) % self.prime) % self.prime C_acc_y = 0 C_acc_x_f = C_acc_x * self.scalar_term C_acc_y_f = C_acc_y * self.scalar_term if self.wts[1].coeffs is None or self.wts[2].coeffs is None: raise ValueError("Witness columns not interpolated") - term1 = vect_mul(self.wts[1].coeffs, C_acc_x_f, S_PRIME) - term2 = vect_mul(self.wts[2].coeffs, C_acc_y_f, S_PRIME) - res = poly_add(term1, term2, S_PRIME) + term1 = _vect_mul(self.wts[1].coeffs, C_acc_x_f, self.prime) + term2 = _vect_mul(self.wts[2].coeffs, C_acc_y_f, self.prime) + res = poly_add(term1, term2, self.prime) return res def compute_l3(self) -> list[int]: @@ -74,21 +90,21 @@ def compute_l3(self) -> list[int]: x1, y1 = self.acc_x_zeta, self.acc_y_zeta x2, y2 = self.P_x_zeta, self.P_y_zeta C_acc_x = 0 - C_acc_y = ((b * (x1 * y2 - x2 * y1)) % S_PRIME + (1 - b) % S_PRIME) % S_PRIME + C_acc_y = ((b * (x1 * y2 - x2 * y1)) % self.prime + (1 - b) % self.prime) % self.prime C_acc_x *= self.scalar_term C_acc_y *= self.scalar_term if self.wts[1].coeffs is None or self.wts[2].coeffs is None: raise ValueError("Witness columns not interpolated") - term1 = poly_scalar(self.wts[1].coeffs, C_acc_x, S_PRIME) - term2 = poly_scalar(self.wts[2].coeffs, C_acc_y, S_PRIME) - res = poly_add(term1, term2, S_PRIME) + term1 = poly_scalar(self.wts[1].coeffs, C_acc_x, self.prime) + term2 = poly_scalar(self.wts[2].coeffs, C_acc_y, self.prime) + res = poly_add(term1, term2, self.prime) return res def linearize(self, l1: list[int], l2: list[int], l3: list[int]) -> list[int]: l_agg = [0] for i, li in enumerate([l1, l2, l3]): - l_agg = poly_add(l_agg, poly_scalar(li, self.alphas[i], S_PRIME), S_PRIME) + l_agg = poly_add(l_agg, poly_scalar(li, self.alphas[i], self.prime), self.prime) return l_agg def l_agg_poly(self) -> tuple[Transcript, int, dict[str, int], list[int], int, int]: @@ -98,7 +114,7 @@ def l_agg_poly(self) -> tuple[Transcript, int, dict[str, int], list[int], int, i l3 = self.compute_l3() l_agg = self.linearize(l1, l2, l3) - l_agg_zeta_omega = cast(list[int], poly_evaluate(l_agg, [self.zeta_omega], S_PRIME))[0] + l_agg_zeta_omega = cast(list[int], poly_evaluate(l_agg, [self.zeta_omega], self.prime))[0] return ( self.t, self.zeta, diff --git a/dot_ring/ring_proof/proof/quotient_poly.py b/dot_ring/ring_proof/proof/quotient_poly.py index 5e81c88..f25a545 100644 --- a/dot_ring/ring_proof/proof/quotient_poly.py +++ b/dot_ring/ring_proof/proof/quotient_poly.py @@ -3,8 +3,9 @@ class QuotientPoly: - def __init__(self, domain_size: int) -> None: + def __init__(self, domain_size: int, pcs: object = KZG) -> None: self.domain_size = domain_size + self.pcs = pcs @staticmethod def poly_vector_xn_minus_1(n: int) -> list[int]: @@ -19,7 +20,7 @@ def quotient_poly_commitment(self, q_x: list[int]) -> tuple: input: quotient polynomial output: commitment to quotient polynomial """ - c_q = KZG.commit(q_x) + c_q = self.pcs.commit(q_x) return c_q def quotient_poly(self, C_agg: list[int]) -> tuple[list[int], tuple]: diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index 273d9a5..4ab0c66 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -1,19 +1,10 @@ from functools import cache from typing import Any, cast -from py_ecc.optimized_bls12_381 import ( # type: ignore[import-untyped] - curve_order, -) -from py_ecc.optimized_bls12_381 import ( - normalize as nm, -) - from dot_ring import blst as _blst from dot_ring.curve.native_field.scalar import Scalar -from dot_ring.curve.specs.bandersnatch import BandersnatchParams from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA from dot_ring.ring_proof.constants import OMEGA_2048, S_PRIME -from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.utils import g1_to_blst from dot_ring.ring_proof.transcript.phases import ( @@ -28,7 +19,6 @@ # Pre-compute Scalar constants ONE_S = Scalar(1) ZERO_S = Scalar(0) -EDWARDS_A_S = Scalar(BandersnatchParams.EDWARDS_A) def blst_msm(points: list, scalars: list) -> Any: @@ -92,8 +82,12 @@ def __init__( seed_point: tuple, Domain: list, raw_proof_bytes: dict | None = None, - transcript_challenge: bytes = b"Bandersnatch_SHA-512_ELL2", + transcript_challenge: bytes = b"Bandersnatch-SHA512-ELL2-v1", padding_rows: int = 4, + edwards_a: int = -5, + prime: int = S_PRIME, + omega: int | None = None, + pcs: Any = KZG, ) -> None: ( self.Cb, @@ -119,34 +113,40 @@ def __init__( self.relation_to_proove = rl_to_proove self.Result_plus_Seed, self.sp, self.D = rps, seed_point, Domain self.padding_rows = padding_rows + self.prime = prime + self.omega = omega + self.pcs = pcs + self.uses_native_scalar = self.prime == S_PRIME + self.edwards_a = Scalar(edwards_a) if self.uses_native_scalar else edwards_a % self.prime if self.padding_rows < 1 or self.padding_rows >= len(self.D): raise ValueError("padding_rows must be >= 1 and less than domain size") self.last_index = len(self.D) - self.padding_rows - self.Cb_blst = g1_to_blst(self.Cb) - self.Caccip_blst = g1_to_blst(self.Caccip) - self.Caccx_blst = g1_to_blst(self.Caccx) - self.Caccy_blst = g1_to_blst(self.Caccy) - self.Cq_blst = g1_to_blst(self.Cq) - self.Phi_zeta_blst = g1_to_blst(self.Phi_zeta) - self.Phi_zeta_omega_blst = g1_to_blst(self.Phi_zeta_omega) + if self.uses_native_scalar: + self.Cb_blst = g1_to_blst(self.Cb) + self.Caccip_blst = g1_to_blst(self.Caccip) + self.Caccx_blst = g1_to_blst(self.Caccx) + self.Caccy_blst = g1_to_blst(self.Caccy) + self.Cq_blst = g1_to_blst(self.Cq) + self.Phi_zeta_blst = g1_to_blst(self.Phi_zeta) + self.Phi_zeta_omega_blst = g1_to_blst(self.Phi_zeta_omega) - self.Cpx_blst = g1_to_blst(self.Cpx) - self.Cpy_blst = g1_to_blst(self.Cpy) - self.Cs_blst = g1_to_blst(self.Cs) + self.Cpx_blst = g1_to_blst(self.Cpx) + self.Cpy_blst = g1_to_blst(self.Cpy) + self.Cs_blst = g1_to_blst(self.Cs) - self.t = Transcript(S_PRIME, transcript_challenge) + self.t = Transcript(self.prime, transcript_challenge) # Absorb into transcript self.t, self.alpha_list = phase1_alphas( self.t, self.verifier_key, self.relation_to_proove, - list(H.to_int(nm(cmt)) for cmt in self.proof_ptr[:4]), + [self._transcript_g1(cmt) for cmt in self.proof_ptr[:4]], ) # Add quotient and get zeta - self.t, self.zeta_p = phase2_eval_point(self.t, H.to_int(nm(self.proof_ptr[-4]))) + self.t, self.zeta_p = phase2_eval_point(self.t, self._transcript_g1(self.proof_ptr[-4])) # Phase 3: Add evaluations and get ν challenges evals_bytes = b"".join(v.to_bytes(32, "little") for v in self.proof_ptr[4:11]) @@ -157,9 +157,15 @@ def __init__( # Save transcript self.cur_t = self.t - def contributions_to_constraints_eval_at_zeta( - self, - ) -> tuple[Scalar, Scalar, Scalar, Scalar, Scalar, Scalar, Scalar]: + def _transcript_g1(self, point: Any) -> Any: + if getattr(self.pcs, "commitment_size", 48) == 32: + return self.pcs.serialize_g1_uncompressed(point) + return self.pcs.normalize_g1(point) + + def contributions_to_constraints_eval_at_zeta(self) -> tuple[Any, Any, Any, Any, Any, Any, Any]: + if not getattr(self, "uses_native_scalar", True): + return self._contributions_to_constraints_eval_at_zeta_int() + # Convert to Scalar for optimized arithmetic zeta = Scalar(self.zeta_p) sx, sy = Scalar(self.sp[0]), Scalar(self.sp[1]) @@ -237,18 +243,70 @@ def contributions_to_constraints_eval_at_zeta( c7_zeta, ) + def _contributions_to_constraints_eval_at_zeta_int(self) -> tuple[int, int, int, int, int, int, int]: + p = self.prime + zeta = self.zeta_p % p + sx, sy = self.sp[0] % p, self.sp[1] % p + domain_size = len(self.D) + zeta_n_minus_1 = (pow(zeta, domain_size, p) - 1) % p + inv_size = pow(domain_size, -1, p) + + zeta_minus_1 = (zeta - 1) % p + if zeta_minus_1 == 0: + L_0_zeta = 1 + else: + L_0_zeta = inv_size * zeta_n_minus_1 * pow(zeta_minus_1, -1, p) % p + + omega_i_n_4 = self.D[self.last_index] % p + zeta_minus_omega_i = (zeta - omega_i_n_4) % p + if zeta_minus_omega_i == 0: + L_N_4_zeta = 1 + else: + L_N_4_zeta = omega_i_n_4 * inv_size * zeta_n_minus_1 * pow(zeta_minus_omega_i, -1, p) % p + + b = self.b_zeta % p + accx = self.accx_zeta % p + accy = self.accy_zeta % p + accip = self.accip_zeta % p + px = self.px_zeta % p + py = self.py_zeta % p + s = self.s_zeta % p + rps0 = self.Result_plus_Seed[0] % p + rps1 = self.Result_plus_Seed[1] % p + zeta_minus_d4 = (zeta - self.D[self.last_index]) % p + one_minus_b = (1 - b) % p + + x1_y1 = accx * accy % p + x2_y2 = px * py % p + + c1_zeta = (-(accip + b * s) * zeta_minus_d4) % p + c2 = (b * (-(x1_y1 + x2_y2)) + one_minus_b * (-accx)) % p + c2_zeta = c2 * zeta_minus_d4 % p + c3 = (b * (-(x1_y1 - x2_y2)) + one_minus_b * (-accy)) % p + c3_zeta = c3 * zeta_minus_d4 % p + c4_zeta = b * one_minus_b % p + c5_zeta = ((accx - sx) * L_0_zeta + (accx - rps0) * L_N_4_zeta) % p + c6_zeta = ((accy - sy) * L_0_zeta + (accy - rps1) * L_N_4_zeta) % p + c7_zeta = (accip * L_0_zeta + (accip - 1) * L_N_4_zeta) % p + + return (c1_zeta, c2_zeta, c3_zeta, c4_zeta, c5_zeta, c6_zeta, c7_zeta) + def divide(self, numr: int, denom: int) -> int: # Inlined modular division - use built-in pow (faster than external call) - return (numr * pow(denom, -1, curve_order)) % curve_order + prime = getattr(self, "prime", S_PRIME) + return (numr * pow(denom, -1, prime)) % prime def is_valid(self) -> bool: """If both the verifications are true then sign is valid""" verification1 = self._prepare_quotient_poly_verification() verification2 = self._prepare_linearization_poly_verification() - return KZG.batch_verify([verification1, verification2]) + return self.pcs.batch_verify([verification1, verification2]) def _prepare_quotient_poly_verification(self) -> tuple[Any, Any, int, int]: """Prepare KZG verification data for quotient polynomial""" + if not getattr(self, "uses_native_scalar", True): + return self._prepare_quotient_poly_verification_int() + alphas_list = [Scalar(a) for a in self.alpha_list] zeta = Scalar(self.zeta_p) v_list = [Scalar(v) for v in self.V_list] @@ -316,8 +374,36 @@ def _prepare_quotient_poly_verification(self) -> tuple[Any, Any, int, int]: return (C_agg, self.Phi_zeta_blst, int(zeta), int(Agg_zeta)) + def _prepare_quotient_poly_verification_int(self) -> tuple[Any, Any, int, int]: + p = self.prime + alphas_list = [a % p for a in self.alpha_list] + zeta = self.zeta_p % p + v_list = [v % p for v in self.V_list] + cs = self.contributions_to_constraints_eval_at_zeta() + + prod_sum = 1 + for k in range(1, 4): + prod_sum = prod_sum * ((zeta - self.D[-k]) % p) % p + + linear_combination = 0 + for alpha, c in zip(alphas_list, cs, strict=False): + linear_combination = (linear_combination + alpha * c) % p + + s_sum = (linear_combination + self.l_zeta_omega) % p + zeta_pow_size_minus_1 = (pow(zeta, len(self.D), p) - 1) % p + q_zeta = s_sum * prod_sum * pow(zeta_pow_size_minus_1, -1, p) % p + + C_a = [self.Cpx, self.Cpy, self.Cs, self.Cb, self.Caccip, self.Caccx, self.Caccy, self.Cq] + C_agg = self.pcs.msm_g1(C_a, self.V_list) + evals = [self.px_zeta, self.py_zeta, self.s_zeta, self.b_zeta, self.accip_zeta, self.accx_zeta, self.accy_zeta, q_zeta] + Agg_zeta = sum(v * (e % p) for v, e in zip(v_list, evals, strict=True)) % p + return (C_agg, self.Phi_zeta, zeta, Agg_zeta) + def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: """Prepare KZG verification data for linearization polynomial""" + if not getattr(self, "uses_native_scalar", True): + return self._prepare_linearization_poly_verification_int() + alphas_list = [Scalar(a) for a in self.alpha_list] zeta = Scalar(self.zeta_p) @@ -330,7 +416,7 @@ def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: x1, y1 = Scalar(self.accx_zeta), Scalar(self.accy_zeta) x2, y2 = Scalar(self.px_zeta), Scalar(self.py_zeta) b = Scalar(self.b_zeta) - coeff_a = EDWARDS_A_S + coeff_a = getattr(self, "edwards_a", Scalar(-5)) # S_PRIME is scalar field modulus, which Scalar handles implicitly @@ -367,6 +453,32 @@ def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: return (Cl, self.Phi_zeta_omega_blst, int(zeta_omega), int(Scalar(self.l_zeta_omega))) + def _prepare_linearization_poly_verification_int(self) -> tuple[Any, Any, int, int]: + p = self.prime + alphas_list = [a % p for a in self.alpha_list] + zeta = self.zeta_p % p + zeta_minus_d4 = (zeta - self.D[self.last_index]) % p + + x1, y1 = self.accx_zeta % p, self.accy_zeta % p + x2, y2 = self.px_zeta % p, self.py_zeta % p + b = self.b_zeta % p + coeff_a = int(self.edwards_a) % p + + C_acc_x_cl2 = (b * (y1 * y2 + coeff_a * x1 * x2) + (1 - b)) % p + C_acc_x_f_cl2 = C_acc_x_cl2 * zeta_minus_d4 % p + C_acc_y_cl3 = (b * (x1 * y2 - x2 * y1) + (1 - b)) % p + C_acc_y_f_cl3 = C_acc_y_cl3 * zeta_minus_d4 % p + + scalars = [ + alphas_list[0] * zeta_minus_d4 % p, + alphas_list[1] * C_acc_x_f_cl2 % p, + alphas_list[2] * C_acc_y_f_cl3 % p, + ] + Cl = self.pcs.msm_g1([self.Caccip, self.Caccx, self.Caccy], scalars) + omega = self.omega if self.omega is not None else pow(OMEGA_2048, 2048 // len(self.D), p) + zeta_omega = zeta * omega % p + return (Cl, self.Phi_zeta_omega, zeta_omega, self.l_zeta_omega % p) + # Legacy methods for backwards compatibility def evaluation_of_quotient_poly_at_zeta(self) -> bool: """Legacy method - use is_valid() with batch verification instead""" diff --git a/dot_ring/scripts/export_python_proof.py b/dot_ring/scripts/export_python_proof.py index f8e5ec9..0d27e69 100644 --- a/dot_ring/scripts/export_python_proof.py +++ b/dot_ring/scripts/export_python_proof.py @@ -1,8 +1,8 @@ """ -Export Python-generated ring proofs to arkworks-compatible JSON. +Export Python-generated ring proofs to canonical JSON. This script generates multiple ring proof variants using the Python implementation and -exports them in the same format as Rust's arkworks-serialized proof vectors. +exports them in a Rust-verifier-compatible proof-vector format. """ from __future__ import annotations @@ -283,7 +283,7 @@ def export_variant(variant: VariantSpec, output_dir: Path) -> dict[str, Any]: def export_proof_to_json(output_dir: str | None = None, variants: list[VariantSpec] | None = None) -> list[dict[str, Any]]: """ - Generate Python ring proofs and export them to JSON in arkworks format. + Generate Python ring proofs and export them to JSON. Args: output_dir: Directory to save JSON files (default: tests/vectors/others) diff --git a/dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin b/dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin new file mode 100644 index 0000000000000000000000000000000000000000..e4ad81c1b97317b3d8eaf26c1726b1698260bf0e GIT binary patch literal 98640 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

$ZW+TkR#IJ?+_4 zW=P?Y$|-){&c8kJ9Vyw8TUMo0 zS@l6s-?3h${sYT4U#ekW5N0t;jdpujPZdrNnUF2~!+70sb$=Kn)^K6VgeyODbM}<` zK_;P-6Cy)P+K>?HylY|xnz~%9{P$ema*%&aC!mfU1m=!PgwK} z@ViJz?T^xgOo3O=Y5)@Iqc;2TTVAt0T=uXSNPShTq{62Fr5>}bW4)aM2CU2gzQw1w zgPiFn7O$HXkaCA65RSrQ;d4UG+br)F0#)Mb2<6CU1;Ob7j3uR8V3b;!GamNidJo~Rke4^@V?cBQUc)qHXpT^gN8qf-b=hVh*8IYmd*47WB*>_}ST_NYm; z6FM%LnS_8$b-+i^q7}b1@tUgnw(0P1pbg%bBhk^cDv?w$!vZUr?uIa@>L|u(;9wwI zHy(SFGN(m%=D*_R8Z+B$Qz$Bp0#_ zgq2rxmT3)47{-z9P!sXZ^6EZYIT+l*9G0>Z8zDB&M4yw{qAD?dnyr91-v-f%X6Z;C zLVi&up@ZlhyQJo&XHbdh19PQ%z1^e_y{EB@%_aWto=v$$zg|bEfFhQ7_rvrSmwnU; zj1z5|Wa;Kq%Hm_en1c0g=52%hp0JG* z6mFW3d`sOB+jMA}zS(^ylxi+IXVwwboJC;hOG%i3EsA^&Zr4J%8n=+--y%#~Y|D?u*fElwv-220!-^3-WPY18Qbq z{Z>Z?E6=6tuDE6|D2T5SJOvDLdBSM#QwsRv4L*5s#i?$u3^zpRP$~SIRY&-BB)K$k zS0nyKbp2F4LF9v3wwu_!Wk?L=oD2_lBQi)|c%-9n-RmIj5O8AX6+?@++_3a#g zYY$t8a!Y!exIc9*QNs&)BigY5+8)y;Km%KL+86HHYb^OIZuLWdL%k-GAB&i1F0Rxkt>24X4eo`0>R$0j!+*OSzzoqP5*VrQ3`J7P@xHr{;6(q@KEx9}?{0Zd1 z4m#YypcMdS&20adwf7P^wq3bhW;W$gG<&iD2OIz>TRGR~W9utU@I zF!cFj(FX==RpX!rVCiMh`nG8n@9R_d1v#p)NJM7TdHW+iO)HkPTq(yQWos@0gfIE% zP0sJzj@LU|Dg}tqPStlYrjxL`SBwUqddc7|2>_12V0oCx$*+QWpVjgf=a8Eb;`$3` zaK$tZVq4`?v2c)fP&aji*C720NgDP#m~p^Q@GFy@YY2j&AW7Ef)sy7ehC`igK8p=P z2?c;QkOx`wZ)fkZh}9z+i{FDSG%CwVzWS$fwHGi|b)tKZ)q5XO!72`l=AkzM8*kXO z&3GKhyK-pBT!n4BJLJ+)+$QxjvOB|ql17p)=f)bp_;|2jK!Hqt%%Efuj-(_!$CH>D z{_DSo7f)3K!hjeEa?m3Q&#&HG>IRbLGuCuA(Q#W#2IUcEXxEf z?*cE)HMBOp%dtCkI)w3gT^qd%VxXzamJwzi0+NMz&MsmpbeAOX8VA$y21erJMFIW{ zqPOb{zKhu+XDrNSw<#wcBpm{Tc-55kqwtcTRF*{#+y^h*>jWkM4a;nWSbIKK)LchK z63)~jpldoAKnFN;2cCys4P zSp(D;?3m{Vn+iYv+?0I?eM^oFAv{DTPRC%|#dtfxcS~q(d{aXWtu7?vD6D}7%=<9q z;Pz&#V3%3UnvLH9tkKH;oVFWV^YHz{^Lrr*Ynqr%5==eznZ|0voqr|(Oy(z>h z8(da_cSv;<2U%z^+2rP$`eeZ<%tnTRFH6<7$qV`-;_Mmz!~e4ZEYiGCM7j=cK98RO zk25$qz_~|ABs*(>v8eo`zV)US+G(S=FJQXvUr!RrJ^&D-iZ8nuEP0>zZlXTc%B)r@gl-o zenP**!zRKuJk{A4E%VYR!dM6^8=9}IJ@-1qjsuq)|rkGe@?l?$x6H zVS{)W4ldRz&V*O>r2)7+(+C~xS-mOIdJ*DiqMq2rmr@-Qn3XsHAW*)c z{B)8Mthclgc;CBcVoi$d@x=G~;wMVId5yA*r5V#K5n~3`Zb+67J1oN!{fN%@e}=ao z&2K9%-{_k1SDT%0o71y;Sz+zEmTk95KDdNEw zjqxKVV*c4wkB_vH)0gl*ZDEGbA-QybtsBptBCe31e+$C(5Dp=+xJ-(Ds0{oQi2hZ< zznClZ09zB$m`K4IwFd`$fw*XchRB)s@GjCI$hR1ny%o4!$B*UJq~ZOlU4ehv$5zSI zAZB1TcYn(X#IGY4I4unhEeye;lv{KOebp25~w9Qz+?xzX(pkef8 zQrZRpink}R(TCG<$RByeTrEQ!Ks^ON9x$%gzwB~SMLKK18xX)OUZ=|W_YNM%tnbX716ry8O9tIPk;>=4P!1gyzfZh``qNb3m?;w2 z0k2bqb0?nw+yxMrS38GUEf0V^RyxE3)EB2=2o&?pY<5xuQ8FE*wUY#v1eSTe#5F5E z_w_~-Q-XKWxOR8~L%tSYG*T-KVK0?aZIt0y-UAref^_p+cE;ayvv5{QkuQt+)i4PM z2jFgd199)hZ))@_3gCnER-w+`-?yFJslg?$CF1}f=8KRWyz<3S!PpakeyQBiyLnws(|@&^Jem++m|hmgN^zIwv97hyy3z2RIWdx zT9ybBF0<6rtUi8wZq9ARC6I>l0xh2t6HNx?nhwxmM?)PVg{@QV+1^+OO_|N?jde@q zTBc?OF37) zACcV1H=muQwIK|-CU(3T&Z&lK6%EvCX#ScA=ux&W^Qa(*aLx`O#vz=nMwein=ppX; z628Yf6Gyiq1?OBHSXcxkAN$F0XO|`7ObFr**f>BUV?#(A8YxcTDX4G=NfQh|hJoCW zpR&eNH<+*sq}OgIwGrX*ic&S1BG_mP#_Oo^V8u(xRNl z+J>cGxAaP3T^2gN0`g3X&O08i&n1*fhsMJU07P!A9g3Prz z$@@Cw3+0JtgQ8^{FPJeY?>d5HGyRd?F~|R|_R+2C*Zff-&6BhlmdZazaQxxs_Y57T z9trSg);+Zj7I;AP85nZ(&Ug$U*+5_6TpjEviR5l@WfMk*WV-|ww}WbzJl9-b#q9Zb z0DK&Hi%#V(Pt6a`#oRe6?!nsy5$K6s#c4LEnJOQLW#R;Xi?NWvyf9Rxxa@tVZ6Dm8 z9ex>LyMZWF8g1$??DMKQm9!*OitF_pvaaIZ_H3*omtbC?lbf(AB7&JS>&`i5c`<2) z3U>9djr}R<`WB}ZMa)4Zb^Ub6?U~PGOmKnpXI{o*t(bOx7bb_ZZvsgoC$`kJ2el1> zybtWh$=fZP)Q%oU6-o=4p}NsCEarj+7{&xQ(6oE=Whh8G?AZF6)fz}k#C#0pMpe}Z zK&!zW>@~1|3_e13R%et=y;`wGzNbL;6p>i_^=B~AWh$nlt23I4a&z3yrV@zP)b-tG z@1q#ENxt4EFEKm!)YBjh61o^2ip`GoYBB}^xc~2B0f0W@oC4$Bh_E-HTvMr?Bz-)A z;18gbEMO(4Bq~*TFmM<*`!MnQPECj~#h7+3wUK-Xqgkh@xp*<0M&Ye)^%1CPk_hhF zw4F*7-OLWJc9Tj6tMxWn!5@^SDy_IgcnonS2Tw!ylEN^_2YlBhe`6_F$u-jIOX?Y6 zF0o_N7_upUd}I!6ZqM?N79MC3KJuKxT-x}k{Iv8Ko)}N@9OlZZOgm-bv{UrpCVu=Y zWG-13#yN?YtxMBlu)?RRkDKB8@w3}pN3-&Ha_9(~k->uRkW;hz${YnJ*WD*ZJvlW% z9A1|mU=n%oo>-?FjYmorKLh4+R*WWP;8UJX@A+AiDxa=tU)+#+)oflA5bc68{vCdN zBS$8}MoSp|eASk=_d(8^2IU3hpez3$jWv7A?qHbDya@)UWv}jg`JxG?adaM=BMSx> z)%1K7pFro|0{_I+<08%4p`bp@QaV^32H_jL(v#k&0pPPNyB!k8CNt$++T)4F#Sj}^Q0$@AZ9i!D|&p1m#_tGpo2 z)JDN-&~*5)ix!hA9!3lWCf}gG%}+;>9+!vG}!0o8}>zzC)Cw(kzCfM|=7DyL(I(Z3pv+ zmrw@*CN_7izV`R<8dHKXveF|G9IDlxHULI5buzUdVRbhmuYg{Xzs3iPV44{AJr#;P;VSdmR}p>pT_B zcs|ikU}HaSrw_oN>$yf^ZWT3_|3;})vDdE!LoWC<7m`>&iTl6gE75~t7~*7*M?L4uGze=KGb7?5|rvrN9Sa>aW8 zWzn+`JzS)D;t4NEqMZNc(=plQJZNl=-!)$BEIITvU7PNh@cD)FgV{{Aa4ykK z)|pV}z_GlkmDx;y zbA~k<;UUSQzqB9L-YkMK8OGp%FxP&|;i*Uz<{P94IVq0)oDm@xZ3}A`k2+7bE^>T& zBVuv3WywY2QcrFe;|HMrtk_@#2joFld*49rco@UvbYo$?EdS|&pI<}o(6s$lwB&8T7aJS&`{u^&8{Tvz2qG>CIkvf!L2iF zoa#EAraK7XbVaxe#?KGyf43YU1di~|@hKK3m+`pIvxS%}O#SC96UtV5GG&X1ev>D2 z_f)ZITiZF~4O9wUdmcfU@w2i#A(z^(9z5TEzjXZH1Ein|*5{asdm@wTstWZhTW2-t z(^x;w+`l6Bg8?h6q%%L0)mB$>A~a8>(;eppP3^XJnXZ3i{1DT%^;?vqxTv9+p$Pt5 zY0BJxMznn|V%u!h_`dO{YYkrnkEi0%sIF;eM$FOJprmct@zn{SJN72#QdFbhb1KOe zd|hfW#xITkg=Evui>`J6XT{tq5f0!DyTopq1~3kyyv;0aPO1!RJRo20Q;B~o)k+Hx z$J|%Vi!shkC?Q+Z_BD2#pU(Z;EM|aW0cg+hAkcFTv|kA5J>(b!RCbYQlSaq zuppydh=C)tUX>+nOyKJFB~(xP!_J4sj`T!#cZXk^@6ErKEgl z1p0A%))pc=+WRC>u&>c0NP_c$4@HR3_E*6*t7*@^gAf%H%Hd>1T-R$*9(ub%Fkq9)l>jm+&bgz)X;&f(` zV)+(w=ar-p>@kV5UDeY#Q;~Yd{pblUz!vX*6VH<&`FF079ya#hF$9I zkgo32h2&bOm;dFNx0Ptp0U6>53_M`S!YH)VyXy{W141vEcz7PXI4AqPdXM?>-$ZvM zSYyzKR!O(N=S()GR-8C=H1cR=27*P^?HV)s;wsLfvH~Y_!6WrfC*h9S- zBRJOPR+BNYj)d_mOo=m3sD7*=uYr4+r+~ivWpbB)vHgoq(ViSMK`e(8R_3mvG!lDv z;fzCj0=#aTwkh{WX-?vc=eaL4xi~f)Z=ZOTPiu+qCR$ANn}$$A-nycbddO_gyrCw9f56MB#;5OnhvP8IiIfD}uOc$$Dk zs`7e9xm#~SV1OI9!GP$f^VRrcSli?3Z*JnxB@Ti{IC^jANlA}f2MtZg#p*mI`L0^^ zDLskA;v5g{vg!6^YbZondrc9}xkPb;_+|kW8Gs)8V`P{5uWq)EA7T_tw*U|HQ_VN9 zA&Q?aLZy==;7Dk~nDce!iwnwMWG{fj1lYQ=oS;dh$LHp>dRq~mvw)V0>*QzSYQe-! z=R#&^vnfOXqRz_@U-W-bGVpI76X|DoD^`h+ymr+x{@flMqPq~8cT}nViCZqK_0WU} zYQ^v5lW-r3xTQ4@3OG|b6An=rM5E(~7W0Y}cLdZ0<8q&wvHw&Cs6Hr8a=>EY{(YCa zWdzN3EE22?FB~Zn1dR(_apyP6va=SBBHz(lRA;(Q4Kiq_b_KCK*6mg=qRln&3SahSup(U0;4Gnfk_(ClGlPiDrv?}GcDQo0C(8dGeo40LS=!LAxzVx9 zF#-<|--rB~Vl^qpF?-50q4p}f(2$J58P09jWR`?9fFeL z2z1mIL4>D6Jg%cUGJLq{9!I$TBklNsF7S^F>n63twc*bF1JDux(MvAi!W5oqV8n1$ z-u>QXJZ{OJ?o_sw3b9_H(rooDZ-NVJ2c58!Y5xqCy;$8-X8KbHLfX@fYY(4fz)@iK2~QiIw_QXT9Aw)k@Z>F^95C-L?y7^{l3Qh$>nAyjii zdH$4THoR1C2;@63H;mm=nqHNWX|OUcT#qm`fdfm^A2xQMPW9#F>yrYlxP`{^|w z|3ZN)KMw8PupHkG*CnSIg5tv7QClXA}eLNq`1dRB)T%>+Q+m~xH)mZ|I~?cFr4+Oj-GP9qAx1nm;N@;UK=U?n(A8D17Avz2L76Y<#FqjiyDv)F zP5ioO+RNjiAy?1ki^fqE-)PZ1?X}&;YHYKhCT5djlUpF~)5p>J66~iNbv*S&HsAO6 zrW99r{{sRgFi7g`>g{}aA{bdOJ=-c_=&oxF_k9^vor{;yENWOd(IYR~urUm;3r!oGPyT zu-MWsS#j`@8dC-&&;1XxKK}5AWbMKBklEpwH%HIZbaZq zEt5@Tnvh{uE`OE)=d@oiV~ylYTVvv|Zcp>=!?_sOP$*Eo^#M?$6#Q!2C%tS7LZ%!> zRRIq@(zsnd=|x1j>J@(hbzaU5`#8~W?z#sOxs$nfgL3zj-S@T`J-QF5vRUqaLM0 zor^_7eA#mHSmo}i?PTpef$(iTi>_+~FT6@Hu%d=0xZ=F-$YJr=eHnGy<2JK6#cj6K z_60Ys!*Y0qrALo-p#|p6ISKUz7CmxY^4{B@`^rVJhuTREOW?KpdVBJMoK!r&HPbPi^2e9Gb zQ*hC_=8+A)$SN;7j2aXbhaHckb5%~NID5z!KWRQ>#E~<2SH~ zBEN`m|BuxM01n>ca_U#R@y4xCOv<&VZ2=FXPCju3J-`h>Z~@_^q6L`ynvIrLZ%}DI zEQ~|2oTX^)Ex)40Tug>$;K-G1Q-FwlW- zz3sEVZe2kM0Yirm6&+K4u!JP`68j>7kMcI=K@sQAv_Walr!E_(^Z^nsYJ#z36PdWh zml_x|i&N$1&mVwtWDQKu&!Dr! zl35TD5BlAp1)scz^j8dVkoBC!#I3fep@HnN!n3XtWd4m{JGBPRm%^fcZh zIbBiq--J{25{nUnDM*t`a>D!$uKpHm?-Gy#GZ;%dq#MZne5&9O!&zIHbJR`d1p z=X1ebB7yV60S_<+XiS#!IE5?kWcZ6i5w4%CVA1w5b@{)if2Qn@gv3NsdP=Mv1Q z+g5ofMSxSA7Mwszfiv4{Mum=o%LSVclaO$qqY^xNeyZTV+`Sm%I`77 z2Z=9CNV5a}<3H4PII`E8^+-U8Uq3>m{r7B4F-yrzPWEq~FO_F~x}h194#;rSQXo?QZn?zn34Pe$ z$7Ibn@BFwUv0Cy2g3%t&Y~Yu5LfJEsv6X}?qD!=!g+79iyLQ+K(i3fE>#^|#Gn?tP zIG9k3&pF-ES7A9@9KPo5w{Vmb@^NVgxJU~rDi*!;Wke^%PaWR6$3=wB^y}WTGC%XL zfZ;Q#$pU?`LQf<{vb%%bbsMUHi+_hBsMY%$9;oLF?1NcglgDAHy7E^*<+t2AGxs+_ z(lRR_N@U&}dz#q_4eF&-8UgcC5c9|fw9n`&xB|i&+R7ctct#*y*#1BOJkDAX_FFj2 zP|SGA7bfTlImHZfcoeWxy3`aYsYO!+OoJ&3&>t=M*Wei4@tcY}vPmM#E@eLaSx7mWbvHH4iDFO1+FzBiUfk$j6nnoJRNt}WMaZxWOq@2x^RPZlh4m44L z3y1UJK;OHim6`p;XeFexD zEQ8_HfZ2%x%Lb0wzwFx7=a;-vWq#vqDkxnzXiRJO zD)fK5-(Hpr@E2?wH-*8>PLvA9u%V>*aP3Nag0XGww9N)JQWfb4J0{}&S>XoH z9F^@cDcX&7lC zOPlH_Zn{73aMf3=`zzYDfHKshxN@7+pM@rpQ=VWHZOP)qwW3ABlwedV8%|@Jmeq#| zSdrtj+OX7rwq}wENaGSjoTJ}bgy*53AX0G_I;3Pr{ZoX)vyFva>IM1^1P3XNiW1AO ztAIJ(Lfw_tLS)HSi+!E13lYUHvMnhFmuP5!hk8w(e~3Xv&rbkI^c49d>N+{cobIVn z$Lv&{dCH2_f*;F!(n}RAzb4mh#8${+P9W6zGSC1k2p0eyY_vcR>xYr04g}X_`DGZ& zG}b&&$ka7ST3MJ^HZ>8NamjHsBW3qncxg;;sh%2EEpuKh%+gVV$))`UHEwbu;UCpt zVX>5hkDovnRSaxfJ1BC`jFgw5f40{>-^6Enu{XHrTb%4nh7{!Cff~jd zp(kn4fEGL;MRBe))OW-%>t)*)^(wcinNia26w;@B5g;M#Gj^J%Dm~hjF-eh*wx>1x5Q zn=f5W={Gf*@(1vU^ zYnGXZ@_z`-5xgVPMrF-X?sG9^Km70_@kc1VJQB0Y3ja^<&@c^ z1mBq`n$PO6`e5w*p6sS|gHpF_E;?NtQ0z&KBE56Qj-(A~LqMNu3c+RP=c!f=f* zbf!03FSmjcJvW(}#JuvLNgAj&E;(p1Qyx+Xl$ty85YHsaO-Abbw*5k{*W2Qb+}zsw zKq-_;Y6gQ5f0eiN^-pM~UAfI@#+Y`Zx=hzZWj%7)^%x-Un}19TlfH+YMXu?1q%$GB z*&1rgvHWa9mCtYjMnJpqz6dA*kIX)Z1skH(U8jepMJ^D#hJ}vVIGY{#3s)lwvU(x| zRGCJ0`kW9!D{qOJ!TBhDir(euyR5Vl6?sPd-+3nz_+qab8@PjGO}T!rs!gaPk0olVB7%PR-vaj3a`r;e^6X@M)ls z+`g*Z((RNfI02C9rwLYq%>Jw69|a)}c$dgIX+rRX{{NQk&^l#<^Dn0hrN57ZZ*{|N zWgHuxH?kc-7+VN%S7Wf zys-EcyJ`1%5-OB+KfXMy9Qg0}rE@$wLmqg&PousuYG$hMv_?_)6FVg4l&J+H9e|r< zrlTY7BMw(&wCMk?wswg6fps>Fz#!x^CYUJ?07F2$ze!bk3u_1P1*G8EVzdg@ zl8Fb%D%pGQIvRQ`?nCcm9$1wcScGF$6jkxK3VIns=onx+J=IST2!a~DBn$Q>uePNn z?eAUsrpvIf8CB>VfF02Ka3bJ+AXiJ|qrg3qq4xQGJ02DY6Uns0PLGdu=>rE6Fq4cD zbD7yq)uqaVp*I;Cpf5CaEP!o)W310rCv`2UDE{OQ?i~QQu#l z@>C|tpQwj>sGxPHgk-4A=X8mr1SxzCMYo}nL{q#d>!IB8>0sTh+_cRakdxdftiA9~ z1X6||4-=;2!AA|remU|H@l_fw!F$xtu)@ab_URltFP^6dyu2TT!S1;2sm%xTxms0` z$ibHhmLcgDa2ie|saw>ToZ92oF|0JDxE5V(3z=opmW&h%8cjT(^XVK|Av^y4R)zGD zC=H}G5QwGo#-IT&InMAcClgq3@n)U{6Ahx-!GUL~Zo&l@g;F2Mg)uY;;$Y!DANTodk z%5jCfl!5Ry`cpOV6t&m>ET&Gq&l~m5*?;6D)fC5oSM;3Y)X&4;N=z<+m=zQVp>kp& zg?~M2a3l+h6^##MZ9>z?J^kaz%3*sL} zwX~+HC|Dk2YjKBb3d_-&#B9~=jo|BRtp>J+B;oc>^uVd9R^P~hw5?>h3r0@vTQFuf z8sVA3oyW3Iq>?WM9r2uo2aopT!>({nC5;fDg+m#C)!MuDdX!JNkSSwCAX(hVj&?CzOj-V5q)jcyVa=Yt+|r>80TBhsU-UJ>CYw1djwK|0?D&t1#0>Sw z)wJ3Xi&l>=DNRThzc2+Wc8E0FGDYN8t(kU_8j;$HZZ>h7lqV_Xs8ow!{J`UfuCy~E zR31i`H64x~cF=mH+l(rkG0^@}4Ne%l&F+Gsc%g=8abSn1WMfiAxHcse=wEkQ^bm_? z1JAvmF%Z&Z6&R!qiCja9KH%JHI4P7f1n;pl&CbXEY_#+IGa@bh_f&D?i9XOFg$pVB1n)1(XQ#L>^$8m1qd&6!Pc3W$`n3INb5_X zQH5T9*91Z;4?56Avt87#40#Uk$go?%o0^%3acsMDj>J^RLU96_ZCo)KCwn+1c~H)or4OW~nuz-@;*JK zXLRAItJsz|5@LV0FOYUFL6!r_r#4+w!q)69ADE=H_`O2AMeAjGAC_3C+6twZvBw06 zzihl(jvUmC-W5GOy$hp_ie$Q$}Q#-tgd0^qeD-K2uO zE8QoKiQg5p$@_4DxakD7#1)iX_jTAj4I5nEI6m#CpMX$j7m|_nsuE?4qO@OIrAF_y z_~7kd71u%UB_t~CDN2*aguI2nX3GzE<0Pu)(?KgIlfzi^Q zO7#-z#a6%rC|}f;?fqC1q#AYBec#uz`@u@|IBKC3B*mg>gVrQp5mHZzmo1J+02XzL zi8oxk&xhjUcE=u~9LFrY=vb&F49%am8yHoo&=)&nw86#klh zA~XV7`A|_me(#HNWon)b9oL~dAfC{l5DJ8&s$XOPVb>Beup1l4+T7^Tz})KN21e?( zPhYvUZB}};!1A+Eb*bIm%?{-`lFXog#jf5!E-uldvwWKK|I(KZX3A0Kh*{9%f^<>DR{5q7e3pLMEFiG^Gy1RAwvX_r< z_T8qL*e?Cg%No$ST--Z(6|0v&3Hv-z-lT3%{97^cu^->o5y=IQFFVgJI{l=6XJ>57UA!K)e?fC$QJ>+dj8665j8ilH*661>d{F_8|>3ec0hfNvzT5-aIJzj z(O@WI4489appFiwM)93Mf=%tkG8bbsfAZD(; z$4Lv=$BgK!i4&dfA=#szcgSD&tXskQTmZ$W2X%v^LIm$QX8v|Mjzo;B3pr2P1~eKi z7jyLKlj<6u5C^s3O74mv_LOSUs6&tWH5Lb_aJ*lI6rFFYDH1i|23y8N9P@yNg(8?J zgeKw@*dR}ox~XD%+y4`Mw^y2YezvKTo>0D8{jMT=B%$8 zMAoCUoncsHMuRNQlz@D7q~gL z4X|A55!7x|HAG%E5d97Q>b`BKAHn+L7tYU(^xd%ENi*lCqebSb(u=jTPq~?ba{gJI zOlXn#LGD32z>#!cs5f7>6szu!cv~Wn@vi^IxyOCX4nk<5&>3E&CqR4f@$uwWt^5$d z>BE^U`+g2J{3b-09It6JDn=5E>v{QDbG|@2Dfc%t8M=pnd4|dFoyoB60G7YpP+zi3 z{uG?T#`@)Z6aU^y-DYe$rz&|&1(RNZ7;5Y<{akbQ0P_Gk9hi~t0^ePe+Ph<8Dl}24 zoo$C`KruDMd~NEkC1q%EMXraSnjh_EL;=lSwR*247H7BRe;i|CKHw}HBPg(K#F^R4R^#0f#`w36XYuh!Lv{LRi>@BaPyiiHlUkU zMomlN+Z8FBxIv&%7>~wD0#&`>6Tlzut|zB4oFLm)x~z~&NVKFWxZFyB5}Z>b+*bxX z_ttGVr0D6h!nGfwZRIWH7>s0eXchy28Ak?}KN^%E{15w7iDjBc4zf5V6V~U(EOHAD ziZ6(U4e6V2)-P-q5jn1H_eXWbz*>Z5CmxR2sJVGxpR|m~jb~sPl9n{jqBZ8F9E75hI3Q?i z6m9d^jCt>!RehiQeAYt|AAnq|d7W}IA&Otwg}-tx-3s(46UnTMaeV-&&?p9CRrl>3tsrkPfT^a~sn^_TUN^7_7i%OjP1ty3TZTt=Y5 zk^?PK5f~T&k=Q@8YHD$v73#(P}QO|K#hNy8~ z2`(}PT1*4<18@`0Lny);Ryf1^(~Lq?NI2Sf;&xIyBcVuPep$LUJFWL$xB1A@PP7jRT(Zbr3 zj6s?~`t4iPCev-T4GJKxGZsSt{#J@U!vCRmTyNanAxKn&e7fo)q--A@eG%bsTGLz< zHSt!H{9(~E-V#wjtO;VbHHeSHEod*J&ja<=_k~}|(E45nejRXja7paZG+W|Q%}gHr zsUX^IzWX`DUAPJmvfB3d*AFAU=^v=k3{dXHmxv#?CE_Foe6lmO9|}}vvZ5O@g_}Q7 zq_rbC`c)RrWM3dW704SrxN=lLX1iit&Im{FSg6K{TJI&HW`7(3B5qxv5OHWihXUdU z{?OJ_PEQ?pdTqRkZ>BDnrgc#VRvMypnp;lC>`Jas^|p{AN)I?Fe)6g^3M(o(?G%&5 zw1^~REmij6`}Y3wG73w-g;U{VIl1DDF2PL!J0m&a-`WGxq6+*8=#(*gzoO7C^9%(Q zQnbA?dahc)rH8mU>c#WtF06>beHGf?napV^Cb8&+3$XzJ)I41^n>sBO;bD>u4%A%^ zAoK@g`az>RcE>Rw?)J?Gq?PB3KYgyo9VvIV550HsdQWqpGq&jYsG;xK69bFr8tIJ0hf-u3#JxSvuRWl}W~iov$qEz>u)t z!mtqsBahNV7<&OMUK$0Qv7w%M!-d|UOGB|WGs_3jzds>9F z=x16G=-yrZns5igq3;Le;Xk?$hH*iUA8XbN-KYI|Rs(F3W|S6pm1}8d$I$5RhirdE z!O4R(3gN=>HG!6>X|kWt=ira*FeT3LG+!3vVi+<2@cbIR5j7FeY+eXEy#tGUw1d#Q z9;^>l$|ER#_$nt0uvH`46M9FE_4gKZFyL=NFwpw8$h?U9)phhoK>EMJ1T1Qa6NGJdg69As70z7*0d+33FxJN6T%lSc5KW0tN8kMyL4W0J z_OXtrm#hOd9GKqb8^#f<b41DBZKhZ%m4&L)a-sB}zVSKI(N~zZ^UZF-l*1ELo7` z73KskK)^9Kz^Y94FvMu`BRkH-I;3kOh7p8UX(t7yAqHcb=-KmTOU(RrKtUBl8T!uQ z`Cn4c)jd^3kHU-^61dJ!K#jNlG_C-MU1S&=`I!($8D!G(NfS#_6T01(UWraHGCcXLfvn;Z&abU1&L& zYY_=j5;-`cE?tyt-{H}3`QtZn-f~M|e$vxzaj==#eba3Tq6cm=0S+q`NA8j(QrV1Yso$~moCzU~ zs80Td&aRa-zo(ru_O=bRwg1imcpkWv$KL2C2afY+u5t5#*J+5>rOuSv- zuFB)Wz{C$*12}N$a%RTCnP=KL81ZL$o|d8p=)<}I%nZ~NCkQhQ4#lc%4ChTJ^x7fl z;NDx_%(kW0M9PU-;fE-l=lWnN6lJO=@-JiJSHOy5=mHQsuLGuIFhzFx#dl5csdP=7 z6bJ_dU`dthKQ9QghUu~;c;o&2#RrfVDHs`NSA?0upE3d@2hg@ElK}HDx05j#B<5F; zHMc;A!Qpt&{}KEa5^;drxU(thZJlZXhX7HMKo+^m<_tXI_cd%^mWe+*pZuAiU1V1{ zCO1GL=Dc5(TUNE1UI0e)y1$~2XOB9xDUpc0n60y4cKB#Q*F7}4v&8GXfzUeLPb;A- zpIEHx5v84L9FbMGTDYkNjmYYc(azt-2$t&Mz#&#++(37TE^cng@2nU8X0s`M-9=jK z&E{?rN~GqJ4P7$HP*XfBoU6DxRUgU3LOxu(5%fiYXnDX%t)G-y_D@D+P!a~f73*4Z zjSHU01mIFPAuv_Cla>M4+DryBx5$f)!;%H0riq&~!JU-0%OchsSz#!mZ6pP0e-^&M zd44PW*#YZMBTQxE9mhxG0qgJP6Z_s^eWM1Um(c_Tj5vBk_WC*14Cea33#EefO+5;5 zPh#R&wrSZ|mwjHJocg|leFiTN63Tv3SjJpSW&H4lgk6yb#GrAlE8OItTEW&*q~eRw zqK;H8_bIng@emGI-gYxR`0>I`NpX40(Ct6%chf({2S{e}_KN*O)z}WnpzDYo{Z$Gc z5IW}Vf>==X*XfjC6XKk?z_kUP>gyVn;5_es!o^eD;lJuIGpFmS+HRXv^>lk^6HJvO$AEaxFhG^fh@GlYV9SZ?#x|8@Pw?6I3yRTP7O0Kd=_zqf zlJeTuZGIPyGvkkOFD2&NBF5Ulvb@2|E3+vAD71}c~13ioG% zmcJTIp{;&R6MPs?+F%N$7>76zwF0ZB78B@+8@IcMxwmI*5aixYZeDcKlXHkW7uRg- z?$L&zdU$`dpGW=tp6#}R5Mi!_3xT(E-vKh;_QzY)fDBfSIJ7g*+hk;8T}&r=C`JU#`XEV=_`d4SwY z1-)zpdxXsO1+L1&MZ&=3xLE0o4oWCx*H4tKy-Ju0lIw+}_b5F)koy zZ9qJ!LR)ZopR&7;(hNxqq*7CK8m-8ac_=Q3Qnh2b+qwf2*u&Ae3z}w@LPpFvLyqG< ztlreVPsH#XV{qftqDhpngBA7PN*t;vERLiUmM7)sZlWV7DX8XS@@JSN-HDK|d^rq?*H z+q0VEom%`+V)nzhqrSYq8N?^5rIk1bq;?m?;ofz1u-2-m%UM z(0L;K7Hnc9WQ|Z5?5QP+(}3AV_@1Tzc#&cL@ZyA_e>rZH14cFC;5JS;hpM*~BTV(^ z-MtGLg_yhB<9iN(C<}rX6da%)?-6){9Ak6orwL8eeYcG3nHPx)YZ?7-cfTF16uy3A zZa6E8yT7!Wu>44RmazFcQW=>68;~^%&&|#+BRVDNFH3XR6!p4hcES(xyIoBa_iC@! zJ+c0*bvDr-5^e9ay#%#0XkqACGlXfdttsV$G@8+$eqrYv)|jhE9PI25FOY=4lzAHy zZ9CEPvRYWRNL%17n>8lNXsM^rsF@wI$yf2b!n{9;E^>Z~weTa!C=N_YRG|LpvyH*4 zDR4R%oxZ?n3XstZS!fU87}TdkS(FF3Ow^>WS#mL?o#sB4>|JxxOp7W6jbq-=&(Yd+ z?d)T%!^nGxI^hjm6$a+Ag0{-Ta)1YEyp;kFY(IO#HZ+%sHBSygu@fXw1?X&&4!wpM zYVYYpy(Hn`s!OmyeJ2mCRdCeM3sTtwA>wjCgHA^_6ETrsJU)a8;qHPp!GwG#JfVB{ zHQt)K3Ps+2K&YLmHjO`+qRy2WR24N9vlvOGY3HI|vn|TY7{-jd1OiqjC zgTZX=8DS*(GgusY1OZc{ zgXG{Gj>KHoM~K!QfmXgHF136rv0z;&O9DB5o?n<`-!q9?PF2EKWJrR+v#{GI6+xQp z;~5FOo|PU73fkbqNuo1PLXF&H{CZKFQ*#Sj)|%*OER|X?ailz}ue{WJ?eo4e`(;du z-!N<#o|FjRkLJdcBJ(yuE3-S=Db^21!>sKZpSJ!SZgxzCl3r0-LcgZ@9<(T__7>I+UA;tGOULQ;V=*3E zf0g{Q?8OR}2Km<+3e2dRh>9-pg60<$&7&q+a|>GnZqpa?WXLv^QYAUzAVMg-rDtK0 zPHFdC32m>|A$W8DAtni*Ir9BS!q-Qzpr#zcB(bDSZ!^}q{r|(7dzSUR2Azc3uKZH> z89LwC0vPt!Wh=6mV`jP$*V-BMSsziM7(>XxVlWV5RI8dEr5;j2{3ab7qpVUsn_&n* z;NB%QzTnT1L?OcBx3t$_0bM4(WX!p3=CuAH4JDwx0B%;L({7M;FiW1Kn7F_>3x>Fw zDffPY_W;zZrrlPOxVp}2k!gJa6^}vXV0M=gCgy_q-D+{P1}t^r%WW?+f=}zGQA(US zpc}v$gLW|P6hnY1>p$@c6TiaI#OxUfSo95ev9pMTsskpn=LPQS0dLs(ICc+Fo{JmC zxR=M8uc=KMUr!_HI#XWvfhcHf2S%#7m1(5(N9uS+10;1vdB9hF^W2C-YL%}pCC2+= z0m;}s5fI5nJ(ta5&EFZ^8=59D4Mfr~GK?0RZ22zJ0GL{b-FSh#PTB%Cnh!%FF&4#S z)dK>ijC2Dxhc{nigPTmUwW!4ZjO%_AWrC-q2{U7|MPbr zoXjnAK*OTWJ)qY-#jl_I(ys>R=nyIdCyIqq)tc@ETqFczCO1-QTbVuba#}qQ1fQ#9 zA6Crj0sRiR4Dgn@7q1yzr8|(t1WbEx;+w2@4b!r2NNH^~2T1=N*U~%)TT$89;xb^6 zj6av3l8>7Wi+ z^3RxwdhS_9go_O}oo_ALZdd=~x-t1mNb$95p=X;{ur;7txmm5NcE$d9CMB(`3jo7o z8knT8RuUU=6CJ7YzlT#p1j1S!K$-yJUcLCPBP0`wV0Fz;XumY*sWnU5c~l|N-q0io z=fUcj?3y6;Q;nVm8z))I8%V31ENF~%nZ?kX{mFpvulko91uzhpD(ZrJ1^4x0&6eo* zr(sLktNL6(vVq6dh8e^g@?QbXRErE~$0OrQYuDcX8*e(Xo7nW^$Ac);Y zLGB~5Kf-$AA?_O(snU&-?i;jPy3gva@S^`3B22)xkZxf}Pdx$S7(HUj8YzRL1l(n} z+61PD+c|)*)q1$06`lFxB+2V6c+&0^73f#L-@LFm2Ey0^G6Ms8v%ongw5h{7XEAfw z_NETy6X&|#)@2LxLFUKnQ+F;nWKaJYn8iF9(a9=a(^u()1@-6zLlSz6@S{%W%D*gB zDy%GLAV_W%8b>?NslgXl7-U>G-T<+$uN+%9h=~%5g;BxN!MTV71jyFJ!Cwt>n#{v> znH9O^|MxfaI5x~><2;&CM_^C;mG`#ZTNx=R9^lf!4_%I1`pvkJFs}Bk?flB04oC`r zqJ3=Fme8xX3q6BMel0=bgk*45l<6o+Vl!<}?a?>V%*Pyl=n5PmDVCDMvigFWo*}GK zRf|)nnF+^XVInFuH2j32lKuL?1=DQlq3XaG##F_jdR~(z+m+(q;#nDuup5qPqfbVP z9w8%7;a^j202*yM5T2L1ie(`Oo_`l#X6r^S1{8w*r-7I?HxIVG)hSx|fy3j{JGnDK z8WN#YK6>$jU9(QX2NobLt!tW9eX?whkXE$DL~=|F63G#l7ScgH;kZivwf6}p~i z$Y(R`8E);6Q`1sigPgeoEL3w|ln-P0nnwj0qJ@uJDbP}eK9e#8yi@>gb*FuG&B7{n z;*#H3U8t`#Co+;F5eF}KAIkO+tHz=J@a9h=@YrpZFwO@Q(s{Bl3#tie3!F9(VW_d= zR*g*JNGD0j7(*WH3v86(-ycS`7o@KQqOUp&YBGM=NSCM%rYMc-B7)d3ognW#5Bn7> zsb!BGl9#8HS;Sq%$1x})QlV7#jkaoVxRDrE99nCe5o1G}5Y5KVfqkVG+AokHPEkZ0 z5HI$JSK(ZQ6R3%0oJa;7eG66d9!pB&Py9~a*_D#OrV2440rJMRF?%GmBLp;rB!0}P zD~!W*emL(7$D%ka7a!d-8PyjB&(`bIKE*;u>lKoN zU{ai30I*Be*~rSTC$YGxljB@VYI4b6v0&ZtxL*zUhkZH_g9!4 zSVC&eS3dgQRqDO$Kj+P@Ae?NUMS~-SD#QZz?NUY=&=CRK?`DdKwQlI||91^(1Rqm1 zL3lkZQV$lLs(H6V6i~S6QIlE-{5Nu<>V5tG8#U&>wD+;GVHL%o6SnZ~>Gm1Z+HUWF zH4!^g7I?+L4Oe4j3kR$uV(%coC9i0_EU8g?lTc4(ay6jh+`dLQ7z2%%#5{qs0r6#_ zdZGIP`!{^L8~asfyr z8pDVnL2~T1l1e!yIm{g1m)tDJN_0=g@iV4q% z!d{Vp6%Y*r5eY094On(FYoFCd&px9n4zO2wLI!+k3%6ZnlKE@&CFq-2rSbdKfLFz$ zMztN5nCjGMl30`Pk+{080eF}PVQWY_0!W6T}l=j)5}N%;0x0PneKYRdu( zTmL>ntsJ|81M}jp)C?mY_{jzg3LG`tn{IaPEOO@fFOf=00B6c0KT^EX2SC1fmv+G1 zj3<5X$_7=9=qTP<-)Z_bp~|03wpJNKqV8(tcdb$xuwx)>YA>Z;yh%UKZB2|282r|h zl9{^AoS0|o8M>!LfTtN<$dV&&G|78YHPt4`r@xzJV)D4?sUg5LAy z&86enc7`WkAj0E)0I>_oJ-R{xFa_RK=TTd+KJ|R@>BsplQ<~Tls7#zb*Ri-3H2tTb z@AVAy2;<-6T72r<%>BsYx;U!85EnA+xf;-^RDPW8jDG{YQU9;w5E7|pz$uo0PCF{t z5gk>nc|N3l)>%rii>K`7BwS=*Uz;xgG@F%5^RGB82VYN5oPT+6=BzN38^_ z-0|A2MZ<6*;>7^=rfmPh`Rm+!wUoAqkTHFh>)|*yoeCARQ`82ly|9~H2Ld2V&Ca7qfkQD#vZq8d=6=YlN=r_;heCfH25;*A+y-LQGT=O1hND#t=JMvIP2ru1; zI1O z7u!AQ-~^IN7g&L~(Q5^aTlzQ11vGH#l1I0w8fC&7Oli707@ zde1O9st@#YO=!Pq1yKqEo@uQ>U1$XrLJ0?fFe7j#)-+OmzZS8pSYgmV3IuJV2p0{N zW@~{AFIfApd7yJ_r${R;07=MbNpi|76~%07;K_Y@iOmhF1otlez{ns`e#5sE11gwI zc7_(o1%XM?=<_&hvesgw;Z`RC7D+0bh-SL@OE)cI6KWOo33@)Afjv|Zti-c0McPd3 zw+3R8@~ca2ZQBCw)qSERs3M3>o}-R<_^qfRw9i}Mea9+B+@@6>2^f)v_BpYe1wwtD z;#pu-~Y)?%}LKK@Q4oGh^(>l%|zRGN+{24i(anLjRTo#REI#G&BQ_)t% z6?V?b%8XrXfJYSl3nY2`CFoGts)E&w&*O25IceZ^45FFA&ovHG1Jk2z448R+oXf-WpIe?C)3N&2j4(e^6_5TbyDHX_7x{_JN@KPO22!Y)@5rdf6!BClaBBxL ziekQ!6yw&=0=*B)4WsX*3sx&mpjENPZ@OE9*Yf!uiVi)>dBF!c-5;}FMV0lb4KQn^ z&~0YPG_B6c%+Ff4tj86wqzHLlMo*ZA{e6x(FH2Su#|U*9jR>$o zIndooCd;SJsaTqSjg7I0Rb$#y$OShpeqwiE)-%x`DIK43z2}LL4QWr;7Lw7jmr>FsZ}cMTEUEi2q5Y!<@_ttybjaR=HAqme2;MDl`3xhv*zVTLu)J9g&0VU>U15 z2wlrQmJ`mG|C3)Ls_Qcp|2Xg@bCLGAHQF-|+LEjUtZXO%C*qg*l@B{y9v>6g++_U` znT2kM-f>rkR{lT*2b1{wwf5Iqs9`|gI(?{qdS@=f1z}?PMo~!f($Ho(0*Jysput(| z_&R(e5Mhe(!0wMO=Es31zpCGh0EnT9XEhlRQP(3R;f-z$(XU~}?J9}eCK_>^)0Oq!Wp4SXF{ zOl*M^tp<6Mc~fdw`!5gp$A5wG96?Fez)n1tr5A&T2P9cM&wLxfV|ehSD*0xUrLk-I z8>8F^bXhvCT$aa^{2QQWOePzJ-I_qO) zQ*v2USMhKd_V)-$-80{ZDz&{K7}@P!qISCIQYwO7F3<8 z`cY@A25o$1zD)I$FoAs)y(X_E`13X8zk0(S#a>XdNbAXH0bo6Yv&NPbKZ2e>?LlfN z6BTAhy5^+&59x*p$mlfk38>Ow`oYmNo2Xa}+gL0?FoB~~U$N@yy}8bBi9ylP4}U}n z*^)_2xJW!@K)C6`Zjeed483fhTIIE0l3<{A5AQiZ62i*y_2BZgRU4jLwK-I|Ldwf2 zbHG^_OmdVwsh!qwhFOY5$u-ywnaN&T4Yz4<%jGVgzP(6FtSYSEgBUT{`+s4hRJL(J}Fjn zCzrYk7)%WcKnP3gHpcl(sGamWK^qO<4EMAVO7+_!f6WuSM0)i;OH9JAAlbv-Uv$_&U68E-YDj!~nXns){}kXBJ2xkwY_2W-ZTSPmAi6JBQG zW)!Q%$>}cU0#n~cFUEWk7s0-1aDgXHJ_gSYlZ}h>B#?0R z>>K_Q#?&9R=H{nT2%_hL$+>X1?|*YfTfTbJ>ul1c_Ia$bt5(qXp z6?VsD0fUvK$y6CSJ=D~n>a-%6Xk6nPZ%+Um2SPE5!5Fk^eE60Re}Nqr%y zqNs7%JUg7xs$!0FtI>a1c$%B`ll-U1Ls5BRNyO~WeSNg6j`i{pRxB&w0d5r z5~U*A3eHLGX%zFvq}Ad!DNx}(6wmWM)e>#(ya~q|O!qv%22_SUlGD^}U4D&-$rAHgirf!gI0=BMWZ>;0R zpg=;{*JM6Heo>MJPS6l*m`8}au2jDvDl)g$c&Zg!bDM0fCA4owiFfrngylNST81j1 z<#DzK1+z8L&_QhIIq-v?l`>rbcTQm2DdHZPbfcVnJE*nB3{qXb@MT<(HzRQbNmtv_ z2_tVH4*E$1iSIgVO?wCOuc$Ta%{fPt+c z%5ZQEBWx>g>8qpnGm0EDvOqej3gFf6gEl01%IALjQ*Q!qUY45XkfJVSTafWN?idx` z3`Q-#)0Z9)u5CG%`qZ`o#snDwU}z!G5<(3cM};jDD#wFh#Zg{{(2Swi6&ZII7vNDk zEAD{x4xu>&*)o>M1prmmL9$G9MVd3uBEetO-{*QI;O9!Jl0bNC!a6r(1?n*@03YvN zXO=GU71A(v{EC24&VV@_>L~4NN1m_s4t$<_kg=YPKKD|+x^NR19KUY(4iS|p!C7BH z4|O@M4hGRm!F+i89m$XAT;M0<`D4Y^Fzm}Yp}?7MHi_@va6m~FAovu^iyJI^ytKDJ5Y+U+ zpuF;={G8{<#*`#G(1C7g0Sw=g|bYIiFH><3|J6`c2tq6Mx@8T9UM4*Cj~N$I`TbKdn-m!5Uip=ue61| zq(D}gYE_Q1|FR%wysvfTKhR0krZjteELW4~M>~y@!JoU<;$9IO42WcJighC27#Dp1TS=xS$nviX3-dSRv z{u#A5As7)@EY~y0a}0~$9ys(z{-Wz0@dpVYi&&2H72kXMUWrz~n57iR05{@;(%ip7 z5i&NAqaA{&MiLhs>>1zQhcUOF5t5auu_LUPDQ{)3OiV_W9!u5M%RCSO+I9*_ofULT zsI)o{BYvUKji80EgF6RNmm9uEMURw`PpVQdRH6(V-euVgi8 zrL%95goEF9!OQzJ5j2J!Y4V`spnAQ~(zb^Ok&+ZW zZ(U!E1LXaZ(e1oJE5B+()uPOPgF5S#3Z(bz5KV}SESJ~f4<%(pMj^lR_WSq6?r9!-$`Hl@U3!Ye&~QFceV2Chh;<# zL~sEccSVmtrxt=DL1}uMqrO6aysrJA9Q{37zt9EQBL29YD^2sa5$U!DicMx3sZ@c8 z>K_!02_kom3q0z4gAg2PYeYfV{gPZHD(6U)#E z+Wg2wpo0)P4t_ZK`h=?v6+I90L&8&f1us;MKPJtc$pDwCF7&J9EH$8IxhZKbwuAH3 z6qPdl5|VdpR|Q}u`6^Gp2A1G;47MY>4V}@SIH6=l-;m@@#1duS8TKG>W*NBR%+RYT1W5fg%ywM2{eRmzQ8`Ld7D z4E*k+y8(3`qYT11|8NNNTC6(EhVanN7flxuZad$V6<=*UjpPk{#e6DGKf1xCC}$Cs zxV1?9Kv0^5uLWahq|Mt}wZ9kSwlUk;hQ1Y}Mw)m$vCAg$+^)1KNvvtACo}#{xcTv` zk^#bQqj|#d-fJ`hs$aOOo@gk>z%k;W3*Lj3p?ZuFXHCA;6Ny^{_eubvwLMXO_gQg$ zLT9UITkc1jp=W}DU21>?QSSKrjmiGW zBBTX~Z5z*;pkAn*4wM@r66r?Whp060tg{#Y3Y^(>WPRpB*P<8KvGaZ>gH%OTU+q-qW5uQ0+%*!&cT}OVG*Fi;0r0{C{yX2&msxwx za>hQVy*NV*Uc{@0{>)O^*b)9R0d4J7)n~+)7_XSROq!sICREqimhQ4tdJDitrlr({ zDIe~0V9z8t%7e6Xt+l@DJE6ZeEBb?%88@U6BzYs=ry2KO`%}2L`Z*Uk1?f2-7r8T? zT!`*g5KDE0rdIffBk`FJj=U{8Asli%38|HOM!3GwGW)VQw(X~pDU3=L0v2$0BT5L+ z_fJ}fffOWW5@1>?k3IH?l>j}(y!+3@DG(|RbmX;+G2E2&!(i$#ktQ)q08(S+gTJ@A z@si$wi(?DDZv==XIDZdRfJ%=x~Yb9FijBUhJkUDi&g!(9)p>Ih&emc;XX^ ziJefc6oZ1>UZeHJlcraytO>q!^8}o+nd4HyUqIZ0XcjKYUQ%)kzn1sg(Tv`gB5*&z zznp$|Tif~Ym+hfKd@S2$Wf|tYsSXrz7i0Zy4D(~)zViuboOA%7(~3k#QHID(Y2pTo zO`kYd&+EC=C9fPQa6R~|&PGj2F||lbx=3m)b696$^dv4rv~n1&r@1j^Pc*GwY=*_z zPhG1zQZEaAHVB7&4FZUMy~u%H8U-hBDpkT9JK{Wd3WB3R%TMlhxN^D!y5?*Y&n`-- z1xb396m~F2R=y4eoH9|^WH3*jez%PbPF1d*+XNvV6UbAI=GSb8uQJ+`+I$Yq+Q%yX z5PEDWOVcDwo}((Tq-F5GVL{n1x7#x{B+Brn!dg(UB1pn~(ZtRPF+eN29P^j36BH$0 zkTZ%LJjD}}YT&WXjS{}hi%OGznY4{@r;d7ja)(Lib)q1JYdAiJUl4$?!`cKM1QANx zlD|Ww3Bty#a5gZoOATXnNA;IM^X_;>-36{d>SLQd;nx?z5~0AROX5K(ClSiIiyu$F zMQPH$uD#JB&MW(4PjIme5-g!u8}QlHRnl~k1@Pz%M*j~aw1j&JSfVsaD`9#MqxxAA zZnsAyvWfuPe!he{Fc{Y>Xm5y2l3S4_oy9*93~nM1iBYa*ZmIN?4GSwf&udYwcr_rH zO>qI?06##$zxVRZxSY5RIRJfYsbJy;b}<>@w?pZwOy6E4zdmtTFMtmpP8Bhhvd5$I z+0)ZWoQfo61n=WEN=#ZZ&Ew(cPQUm)C(iVztnjcS{GR57SLQbHaMR>%YW=PUT3j|k(Up(pw+Vq5UtO(EN zjpA`!dnB5^MJ0i@3eviP$zk^zOMH%7sf={od>M2lzn)h@{Q@Kr?hIek_ebl75W_yj zYys=C>N#6NSgI@`igXyBSF{p`Ag>;jl+`dzIl-J597RWDm*;20^45pu$?*6PR)fVp_w!Tk^GW{awzc8l~!QQ-JI>DTYz9oFUrQ+i> zR}NEu5@FQB8c3rp-}O<;pqmAx_q2Mgb;uOFdYs0aQw{x%ie!XKHe%rH#OLDGYVSSv zvnlyVGCSVZ5yDcxNgG4w=Sw^efnIWM;m*=}0Z>~y1noS;+~0j)gE-@pO01{z(fd#Y zoorKQCMcIiSg)i8%)WJg7s3j*CrGzQApzxNge1N80rQ!6$4C#L2#b1*7_ZU7y5bv7 z0<_9`V4ti&Iedq(T4cb+EZZ!{Iv?s3fj|A)Mxs@nF=}1qNG@@!HWO$0aTJOnr>13x z2k4qJz$@e8g$QcPH$u--mcCL2(Q&BWp4@j-*shhh_YbC(SUaZ}Q3LNFNqMY6 z(#TK6f#Y1pIS9!Ki0>su@I=?;rb;=d1B`Ws7ViJ(>l0X7rdM31Ko-PXL=~9Qu|-tb zfawgCd9AUmHl2mpTN6h120ce4=^BmjO_fZggN^UfKCBwWxRsyRs*RBx)u;!>r@=@CV%@x3N4@+oea_YPQ!aI~Ml=;Qwr4^XKtuH@UfBuqa2fokI-<@eIPl z`VRF!4+@;Y6LEpC*5=U8*ZRK15UI#I_?e>dXd5n}8W7S&m{;DYY4gIqtb6b;4og1V zck=f>?m@0&;k-z)p#bm$^$6dBtq08@jpBOTYRPRMGC7XvHB;J-+xHP;zX?jRZBE;% zyShX7r*lP%0X+acYWAiCJr+t508fLI2LI}UL zj6B|mo3wYKP?vTO2g_&5!U-ZM6U4*;{Hab4F`+kGi6cVB^EThkD7H9oogCU@j2|y* za8AAT{8ht{7BEDe=cp^6iNL+~?4JFM#oz~Tp}D)tqR$}5t+9YrXAXI)XK1%qysTMI z`vBYsCNh>(58`|vc1(8b_Oco40a|Pxw`X}_9b$jn~=OB z*`9bszGSg8Yj~S->ilAzxW?H@nxSu0mRBH3 zVY)9lChPIpD&O_IfuehRHe?a9EcRV6@iGNk*6Q2lnfBQ$&lDDztQ>J|U=ToygY16U zYm8odPhTlwV7Y|5i2YYLasyIWFx@xR{-T>;>awLTTZLUMPjv72nfVP9?fMjLY{>?0 z{fTupNqkUX+&wC(Sf|G2x**elCa z(s%fU#hL@)o}|!Cx}E#O!EL|L5Dk<(x89>uS~dE@P;u=X-P)&Ed zA|Q44m{aql=I#9qyB{K(T8iZ9pu!?}C@|3}uh#%cyb6QfU22X%p=C6RIsz(H_Db_= zUh~SN;8FT@h{Xcc_ZH`9OIC9lJ{ajo%?*q9-q9u#aqvTwmWOIdK~DK&PZ9?-$pfij z{h|hET=C2EUKic}o-UT;HL+56xiM*yK_K4%hU?tgfsnk^DDcV)Ygx-H>{{8)(cs0t zNKZgcMx?$KeO320=FXazOO+*ov3^1SAuj27l>t(k6mxU}2P&FqT5zEr)m{lG77{lE*Yio9>~j)FqO>RbrAXEQ>B9WZpbPk5BCT$ONdPwh6IxB z+Qv7VZap~kF$lG}_zE4GSf61Y37Tl%OIxQ?U$0ixi9*)#V2slj3fey|2n|%Si!VxE z$NMn?k~w;$>hw^rk`3sUBV%Adn>`=z`J7JWc<);MskwxnRI-iqC_%>+!t?|?o~Hz? z>}C(LAs%(-5Hb%vZ5u2E$p}5N2vPjo5m-^Vd!!pt``e-#7#3;ab~KRi^LJ8S0+FW+ zV}|zAPI2+23Icsus5e!!?HD__BD`BT;7usLzW_D#Xj<}-dMIh*Mc~DnaW9dKgb&_n zlwI>1+S*kcSm#WKB$Gb#PWnv zVZ6oteMI3dL7($%3DMHElSjz+%W>A_28@NoGYbuYbi=&k99fAfxe&>XpQG=jAuQd% z39z`otXgTLve|ChJNh87_A0n3w*{+ESX)dfyn3sH{Nd7sX=kOzfR)O_x(Ze~YFeh| zryxCPyEbm4y~EI5Bo@i*?|v`YF3xKB333P&B7NB$)(DI1XQjy=`HcebyQ_0m_Z;P#vBXPEYShcXJz-|aIcONvVpclhYLw7Oz$nW zMab>@&j6T1NK8?AlrJbrcPtl0vZ8?*XThdxp60y@G%niae=Z5-C`Ez^gt-5m!Q?a# zh!O-DKFISBoFh7DmuUv8C=%eQu&qu`nLT2f@A616!1hhmmkWdn;N75v08^i$g%8r` zedxba_8r*zJIoZKC4`&%<}#njBe%Iu_Vuw!q@5=xXss{N#-U63D9@#=U&GU=9}rC8 z5$SrUg4q7-FcK%LHF*{OcV$Zuq#HXG5^jpmNc=J&3Yg6#G*doi1g=BzfBG!zu_R-B z9N)J!FB#frv!E1_znqD)1FToTXf90#WoLj--oCFGQ$wH|l@(II8Zl(|=n_lkncIJ5 z8wJw#sKJEA0ML37sgo{YCjB5y+9epx3IPOBLJt_0s|eX7aaTkWy_S4|Qf|TZY@Hi4 zPpY^Z_u8UHf)5mpixbH)-Yx;;?lZuCbcn!VNqH?n?;>`A?yIpGNP8(vzQM7*JJxN|Cpi4YEDpB^3GQjCEpo6@d; zEgacY>`%(ooGekXWBu=|84qET?-w$S)jA5aIo0<3@7I{)TtDpITw>d!77cV zyX?v)m2Es=F$1op>q_K1W2&y;lp?SuK0*#CvA?6?_{fz=ZiBO!_zVk$gcBEpAoX~r zl+mY4=-{{WTcRn-;;ljQ%rtNWC>JRE@N5O$af0^Ro_!n}zTlt~4_>V0D);+Niaj3K zEgSRcn@MD)q-hnzxdCw9tr9$?mByN#F^EYLNO%v#_@>D7U7b3DThu${=9>zCk0JOM z8036LRdwy=Wd5wvHw>(t6aR zC%cWzCZs7uN(%49qOd2id$(mM5z18Z6Sjpipv=!84NP~_BPxJGX|6tAQJlSrVTfw{ zV7J{eYamvSeRErBTAQNATbbYlPF?V`2h;~XU+IRhIbB>S+;PR88cRuuXG)Xv8YekD zx8xqXixwI^@#&q&CrRSX8m!A!VPcSI;;eTTC$ROzks1f#XiG;|WeIwpPY9K2(nCjv z`!b@l7u3S@AslD=fbO$OTA+WJyexQ@ARG!NV}q|o=_O-kwQ6@O`VABAzrAIxh!=Qj z%%dXelbUn6NPqR|A#<8mm;HX)jVSV=OGY{wVYjN)%IIOfw|+R9C)H?KR`>ILSQ9nW zQLpE;0T7xXX5JWQ;Ft!{wv=B+No&kO}l7LdXySUY!LdUfe-T*7;7^(<{E~dwxn%6=wl8>Z`p;%JUZTQsc^d6 zqKMA*VA)|1w*d_K&$PoFBY4o6)D7 zRVW-qDWnF_|J?Z5W8^SWO>e zpHnkixgcc%h%a@PLM2?tqzjwsapqeN&|^XG!HllY5ou;FWru62U&l58?x^@W;Guym9-NVV_v@-*akm`RvHH28E2cU|0wp~UxSQ8F{ zy3~65&qUm|`+a--!dLwfhS5e>bgI@#jvoKds~vVY51MFXoNqj%Z)+17yZ*|Ojf zQk?{g8}T)}Ni)(ypd)sHTD@((!9E{|FGq~$siSM+9~Ya=#o^(7xHkjYMu(oiG1nf$ zQZhItD+U|El;9@e$e_LBBJ$b?(mmn5PZ~&}zigb0)A=77vpt9QIu;O2l@{s-0OM?! zM%x!_G<8YGf?!~M`uUy~UqngrUfi9|YoCVu=)QHE_*PQ}Kq$*-u;3mN@%rczzJtk} zAq!`nIxn0is-xdyupHGm6uy~4ZPnjZC>5D=wrFV*Wx%290GgGss{g9$P9ba z&#cYM(*4p*wp!2m*|{a2!5*tHqw^^;jqcicMB*89T{+d*_9dYs7i(urD<1l^xrQmuZl*wWm()g_Y_z7}ddZQr@%!Z9P5bnXc$XDrtmlpyPe{i6|o}UNlbh6$w}X{lv@hp^z&GI|1nDcqN(T^N&wE z)Lc7yIz_*0{`x=+pVT6)5rTIFq=E#M+VM`(=_oqj(aLkk>~2O;8V9^MM} zsRI%6 za=c%WIQ;Q*jdgMRa14dCT!jKqGxw6)R@NNd{`|=tYbeVhTE?n_-`Qf8c(3Bbz|Ylx zfmj+{$T`WjfE8xG_0eKd<#(T=DX1d%d;_y3o3GJ515H$ZMC|9N=M_pYV8)>>>k<>o@G=MM)=2}`f=m%1 z|Lp4j)Ti#lH<>wPC**fb+!hlnogv~U#i9x5NC~r^vvZFXH}UQ&l*K88C5PEP2erb44Z1|-Bmpr7z*NjZ zSw<#N#GgX!QJ1+`w_c+9d{H)o#R6?hRHN-F4A9*9J0i88$eLhTaAA0 zkzBaNBrq;F&S(DR2dWBcSH1wQlF!-g#U5Y)}Qrw@fAaJkseD$-+9_CQ<) zz)X#h=)(0mT?sm4?6ik5+!JGbkxwL5^t#%Uu)8yhFdNx<2x|c?3#r3PwQ8Pt<7Ld!hx&jg0^gd9#2H zsc=$M43xJn`>)g0jAJW*+_-cA#D%*f>sfTM#LyP>xJX!HB^jRT2os@Qq8lH|;K>%m zKjT1Kv8WOeW$Vx>+wrEZcLB)CKLAqu*UO^?R6O`)Ai-jQOW5$%4UGp4+Fx%u)_<1G zcPvBqO&$SxMZG8N%xxCYv{kD1WZR0&9DVdHcOD+HVK z0NBGuJ-6u?6{n~4J0PFB3G+>{U1ES<>4!(}0Znu^mg#*K;?L-t4=v)O0;U5}i6J1^ zCYhQl+vZCc=HWaYA@Ggqazc#y)t$4TN@}*F%ANbP~d^Msv`34 z`_Qw5c_sS4CYaPu4QUN6_y@ksZPLY2M-QYmhwhj?8ZLd#dhZQdEH{h06J;QEhbw4v z*qV1Hm6;ycK!1f<3hHb$qoe3%@^hkC3CCoJTdl(!lNsbT5N?@=9+_o0Gz4;$y%=r< zu65ek(G0@NRSY;{Sx1s|+XZwH)^WLIO8+~uC3_24TrC;l35Jwfk)&``+?A2b5O8?G zK+SWjP&hnhch)#?dwY-c4sE=omIpFskbpSVvBFu$UB;B!AjnfEf%7DR66(hWL`=*s z@vXK-*JFu}>OS(ui zrL6bbA`tyeoB*Iz3J(Ye#;0HFZ_yQ)U(Y)`46I$Oo3;(!<72T01vm|Q5fv~v z(-Q;*uX~jGs9D!{kZMT*q60%;X)=8go5Sx-MBVyJNv4j6sc^cJTtp7K9OyCh1Qj76 z>wV_FV9ggTzYtSmISa3f@=uR~-FyKCkp^MASaefa3Tz7RprLn7`@cTU3aG%y6>hbQ zOA3`xH|5WG>4lc*9jkz2Lr3A|*|;asQXP?3ZnfFw{*J?B{m>|6|0sS_gJ&i2z$)rx z&Gc{MY^E&aRWgL&+^-C6j2h;PHov&K0MpIKu5?3+Ma7!aZx4!;dOSjbcT?H@i(WrP z&zeF@vhT{-QA0f2Xx3|(%%@T|IV`?_nl?8(E|f>(e*49W4hq8WbiMm@yvwDIm<~1& z^H=W@(#9zDvl02@C$sFNW|KwP&KcLw9tAU6lD52APAj63r=-qS5)4 zcM+`7`&)I;>bEA>%5^&FgpXiP&UxN=md3pz_+jL10bnQy+vyMOG^U z+Ti^$s7B}HzJ3vD1BI0t(gdPQ~W;S`#?#uu?Z zPgJIuC)M9+wz(dUPZQZ1_AC9B&likIwh5^er15@#O+%T)W=HxhPmlIb*vkyj;sT|P zM#tPPG;0h{R3T%2#I2v%h)qegjO{PAEiT9ZY6>8TRwlanLNXoEYTl2Hc(d=(Cn-!r zoCx~PIt@Maf&dyq(sNKXK)s+`_VRgx#YjU|U5!kNg7t>Jw@NL~yAK0y0=NwGphX>6 zVb@m*V4zoE>b{H-tuOF6SbC8b_w#Fc9f&l? zr2sD+W$hH`MJhEI?Hfgb^l(}>?IBLu3pwn3p5B&3g@=Y z?vX`<&N_KNv2X1~LXTKJh7I7%f4ho>Jk-|38vEk&e|=II+I=A7gc{GLsb1YfQYP{N zgtU-InY>;qOH{qh?Z>1?s;=*7UEc{!ISX}#u%KtZBsW(Yu5bp?2hl(4i35deVW^ZU zvDcDMxQU`CCJjtukV&~d`;2tQo-#Ctjr=deGohGv7UPDKzm!K9A_Yl0OTYqm;lG-L zGj%%mcinZB-phLWI)qs=sakXG;R3k~>Ghf8ae5>b1N!km!OIv{t zxu0?$t`OSJ!g!BbqLTqaev4gw&9{B3>ZU_R=kkM&a3ci@pkuB+xDtD@-MiAsj=qZ; zT8jQ9ajgYdv1M`}b*ew@vv~HggmXhvy&9OS(1~yX9(5Ee^APTsM_=)dCkig4(&g1u zc2Fs zO?KiHqZWo+Eqnsq`X9I0-6#NqtI;mbW-X-27;oUtR$L4L%c1ABSw9ZipYIPW;?T^) z=Up3ms@Sf>iDEC0laSIO#UHNWzUF_3RQlvosLp{C^^T@PRpMGxb-SmW13}J!!4I2u zbwC)1VjwtiP6I`xVm?8(p%7*rswix3OA{iDG7`*wC}me%B&8696?-cbCQlPmR!)W1 z(a0>R-TGdlTM#GbBHHpvs&c!Cs&McL5bahy5?|*`@Ho5X%LTP&bQFJ=-)@3M+z0or zRtJfrDy4S^Se?x@;qbiXn(hAK)&>-zU~a6y*py-=AD?1R8hs?D%hE;=Q~-mrOhoSP zQn+$2g#fqK3ubb1onP`4!laiCOJesG)1>!KIQWAHeeU%h_yMe&P(c)4LD~)hsO@%6;BKm} zu@{%}P{FYLl(sImtT#od^=)qpykBelHuDiWm~?Z5FEF!gs|eaT-0Q0kdiJkb8;b{% zkom(8Cpa*DW{O}iZAV(ak?h^Ezhv`JT4qn?m0sL^mTqtCpeT(aF z_Kuv>(T$a|6RHevS5UmCW?@W|BPyF*rx6cZoiffI4ygUb4KYA%6Tvn}si5R^25}*G z>a5R#fdurRij&n)CYcDC{_n3-UpZr!r#<7r>+4&x%oflgpsNN!JWun@ft|SC@X}{- zbvu;m(|0Aj*IyW(JumQCg)0q2$^(JReUBWlO5aoUR8IP*7m(IRPn@+~p3)}o-k3mk z#w#zp)GxH-&ac5-KfWTql2!~F75o^2kUqzqGaEhKHPnu220!l+sMdI1?H2Jb`ZKy6 z2|Nj}7zF8C_X*O2(TwwdgQBT!w<`7ZH;v;(GbXkz&>cKBMa}erI~g_s)T6W0t6PI) zSL=jS`-ilG60pkgwj0?}c%1mO2c?I1dW_6CQz(~#Y?#VfHmtnNh%Cr}$j-Ww1@<=IL(8eX=z<_~MpJj^8@ z%_wU;QX#1LWbCO8!N+)r)&Nj9sCasHH6mJsiaJ(+a`l82*p~s&%K|M1SROWu_-;)N z>4p`()R;jBMvxe>;x8Ha-sf3Gvtnq`gy-qi3_7ruynWgF()1j1D-I|l-VlP;>s(`mkgbp^i-5FXTA)Ac??MEOv`=>l~R)=%=8nh;H2HNpj2c$(S-AJ!0H;}@=X)O6f4+^*;LMR;4W{^w{a zc`3j6BKniPl+>xd%$)Q#a8y{&9vIUY?;%prG*!V5+1ttZu`Y>1SVnL2fyZf@v1uHe z5T__Abn4rnWP1dO_6UB`@O15;1je3!J^jQC%XM@pXf70es-VzdP^`}#j4JD^y4Sfm z2V1T4C7NFgd$vb%qYLm3!>c0aK>MYW0DHHj0E=+?HZiZEy2K&=FvDqI#Tk$7TL5=g zxX`QuDy~0QFot5lUDHUq7*4p^)s`13Myy}bz~3iG;u5&+irgCaDs2+^oLuVu&K&^` zF*3T**9W^JA4*Lw@#!LyPmW7h&d~0$92v<`%_M2;QO+(KyqricfYUGoOz$gCTlM{o zDgL`vr-+<>;{zilNAmcb%PoTM)_ly4_ZzL_GvdB${7DClD7tdwci}u0_Prrm?E#?( zy61E=x)@E4j@ywSCN%KBVxrK)*Y$k`wqC$G7b81CS1vnf`6TyjB(YVJX}EdSbSwi+ zf!Q2Z1cm+xS)3soTuX!9oy6dS>rVLu73DMx?`uDsB>KB;LW?|6zT1jzzb?`k-~rcDm-t;&vD9m8G#XfJz!_9k|Epokw- zrmC}7MG7S(m#&aby2T-dOyE-I>dBM~#`F&EK>d555XTbH_bQCV_jU52Ls-%T)Xh(G z(`_KRWNwbXXpI$nnY3kD?$&;UDYi*5B8~WV?Q_`jvWq7 zK^Cb27fW9nO(64VbZ#GwFeJ;P+ZRb)yV)lEb=b}$le?Bb^dQ5=&8~u+%PiBoqXI4` zn9{;TVa)YyLUWM!myaYp-p)^i+y?Cv?La|4VOWQ3?jpg zc&i8dZhL?Buv0dMgyB!>T5$4~`}V9Vy0{WdMQx{_#TOkoi7hD^d;gS5iddDSQ3UpI z7_=%;&TnyLQKCZdf2w~Jz?cThMwJ)OrxjdNk6QU%9L#V+EJ$|}KM)ZH)gVp!%TJtF z{{=VTAaACYV+4X}w&nod!?7nLy+q4hM-r4PVSIwVA)KBZ-6$HXudUv-GabaTgK5S@ zoXzV(2mo)Y-qac@5LJS+_#y0hZ@Y@bo7HQ%yY@`Aa!ulPkqRTj-z{0C<$yF(1^c`O zX`aj$(Qm8`E_$eU$xY_yXC~!4v;ZbtZk3!sZxMp#L2(PoGGgG>k|wbtANlPLxDV`q zNZnktFvr&(?11;6Hs{w!L3yC@S!E(l=3+)Gtq@dXT6GV&_wH{C#ZfN-AMcv4^TLO} zy}RgD+L7GphNv}<+ma2=p55UxL(MgYk}LE?V9(mZ&G8-43i9HwYZi7$9<8)6djY_m$rS|!U6f|uU2UNuM+FMCQR3C^ z)@-wDhb)qaR%0uedJ|qCKlw%q5B+isuTkft9_$=*Kj2~8-4N-0ayGJMb_1OC1Ma_- znIk_^I5Hk`{F^%bgb1daPpGZ)K?nv1o-f`Vj>hAnmQ1X^w}V28$)=;-(kHNi#q*B7 z{|s}Sd8yfk;p)0M)p?BR$$GYX`qVGP_(+iW@&il^6v-Bxp9vv8St=mq((fyY8h)^C zl60SbE^=VToi^3*P)SpOP%6Pvj$r?+i99Sgshkgro_{z^!+Mrw`039&e8j^igt~ zmj<~8<)zQh^APm2JfPnR*zsUhXV#x*+L@v@oUAZ+e>7HxLu4Y$uon!1^-zIk&cqLI zM2^K%ZvlFYwRSCOBSMzHV*dWa{Q%VRzeuAHTZK(Kw_GZTLX#vz6wkjbI#Ab8ysato zk)Zo-pKLAkZOYuopO4`|G{d)9tiwin%Lg||DQ34XLAEnLbsqY-4PK#^4kM>Gu~s#){yD6Wp&8sb*Lb+2v=xcC1#eW^a=qf zYy6m$j~teHxh}uR2gq(m-wozvdgm9xyVgl>YaoH1R#sj;feLUX7K5hdQ80N~tdYy# z;{MG!RsiFb$bquV~*j8dCDo|0W7)ug11SbG5S#*?;Z)ZxsIXTNc&y zMw@Bd)%UPJZpHD%%xL}BdXmh#$lZ>G7YKg7NLaUZGyxR}>h`JQ|+}I^(A8tR?mREjz^Lz31Y|$`7WS zc&FP&(6)v~sj)22YurxSM~Q7^+LY!1b+?YfdaZKqjAu^p zIw#b6#GUCs2O=qXH4c^0@k`ai+G3*ZRcny)nNs_pRYq_HTU}4&XsaoMcJ7CUJpi7D zix~1+GbG~F@I$eUwL!WV^syBE7b7e&AERGyLXnEL*WvJ0or$+yl_`>nsXfd1(uJgbEaWXbVvr>$aPawDn-*E6PLlqQ!6o3XZV+t0TavL@#uAg@fc zCT-{VMjguX)$@angbf^y_d}S=cCLa60AS20QcoXdL38Z8|}-e1G3C_)YUw2K6NFp@NeAtP>S(X2pPgG#UmrqO^K)8s4e zcXd38$%tQ?rJ>*jwI7s$zL)i;ug*@bK*3%MfG;C);2NMjCK?X{sprj zNt0~&_P~Out)yGx;_MJTV1iO_pLIG9vN7qqjRA00FM<3k9%QHxqYUOZo96!;G76Jgv2=`WwC{o4UMh&F z;xdzK*6s;7xiP-$)E;je)eKx8Bce{i-bMs6qRtRVXnw0FRu_rUQfnmx(4!J>rIcDl z%1iqMqy@L0HM!8|l-q-Sse@*W!Wu3tyAV$n*_C7L3_@UBMS$VJ-0w&X=#9tW3hWjtwl1vGkqI$6fF zMHOX_Z8`1&CrWq6L(18H8L9%RygYCim~*8 zJ5Bsr9j(e0H{)eApNi{a+` zS*W(;Ox6=-hqt(M=v(p?w22G6e5zd*MGYZRcP?JGbt_Kk;-_r~ObTX{z|gKoUaf&;p^3nQ!UU>RFJ>16m97f2W3;jQSgnm*IhP zp%uPIk^XI}O{nrR zkCM+O%wQ8zv(0HUQ9fcsXY_H~*pM|Pr{T8y^#^}@NRxeA34c>pNYH?ppys4;ou5Fg zjxn62nHX6xlMypM311=CukKG^c(;}R`=91S&-nB3&w{#{5hm*19>CBR4iT>#yy3!}hVVVQkz+r2pek^0kTCK`zq183C&kjbbv>!vP2LoD8 zBQ9lN+;_MIQkxjDw9pP+=nCqAsrweq<>1p@{=gV<@e4~XWzB%iOQ9@R;*e_%nhvuj z{ReNop6}-j|LdVu#|Z_nGm1e@t{3<7@RKV;us#_`j(SjR_Kf1u&If$wE+~Z zzhY|+SIvne>K}11dJUTniX(|4c4AC)CLHLjD#O@e3_gdsU;3qblm&W;b{52Y#ySpQ zxy0#Pvxl<=){1BIsF!ie9jcRn77n_E-Em58TKd|s1ub7{XMLS5Yxmv9>)3Zy(H#EFEDwn!{o73VsQf@IAJo>F z*~*f|pAH1J(Cf&z{|`)BZ%c1hxcGu=UlVGc1)X7W@#)@U}ueG zh@pazaBZz%bqaBqlXzw;=%-0i&`sWRc>tU8czgNRKuBi)(98_=$M;X-w&qGBLdh6t z7;+$V8W2`%SWNjb&oVu#qN*Et_Y9~7B%={Eg4PcoE`}OEp9clMVZ~`e3J3Fr9u;DF zRxq2ywA6WznkaIYS-R=`2M6N?yy;AQAW5ax$#}zCQ>Qk?^=k3NJlM*eeerDDc9`qo?pIRdPF1kk8B+cSx`3 zz)`@I7p_zp7z3=GSkTtgkX_el7dm8Bej4X4Lv7cYYXmq%8}&FT!xgLR^37}FE^M4U zM+uUDjV$&=k?Z)2wZGU7ygU_w-3P;s`*~D_mS>#Tiei|codP5kQcxCQI3p8AQJt8U z^##j?isGtWW;l}Qy&uLIbE&iV1PJl&EeFIzuow32xqoUC0T!c4v4*4dBh)@hZl$T23 zr8aPEGp{}G$FAi51EsWu2W;@xE#uAXEw#xF?1ZML*d~SFL@oO4fH`&sI?mxZ1f)#C z#O4lK>X@nSu*L_f^c-d}aSDqb;2ZA7LOldy{QG5DUgx3C0aqc%_|6X_>gyz$lpTvBL4*-~ z9Y6EcftK;v^DBjY>~7YhAwYbi`3-NPE*H&+pKpSU1Sb3)Vzb8oCX|_s zUcw%^Ff9-~k&6Kp9((Ko<>>F{L~qoglYLmgMi*|8b6~Hh(hr|t^``78X8Rpg@Bz^j z_cYgLl`L$}Rfblm_3Av|*9ogjdX|VwZM{|yjWuI8=?=~@^$?xXBtw$iD#bw@Ioc-1~_Uv6v+PcgG^m zK(E#vyIVQCEe{SmCiL&b`r|(rQrEO=k`|?#1vuUHD^U!7dE#81+wejC6A6iA>XiL- zX>aGfIR$|6XVc+CPT;PIC=>f2{xA&_cFROdamyiY%#p}dcO8q0)VQnRSdAgMpF7h@ z!FbWsgvontUO=O!RX)?fTAe7!47h1{qq_2atM=3ITS$kKPywMO8RO!)=dDI=5fs^U zal7#(M%XbnXaFF<^>#G8t5`D;$>eeq%vpA^t*4CtN_wOz#gmg_N`d!eo0teq@8BDS zS4uwVeJ@nzuOawVqBK1MBj7&b9tPG+xXY!!%o5hHgvOES@3@V@i>(X=TNhso*C`^P zIp%iiPTSM@uvYg)`i?L8ZEl+=r3Ehbny;~bl*aev9#7=c!2%# zzocJU3=vgRHx@jmJ{@!{MGw9=_z1#cf9Y1>z)vF4djjl)J=6n;NMQL-=NUUo&%kYK z#v^(7W?$)%^=&el!ml5`U-w3r#?_Px4OwFg?ojKVESpl=R?j36OtquT;1B$veV@<^ z{@-9}j7TM9rG{}%Z407iukxBD!n9cI%0$SX)1SzP4vQ3qQ~YdoSMjSu zqa;|#XNVuDUH55`q`pre*QJtwlq&8JHI9NeD2RV7sPwbzQ%PtU!7d+zt^GxbvN2gy#P5t1 ztm+%V*py*thGk5JpMt5#5}%!g2N$L*9?|`@PC_aTpv+0A$(Jo3pfqk34D3ci`yi?z zvx3tLfmVhUEoZ)9vy6brMZnO%ItB(v06jp$zcIm4{Z_7Tg>NOW;VpG4cRDVyUo5-o+AF=(#p%ca!7sa^8hO zWlxo#93zNQw|M|)YVrMpk2-8Rks7){zz@d5Nn(-mi*0qwR>&5I~ zCj=7QJFhwiw2k?JP}v(#P=<8x%D3HEp8}|K+>h~3d2$Mfw(}W;83$VQVn6I?ThjD{ z2?!Rx*oH|hz8UFv8=wv}vcF$6(H358-jpyaMsr%QHhtc4^!rgT-^wDgMWP2dD(m}m9r*0px)`xTd@C*PE>V`swn!-z1XqeU*S0tpt@DN|OE<4h;wPU-8& z2zRcyiDxY1YQ0$d1a+1{v_&75?)#pt;;w2Wv+_6he~UK3X_^kH`-{Aur2f;zpH2b| zi!5^U57rpla)KW)P@;YccDJ!mHi%}}Qq$L$jZGS`IOT@hdkZtAI-HQdFC10a+Qz(w zq^a?X8=d_jOGFLFhNAQ0N*COS;esJt_(5W<>uYd3qFRFypx7dJknBHIc|WR@o@+d8K>a&arxwUY;36 zWGXJIiS=Abt+=cWY(SwoC1>8O{xY?>w(?1k8@C|)PNl28mNqlU2iOxLU{ys41gpUGxyzVE(})m}u%0vL{JTfZ zbIkz1PtK{vmT(Zok=cQBzDn0Sc{RwkOy;ncOvcYs9Hl?KAzu>S4&>!A4g$KAcG6e` z1vXdpFI8r-*0R-?;%KrJzmlb-|67lCv+>2OJ(Y?k)kPpPu%p;hee1aP$Ts%*+@Y=Cler!_CxCo2m{?*EZ|+<$X8`XI@b- zVfMpgP$&L!6u1x18yA?VWU}OBD3$gOqvX-^eV1Co9+#T`12K7oV*RRoBt_;cuU)Cr zNfTn_tPFu=JXu(f6rTwhsWNJKoM;i;Z@Zi;cYie|)YHokOEMQcZY`gRbyYXP?-}Jj zdeavI?J7=iFr!r9CKep03W>w~g5?$gxcaf6)ntvA%;7IuxftE#rQvJLBxcJABNw_p#N8C$ZymFloq(%)% zUhC9CmYc`tXd9ta;YYpR)d)u}0?LA(sebFTxk=KGbBhs%E>ELGSYBx$$SxVp4Yf8rb0Cv=MC3b>yX@iw zJ*07!e?NjYpF~dZ2X_@7blE1@E!JzT+S(f(Md4N^Qo?-*2UFNe2NpI)*54EKe2>ti z_uzk=E5UV+AGdXeXu0Trdz3qM4L{UZU8)C`_H8r^nMFnA^RV!qFdO&>5=~4O<3$># zBnC&_Ekg#|GZP6P#7mpa)}n^YaA;mri#sn3F6*W&Cb$TbXJaeu4i*H>nEHD{h& zEg3rfmT1}18`jY$+Be5s)%_IRtBkSK8s&-WC4WI_ZrM@!ZN#EF` z@+Ba1y$$7vT@o9)`P&Kv&2X@4~jx#1SD_+2C*3<=&tRD(y@9sJ2(Tgaqy!juh14*V=(2TTCf9`NC4Fph zZiAbqPThb`SijnioWn)lecEzS>v&#k_Qn=n;g#&2%`98XB4$YhG<5?~IBB}9jRB6i zr_I;rsqO?3>_N!lm5*DBDJ(i4g+@QR@O#?>a(s;?o$?>YBDoI1OyLEJ1!Rky@<>%D zPJB`_V%s@xgADV0N=}+pZ_^16LuP5L9=*cw5M(E?E5kVTCtNq#N95lg%g#{9{_X~N zzq_0Tq^=*lZwI=h^xKWy5TR48TBH zdA}bx;rs9J-323Uc^9WFrSWaiMy}+a5ZHch3n|qB!IcC{<4yZy^)*CYJrC$_s=;zU zi25qUfU)rdsRfoQrXU1jm~#O1frXm;C@(P`A}6 z^;0Z7V9$2&rsLy)FFhgp;dp%mdo>2h55t24osN892Z}73@NEl<=Zz8WfQ2z>eJ7D8D10>G9oRab* z5*7Aot5Hi@ zSAap@R}7k064pFsohDmfv@2g***_7t2$mt5*_s3wL?f!N=)MU^qy;O2K(3plx%EiV zI@gDjnVck)DWf67uh|_3yACUuwP{R42=Fa}7}r5>S?+nv=aBu$7YzaXV2Z47hhhwU zp|D4QKmf?l2sDca;DVZ~OQoME)lLeb2fH=w%_k>yDbgg7WAqIr^kRGKB3*8CDQ^+# z292Lf8t!o*$DRGy%ZlmgNp0^fJe1)M=LIy2T%&{fKZGQAL2BRu-Jydp#{t#~UOtNd zen{R6hiVpsH5pe5EXc1pTn3??(VichCJlYJ2$#Hr?oTAxnat?Q=_Ghh9S;!gRiEb` zV#R-vwh^UktE^jSWBv*GkFz*DQt_)W&k+HIbls1_p`pozF)(Y22C7i0-~idxsaMYt-fZnx=eBUSxFA08eNDcVzCZR zebvtMU;0ggM>f(BC4-&WTB(kz5=y}9=RhsU@lT!`@TE1TlBcR!_D zD-U?IWFLwB#Oekd_sz|LZR3mYK*0Aj1)hf>YYV<|N-X zs3W?y>QPDfUkPet`u)SU2!YNEaK{yX=FdDLOJy5+mWkq5zI9eS#{M7mF7fhRQN=S4 z!R-RP0wu6s1;Oa-?k-~O*$@+I34Hgo^&9tRidx_EBsH0}x_qXv;UswSy>+qK=*jgm z=G`p4yzjq;wR)65L4yY!2YQG(#bY|8l1kTtiJ6-fRNmN-n4QB0r5xcbQS}5N15*J+ zXdhNl{X6viu02I1Fcji9A*-6M#3)%=O}Ymrbhf9~7n&wpb?ZjwA-`^c#(Z%c10c>T z{QVTeQr54{j9{3*M;7B zdfzRg#*+otTyGqPfPhU;4#O9b1BcxC`;lE&nBS#UdHwGg7PrfldL3fhA} z4&kj%XBuPdFlM2S;ujfCu~IRBR5&g6GSIgp-SNC)n2u9@eypVIV*)|6>1o+%)KY%= z7a9a2wC~+#sqsus^prm$+o#)kt0{eiwhaJCy5849m_`;?8YG)c6-1mWvE@{2lk##` z5}>IaWF-;tOcS~) zh}3;@8-&Yxjps{%(204yY|B;Z%*;=xZT|rv_&2gEClmf+KMx$9QcRJ`!7upg#wf5w z5k&SiMz$5Ad2_8Bul;GGbwsGp1~Q!`SVWG>sr2W9qsPJ19-A)2J5#x+ii-5S#==Y{8*Bfk&}lE<%;nfTBZ-=EJM9d& z%4#06NL2}!9ljEgSBHRSr>k;{gA`DtaBjnd%nJiHuPXg23a``s*!Q@QKUwkx5|oIt zlO;uAo{(v0K^?9)5MhR5efMw6SY?3uk5^Nik+cw8MK0hbtyPSl>98!kw{~DN<9xcd zU(BOKzZZD+e!86>=KO8`R#4HIZtST?*+~^uU*}0T%K~n0t_@zyZq$o9l7MhXZQzdZj}sN$~@_DMrNL z%(gT#ZiO9wzvkJ0&>^2E7(S=JTX#8;W&amhnY@3w@WYwQ%-buR_aGB{R6NWwF=Ybe zW?^@eg0l$f4kqz*&?G%wQ-6#7z4OEhpr#z9E5~jQe)gC&ZmBL-$A(oVjPMf&7v_lC zzmbVHJ>0+^PU*6{I1lhjSgV{y4%N;r0uZh&z&fzSwaR`@_$(N~P-EOPQr(@ZyF(6U zP-QgxdQOZ=L8#6^`N-A7Z5DI11N6a!x)x}ZO~4jZ-G*h?CkyLG$r&B*{o8zYHDc)L_;SvfY znNxz7%cu>{NB5BBTpODu>h1V&;utTA;4>U_xLH9Jga;8uO90ZKN-fD#s$_r#RUR5( zCc_PNQSqlULtS@V&L!~`yW?cQ{nS>T*kN%AGn>d=#zq z+`1qi0-VDKyYX%aYb567UcD5Je^$QCu>Tw0z$Bf%;n0k%>1J!7u+bDkOZZ~gS~~m(;%SAoLPdP z&YTl59D&YA2bBHt*7i+z0<@CcDMu7ESbMlw=VIS!G~x@@>?k5C_4jZj-P{kl6c6!T zB7Cyfg4lret$zNWR8}h-E|9;9X)BlsVAbmt@U^PATu{}c8%J)or4EmKqoJy4_IZc9 zSpxa+K3926kqHiy?vn@WF<{PO$->Uxo@xvFh7J-rGO@e&^GujSglJMJSlJ7@5TZO* z;C%&W@IM!zpPK(OhSJpSuz@#*wP#468!lS}3-R8;G`6>|WrZSS4m6B$Ir5nD)KDnn zIhV{*oT2UXSzv(flAE>j_r42(nW%fsgFUL^-KA~O!d8mI*HJ=$a;=q+=AN^XF2pYb zGFy5HC1`3Fpn%ExFX?eC);e*!#SXy=(Q$wBJ24EKg-mswD!DdE{0S&Wk}HIx87EVb zZ|Smv`4o$=b^sH%>YOidBHRV3o;m$0EXCj74qc9chAzjN+!60hx`?lEy~R{?nbhDX zCbCW4L-02pS3QjE;GrD0*Prjz&=exD11Jd6EV=>&svhPa6Fde?EBu;PWKU&gBuTu= z4gj4$8$dZd4Hy}w=-`ZK>m6iihOD-dvsP+?Zm^UaEgvDX7}@^(tq0R<`HFgCyYM?9 zJr&aB;@_j8f4&)>c7B?6>{%!+Jgv${k~`xH2*0Z08blUrULHuM zNaTBIiWG{}&0id8%V$9pg>>nB|~767FqDn99>6eLO99D(8tnN2uS%x< z3YlQTc$UZ#@Xs^DT5c%wCK+iwTq))EdWXgwAPg_5j#Ok_^O!;OJYSljIw$Ng_x8K3 zoi9EuXNBK?y`i3pzz>mUG#(0!L-fz0p0yn>GMN2c>kaZ2J_S*tC14F1JNBV@_e7}v zC+QcBnyztb$u<_(EBJ`J->i`yH&?KQw!kVmEemyV5p=z`T^B&I>~nvyFCXA=UFu{g zJnSjAky12_V2238x9G6Z?7P1~B^%;Un4pnYpb)A|K&cYbv1?(S_LXkE`yPHZ7A&*@ z<-;O|=k<={_ZKRg@ox`1rb_hX%%b&HLcKfOM8c#LO(vh)!!N8~tN zmNqRn%paw*(XFIfVBS#Z?8Fi@b&t)+DMUKpd-V0~+F?z9jQAfKK|1NA=t}NB5mTg~ z0WEVhyJibG^t!4oyS3w;AjtuBD1lsDrPC8{GDz%~N$GJdk<_03T<2?VI*4>x+Q>mxmtBG9}%Dv^3_AR?FNUZ%FeAmat zz|wpS04of9nFAq9&?UWD>W>4j%KAUs85o6Te^uozTFG!D4(_4k4D_N-PtW&%5%N#*!?VC+P|$Go=U5Kh zG!koOJ%}W$D#wXQR9+y%OJ@8FN>47f_Pf8b7SgVTu-I6io`wp--v$ZZ=IzVT=?IqP zNEr;8I1IjjpWva=$CTGa1EDMgQI(?e%LQq^J*>j`inZp(mF+F_;@FcR%XXyxE?5U$ zwk#{?Kp@526O9A;eJrg=U?K^*Xcn154!A<&HyR$XMvZKpN$Z2KlZ1R=+^O6j@LJ0t z&I0zNCr>RG6bb~KxkF2k$sw);$l1D7+fKYdM*tPnf?Fp#uP5;|we>Co8dH0KrjQmp zsRJ@HyEFxtS2hVTY)MES&P$zivr7>lbo>|NRi=@v{gl~MQJ<5(lkoSLnHsp?z@)2f zmw+RR^x`pQeyy+h*lcMUN=%Kb#e7X-D4Tr}{9~WMOvMoAD(1wJBB@8}IlnbAMpJXN!3^&jlJV+8?dx_=BgOCXfoqKe>}HMSv5 z5JPEv3q%U1>-shc(RJW1prN-Y+6Z=PgHB5i5&|4RZNBrg zf&Ch%_!nOvqSO!pO}T2NEENLPuUc>GT&twc@2@y&f!4ia=1pd%>un1(*b%*Uznfl2 z8ft&!WZl3Q;1+zF<+mRjYvUC~N|F>?Jv6Viq#htH?Ydr~KPJYB*TjC?jdYv@TZ=ND zmx`KB75JT3+H^Hg&5z&hXb^TbK(&8Ao}wc;X(Tm{0TdlF+mQCC#xUR$IGZG4=rzwo z$E6e4NgAo{l1V3oo_GY~EfT8z*eI!`($6MYj_wgOjK&tF-MsJr@`7y%D?9?Gpvbl1 zC3M!uS1x}M^h~=S+TSKx?j?pU1!!8Xp*NV#JraQst+%h*bosH=E-lT)?sHog$WXQ_ zl15h)_i-c)01lO0Z%MFii#zK|nOiqaccZnYbH8%pqo7+nY>Ed+w-Fz#hPyRB#0-0Zk}v5wxFF9eaax99yNl)HT&)={~h-Xq8(aUfuf)0 z&co2z1HYLymejyTM*EYsw#*HNaWQ$xJU##e6 z?@l5hxI$+i-r#g0hV>{8n9q){Pz{+}0N?Oh!z+k76UPdq7X}V6dC!z!>Tn_#AJ}?ehzw@|Oh*kx-+5E?8r)%o$balW-=9m+;`TVFk@8!5(-R816U z2!-5LFWnGkJ~Qh9o({C)d&Q{@BiC)y#2~6Dd?ptqoD>fvXiHLWG?Heg%#!$La5zD~ z3Fdui0byC_nI|2Tw8rK_E^gUwJW`Lfg^cZvQcOl)lwnqH$BOKYX-Kc5ByFYjbRyy9 zduQ=nzUsowKfr$Qj!#0g16zShdS3!`cY0}$WZ7|dBmDVqKQEhQx7A}w^T^fFTL^xZ zZk-1aANg)-s&rMVju?SWoWCAWTp--Fg6B$upKG)rx4l&&G?gtiTlJ5{o zkSxeyF+Bx8YISjR`Tu}1WK&Oap>1wr)iw(jo}hV#?VSMppfS)OZrvlG#FT{q(sg{D5?Ki4Wt_k9eWhJ zo26a47kz&4gb~>%h04MV+?h!Ig^YF5Kt%e7eSap*vyrlSY^LD3m)RX0Nq`)Ja*x&! zGJrlB+`k59M|Ky(uk?myIt$j_MUD(PW%<1(2@)3ppKgC?7gmo83v3K)Y5%FWJ9#I^ z?HEh}Ij~)EV99}ZxT=;r$rUuM{I?ExB~wzlkF-0vg5uby{Af2KyZ{ygL|^9K;<2!+ z0uL~cvfg*>W1J&zhani10ee_|={g%9t3CQ#OQS zzNNqIi^t&-QR^Ua3w(o5yTsFZ_CH0YabuIwYm*;)}l7Y}oaTM3a5S_NK8 zviu8^4LUgDrLL2qwz@eR(4efo&{)-N&U2<;&!W&AfO`vV;ABw6hrUxwT!+RFj&lG3 z17Xorj4B~lYerAy;-5b5L7#0~X>xcECp5R244eV-$NBBz1A!bKSV1Q_FGT+nbMEue zAR{Ym0|x?vh@clY#$R6>u88QtEnOa+!1?B*8vMc5__WBPnam+~-_Dwa%Q$^`dlmF| zTnevYsL@)z$$Ig94lz>=B^JZ%D>XQCzn<*9MHA*q~8(OMfx8D)eyW2NQ zM+#`?7_^1e*(G`%z`7D<+kjrj%1W6_;))CpQ!f$zfvA6alDHN*L(4~*vrX5?QfHBX zRN~5Fa84yajNLv}U9ZnM>Bn&~PsWiJMuZz3_;?Q|sNrBUldcX1b+zGH@e)B208@_$ z4Le3R;rC46oEaiv*2M+O`~WSwkvdT$OJJn%?g|ZU`gm;bYy72KYD+ZTi#Cgp(M1nv%eiw8Ogwz^V2j-t?XVu|VMAs{V?`D$wsey~ zBTg&Zpw)G@;WB8tB9?KlJ(&N@s8kcxXlLo-hm8P+_WzoGk6Cx~gCk*qfJs$xgZ?_+ zL~NY;&+viJ_=f`1{PmtY_bjW_!<0yqS;Iu%JHxt8e(4Nk(Bi=;x1*qsLc}v%VDA`F?6h*;{|Yjqc?OLmb~RqFtE|Z?xbQN!>?V2+|L}*m5Dm- z#V30AQHcf*jN)jS7Y!MDvPCTdwSKJ7Tt#2JBQrw(EZ*0>&QT@*h{7a5ur|c;J4(shX^UkLFJpKfYDcCEKb6zluYQ{VA6s zYN;VdN}bFQQd?{y8@hhnPYIZt8i*sXY$C})ZCJuXB2S`444S6F*|1|aOz?e)Xg2U+ z1^-oF^Tdzzskq>?rM3dTG=da(cBIgo*er2ahU$v{2&&ytuVz(apO_Oq=xnB)HD#VP z(3b%41cmRX&i=>S6q|n@4%HJU{xaaIMKwYOZni!`>Ri^)w32$x2t7&s+a zI>gowU`Vfyls9MJ&NZC?ojX(rD-s>&2m1h($yYsi)O>p_%6BBl`wfMWly0~@N)$k2ZXQ`> zk4Z{jjj_TU_v0dehM4lXoO6iQ|3@;>Ou_3#sn&8#$=~J@!vkE*333ZdD12v~1!{!N z^te<=${%K$a7^%h`Q*Iqnq$Ib!uTy2iNjS;K~V{g4eWYNFfOUqDiul_C1dO>y24j;pw9vGKFNuhs=Q3dh&M9D#Ft&_FO-VONPZ$uF%bGm0zY1jheW;7Je@tw zjS_ZrXRbj;AhD}yM_>ZY$q46mv7#78O+oeBDH(Vo_DpgaPX@GD9n(B7c&LaCx=x^jDVWemA( zN7a7m8^>UIqSzc)K3Eijzx53Rl25p4f2|M>$E#|;sx6J zJMI_&$_=$ssKl|^DApNyqBuwCr5o{8n&1xjG9=-a32pltIk%@xbBdI#@K3N9rT?TF3N<}D)q$ZU{5W-RU90G6~{$0e_4PBM@zDp zM$bzk%@XD(L^dRwZJWTo`gFbHqEGq#!o*!R}2L9P#FIo^0)MO@ssU9n1HITl-?(jg=})Ro)>mZK^F{IJ48TSsL&?w0{yAe;m?^3_dw z2%nZIR-b3_7gx^_!$t#1Fup^HaZ4o`J_m2BlC0 za7(>Pe~FeJERq?g0^k27CyF5WC_{*#h#Hv1nPJQW?e9&J*8s)(@T=+EL0A(wTgDYG z3>>T`xagmXOat`-7a0E~m~e$@<>}AyAdllIhZGj53izuX)BBnETIlk2Y?+V<-A-(SKyc(V(4~)kp)@~PvzhnGpSqK zh&2zE%pT3pawVnh!OCDy96V%6?(A~Zrm>S*;DfmAjdBEqVQ$y~0qEo_MPwrof_)GF zKt5~kz3n60SR#9A3VS63qP`OSV2Vc3y^jdf@s^(9Pb4YCLBO7i?ljg2v-Gck;-gG~ z8R8Dt280D|Fx0l1)u9(pe`L!?ugchdeGV<}N}?TqGfeYLHwCOM3iDh2D+cWjLqn4Hk*ryZGaxV_(~aj@r3pXl1c!?JEMZX&3$|Wzp{GO zCDIbUx9E^i&$(Qxvzq=RJ4C52%12(v87in-O(D5o|*xLvP^F{~pu_sLhQXMP(#RMb-ah!zrUu7nZ4~?hQafuavKfgDO4^ew}Vp3mftwOjZoE z#2J8%gP<%#9&g_|Z`ciEps;brA$+PD=tul{`V!CkF{Xm^`Mu7)H zwpES6)RTkrL-%o$wzyQE``4*PXHVC7nsjc+mb@II1cItzGNTiyq+-dt48*n@7=|LG zQLaE8m$AadgdUsdcl7*62pJz5w)D7JySG9ZaP zk$NEi88Na3#8*HF;9dwg8iOupvX!DWX;ZUc1mvP1e$y`JIu z<#y-MXvvT*Mh4%BTu_Q#OR?RAExg|@YY1LRyD~a~+oBaaBS0UY%y2X6abdKuR!zwfRdfn7%_NP@- z##u|>v>p@`3kt(B^uDm-eHvQ=Ye%!oalcpiQK9R(H12|F@@y9|O~)(`?4Q3bm{xRSmag_d#W=Qc%^5-LPCHn?beKbuaC!%hjG?^RZ28-AF( zxm{%BdXm&#iR=aE)+JQ7BJHu@PX#D3VAto=+z{Q!^NZq0$svpLg$>?OUMf@%5w_V) z89keB>Iw13PS{kyh?()n0*le0pNigoYyY0U5Qn5Q0ge+VKDWk~E3GEEo7|)EcWcx0 zKfWv;`?8!D)Thj46i=q_?(s@#YfRQl_n2})qdBDAz~m!fAN+x2Xwfc4J{vHf(2BL! zFJrnjOc?-^!D|<>p9!M>gJ{QLa332FD3h1}DbdR#U)@vU32%b`Po7!)u;cKaGC$ zq!lLgZKvLPGs&Bc8N>OhCLF%Ls?9&$N2^I9nnz|L?7`$$cfFf&(dI=JU zFsv|q-cCG?d50l8(6 zp_hYH=}Ab4Q6C9t6_-Q_q!%YcrQ({>$Xos3M>XLM<~e}w-S`HS%dj4V5e~iLmWn;g zIN1kIaErey)|`y#c@@}*mhAFRb(O-33gH3B7cS)XnVb*P2uHxrrMdlX`Cj~gd55eP zCOZhRD6 zqSAJmH~@C7ZO|z`lC3HtAsMgexKj9EyEPg`;0j(Ac?-i-E7};?nL^77K>Z-Uee)LQoTi80NTP>A9=0fh!fJtf-UYHRJaa%f5$#jjTY8BGj zir;l}&wW027$H^+{6!YS87csx%!x7&GJ5CbbvyBX+G1VW(K0NmnfBfxw6WI+iRB6r zOPv0(YGNMbBO$+lohf%8*L!1r(gJsTbj$#SPQRsHl`Vg^0r`Mq(_y3iTUbCzcfGhR zNn!QfNSZR#CR+_sed}oFmZ=hJ_X2w#A~)G^JKf95^l`^<_S@*Bd8P#rHjc&;kh470TxXi3fnY)>K}%?<8c z0xT5BDFhq>=*jHokd=PulDBtmRMydu0js&Q&w=hJdD#ko*fGg>>*w0JbX!-Gd_>FR z7gYja(dfy*6T`2eLwp2d9FF6biCzIII7Jmd+PN0NJXDxsO*EB@PQ8I)9gNmNjGEk%evlgdoL8(QaJpL4? zPtu8^)Tqf&?Ih9Qu113{PV^4{#+*E??N6-MSfhzpC^edff0alO@j!vgh1H*cqcIoh zS;6D14<#{2wFv)MZOPzTI}Z4V)El~_@WSx10PPCmsSu3pA~5#G zw)l}rV}&0|VT1M2<(fpaWK11Lp9`?`99I}P)st4|r-Z~SdRDqOv}KOu4Km?_)~Hg) zfs!0yv??VMV~=tkXXod7vhhVvfEH)RNA!8sYl zf1(}5N(%?k&%2q>brsK9gbw;x(12_RUL1;+<=(6gex?GzOy&{3wnIc2 zQIH|@+tx4+YY6DlkvNv|+zAmPOvd*fDUr#&Fy-N4jrc>wu=4?uS0{a;81AT*v6UuW zW7IlVpJ38?im15RJ4zGUuG7Ff?A%Dds#9lcWV>9V-ONHFn+J%uhuC^g`KAxEpjA$cO+F z4H@ivbos)lh2V#nR7DEWQh+K(vu4?LdqOd#%j>+pD|cD57S`57z`<*-+L_b2O0@(U z12K~kx3X67eP4YrvcC=KC0&%m55z;FH-=VO*B}yX^JFeri#I?5>~u*8xH(tskCAHd z6Zvm96-$u}f%B-`zgDr2ts&K1=jTj+$5OKhoU^~@t&enaaRBv|XW$_92OAbsez>+8 z>4f1L8z};I5es`uw+^BD)mX=4@B$dEylGI$?ahq&zI9<2-637xt`vihQ?x3CskJ%v z9~>R1)-`wh+W@xf5-$V9FYX2&=|cwRood&SqJk2mpd<%aU;N)W)wqqrdoD1ld zSIi3We+@p%p= z)fJSjcIek0Y9oUI`jXXcb%-zdNG4E5W_(C=L^Z5jFiAaIERE{F#rlL@!Gb7{|GS2H z1BPZkt_OJq2m&q8Il8kJak|tcWw(7By%Rx5E;9#lqoR{wxB_~iT;2@P49|pjB)rEX z*NJhjWQ6$Aw+h*(kgaXM$m8zYp=KhAfj`R#1+5qm=>=XQE*L7?I++sVp9W}Wl($Tl zJkyZWqdR$U2*hYc(S-(&fVRtOcGBVRvM=X=5ir*4 z`CL>)8tPxMBlz6EE-n(aZo^F3BkalA7?Lziu;4s;+nczT{?gp?rf6lK!i^~}9bI|4 z7Jyg<71-+F;n^HKiq@&p31hrjVYIU+UuYB=9J(nMNY&gUqukdK}S&kqt7OxMHMJ z4Y>85&3zE5zCW${R)2Iq^O13plghNW6L};m5ev~WQ=U%ElW~MAQ8*b#eVtD5;c?{u zdie-(k;M&zclmM6&6_2|`9pSx>*TFN8Q*KiXB%mNQx&^af!9X=+hae43=!glPImGC zSMLNW5kmOU5tQ^6Zh}Fh76Z7U?^w4S9n$Knpp*Y)Qq2&LQd~x4OoxRI-^pM~T63SI zaAR4ZZbIhJT?UhVv&;<#7NMN3N9iVekj?1o+JVSEUON$K+!Cu))CySuX?C2w^G?Rz zke=WNo}k}A+K#T)5kFGX%d}F|+aGd^izXA8HN%;j499d9qfSW_GtE@xBZ5uwAJrsQdaOUV=wvqSWRS`O z?2N)Xd8L0QR($Mr8{8-zK-L6tIY&q^$ZzX1J2A4B>5##9+d4wj9sQ_nAxWWdW+@H6 z?NTgxV(L5FZ#tB`{FqvvcU5SP=D#wkGB6o zajdb~{N20lW9-s@DGJH~15xb+4e2h%PgFhur8@PJWqhu0tu#0{~t%{H|m1DO-<^5T= z8hZqaioHHF5+rUUk7*9$O;emgbJ*RTKEK)p)Z=y~l4qucL!47!0}#BOA>AX?ZJ7K? zHj*-%;m8nvfA#`&O*gd72ojU{>FcIrrKX@B5=aQ`L@4YXIc)q!yHb{x5U_vk$|t;; zQsw`+cU=#UrbAfsfI?eX5%zN_EF0=rnJ~oAu~3C`v#3IeyKkk^|G}TAn`j$V9*=PJ zGDpU?Owv3E_H6+C71)>MUug=p4K9WM!hC#_yr!-zU6c2=J}2ywv#bD2K(oK(PDqE< z8||ePWhjxNa#0z}_DQ67g63q(#`n*qM3-k(r#LzT9n5AWS$I8fDihIyLSz})v0S=1 z6wazR=T~^C^>kd$N)#RyR$8SLrs=m%O{cJohlqUK5!tu{dSe z4}m}-3X-qraZL$~(PX#`+?F6c0q}obi{yQNp+RLmKw(eyFyAa#7aze(x8MSC3?iZM1oq7Kpt zG48TvD=~DyQT6q+Qh(7hzVrRZV>7N&zLch<#O}GBbOTJ?hbc+dCsCN=hA1G&JHC@w z4qBWqEw|2fInpc&WiB=qHv-QigE^r>r}Ow!ra16MBs-cY2OmG^v4jh%=<_BHW`ij; z6Nw}ZaTs#5Z|^@a5cCqu0uZJKcPiuolpt%UIM0C14>H+m_`MN{h$Yt4qaV04%>K`Z zoL$=&XzP+0<);~#+@$H611j3V$XUjAu4TI@EmPIzp84vgFe`*SZ8}y%17hF4b+5bVD624G}$;sbw+^2IcwxnLx zi)Lh!o~zd*vuIEpeJO;asv{4V8ljvIqiTJTP7-VWlYy^C>TOK^dZ?zy2~1 zNnOP**weLZ58?BJ4FQ}Ma={XO+{m0^;R)+EA4tj+9VSoW7Ys(O*{!L!fS~~}V={}? z1^b&fw$6txIpQ_1xo(Zm3aSMfa3-@zxv68zWiB zoo*5>lFzac0ufNnyW!3Rq-PMt`?cE%K(y>kVPq`IY10p4LEC#GNEVV?R@I5$$VP^- zmlp#dbR+5f=p2W7s~(86O|){xQJn?Y=X8@Q2cau|Lv_fiwkvf%3~Ia)pl8JO!%?*P zmhCL8t~xJpur_-XwhIdfRFT;cadFlTMvfyJx*;xQbM~vpQF*Hv1!e^Jw%L~pu7;8H zMFMtTUJ_cmrR;+T z?APxlD+67CsP1*T6zQb-k&veb#Gg!Us6Q+?5sngAK`g;W#xP1wGiIdFrBCAd&u#x3 zx*(y_1>Q4Yfk7>UoWEfM4+Di+kabaVhX%93Cb!(750I+Z`kE{hI9L(jWGm~!uwyox z)19v+kK_-;TK;hYk!1RY?{TsQ{AR1@qw!lXkR|cF12>^%#)s}Be=>jtkNgcemDwrm z$P{aQ3*n5@=U-2XH9!LLnQs+~XOndX7Q8FtU2Xqu~aAlZ9+@@qq{nC{X=jJ0+gb(L)fe*CQ^bobJ+sO~_cpH#=bmZZGL}X3j=$;CI{*e;3kmN7TYov9!GdAz>L#Wr!i3A;xBe5q z3{@ zHscWyLubG?@iF=>q_<^8P_r2UV>yJD_`|r2pda_zG&EQi8H~D1rH1P~tRq#Y%W$F@TMNK^DE=Ti-PbJsMVjInx* z;(qI)X;3VQjLP+&O@mQf7l}C_#EZlihdZM9am*|rR_nI zRNe_DfP?J1n2VLCyZ$T`m0!6nj6?p`Y_4~A$ z!&UPa?~esyg%0>6hXo-&T-0q!qFx8E+}RACP*I5w(vl%7Hm!^8)3qOI72_o_*$jme zLZHjk2@PZ?5_g2UBp|MzH4V*Igq1X;!*s~7_Dz*2SgfYwTuC%cn!entcN7sWL7L-_ z^)2Mc<&oBoIwCq!iJR}n=anMF(L;>a#BW%diB_jnP-5x^**M8ajv8;pLj(9BB}Y-2 zj4Te|)jD=3!P%#l)6u1T;AnaZEpoGzizZ=1T2&FOQ=^Ky8V63Q+6E*$1k4Q8q7Vnd zQ{1YIgY1;_h|Y;FHoJ0kY`k(LFro*A7RFK=ad_sEC?$t>uvij;WAH>rn&?SH0IV4o zSzQ=D{{?hUmXT(bLKZx!x&2;jZHl_h1smtWtRyK4<&D7@foMqig3elk!0xS%`2{~| zK;Epxz;eP=H=^LJ`k4jypb(2nSEXKP=(f~qj8{+0eyaO7B`EN7odPo`D3#W19>L_m zr)R|3AJCb{E{If|mkl`%2%LMAh5FeP?HbU51=I*9hSN{^I5nTF>6DD#62C`kf;gU9 z{tN0Awzvv-+$LY45M^8q4Ux1OQKZ~YxGsX#`PbE{y!Lb!0RqM5I{M$Gw!AOD)56}g z>e`A^J0K*KJpH{^O@PV`L5wQ?)!2_6EAv8)AAleh*o=LyEW`q0vfpFi z6XRw-5|Aecu-EfPrFS0cJJeh5MRSgIn{}{t{C&~LZm#kWLm;g!%pgdflenI%eY)ZS z761y`xA=6YAomUyu$fWjL(qb)sA;%iWBfa&YPWkmOO5hLc0(Rysr=m71x;Vc?-)Za z7dMcL?~i4#(I1S&Iv8((weT~%tu(|AY|w4%1Vxvxo!w*;iAJGW3BU(ZKF`}DN{0k? zx&uh|Iql5AuVo1idWCVF9$Yod`v@8fW!EC6HWaR#6kA<_hw%4qnj?mw6rQzPqo`e)>?{z&QU5I4p z&+EOZnk69n>Us$>zlRjY}edU*K(~E`7GSHnUJH%9MQ4KZv`X6z5$C0#S5zZxApS|-3YB= z;i9h^$F!xOmID=iV_WrrptvIMNGHkgqY5t-{O`ir{FK@m86I@8!=a3acxf_)UX!vb zB%M`V#aPeOgidWS$DK#TRHuV-=yV4ZQCY3&Wef|Rl) zJQ0{Y>|VdQZz0zaax@ap!nX8q%^&I@2S~r-@LQCXk9Yo8=W4So?Ddu&k73%OiyikYR-WT1>NEP-GszBf;ypFu}^_Ggr;Jo_8u%O z@H!&a6;dGh#c+5>>1V@oA|$k{;y1!M&Gr-{tBwZqPOdYyOV80&gz~Lu*;16?7JP|{ z5;##pn36CGCh4e9l)Y^9@XqU++r2tF0+t;3d$9_ncPSW?-7;Yp93XpWzj)5%z0fiG zmi=DL>fWV2EYtcAJPbj9FVR+69na{y#wQ=L1q8b}k4blr{Sz;06#d<}9?KVb#stgxWQZUt4SsvB$9&purp&c3i7uf*mGk$v> zQ>QkG?sQF7>3Fwott+fmSJ-P9+h@`zQxwt-ffk09;=G&2eRzU<{GeB$( z7whK1TR{9@KQ1{_(B+d_SOHf1CDAyT`E{!5{iekn45;mHjHq&j?tIi6$Q<%GU>3VC z`8&vtG`PLXpMmxf$$}T;o%0H#h1*-xjLXOgQJ8^BDVePB?fbmW{c>$1UTds1d?J9Z zD7_5u|37uuikeT>BEQ{ks{@h4VR-1L0`ezY#WkgZz}usk@u+O&h3bx$w2wKF%&1sZ zCeFGaOFo@9;cVda69Nfolv;D)*^5`ETocJFklH(VS5qJq9fg9~vhp)kIgHX1i+?Os zM_;36z{+ORsNjV4WP%D56I09vzX?jJcy{RIk-D7`&?4CJZp}jBf@AJQ+d-wQmh`SL zcUp)Y?%l zI&~)H_)An}$rY4FGq%NMCV(1qux^t7QT!k-S``ec+jy6~Ql2AKuUVInZDJ!%%H)H3 zE6n03b2Kt`P1;~DxDELVd$qX>M|Xn6`;vvZFy;&G;k3qBb_}occrNfdpp7kpG} z0Q?j)1ok$R0w)CZ#r@7lCJb&-L0}CcJ&!WEdiMCH-u)wd6VPm)MU`R6!V`4O`PDlj zxXd(|@V#tAAjK5GX`*}?JG%3}gDx?KLNo8>fcb#SdIld3zJoxdc)o7KK&CP)+4<}* zFGB!nxP5;gjk1(N>Q>{aYnL!eNcQO818MqEaiT3x2DXb_jSMn|M*>V$0_^Mppnk+>sfi1P7oP#YxUgZu&(L%teV@@9m7PBZb@(6LVnM83P7jsd`AM-oh35k3kJ zf~CVg%sS9B`Lc<0ttwgj-{|S>L3ZEl#N%?Ru}BOqm@|d89pNbWP7~39cbB|`L=g9` z><97D&B`{_tmG)L!;PaUm~>AgaV>yj^phQ;Aea$FwkX5`H(YY!Ny5Dwv40l{=rTNF z^{1badEr$D%Xlnj=0fH-JD)0J4n}7dCZ=4;;wu%H+jb%%WyB$3dQ!6Uw38TV8!1n1 zdVxg&4l!QMILSD* z5%F$wy8b4`?HtX3?8vc;9^edAsC}^$F>6TYBzr zG10RKQ+$yg%m>^2f(*W#5vU&PzGK?&iWeo!YXa(nvP>I|n4eThUCZ#5Va6B-atTEn zgvFje6z!O{W;CxG++_SK~*cX%;|7d#Cuj|8~ z>123j(@+!wc`3fP_$1F}Yz@f4b#NG;qb+vn~l2Q>M4S=@`z$6-WeaI(ypzsB^cWX-YAjjVcI8&ORLw zyG4A1eDJ&*{BnE4)B?J5G;%zJiW~N>Kc`VJ4Ar^_JR})HoR+B}DH7NJ^0Z|ejC(7}H zZfNdBkH)+fE6~9NMup5c`{9CgA;2eMoqd@>`vD!hN2Dj()4VwIR(=r}^pP^r<`{V2 z8%aX0n1}lLUk0_)Rc!?g)pCS`+4dU-(dqY1FDw2nH(u&(E4Spb@vZN6rHqa_N9oZv z6U>U3h)w5ex->{~3lv(2#ej9Iu5`q~KccM(Sm6#Tm()8fR5|Z2GI?;mIAXbYmI_F* z7_f;MM0jpH)C-DQ*Q1dSROI~5iN2k(IBe!vrY=TS_u_MctPs=L`^R4mUEEt1g|mTY zY?|l9gy8xBuiom)4`CS6J73e$L2D=Zq7vSq5cmzdmswzr?|lsNbuftT7KH_6;%9gI zP>2-HkD~t)LuvF=cv^E@_ok$-SycO)u8W70j6orGOsk8@-utByoLUq4px9p5dTIZ| z;iM6^SHzO#jSMprQUa0^SZg01kmWFv%3Ol@7qh}mh~J}ZELviKZrwt2_gY~S0Z zi)=BpeGjgx6}Q1o*rW;^^cq>dg#O0UYIY(?U?^=31jL%6X^N8;T>#W7Y4DxqRbIiW zblS+dzK3P>O9y=fZKtdf-)Qbj%WGD+U%Acf!lr)~51WaFWrfGTTNDB)}4!rjQShEuf6Gl6q(2-J|`u=su+ zstgJlMH2-(;T0yKJ>Vc(Pq);!3wh#aJ?V?8^!z+^YJ%*UZ4AC$dI`$hTbfUJu@ovd z8r2=`0uTqn*0Vq0eFk+SfV54AFBsWC1_i5|(BYvT*#6W1M`gE3(xNugzma7xsL*L|nna}rzBKl0Mj&x~t1Tmo`QpwiBSLTe#Jr%Q zDjm7VVbiz_YGk6sb+}0Lc;5YveIs^}oAN0qpfVI%i6sw8x)nDLv-psd!pxo%sZg!Q zkm9zvLl11_V@o;I4$Dj>yzY|+L)}?PyCJkCiF{^sA3zw}fWgZ3TVE5_n>t%u)GUq` zY377$lJ^D0GjX8Xhn+j%%iI%C$IZ_7+TMoH03MkEMwdd+oWb=M@65wgckRaM7|xgR ztYZ({Qk|ykO5ny4aaz)?7O5BJH7ronRX1aclj!2B5@#RBKX0&93+|T-BUb@QaH-a? z!5`8ytCdvS;Sl=0WwJrc8$g!nyPN9WAG2)u(-t$8Co}-(0A8gfNpS9IQ#@ z${at~-Ir>_)d2LvKJ7jC)rv7|Wd*6F~_P1rg zu>EAhz5rzao{=$9qmT-N`QW#i-@9#%Q zV;5#jHlzKpy7>kX_%NfW=U`+FXE7OKFk4UzO8A7^Rz&)w`KiX;o>fcg&LEqoH^>yM z(k7PFJI$jtp#&>c?oe`W2T}+5w+loND1U$qlx+tk6{GV1>doEuSHT6UQhOTy+jtDH zlnuDE`wGYbDNd=GbA&$!F@`m~GE7B1JM?jj`$;af=NfQ+Ll1FFZauni5l490_e7ae*e zt3e3O3Um3lXzvR_wp+y|K*R3dLp7*fM{lF9#ah^ zsC$q4i&1@wT{* zm4dh=_|cNH%MVo~PIznC>t8;Cu@S&zJwmnz%brV`EXv8zCyS27fz~#4CD*Wu^!h0@ zudTJ>oD+Ha-sq|VtM3h~_0I-#qWTa}iD=QT7 zqEzCr^Usi4(sQvz@`XvvUHr_DjaL%8lr$Ot z7s+BQ!eJWL;|4^icy-^bAq^1i!QFa}z$m$kKsJvr6o$%PCu_0hQExq;)73Jzg?+CF!{-fxuVf?S_y>1fp-1$H2;QrPT(t6K~z+h4A6JLP6 z?F)2||14s%&O>aFVRP0@JC}PMppL40kQD`>ar? zpr_fnfe9{H(*NUYwaSn%m-z(&E4FAX_5hn>zPw2=^=RN9KWHaauBUZn^_z2wUtRxt z?~}T~d5wianI~!$A9jUcrJ1YT)Y(b7;KI#prm__%!#yzn5KFtyMWm-C?EOXp;JZCI zfrGAuaXrE1X5fSwE`l*GIbj`9xqH%k$X3+^dE#m#SHR;ai$_l zZdn};GYpLW^!7KI$wjB_ho&|l2V*O_J}vW?F&GIYwwKNTa?nGj77Q@erRvF}d{P;N zW2DGI)pXM7zX3WE;0mF`O1m2#$v+Fv$Y6?BlUNz}DA_E%Pu`O5_}FQm zzAdd83f4pqHN}=r&o`(GWa2?iAjkGFWVWxGc~O*Vr$~s%ycClLhRS%|y39OSX7$#t zR`~4Xqzsyjir3q|XALql+no9WRE_m6R&;C022s=kF!TQ?MJzNzUC4Oy@ZsQgKXic; zB|l#1SXg4Gogo<`nKwd_I1LPj;uq1(E4wYZL~;hFy>PmjZGHI<9GWPl{$zNce07X0 zW*p!mi242T9CO$%QJrb~kQaZy-bD!D0eakq&hqXgIa0Bds9l5Y+Xm zD~BnBecXZvP{+SMbL!VhDE`~WuvD(Iacf>_35|Zjs-pm44|^8T^P9( zTe21gD@OcoiEea;QpSO+lh58iwhJpDt-cakdaMjjngD1B9Wl=d5Ux1U z_l^EavJS4{bjlLXA}_0kY=?vu4-oZ+?IH=Fc{etQZkF1S#fb6MtDbxbjN3JwIfT;i zWhbqk3r`LvhWT8M;<#HTxZ9;ML@Puwd;=rO#mT1{IZ?JnWrSP+Q!LnWc11&+Np;Ao z8mpGo@~~Y_SLotnZe^3@WyAIi=L~=Gn?{T;FUuEYMF^-Af5i3@RKeO-S$bF^RpiX4Fx1sgcl_<_NbsjFV<`~9SYqD6WL;Au5XwsEu9xRY`1ks zQJ&#Ofc}t6*8u$Svu?w`aeonfAabRyuzPLew~YIHq#G^r@H5fFl^4XqjC-mjL2~?dAH5j2;GO&4Hz@4Ow-p$NE2vX9BaMhaeC(W4%l- zK!O)r;*(q7UUPgLr%~&#*ZGTGewtz$8(R=><^M9?VT0kB7@S(MP+!TT9dhk; zz!>`$hg@ZG>GIk34)ajpH5FS%q)e&wunKqwytv^OT@lo95^grYA|h378Lh_y*kz2+ zb*IkTHdXkYxl&?^*Np%IyNELv8)b}`Qu_3ZkY+L6m1bMHYIjaF^i_)h@T zjthHgU&RPmb!X5a)q2bSeHr(A%z8MT?{EpK-2?5IhdKraPBJN+5n-sKIqYBgDD<{Y z1hAqFVBh_Txuv$>I{tt0CV-B;knF4xLQb>X^FHM)pI9(0X_GeZ*{+eWfW(Hh+Y^(5 zsO+i=fBX|JOZYXH!a=x^aMDw@=O1^#N|Z_|1qdm)sdUK_6#WG*mDAk3!D_Z1Xyr(j zXhRszH2#q^ey2OR;Rqb}L!-rho#A($Cp=-Rh$kW&=zFM7U@%d$)`lud#do zUH25^Jb*5v77OU&*P(9p@5t7j@|S$`!_fBqtoyauz$Z(M9X?l8v;+T zHqVGmTq_C(TUN@Hf+$G2fC>H}RewlcRzfjfENW)wURqP`sO5vLk*pOk`(-fP**guJ zjoB%4%pecSj}pc8UqImWO?^hoDpnw(2{Sr7>{L%L6_Id*KBpDDEm zGG^R=A$S-Q!<(K-=;bGir8S;;%~R-~W4OPYDcI3r3Fl?*`M)s7B68bhLaKqcymiE9 z_RzZSLSem0=7!M7NG4CroPXH_`ETiE98TLC5BD) zGU$z5to+BO4|t-I%P>(F8Kaw46n)nY;S@McgVu|;3N>b?_6a7&kDmH#Js=Gh;tHX9 z(qFf~xisJ@T6x5INI_n#MTPK;&3J3jxyLFad}__6EB*yUbNgKc$)de<^fLgMkJ=Y1yawuL-0SHN97W zx~LUW<`Z_;=9MA`pX`~EMYk@W8-O3Bf2`1}Uia~e9PHAc6W*szPb3!v1yU}b`wcIt z?30zBCV9pankfHot3QPC_Ue4kKU#nth1K+YJNWO~fO@fc>Wn$TN1o{RLjfzb>csj| zvO9|u(PRF@S?SF^cbs;^lwC68#X2dJz8C}>4x;21_#BTGphs{3fe}23KYEbBC1HYD z{8!ByPIT-Oi<2LY=pw`c{v;K1c6COGG^;x_+=?TZiOF)F4gWY8f_mA0YdF;}eEG4r z=nd!>A3m9zysGw1hYw1*&CGQ;VQ!_aLCBteAz-xWYfxpLVv+mvLBk~lE@X8#l zX3JLelU86A<>R_8J5(ICGf=~1+{IZfyY<{L>+eND(HBhNjInkMGx&k%v0mqz(*u3n zfOOp?{5w;@Ml0}Q7emsLQIrD~`eYY7RNw3h*+R>dbTassqueAD@)dF_Ah0m|!a_j? zj&@1P{CNKA{#eR<0SLA}ES>0kHFWjWbd9WjGKoVP5&0>lfnS4?vR3wbVj`%az{MZ+ z7}Ig!Cy8Qmzjvgm1<`%R7boj=1)K`EftN^1lXajd?Tz#==Xy6Tn^WKquYjge!RPL_ zd<_gqz-B-9fK#7Y^lm`58Q=ylcYg=t|TGwO3NoV^{ZR;3#_Ov}dfV zG&vCnmFn~deAm8mR(V1Z_Z}2DQ&Ms2dAJf|vuPV@8`BF-E-+gF*ujVC5Zfz+DQokYebrXlNDEG2;rd$MmQu%}M#rt0|yRg*KJ-Zw`; zPgDlO{ZukLB6z|k^evM+a(8O@q_KDR6U`raNs0PQ{V-1s0$+S9CgV30oIETRz&9t4 z_{#oWXe`OADI4r_chpy*Z`H4^SnLlcg_?JYTE}ZKV$m1%X&d#RjL1zArlaB6>~))i zbQr=BI49!CWKaj`9!GQqxOuoF@hu#FcUpf7e=L<8h1G}xqUU6ZUWB#+6SG2WOG zd+~9Cuxd|8Z%Q1Zt0-5L&bUZ(cRg;al<99A{pWSoUzmum=xHje?tzqVX>e14xf7dZ2fsIfzXbPjr*vaM>6X+*2_*{pI zN;t;aqF=!P+@%I;kx+rdrGS&MhEK`_L*rTIEzd4EausS;)BP z`y{A<8e*~@n>O0Ljq34izBQGIwXwyW4kv2o9g97TU^@4RAaYE0Vo5qF!pz!6%b^-V z33OB~?BC;q?ib%zQ-EkrTe_s%ZSB`$5;JeL)mx48dc)EcE&`Yc=QUVC^&n zq?kuK2oWNWmE7}uW1E~Fa4)JJ;}kcJ*R2nkAESrW&%Iu_sf(vQtMv`kX0sv*%wUCg z5sSr&Xyo4aTy#7K?j4=L1BIW0qSg6x_OS~SZ6RmICvvpIwmokOHT(~@2wI#@J{hnj zChPr?#k_2)0AQ`rn&@$=m{dV%NIbmqTz1TZd$3Q$phB$aV)*VP>hkt>Xz3!TcA{ZT z>?51;5iIxrE!I;|;^f)@_+QzoXz7->aKXm*PUUW)?GX}FhZ9?GJ?1dPa}-p;ZXZJe z0n7G-dEN6?ZfYh{`W&qXfVI4k)*Yhs(ruc9U($LLKfOdX@(MuX)#dic5vaACaclM= zxjX8`j>c`;o-j{kbx#+a{`#1%pe(w>$@2w*CQx~hqs7Da;i;PyL= z+Gohw=PZhj&LhsKQ<{6KG{4%SZ2vhgt7<#|*F{JFhU+7->!=^3S^yKd3;F}&-hAA+ za&&T^nVl_Dg<%m5nX3NVxq2siGbs($t;`Ad!I)b{5d&!?s=Rhm#ojD>_`M?T4wSmAf?1@Zy}fcb6) zh(BXYPJKH3 z=EmEvodt3Xf0HR3TA3zp9`dof-6NnDn7mdo6f|J|{1>&@;6liqT*Vj>V0cFL!8pBk z*i1E-f`lZ;j{PQ$&E^Bh(Ac6UFOELeUL5t85abY{s91S(#FlBLc@V)bU6Hb)F;c!4 zZ=ZvUmdIH{FHy3D;yNu>;^}8?8%ozJ-%;YqQ5-ZS`slDlRuH!fNI?XF%piJ%e5uf2 zX(fW!CnBf}YZoG@r{_pNQ3gEW*Co-Mya-J{eBv^Auxv43INt-tbR>BIBo8`T{S`uD z42qh8Oxi`h8lz;E0M;?{Z2YzU2OfT{+C$0n11ieVmz)Z~_ak1v$?K3|vY6!`fa~!j ztQ}-3tf0CkKLy=;GuQ(d2iny!5J#n;3f=$gxkw)}?!_b%#)QYuC9T(X*?8qT6qA~M z8J7*S#?Q*Z9n-3mxV)hqaj6d(rnxHyaBWKl{aqei;Yt(1K|86e468x&7&bH#hWMxHyl@u3ZT=GZv|pU=9^=G%7B+Z z?h#EE@Lg&Vb(j3Rt6nwbY^ptHsguq0RTQA-H{_zl7y&Oq5W^q~XAZSK*6g|HqcZOM zHk*TFdG$@%v0?TVuug+F=nmQqz+I;y4SWHAiZ9yt?J(TDf^?`HbwP7I$2*m=A{|$|7cTXK(Gwk?w1}LSly4i+ zQi<>gC_5vKjHd-+PH_x*?0-5m)t59Ot>&C2&}ZIF7a+hdb5q&{jzB+d1^|3E(|c)E zNu2d4S$K*|;i>23miLpZGBm`OwR8;SuMccGJ)BiV^NIw^k=p32-OKyu+QI@4eM4bs zf=1Fh;oi{UTY89X6Dm)Npt>Q`2sLlg8}3A$3z{KKAL-0UXbX@4o2myaOMMR?(iZz% zyhW|Ab0b_AjMNxI+%FhQuaiWnMFaB z+~wU8pM{HBU5&i=yEHnlo;idP6kma8Pe(klB(wC9WqI^rY*5&F5o?vlGc@_PR}}C=;E5hXX#9)MaaL+ zSFQzeqo!^_g>GKMio^L*moP85t?W7il%>iS&XoiopgVJDOe{WPR9rQXBn2rapqA@x z4E7}8f>9w0L(!BJ#kVvrZ|~s7d6nr_Y~RD){2xy(BHi*fGA^VdCA-j{(*9T>j9Vd} zl%o_h^Xq>X4q7RQP5$*pu=(hiYjAU%TIOwGhLx27NH&QTsePUygOpwvKc1?1lh1IW8;ZfT;- zQPMMK4{Um>6y{0|NTi+nF!|bwikI6MX%!s8gj_@!yk|k=G3?aPDJ*lrB7#cR8_#P| zm8N%^JapKEyE+?Il=KTNE1z|9_w3InGZd+zPnE9kJbF$Db-dtUJ!7_BUyae=cp{^R zH@9}MKA&R>6V`F@8Sg%%SraWzMYpw%I~@REa*O~A;@`~_QAx~t+LImce1cgYs?9%} zwN((14<;4S3lSw-`Yg(xAd9DET>zuxp(a|d^QY!?o%cjney~OxazOQ*d#*aPM*wH5 z(Po^K_%wSU8?6$bOo$D!_UY_~{ z;Fl40RikL(K^8TG;PT{rW@LDu(oY}i5w2Z zc;R#)m%u5QzX0J0?*!4BateY}aDQ~drEk8JupKgpGTMc}ojB$~Rp37hikHCEYWR@n zPO#ZQ$bJ|ez*zr&>4`G*y60FKEQ8+>51dWrs9SM?RrSydrG41nLye%mcKv{emDVGo zzkv)V&U{!TV58|}cOyX_ZH=F);m);CO0hWnk2^uuq_N`wOFDX*le_>cXPuNwG*S$E zVM(K2gint3dB-KUz>^^j^4ufhMm&xh6yWo^U#V_9J*B{(FJOqXqzqx5o74BBj% zy*#ZA`RJ||$Wa@Crt==>MwQY1x@CdK6CyT8XHgsr6_7?K;j3RI5y z((F49luy;~EM$DxdPHb4)b}YC@l+wFj&;CwyQ6FrDf zH4MAV57a~QKGFm`hH2&9xc^I|Xb3jNu#<$`sjsC4UPYHfa_ZGGS2D6~+An^OdY?GC z^(I%kC47sTOMl4@X;>_i&|d-Mpnx*TlSP6(!tLiB3ezyHnS@Lju~`(TRV%U_KIz?? z#I@dbsg{7DQ$mu(j3SG}3m_KIly8nAwi#r{hP~h&zXpbUE%k2(N{CsywVT)GkrWI9-5=EY({DGPR2*o*R_U{e|*q?02WH$!Ri>pixmdXj32%E$?fOu^Nn z)$SCt%rNfyY6euPH$IAU*?)$Ic-U_r3e8x^nDDOQ0Tw<=HZh_E)sN2tkqfw3(4leo zTngx4vvi?6A$oM!lE!!?ty0<8`}GtN0bYs9ld*VtKz}8lm-0>NrS12-9Jb0w17H0f zxOJfi;)SS9H9@k;nO-tRea2&Yfu|tb$KvHIK?SsXn?!yHmNxvb*=TyzE?p2HQWt;^ zNBX>eW{E0_CAVS9mQxTdq^d4JsQ`rAmKCy)+ldfBKAbotw~pIk6R`Ohmj5)``01j<#z z7gI_M5Sc2<-a*Ip!B=%7fV+$ej7=3B$5umQIiE0>;M_s6bRI4-J5Ip;sXge#4F(kq zLh_{O|3bfCNV#-W%Yb%;CYH>|%X^FduS4J1Y}!cxAXYAYILQSHw{4voyM(;?j7=Zq z>gK*kV)e>;KEG@Rf+|u|B_zFtRN87)D^l3aNV&*{HnvYZ?+U9>uwd>2=8%^ytqn)a z5;71IrRz`7G@ZI~ZWDeXNcU@c4GeZI1`-_zGIO#+27t-Q0OLUpG4VZIA;O5U22h%h@+Eg`^OiQgp$NPVdUE<2 z^v6pKU=-l~Wea$v=#sNCa@v#64HZQpmvPCcCEqk?_cpOSL+NQBc1kKKuwDw(HMSuc79f9R^n-@cH4Q;t z`%HhkeDqd+iRD-{U7%@0M{4c?OPOw#ED9bWvC=E3Zc|}bKET5<1pbTs2BR@q5S6Y5 z#1DU)?XUQF?k@3Dx5z;spDe7)R0yACvdZU(+D_CA9KE}5PT-}+Dq?24qs0LCn1GL< z$Et<=eRU?LodUb2O5u0{V-Qd`j}g0S=A3}8>09ko)p1 z->u(=RB$hK>~PEV`8`QC``-jg#kkuJ0ssI200000zFJttf!|Gp6?N*Y-eYn0mU>Or z&5ok0eA;TmTry=O?tZ^&qivJ75$gHnSvgzpET|ieQ7q3Qv{E0s`)X?hs@+iu&p_71 z_`$+>l{A38)nS~Wg!mqBoV&OdaSM<0Crhh7i(#^r1c zp7B@$J;Hd7zHpynNc_`P>(>Ev4cx>a!k4MxhoenOQFcEFhtkJ}+jfNav>@Aj4ikfq z7qqQ%a|~54nV8`rD!!T}DznFFkxmM%BC$Um`6YHbD;c!a?Dy8ibGYm^`^wxJ#tP#S hNIDpbh{I~VUD+X9>PdoRFG{|IXEAI$cBpghVgR6HF8}}l literal 0 HcmV?d00001 diff --git a/dot_ring/vrf/ring/__init__.py b/dot_ring/vrf/ring/__init__.py index e69de29..6c146e0 100644 --- a/dot_ring/vrf/ring/__init__.py +++ b/dot_ring/vrf/ring/__init__.py @@ -0,0 +1,18 @@ +from .ring_batch_item import RingBatchItem +from .ring_batch_verifier import RingBatchVerifier +from .ring_context import RingContext, RingSetup +from .ring import Ring +from .ring_root import RingRoot +from .ring_verifier_key_builder import RingVerifierKeyBuilder +from .ring_vrf import RingVRF + +__all__ = [ + "Ring", + "RingBatchItem", + "RingBatchVerifier", + "RingContext", + "RingRoot", + "RingSetup", + "RingVRF", + "RingVerifierKeyBuilder", +] diff --git a/dot_ring/vrf/ring/ring.py b/dot_ring/vrf/ring/ring.py new file mode 100644 index 0000000..0ec10e0 --- /dev/null +++ b/dot_ring/vrf/ring/ring.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from dot_ring.ring_proof.constants import DEFAULT_SIZE +from dot_ring.ring_proof.params import RingProofParams + + +def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: + """Return `[2⁰·H, 2¹·H, ...]` in short-Weierstrass coords.""" + return [params.mul_point(pow(2, i, params.prime), params.blinding_base) for i in range(size)] + + +class Ring: + nm_points: list[tuple[int, int]] + params: RingProofParams + + def __init__(self, keys: list[bytes], params: RingProofParams | None = None) -> None: + """ + Initialize a Ring from a list of public keys. + + Args: + keys: List of public keys (as bytes) for ring members + params: Ring proof parameters. If None, automatically constructed based on ring size. + """ + if params is None: + params = RingProofParams.from_ring_size(len(keys)) + + self.params = params + + if len(keys) > params.max_ring_size: + raise ValueError(f"ring size {len(keys)} exceeds max supported size {params.max_ring_size}") + + self.nm_points = [] + for key in keys: + if isinstance(key, (str, bytes)): + point = params.cv.point.string_to_point(key) + if isinstance(point, str): + continue + self.nm_points.append(params.point_to_ring_point(point)) + + while len(self.nm_points) < params.max_ring_size: + self.nm_points.append(params.padding_point) + + fill_count = params.domain_size - params.padding_rows - len(self.nm_points) + if fill_count > 0: + h_vec = _h_vector(params, params.domain_size) + self.nm_points.extend(h_vec[:fill_count]) + if params.padding_rows > 0: + self.nm_points.extend([(0, 0)] * params.padding_rows) diff --git a/dot_ring/vrf/ring/ring_batch_item.py b/dot_ring/vrf/ring/ring_batch_item.py new file mode 100644 index 0000000..109854a --- /dev/null +++ b/dot_ring/vrf/ring/ring_batch_item.py @@ -0,0 +1,19 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING + +from .ring import Ring +from .ring_root import RingRoot + +if TYPE_CHECKING: + from .ring_vrf import RingVRF + + +@dataclass +class RingBatchItem: + proof: RingVRF + input: bytes + ad: bytes + ring: Ring + ring_root: RingRoot diff --git a/dot_ring/vrf/ring/ring_batch_verifier.py b/dot_ring/vrf/ring/ring_batch_verifier.py new file mode 100644 index 0000000..f69ead7 --- /dev/null +++ b/dot_ring/vrf/ring/ring_batch_verifier.py @@ -0,0 +1,53 @@ +from __future__ import annotations + +from typing import Any, cast + +from dot_ring.vrf.pedersen.pedersen_batch_verifier import PedersenBatchVerifier +from dot_ring.vrf.transcript import VrfIo + +from .ring_batch_item import RingBatchItem +from .ring import Ring +from .ring_root import RingRoot + + +class RingBatchVerifier: + def __init__(self) -> None: + self.items: list[RingBatchItem] = [] + + def push(self, proof: RingVRF, input: bytes, ad: bytes, ring: Ring, ring_root: RingRoot) -> None: + self.items.append(RingBatchItem(proof, input, ad, ring, ring_root)) + + def verify(self) -> bool: + if not self.items: + return True + + pedersen_batches: dict[str, PedersenBatchVerifier] = {} + pcs_batches: dict[int, tuple[Any, list[tuple[Any, Any, int, int]]]] = {} + + try: + for item in self.items: + if item.proof.pedersen_proof is None: + return False + if item.ring.params.cv.name != item.proof.cv.name: + return False + + curve = item.proof.cv + input_point = cast(Any, curve.point).encode_to_curve(item.input) + pedersen_batch = pedersen_batches.setdefault(curve.name, PedersenBatchVerifier(curve)) + pedersen_batch.push([VrfIo(input_point, item.proof.pedersen_proof.output_point)], item.ad, item.proof.pedersen_proof) + + ring_verifier = item.proof._ring_proof_verifier(item.proof.pedersen_proof.blinded_pk, item.ring, item.ring_root) + pcs = item.ring.params.pcs + _, verifications = pcs_batches.setdefault(id(pcs), (pcs, [])) + verifications.append(ring_verifier._prepare_quotient_poly_verification()) + verifications.append(ring_verifier._prepare_linearization_poly_verification()) + except (AssertionError, TypeError, ValueError): + return False + + if not all(batch.verify() for batch in pedersen_batches.values()): + return False + + try: + return all(pcs.batch_verify(verifications) for pcs, verifications in pcs_batches.values()) + except (AssertionError, TypeError, ValueError): + return False diff --git a/dot_ring/vrf/ring/ring_context.py b/dot_ring/vrf/ring/ring_context.py new file mode 100644 index 0000000..087e7e0 --- /dev/null +++ b/dot_ring/vrf/ring/ring_context.py @@ -0,0 +1,48 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from dot_ring.curve.curve import CurveVariant +from dot_ring.ring_proof.params import RingProofParams + +from .ring import Ring +from .ring_keys import parse_concatenated_keys +from .ring_root import RingRoot + +RingSetup = RingProofParams + + +@dataclass(frozen=True) +class RingContext: + setup: RingSetup + + @classmethod + def from_ring_size(cls, ring_size: int, *, test_vectors: bool = False, cv: CurveVariant | None = None) -> RingContext: + return cls(RingSetup.from_ring_size(ring_size, test_vectors=test_vectors, cv=cv or RingSetup().cv)) + + @property + def max_ring_size(self) -> int: + return self.setup.max_ring_size + + def ring(self, keys: list[bytes] | bytes) -> Ring: + if isinstance(keys, bytes): + keys = parse_concatenated_keys(keys, self.setup.cv) + return Ring(keys, self.setup) + + def ring_root(self, ring: Ring | list[bytes] | bytes) -> RingRoot: + if not isinstance(ring, Ring): + ring = self.ring(ring) + return RingRoot.from_ring(ring, self.setup) + + def verifier_key(self, ring: Ring | list[bytes] | bytes) -> RingRoot: + return self.ring_root(ring) + + def verifier_key_from_commitment(self, commitment: RingRoot | bytes) -> RingRoot: + if isinstance(commitment, RingRoot): + return commitment + return RingRoot.from_bytes(commitment, self.setup) + + def verifier_key_builder(self) -> RingVerifierKeyBuilder: + from .ring_verifier_key_builder import RingVerifierKeyBuilder + + return RingVerifierKeyBuilder(self) diff --git a/dot_ring/vrf/ring/ring_keys.py b/dot_ring/vrf/ring/ring_keys.py new file mode 100644 index 0000000..6ccc0bb --- /dev/null +++ b/dot_ring/vrf/ring/ring_keys.py @@ -0,0 +1,10 @@ +from __future__ import annotations + +from dot_ring.curve.curve import CurveVariant + + +def parse_concatenated_keys(keys: bytes, cv: CurveVariant) -> list[bytes]: + point_len = cv.curve.POINT_LEN * (2 if cv.curve.UNCOMPRESSED else 1) + if len(keys) % point_len != 0: + raise ValueError(f"invalid concatenated key length: expected multiple of {point_len}, got {len(keys)}") + return [keys[point_len * i : point_len * (i + 1)] for i in range(len(keys) // point_len)] diff --git a/dot_ring/vrf/ring/ring_root.py b/dot_ring/vrf/ring/ring_root.py index b1ea39d..777d6b0 100644 --- a/dot_ring/vrf/ring/ring_root.py +++ b/dot_ring/vrf/ring/ring_root.py @@ -1,73 +1,12 @@ from dataclasses import dataclass -from functools import cache from typing import Any, cast from dot_ring.ring_proof.columns.columns import Column -from dot_ring.ring_proof.constants import DEFAULT_SIZE, S_PRIME, Blinding_Base, PaddingPoint -from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve as TE from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams +from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG - -@cache -def _h_vector(blinding_base: tuple[int, int] = Blinding_Base, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: - """Return `[2⁰·H, 2¹·H, …]` in short‑Weierstrass coords.""" - res = [cast(tuple[int, int], TE.mul(pow(2, i, S_PRIME), blinding_base)) for i in range(size)] - return res - - -class Ring: - nm_points: list[tuple[int, int]] - params: RingProofParams - - def __init__(self, keys: list[bytes], params: RingProofParams | None = None) -> None: - """ - Initialize a Ring from a list of public keys. - - Args: - keys: List of public keys (as bytes) for ring members - params: Ring proof parameters. If None, automatically constructed based on ring size. - - Example: - >>> # Auto-construct params based on ring size - >>> ring = Ring(keys) # Will use appropriate domain size - >>> - >>> # Or specify params explicitly - >>> params = RingProofParams(domain_size=2048, max_ring_size=1023) - >>> ring = Ring(keys, params) - """ - # Auto-construct params if not provided - if params is None: - params = RingProofParams.from_ring_size(len(keys)) - - self.params = params - - if len(keys) > params.domain_size - params.padding_rows: - raise ValueError(f"ring size {len(keys)} exceeds max supported size {params.domain_size - params.padding_rows}") - - self.nm_points = [] - for key in keys: - if isinstance(key, (str, bytes)): - point = params.cv.point.string_to_point(key) - if isinstance(point, str): - # Handle invalid point string - continue - self.nm_points.append((cast(int, point.x), cast(int, point.y))) - else: - # Handle non-string/bytes keys if necessary, or skip/raise - continue - - # Pad with special point if needed - while len(self.nm_points) < params.max_ring_size: - self.nm_points.append(PaddingPoint) - - # Ensure ring size - fill_count = params.domain_size - params.padding_rows - len(self.nm_points) - if fill_count > 0: - h_vec = _h_vector(size=params.domain_size) - self.nm_points.extend(h_vec[:fill_count]) - if params.padding_rows > 0: - self.nm_points.extend([(0, 0)] * params.padding_rows) +from .ring import Ring @dataclass @@ -75,6 +14,7 @@ class RingRoot: px: Column py: Column s: Column + params: RingProofParams | None = None @classmethod def from_ring(cls, ring: Ring, params: RingProofParams): @@ -87,10 +27,19 @@ def from_ring(cls, ring: Ring, params: RingProofParams): s_col = Column("s", selector_vec, size=params.domain_size) for col in (px_col, py_col, s_col): col.interpolate(params.omega, params.prime) - col.commit() - return cls(px=px_col, py=py_col, s=s_col) + col.commit(params.pcs) + return cls(px=px_col, py=py_col, s=s_col, params=params) def to_bytes(self) -> bytes: + pcs = self.params.pcs if self.params is not None else None + if pcs is not None and getattr(pcs, "commitment_size", 48) == 32: + return b"".join( + ( + pcs.compress_g1(cast(Any, self.px.commitment)), + pcs.compress_g1(cast(Any, self.py.commitment)), + pcs.compress_g1(cast(Any, self.s.commitment)), + ) + ) comm_keys = ( H.bls_g1_compress(cast(Any, self.px.commitment)), H.bls_g1_compress(cast(Any, self.py.commitment)), @@ -99,13 +48,26 @@ def to_bytes(self) -> bytes: return bytes.fromhex(comm_keys[0]) + bytes.fromhex(comm_keys[1]) + bytes.fromhex(comm_keys[2]) @classmethod - def from_bytes(cls, data: bytes) -> "RingRoot": - px_commitment = H.bls_g1_decompress(data[0:48].hex()) - py_commitment = H.bls_g1_decompress(data[48:96].hex()) - s_commitment = H.bls_g1_decompress(data[96:144].hex()) + def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "RingRoot": + if params is not None: + commitment_size = params.pcs.commitment_size + else: + commitment_size = 32 if len(data) == 96 else 48 + expected = 3 * commitment_size + if len(data) != expected: + raise ValueError(f"invalid ring root length: expected {expected}, got {len(data)}") + if commitment_size == 32: + pcs = params.pcs if params is not None else BN254KZG + px_commitment = pcs.decompress_g1(data[0:32]) + py_commitment = pcs.decompress_g1(data[32:64]) + s_commitment = pcs.decompress_g1(data[64:96]) + else: + px_commitment = H.bls_g1_decompress(data[0:48].hex()) + py_commitment = H.bls_g1_decompress(data[48:96].hex()) + s_commitment = H.bls_g1_decompress(data[96:144].hex()) px = Column(name="px", evals=[], commitment=px_commitment) py = Column(name="py", evals=[], commitment=py_commitment) s = Column(name="s", evals=[], commitment=s_commitment) - return cls(px=px, py=py, s=s) + return cls(px=px, py=py, s=s, params=params) diff --git a/dot_ring/vrf/ring/ring_serialization.py b/dot_ring/vrf/ring/ring_serialization.py new file mode 100644 index 0000000..95a652b --- /dev/null +++ b/dot_ring/vrf/ring/ring_serialization.py @@ -0,0 +1,44 @@ +from __future__ import annotations + +from typing import Any, cast + +from dot_ring.ring_proof.helpers import Helpers as H +from dot_ring.ring_proof.params import RingProofParams + +RING_SCALAR_LEN = 32 + + +def ring_proof_len(params: RingProofParams) -> int: + return 7 * params.pcs.commitment_size + 8 * RING_SCALAR_LEN + + +def compress_g1(params: RingProofParams, point: Any) -> bytes: + if params.pcs.commitment_size == 32: + return params.pcs.compress_g1(point) + return bytes.fromhex(H.bls_g1_compress(cast(tuple, point))) + + +def decompress_g1(params: RingProofParams, data: bytes) -> Any: + if params.pcs.commitment_size == 32: + return params.pcs.decompress_g1(data) + return H.bls_g1_decompress(data.hex()) + + +def transcript_g1(params: RingProofParams, point: Any) -> Any: + if params.pcs.commitment_size == 32: + return params.pcs.serialize_g1_uncompressed(point) + return params.pcs.normalize_g1(point) + + +def transcript_vk(params: RingProofParams, commitments: list[Any]) -> dict[str, Any]: + if params.pcs.commitment_size == 32: + return { + "g1": params.pcs.srs.g1_uncompressed[0], + "g2": params.pcs.srs.g2_uncompressed, + "commitments": [transcript_g1(params, commitment) for commitment in commitments], + } + return { + "g1": params.pcs.srs.g1_points[0], + "g2": H.altered_points(params.pcs.srs.g2_points), + "commitments": [params.pcs.normalize_g1(commitment) for commitment in commitments], + } diff --git a/dot_ring/vrf/ring/ring_verifier_key_builder.py b/dot_ring/vrf/ring/ring_verifier_key_builder.py new file mode 100644 index 0000000..42edad5 --- /dev/null +++ b/dot_ring/vrf/ring/ring_verifier_key_builder.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from dataclasses import dataclass, field + +from .ring_context import RingContext +from .ring_keys import parse_concatenated_keys +from .ring_root import RingRoot + + +@dataclass +class RingVerifierKeyBuilder: + context: RingContext + keys: list[bytes] = field(default_factory=list) + + def push(self, key: bytes) -> None: + self.keys.append(key) + + def extend(self, keys: list[bytes] | bytes) -> None: + if isinstance(keys, bytes): + keys = parse_concatenated_keys(keys, self.context.setup.cv) + self.keys.extend(keys) + + def finalize(self) -> RingRoot: + return self.context.verifier_key(self.keys) diff --git a/dot_ring/vrf/ring/ring_vrf.py b/dot_ring/vrf/ring/ring_vrf.py index 585f5f9..ec29680 100644 --- a/dot_ring/vrf/ring/ring_vrf.py +++ b/dot_ring/vrf/ring/ring_vrf.py @@ -1,22 +1,13 @@ from dataclasses import dataclass from typing import Any, cast -from py_ecc.optimized_bls12_381 import normalize as nm - from dot_ring.curve.point import CurvePoint from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder -from dot_ring.ring_proof.constants import ( - S_PRIME, - Blinding_Base, - PaddingPoint, - SeedPoint, -) from dot_ring.ring_proof.constraints.aggregation import aggregate_constraints from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder -from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve from dot_ring.ring_proof.helpers import Helpers as H -from dot_ring.ring_proof.pcs.kzg import Opening -from dot_ring.ring_proof.pcs.srs import srs +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.ring_proof.pcs.opening import Opening from dot_ring.ring_proof.proof.aggregation_poly import AggPoly from dot_ring.ring_proof.proof.linearization_poly import LAggPoly from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly @@ -26,7 +17,16 @@ from dot_ring.vrf.pedersen.pedersen import PedersenVRF from ..vrf import VRF -from .ring_root import Ring, RingRoot +from .ring import Ring +from .ring_keys import parse_concatenated_keys as _parse_concatenated_keys +from .ring_root import RingRoot +from .ring_serialization import ( + compress_g1 as _compress_g1, + decompress_g1 as _decompress_g1, + ring_proof_len as _ring_proof_len, + transcript_g1 as _transcript_g1, + transcript_vk as _transcript_vk, +) @dataclass @@ -79,22 +79,25 @@ def to_bytes(self) -> bytes: assert self.open_l_zeta_omega.proof is not None assert self.pedersen_proof is not None - return self.pedersen_proof.to_bytes() + bytes.fromhex( - H.bls_g1_compress(cast(tuple, self.c_b.commitment)) - + H.bls_g1_compress(cast(tuple, self.c_accip.commitment)) - + H.bls_g1_compress(cast(tuple, self.c_accx.commitment)) - + H.bls_g1_compress(cast(tuple, self.c_accy.commitment)) - + H.to_bytes(self.px_zeta) - + H.to_bytes(self.py_zeta) - + H.to_bytes(self.s_zeta) - + H.to_bytes(self.b_zeta) - + H.to_bytes(self.accip_zeta) - + H.to_bytes(self.accx_zeta) - + H.to_bytes(self.accy_zeta) - + H.bls_g1_compress(cast(tuple, self.c_q.commitment)) - + H.to_bytes(self.l_zeta_omega) - + H.bls_g1_compress(cast(tuple, self.open_agg_zeta.proof)) - + H.bls_g1_compress(cast(tuple, self.open_l_zeta_omega.proof)) + params = RingProofParams(cv=self.cv) + return self.pedersen_proof.to_bytes() + b"".join( + ( + _compress_g1(params, self.c_b.commitment), + _compress_g1(params, self.c_accip.commitment), + _compress_g1(params, self.c_accx.commitment), + _compress_g1(params, self.c_accy.commitment), + H.to_l_endian(self.px_zeta), + H.to_l_endian(self.py_zeta), + H.to_l_endian(self.s_zeta), + H.to_l_endian(self.b_zeta), + H.to_l_endian(self.accip_zeta), + H.to_l_endian(self.accx_zeta), + H.to_l_endian(self.accy_zeta), + _compress_g1(params, self.c_q.commitment), + H.to_l_endian(self.l_zeta_omega), + _compress_g1(params, self.open_agg_zeta.proof), + _compress_g1(params, self.open_l_zeta_omega.proof), + ) ) @classmethod @@ -108,21 +111,29 @@ def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": RingVRF: Deserialized Ring VRF proof object """ if not skip_pedersen: - pedersen_proof = PedersenVRF[cls.cv].from_bytes(proof[:192]) # type: ignore[name-defined] - offset = 192 + point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + scalar_size = (cls.cv.curve.ORDER.bit_length() + 7) // 8 + pedersen_len = 4 * point_len + 2 * scalar_size + pedersen_proof = PedersenVRF[cls.cv].from_bytes(proof[:pedersen_len]) # type: ignore[name-defined] + offset = pedersen_len else: pedersen_proof = None offset = 0 - commitment_size = 48 # Size of compressed G1 point + params = RingProofParams(cv=cls.cv) + expected = offset + _ring_proof_len(params) + if len(proof) != expected: + raise ValueError(f"invalid Ring VRF proof length: expected {expected}, got {len(proof)}") - c_b_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + commitment_size = params.pcs.commitment_size + + c_b_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size - c_accip_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + c_accip_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size - c_accx_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + c_accx_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size - c_accy_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + c_accy_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size px_zeta = H.to_scalar_int(proof[offset : offset + 32]) @@ -140,15 +151,15 @@ def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": accy_zeta = H.to_scalar_int(proof[offset : offset + 32]) offset += 32 - c_q_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + c_q_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size l_zeta_omega = H.to_scalar_int(proof[offset : offset + 32]) offset += 32 - open_agg_zeta_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + open_agg_zeta_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size - open_l_zeta_omega_commitment = H.bls_g1_decompress(proof[offset : offset + commitment_size].hex()) + open_l_zeta_omega_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) offset += commitment_size return cls( pedersen_proof=pedersen_proof, @@ -176,7 +187,7 @@ def generate_bls_signature( blinding_factor: int, producer_key: bytes | str, ring: Ring, - transcript_challenge: bytes = b"Bandersnatch_SHA-512_ELL2", + transcript_challenge: bytes | None = None, ring_root: RingRoot | None = None, ) -> tuple[ Column, @@ -205,23 +216,25 @@ def generate_bls_signature( transcript_challenge: Challenge for Fiat-Shamir ring_root: Optional pre-computed ring root for performance """ + if transcript_challenge is None: + transcript_challenge = cls.cv.curve.SUITE_ID or cls.cv.curve.SUITE_STRING # Use params from the ring object params = ring.params producer_key_point = cls.cv.point.string_to_point(producer_key) if isinstance(producer_key_point, str) or producer_key_point.is_identity(): - producer_key_point = cls.cv.point(PaddingPoint[0], PaddingPoint[1]) - - producer_key_pt = ( - cast(int, producer_key_point.x), - cast(int, producer_key_point.y), - ) + producer_key_pt = params.padding_point + else: + producer_key_pt = params.point_to_ring_point(producer_key_point) if not ring_root: ring_root = RingRoot.from_ring(ring, params) # ring_root builder s_v = ring_root.s.evals - producer_index = ring.nm_points.index(producer_key_pt) + try: + producer_index = ring.nm_points.index(producer_key_pt) + except ValueError as exc: + raise ValueError("producer key is not in ring") from exc witness_obj = WitnessColumnBuilder.from_params( ring.nm_points, s_v, @@ -230,7 +243,7 @@ def generate_bls_signature( params, ) witness_res = witness_obj.build() - witness_relation_res = witness_obj.result(Blinding_Base) + witness_relation_res = witness_obj.result(params.blinding_base) Result_plus_Seed = witness_obj.result_p_seed(witness_relation_res) constraints = RingConstraintBuilder( Result_plus_Seed=Result_plus_Seed, # type: ignore @@ -242,47 +255,41 @@ def generate_bls_signature( acc_y=cast(list[int], witness_res[2].coeffs), acc_ip=cast(list[int], witness_res[3].coeffs), params=params, + seed_point=params.seed_point, ) constraint_dict = constraints.compute() - fixed_col_commits = [ - H.to_int(nm(ring_root.px.commitment)), - H.to_int(nm(ring_root.py.commitment)), - H.to_int(nm(ring_root.s.commitment)), - ] + fixed_col_commits = [ring_root.px.commitment, ring_root.py.commitment, ring_root.s.commitment] ws = witness_res witness_commitments = [ - H.to_int(nm(ws[0].commitment)), - H.to_int(nm(ws[-1].commitment)), - H.to_int(nm(ws[1].commitment)), - H.to_int(nm(ws[2].commitment)), + _transcript_g1(params, ws[0].commitment), + _transcript_g1(params, ws[-1].commitment), + _transcript_g1(params, ws[1].commitment), + _transcript_g1(params, ws[2].commitment), ] - vk = { - "g1": srs.g1_points[0], - "g2": H.altered_points(srs.g2_points), - "commitments": fixed_col_commits, - } - t = Transcript(S_PRIME, transcript_challenge) + vk = _transcript_vk(params, fixed_col_commits) + t = Transcript(params.prime, transcript_challenge) t, alpha = phase1_alphas(t, vk, witness_relation_res, witness_commitments) cd = constraint_dict c_polys = [cd[val] for val in cd] - C_agg = aggregate_constraints(c_polys, alpha, params.radix_omega, S_PRIME, domain=params.domain) - qp = QuotientPoly(params.domain_size) + C_agg = aggregate_constraints(c_polys, alpha, params.radix_omega, params.prime, domain=params.domain) + qp = QuotientPoly(params.domain_size, params.pcs) Q_p, C_q = qp.quotient_poly(C_agg) C_q_commitment = Column(name="C_q", evals=[], commitment=C_q) - C_q_nm = nm(C_q) l_obj = LAggPoly( t, - list(H.to_int(C_q_nm)), + _transcript_g1(params, C_q), list([ring_root.px, ring_root.py, ring_root.s]), list(ws), alpha, domain=params.domain, omega=params.omega, + prime=params.prime, padding_rows=params.padding_rows, + edwards_a=params.ring_edwards_a, ) current_t, zeta, rel_poly_evals, l_agg, zeta_omega, l_zw = l_obj.l_agg_poly() _, _, phi_z, phi_zw = AggPoly.proof_contents_phi( @@ -293,6 +300,8 @@ def generate_bls_signature( list(ws), Q_p, phase3_nu_vector(current_t, list(rel_poly_evals.values()), l_zw), + prime=params.prime, + pcs=params.pcs, ) [ p_x_zeta, @@ -322,15 +331,12 @@ def generate_bls_signature( phi_zw, ) - def verify_ring_proof( + def _ring_proof_verifier( self, message: bytes | CurvePoint, ring: Ring, ring_root: RingRoot, - ) -> bool: - """ - Verifies the Ring Proof - """ + ) -> Verify: fixed_cols_cmts = [ ring_root.px.commitment, ring_root.py.commitment, @@ -338,26 +344,20 @@ def verify_ring_proof( ] if isinstance(message, bytes): - message_pt = self.cv.point.string_to_point(message) # relation to proove + message_pt = ring.params.cv.point.string_to_point(message) if isinstance(message_pt, str): raise ValueError("Invalid message point") message = message_pt - rltn = (message.x, message.y) # relartion to proove - res_plus_seed = TwistedEdwardCurve.add(SeedPoint, rltn) + rltn = ring.params.point_to_ring_point(message) + res_plus_seed = ring.params.add_points(ring.params.seed_point, rltn) # Ensure commitments are not None fixed_cols_cmts_safe = [] for c in fixed_cols_cmts: assert c is not None fixed_cols_cmts_safe.append(c) - comm_keys_affine = H.bls_projective_2_affine(cast(list[Any], fixed_cols_cmts_safe)) - comm_keys_int = [H.to_int(pt) for pt in comm_keys_affine] - verifier_key: dict[str, Any] = { - "g1": srs.g1_points[0], - "g2": cast(Any, H.altered_points(srs.g2_points)), # type: ignore - "commitments": comm_keys_int, - } + verifier_key: dict[str, Any] = _transcript_vk(ring.params, fixed_cols_cmts_safe) return Verify( ( @@ -381,10 +381,26 @@ def verify_ring_proof( fixed_cols_cmts, rltn, res_plus_seed, - SeedPoint, + ring.params.seed_point, ring.params.domain, + transcript_challenge=self.cv.curve.SUITE_ID or self.cv.curve.SUITE_STRING, padding_rows=ring.params.padding_rows, - ).is_valid() + edwards_a=ring.params.ring_edwards_a, + prime=ring.params.prime, + omega=ring.params.omega, + pcs=ring.params.pcs, + ) + + def verify_ring_proof( + self, + message: bytes | CurvePoint, + ring: Ring, + ring_root: RingRoot, + ) -> bool: + """ + Verifies the Ring Proof + """ + return self._ring_proof_verifier(message, ring, ring_root).is_valid() @classmethod def prove( @@ -430,7 +446,7 @@ def parse_keys(cls, keys: bytes) -> list[bytes]: Returns: List[bytes]: A list of individual keys extracted from the input bytes object. """ - return [keys[32 * i : 32 * (i + 1)] for i in range(len(keys) // 32)] + return _parse_concatenated_keys(keys, cls.cv) def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) -> bool: """ @@ -442,3 +458,22 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) ring_proof_valid = self.verify_ring_proof(self.pedersen_proof.blinded_pk, ring, ring_root) return p_proof_valid and ring_proof_valid + + @classmethod + def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: + return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) # type: ignore[misc] + + +from .ring_batch_item import RingBatchItem +from .ring_batch_verifier import RingBatchVerifier +from .ring_context import RingContext, RingSetup +from .ring_verifier_key_builder import RingVerifierKeyBuilder + +__all__ = [ + "RingBatchItem", + "RingBatchVerifier", + "RingContext", + "RingSetup", + "RingVRF", + "RingVerifierKeyBuilder", +] From 3702b58a455de73a7b92c688074f672425e6ecfb Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:46:00 +0530 Subject: [PATCH 05/33] chore: update public VRF exports --- dot_ring/__init__.py | 30 +++++++++++++++++++----------- pyproject.toml | 4 ++-- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/dot_ring/__init__.py b/dot_ring/__init__.py index 0263618..82b98af 100644 --- a/dot_ring/__init__.py +++ b/dot_ring/__init__.py @@ -1,13 +1,13 @@ """ dot-ring: A Python library for Verifiable Random Functions with Additional Data (VRF-AD). -Supports 10+ elliptic curves including IETF, Pedersen VRF and Ring Proof. +Supports 10+ elliptic curves including Tiny, Thin, Pedersen, and Ring VRF schemes. Example usage: - >>> from dot_ring import Bandersnatch, IETF_VRF, PedersenVRF, RingVRF + >>> from dot_ring import Bandersnatch, TinyVRF, PedersenVRF, RingVRF >>> - >>> # IETF VRF - >>> proof = IETF_VRF[Bandersnatch].prove(alpha, secret_key, additional_data) + >>> # Tiny VRF + >>> proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, additional_data) >>> is_valid = proof.verify(public_key, alpha, additional_data) >>> >>> # Pedersen VRF @@ -31,7 +31,7 @@ # ============================================================================= # Curve Variants - Primary curves # ============================================================================= -from dot_ring.curve.specs.bandersnatch import Bandersnatch +from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_SHAKE128 from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW # BLS12-381 curves @@ -49,19 +49,18 @@ from dot_ring.curve.specs.p521 import P521_NU, P521_RO from dot_ring.curve.specs.secp256k1 import Secp256k1_NU, Secp256k1_RO from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.ietf.ietf import IETF_VRF -from dot_ring.vrf.pedersen.pedersen import PedersenVRF -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF +from dot_ring.vrf.ietf import IETF_VRF, ThinBatchVerifier, ThinVRF, TinyVRF +from dot_ring.vrf.pedersen import PedersenBatchVerifier, PedersenVRF +from dot_ring.vrf.ring import Ring, RingBatchVerifier, RingContext, RingRoot, RingSetup, RingVerifierKeyBuilder, RingVRF # ============================================================================= # Convenience aliases # ============================================================================= -Ed25519 = Ed25519_RO +Ed25519 = Ed25519_NU Ed448 = Ed448_RO Curve25519 = Curve25519_RO Curve448 = Curve448_RO -P256 = P256_RO +P256 = P256_NU P384 = P384_RO P521 = P521_RO Secp256k1 = Secp256k1_RO @@ -75,11 +74,20 @@ # Version "__version__", # VRF implementations + "TinyVRF", + "ThinVRF", + "ThinBatchVerifier", "IETF_VRF", "PedersenVRF", + "PedersenBatchVerifier", "RingVRF", + "RingSetup", + "RingContext", + "RingVerifierKeyBuilder", + "RingBatchVerifier", # Primary curves "Bandersnatch", + "Bandersnatch_SHAKE128", "Bandersnatch_SW", "Ed25519", "Ed25519_RO", diff --git a/pyproject.toml b/pyproject.toml index 42c7e80..6f1e7d5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,7 +1,7 @@ [project] name = "dot-ring" dynamic = ["version"] -description = "Serves as a library to generate and verify a signature using IETF, Pedersen and Ring VRF-AD Schemes" +description = "Serves as a library to generate and verify signatures using Tiny, Thin, Pedersen and Ring VRF-AD schemes" authors = [ {name = "prasad-kumkar", email = "prasad@chainscore.finance"} ] @@ -9,7 +9,7 @@ readme = "README.md" license = {text = "MIT"} requires-python = ">=3.11" keywords = [ - "VRF", "VRF-AD", "IETF VRF", "Pedersen VRF", + "VRF", "VRF-AD", "Tiny VRF", "Thin VRF", "Pedersen VRF", "Ring Proof", "Ring VRF", "Signature", "Proof", "Verify", "Cryptography", "Zero Knowledge", "PCS", "KZG", "FFT", "Polynomial", From 0a20b72134f7102c544f7be35fb0084314d92037 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:46:28 +0530 Subject: [PATCH 06/33] test: refresh cross-suite VRF vectors --- tests/test_ark_vrf.py | 385 ++++++++++++++++++ tests/test_curve_ops/test_gaps.py | 7 +- .../test_h2c_suites/test_e2c_bandersnatch.py | 56 +-- tests/test_keygen.py | 8 +- tests/test_verify_ring_sig.py | 2 +- tests/utils/python_to_rust_serde.py | 12 +- .../{arkworks_serde.py => rust_serde.py} | 37 +- .../baby-jubjub_sha-512_tai_pedersen.json | 114 ++++++ .../ark-vrf/baby-jubjub_sha-512_tai_ring.json | 135 ++++++ .../ark-vrf/baby-jubjub_sha-512_tai_thin.json | 86 ++++ .../ark-vrf/baby-jubjub_sha-512_tai_tiny.json | 86 ++++ .../ark-vrf/babyjubjub_sha512_tai_ietf.json | 119 +++--- .../babyjubjub_sha512_tai_pedersen.json | 161 ++++---- .../bandersnatch_ed_sha512_ell2_ietf.json | 119 +++--- .../bandersnatch_ed_sha512_ell2_pedersen.json | 161 ++++---- .../bandersnatch_ed_sha512_ell2_ring.json | 203 +++++---- .../bandersnatch_sha-512_ell2_pedersen.json | 114 ++++++ .../bandersnatch_sha-512_ell2_ring.json | 135 ++++++ .../bandersnatch_sha-512_ell2_thin.json | 86 ++++ .../bandersnatch_sha-512_ell2_tiny.json | 86 ++++ .../bandersnatch_shake128_ell2_pedersen.json | 114 ++++++ .../bandersnatch_shake128_ell2_ring.json | 135 ++++++ .../bandersnatch_shake128_ell2_thin.json | 86 ++++ .../bandersnatch_shake128_ell2_tiny.json | 86 ++++ .../bandersnatch_sw_sha-512_tai_pedersen.json | 114 ++++++ .../bandersnatch_sw_sha-512_tai_ring.json | 135 ++++++ .../bandersnatch_sw_sha-512_tai_thin.json | 86 ++++ .../bandersnatch_sw_sha-512_tai_tiny.json | 86 ++++ .../bandersnatch_sw_sha512_tai_ietf.json | 119 +++--- .../bandersnatch_sw_sha512_tai_pedersen.json | 161 ++++---- .../bandersnatch_sw_sha512_tai_ring.json | 142 ------- .../ark-vrf/ed25519_sha-512_tai_pedersen.json | 114 ++++++ .../ark-vrf/ed25519_sha-512_tai_thin.json | 86 ++++ .../ark-vrf/ed25519_sha-512_tai_tiny.json | 86 ++++ .../ark-vrf/ed25519_sha512_tai_ietf.json | 119 +++--- .../ark-vrf/ed25519_sha512_tai_pedersen.json | 161 ++++---- .../ark-vrf/jubjub_sha-512_tai_pedersen.json | 114 ++++++ .../ark-vrf/jubjub_sha-512_tai_ring.json | 135 ++++++ .../ark-vrf/jubjub_sha-512_tai_thin.json | 86 ++++ .../ark-vrf/jubjub_sha-512_tai_tiny.json | 86 ++++ .../ark-vrf/jubjub_sha512_tai_pedersen.json | 161 ++++---- .../ark-vrf/jubjub_sha_512_tai_ietf.json | 119 +++--- .../secp256r1_sha-256_tai_pedersen.json | 114 ++++++ .../ark-vrf/secp256r1_sha-256_tai_thin.json | 86 ++++ .../ark-vrf/secp256r1_sha-256_tai_tiny.json | 86 ++++ .../ark-vrf/secp256r1_sha256_tai_ietf.json | 119 +++--- .../secp256r1_sha256_tai_ietf_rfc_9381.json | 108 +++-- .../secp256r1_sha256_tai_pedersen.json | 161 ++++---- 48 files changed, 4076 insertions(+), 1241 deletions(-) create mode 100644 tests/test_ark_vrf.py rename tests/utils/{arkworks_serde.py => rust_serde.py} (90%) create mode 100644 tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_pedersen.json create mode 100644 tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json create mode 100644 tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_thin.json create mode 100644 tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_tiny.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_pedersen.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_ring.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_thin.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_tiny.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_shake128_ell2_pedersen.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_shake128_ell2_ring.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_shake128_ell2_thin.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_shake128_ell2_tiny.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_pedersen.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_ring.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_thin.json create mode 100644 tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_tiny.json delete mode 100644 tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ring.json create mode 100644 tests/vectors/ark-vrf/ed25519_sha-512_tai_pedersen.json create mode 100644 tests/vectors/ark-vrf/ed25519_sha-512_tai_thin.json create mode 100644 tests/vectors/ark-vrf/ed25519_sha-512_tai_tiny.json create mode 100644 tests/vectors/ark-vrf/jubjub_sha-512_tai_pedersen.json create mode 100644 tests/vectors/ark-vrf/jubjub_sha-512_tai_ring.json create mode 100644 tests/vectors/ark-vrf/jubjub_sha-512_tai_thin.json create mode 100644 tests/vectors/ark-vrf/jubjub_sha-512_tai_tiny.json create mode 100644 tests/vectors/ark-vrf/secp256r1_sha-256_tai_pedersen.json create mode 100644 tests/vectors/ark-vrf/secp256r1_sha-256_tai_thin.json create mode 100644 tests/vectors/ark-vrf/secp256r1_sha-256_tai_tiny.json diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py new file mode 100644 index 0000000..77556c7 --- /dev/null +++ b/tests/test_ark_vrf.py @@ -0,0 +1,385 @@ +import copy +import json +from pathlib import Path + +import pytest + +from dot_ring.curve.specs.baby_jubjub import BabyJubJub +from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_SHAKE128 +from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW +from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.jubjub import JubJub +from dot_ring.curve.specs.p256 import P256_NU +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.vrf.ietf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF +from dot_ring.vrf.pedersen.pedersen import PedersenBatchVerifier, PedersenVRF +from dot_ring.vrf.ring.ring_root import Ring, RingRoot +from dot_ring.vrf.ring.ring_vrf import RingBatchVerifier, RingContext, RingVRF +from dot_ring.vrf.transcript import VrfIo +from scripts.generate_test_vectors import SUITES as GENERATED_SUITES +from scripts.generate_test_vectors import scheme_vectors + +VECTORS = Path(__file__).parent / "vectors" / "ark-vrf" + +SUITES = [ + (Bandersnatch, "bandersnatch_sha-512_ell2", 32), + (Bandersnatch_SW, "bandersnatch_sw_sha-512_tai", 33), + (Bandersnatch_SHAKE128, "bandersnatch_shake128_ell2", 32), + (Ed25519_NU, "ed25519_sha-512_tai", 32), + (P256_NU, "secp256r1_sha-256_tai", 33), + (JubJub, "jubjub_sha-512_tai", 32), + (BabyJubJub, "baby-jubjub_sha-512_tai", 32), +] + + +def load(name: str) -> list[dict[str, str]]: + return json.loads((VECTORS / name).read_text()) + + +def ring_proof_bytes(vector: dict[str, str]) -> bytes: + return bytes.fromhex( + vector["gamma"] + + vector["proof_pk_com"] + + vector["proof_r"] + + vector["proof_ok"] + + vector["proof_s"] + + vector["proof_sb"] + + vector["ring_proof"] + ) + + +def test_generated_vectors_match_ark_vrf_tiny_thin_pedersen() -> None: + for suite in GENERATED_SUITES: + for scheme in ("tiny", "thin", "pedersen"): + assert json.dumps(scheme_vectors(suite, scheme), indent=2) == (VECTORS / f"{suite.prefix}_{scheme}.json").read_text() + + +@pytest.mark.parametrize("curve,prefix,point_len", SUITES) +def test_tiny_vectors(curve, prefix: str, point_len: int) -> None: + for vector in load(f"{prefix}_tiny.json"): + sk = bytes.fromhex(vector["sk"]) + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof = TinyVRF[curve].prove(alpha, sk, ad) + proof_bytes = proof.to_bytes() + + assert TinyVRF[curve].get_public_key(sk).hex() == vector["pk"] + assert curve.point.encode_to_curve(alpha).point_to_string().hex() == vector["h"] + assert proof_bytes.hex() == vector["gamma"] + vector["proof_c"] + vector["proof_s"] + assert TinyVRF[curve].proof_to_hash(proof.output_point).hex() == vector["beta"] + assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) + assert TinyVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes + assert len(proof.output_point.point_to_string()) == point_len + + +@pytest.mark.parametrize("curve,prefix,_point_len", SUITES) +def test_thin_vectors(curve, prefix: str, _point_len: int) -> None: + for vector in load(f"{prefix}_thin.json"): + sk = bytes.fromhex(vector["sk"]) + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof = ThinVRF[curve].prove(alpha, sk, ad) + proof_bytes = proof.to_bytes() + + assert proof_bytes.hex() == vector["gamma"] + vector["proof_r"] + vector["proof_s"] + assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) + assert ThinVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes + + +@pytest.mark.parametrize("curve,prefix,_point_len", SUITES) +def test_pedersen_vectors(curve, prefix: str, _point_len: int) -> None: + for vector in load(f"{prefix}_pedersen.json"): + sk = bytes.fromhex(vector["sk"]) + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof = PedersenVRF[curve].prove(alpha, sk, ad) + proof_bytes = proof.to_bytes() + + assert proof_bytes.hex() == ( + vector["gamma"] + + vector["proof_pk_com"] + + vector["proof_r"] + + vector["proof_ok"] + + vector["proof_s"] + + vector["proof_sb"] + ) + assert proof.verify(alpha, ad) + assert PedersenVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes + + +@pytest.mark.parametrize("curve,prefix,point_len", SUITES) +def test_rejects_invalid_point_encodings(curve, prefix: str, point_len: int) -> None: + invalid_point = b"\xff" * point_len + + tiny_vector = load(f"{prefix}_tiny.json")[0] + tiny_bytes = invalid_point + bytes.fromhex(tiny_vector["proof_c"] + tiny_vector["proof_s"]) + with pytest.raises(ValueError, match="INVALID|Invalid"): + TinyVRF[curve].from_bytes(tiny_bytes) + + thin_vector = load(f"{prefix}_thin.json")[0] + thin_bytes = invalid_point + bytes.fromhex(thin_vector["proof_r"] + thin_vector["proof_s"]) + with pytest.raises(ValueError, match="INVALID|Invalid"): + ThinVRF[curve].from_bytes(thin_bytes) + + pedersen_vector = load(f"{prefix}_pedersen.json")[0] + pedersen_bytes = invalid_point + bytes.fromhex( + pedersen_vector["proof_pk_com"] + + pedersen_vector["proof_r"] + + pedersen_vector["proof_ok"] + + pedersen_vector["proof_s"] + + pedersen_vector["proof_sb"] + ) + with pytest.raises(ValueError, match="INVALID|Invalid"): + PedersenVRF[curve].from_bytes(pedersen_bytes) + + +@pytest.mark.parametrize( + "curve,filename", + [ + (Bandersnatch, "bandersnatch_sha-512_ell2_ring.json"), + (Bandersnatch_SW, "bandersnatch_sw_sha-512_tai_ring.json"), + (Bandersnatch_SHAKE128, "bandersnatch_shake128_ell2_ring.json"), + (JubJub, "jubjub_sha-512_tai_ring.json"), + (BabyJubJub, "baby-jubjub_sha-512_tai_ring.json"), + ], +) +def test_ring_vectors(curve, filename: str) -> None: + for vector in load(filename): + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof_bytes = bytes.fromhex( + vector["gamma"] + + vector["proof_pk_com"] + + vector["proof_r"] + + vector["proof_ok"] + + vector["proof_s"] + + vector["proof_sb"] + + vector["ring_proof"] + ) + keys = RingVRF[curve].parse_keys(bytes.fromhex(vector["ring_pks"])) + params = RingProofParams(test_vectors=True, cv=curve) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + proof = RingVRF[curve].from_bytes(proof_bytes) + + assert ring_root.to_bytes().hex() == vector["ring_pks_com"] + assert proof.verify(alpha, ad, ring, ring_root) + generated = RingVRF[curve].prove(alpha, ad, bytes.fromhex(vector["sk"]), bytes.fromhex(vector["pk"]), ring, ring_root) + assert generated.to_bytes() == proof_bytes + + +def test_multi_input_apis() -> None: + secret = bytes.fromhex(load("bandersnatch_sha-512_ell2_tiny.json")[0]["sk"]) + public_key = TinyVRF[Bandersnatch].get_public_key(secret) + secret_scalar = int.from_bytes(secret, "little") + public_key_point = Bandersnatch.point.string_to_point(public_key) + assert not isinstance(public_key_point, str) + + ios = [] + for alpha in (b"first", b"second"): + input_point = Bandersnatch.point.encode_to_curve(alpha) + ios.append(VrfIo(input_point, input_point * secret_scalar)) + + tiny = TinyVRF[Bandersnatch].prove_ios(ios, secret_scalar, public_key_point, b"ad") + thin = ThinVRF[Bandersnatch].prove_ios(ios, secret_scalar, public_key_point, b"ad") + pedersen = PedersenVRF[Bandersnatch].prove_ios(ios, secret_scalar, public_key_point, b"ad") + + assert tiny.verify_ios(public_key_point, ios, b"ad") + assert thin.verify_ios(public_key_point, ios, b"ad") + assert pedersen.verify_ios(ios, b"ad") + + +def test_batch_apis() -> None: + vectors = load("bandersnatch_sha-512_ell2_thin.json")[:2] + thin_batch = ThinBatchVerifier[Bandersnatch]() + pedersen_batch = PedersenBatchVerifier[Bandersnatch]() + + for vector in vectors: + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) + assert not isinstance(pk_point, str) + thin = ThinVRF[Bandersnatch].prove(alpha, bytes.fromhex(vector["sk"]), ad) + input_point = Bandersnatch.point.encode_to_curve(alpha) + thin_batch.push(pk_point, [VrfIo(input_point, thin.output_point)], ad, thin) + + pedersen = PedersenVRF[Bandersnatch].prove(alpha, bytes.fromhex(vector["sk"]), ad) + pedersen_batch.push([VrfIo(input_point, pedersen.output_point)], ad, pedersen) + + assert thin_batch.verify() + assert pedersen_batch.verify() + + +def test_batch_rejects_invalid_items() -> None: + vector = load("bandersnatch_sha-512_ell2_thin.json")[0] + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + secret = bytes.fromhex(vector["sk"]) + pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) + assert not isinstance(pk_point, str) + input_point = Bandersnatch.point.encode_to_curve(alpha) + + thin = ThinVRF[Bandersnatch].prove(alpha, secret, ad) + bad_thin = ThinVRF[Bandersnatch](thin.output_point, thin.r, (thin.s + 1) % Bandersnatch.curve.ORDER) + thin_batch = ThinBatchVerifier[Bandersnatch]() + thin_batch.push(pk_point, [VrfIo(input_point, bad_thin.output_point)], ad, bad_thin) + assert not thin_batch.verify() + + pedersen = PedersenVRF[Bandersnatch].prove(alpha, secret, ad) + bad_pedersen = PedersenVRF[Bandersnatch]( + pedersen.output_point, + pedersen.blinded_pk, + pedersen.result_point, + pedersen.ok, + (pedersen.s + 1) % Bandersnatch.curve.ORDER, + pedersen.sb, + ) + pedersen_batch = PedersenBatchVerifier[Bandersnatch]() + pedersen_batch.push([VrfIo(input_point, bad_pedersen.output_point)], ad, bad_pedersen) + assert not pedersen_batch.verify() + + +def test_ring_batch_same_ring_and_invalid_item() -> None: + vector = load("bandersnatch_sha-512_ell2_ring.json")[0] + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(vector["ring_pks"])) + params = RingProofParams(test_vectors=True) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + proof = RingVRF[Bandersnatch].from_bytes(ring_proof_bytes(vector)) + second_alpha = b"ring-batch-second" + second_ad = b"ring-batch-ad" + second_proof = RingVRF[Bandersnatch].prove( + second_alpha, + second_ad, + bytes.fromhex(vector["sk"]), + bytes.fromhex(vector["pk"]), + ring, + ring_root, + ) + + batch = RingBatchVerifier() + batch.push(proof, alpha, ad, ring, ring_root) + batch.push(second_proof, second_alpha, second_ad, ring, ring_root) + assert batch.verify() + + bad_proof = copy.copy(second_proof) + bad_proof.l_zeta_omega = (bad_proof.l_zeta_omega + 1) % params.prime + bad_batch = RingBatchVerifier() + bad_batch.push(proof, alpha, ad, ring, ring_root) + bad_batch.push(bad_proof, second_alpha, second_ad, ring, ring_root) + assert not bad_batch.verify() + + +def test_ring_batch_multi_ring_shared_srs(monkeypatch: pytest.MonkeyPatch) -> None: + vectors = load("bandersnatch_sha-512_ell2_ring.json")[:2] + batch = RingBatchVerifier() + + for vector in vectors: + keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(vector["ring_pks"])) + params = RingProofParams(test_vectors=True) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + proof = RingVRF[Bandersnatch].from_bytes(ring_proof_bytes(vector)) + batch.push(proof, bytes.fromhex(vector["alpha"]), bytes.fromhex(vector["ad"]), ring, ring_root) + + def fail_single_verify(*_args: object, **_kwargs: object) -> bool: + raise AssertionError("RingBatchVerifier must use batched checks") + + monkeypatch.setattr(RingVRF, "verify", fail_single_verify) + assert batch.verify() + + +def test_ring_context_helpers() -> None: + vector = load("bandersnatch_sha-512_ell2_ring.json")[0] + keys_blob = bytes.fromhex(vector["ring_pks"]) + context = RingContext(RingProofParams(test_vectors=True)) + + ring = context.ring(keys_blob) + ring_root = context.verifier_key(ring) + builder = context.verifier_key_builder() + builder.extend(keys_blob) + + assert ring_root.to_bytes().hex() == vector["ring_pks_com"] + assert context.ring_root(keys_blob).to_bytes() == ring_root.to_bytes() + assert context.verifier_key_from_commitment(ring_root.to_bytes()).to_bytes() == ring_root.to_bytes() + assert builder.finalize().to_bytes() == ring_root.to_bytes() + + +def test_negative_and_malformed_proofs() -> None: + tiny_vector = load("bandersnatch_sha-512_ell2_tiny.json")[0] + alpha = bytes.fromhex(tiny_vector["alpha"]) + ad = bytes.fromhex(tiny_vector["ad"]) + pk = bytes.fromhex(tiny_vector["pk"]) + sk = bytes.fromhex(tiny_vector["sk"]) + tiny = TinyVRF[Bandersnatch].prove(alpha, sk, ad) + + pk_point = Bandersnatch.point.string_to_point(pk) + assert not isinstance(pk_point, str) + wrong_output = Bandersnatch.point.encode_to_curve(b"wrong-output") + + assert not tiny.verify(pk, alpha, b"wrong-ad") + assert not tiny.verify(pk, b"wrong-input", ad) + assert not tiny.verify_ios(pk_point, [VrfIo(Bandersnatch.point.encode_to_curve(alpha), wrong_output)], ad) + with pytest.raises(ValueError, match="invalid Tiny VRF proof length"): + TinyVRF[Bandersnatch].from_bytes(tiny.to_bytes()[:-1]) + + thin = ThinVRF[Bandersnatch].prove(alpha, sk, ad) + with pytest.raises(ValueError, match="invalid Thin VRF proof length"): + ThinVRF[Bandersnatch].from_bytes(thin.to_bytes()[:-1]) + + pedersen = PedersenVRF[Bandersnatch].prove(alpha, sk, ad) + assert not pedersen.verify(alpha, b"wrong-ad") + with pytest.raises(ValueError, match="invalid Pedersen VRF proof length"): + PedersenVRF[Bandersnatch].from_bytes(pedersen.to_bytes()[:-1]) + + ring_vector = load("bandersnatch_sha-512_ell2_ring.json")[0] + ring_alpha = bytes.fromhex(ring_vector["alpha"]) + ring_ad = bytes.fromhex(ring_vector["ad"]) + proof_bytes = bytes.fromhex( + ring_vector["gamma"] + + ring_vector["proof_pk_com"] + + ring_vector["proof_r"] + + ring_vector["proof_ok"] + + ring_vector["proof_s"] + + ring_vector["proof_sb"] + + ring_vector["ring_proof"] + ) + keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(ring_vector["ring_pks"])) + params = RingProofParams(test_vectors=True) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + wrong_root = RingRoot.from_ring(Ring(list(reversed(keys)), params), params) + proof = RingVRF[Bandersnatch].from_bytes(proof_bytes) + + assert not proof.verify(ring_alpha, b"wrong-ad", ring, ring_root) + assert not proof.verify(b"wrong-input", ring_ad, ring, ring_root) + assert not proof.verify(ring_alpha, ring_ad, ring, wrong_root) + with pytest.raises(ValueError, match="invalid Ring VRF proof length"): + RingVRF[Bandersnatch].from_bytes(proof_bytes[:-1]) + pedersen_len = len(proof_bytes) - len(bytes.fromhex(ring_vector["ring_proof"])) + invalid_kzg_point = proof_bytes[:pedersen_len] + b"\xff" * 48 + proof_bytes[pedersen_len + 48 :] + with pytest.raises(ValueError): + RingVRF[Bandersnatch].from_bytes(invalid_kzg_point) + with pytest.raises(ValueError, match="invalid Ring VRF proof length"): + RingVRF[Bandersnatch].from_bytes(bytes.fromhex(ring_vector["ring_proof"])[:-1], skip_pedersen=True) + with pytest.raises(ValueError, match="invalid ring root length"): + RingRoot.from_bytes(ring_root.to_bytes()[:-1]) + with pytest.raises(ValueError): + RingRoot.from_bytes(b"\xff" * 48 + ring_root.to_bytes()[48:]) + + +def test_ring_rejects_wrong_prover_key() -> None: + vector = load("bandersnatch_sha-512_ell2_ring.json")[0] + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(vector["ring_pks"])) + params = RingProofParams(test_vectors=True) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + wrong_secret = bytes.fromhex(load("bandersnatch_sha-512_ell2_tiny.json")[1]["sk"]) + wrong_pk = TinyVRF[Bandersnatch].get_public_key(wrong_secret) + + with pytest.raises(ValueError, match="producer key is not in ring"): + RingVRF[Bandersnatch].prove(alpha, ad, wrong_secret, wrong_pk, ring, ring_root) diff --git a/tests/test_curve_ops/test_gaps.py b/tests/test_curve_ops/test_gaps.py index 57df399..5dab4dd 100644 --- a/tests/test_curve_ops/test_gaps.py +++ b/tests/test_curve_ops/test_gaps.py @@ -93,7 +93,8 @@ def test_ecvrf_proof_to_hash_string_input(self): def test_ietf_from_bytes_invalid_point(self): """Test IETF_VRF.from_bytes raises ValueError for invalid output point.""" - invalid_point = b"\xff" * 33 + point_len = Bandersnatch.curve.POINT_LEN * (2 if Bandersnatch.curve.UNCOMPRESSED else 1) + invalid_point = b"\xff" * point_len c_len = Bandersnatch.curve.CHALLENGE_LENGTH s_len = (Bandersnatch.curve.ORDER.bit_length() + 7) // 8 dummy_c = b"\x00" * c_len @@ -116,8 +117,8 @@ def test_ietf_from_bytes_invalid_s(self): invalid_s = order + 1 s_len = (order.bit_length() + 7) // 8 - gamma_len = 33 - c_len = 32 + gamma_len = Bandersnatch.curve.POINT_LEN * (2 if Bandersnatch.curve.UNCOMPRESSED else 1) + c_len = Bandersnatch.curve.CHALLENGE_LENGTH gamma_bytes = proof_bytes[:gamma_len] c_bytes = proof_bytes[gamma_len : gamma_len + c_len] diff --git a/tests/test_h2c_suites/test_e2c_bandersnatch.py b/tests/test_h2c_suites/test_e2c_bandersnatch.py index 3c24d0e..a22a328 100644 --- a/tests/test_h2c_suites/test_e2c_bandersnatch.py +++ b/tests/test_h2c_suites/test_e2c_bandersnatch.py @@ -1,45 +1,11 @@ -from typing import NamedTuple - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchPoint -class BigInt(NamedTuple): - inner: list[int] - - -def process_field_elements(big_ints: list[BigInt]) -> list[int]: - results = [] - limb_size = 64 # each limb represents 64 bits - for b in big_ints: - number = 0 - for index, limb in enumerate(b.inner): - number += limb << (limb_size * index) - results.append(number) - return results - - def test_h2f(): - # Test vector from specification - expected_field_elements = process_field_elements( - [ - BigInt( - [ - 13667986260176768296, - 7615788394780045608, - 16744902074056285084, - 5843483180372586193, - ] - ), - BigInt( - [ - 10069264885616157454, - 379900787323118714, - 5986637957723933190, - 6530082265195051099, - ] - ), - ] - ) + expected_field_elements = [ + 51868557272037678616201174487618104615692125483749830231812383640086259094753, + 28148112010555661709849764589968816930893551111405645473366527007776586648740, + ] data = bytes("foo", "utf-8") u = Bandersnatch_TE_Curve.hash_to_field(data, 2) @@ -53,11 +19,10 @@ def test_m2c(): p0 = BandersnatchPoint.map_to_curve(u[0]) p1 = BandersnatchPoint.map_to_curve(u[1]) - # Test vector from specification - assert p0.x == 45311200032263316917859627542467284358670199398458214934254495151428460867180 - assert p0.y == 12776320642587906524824617948027275973876805685686439823724827627303230293583 - assert p1.x == 4062918070531615925962241074596089620660059154890696073867928698119996156623 - assert p1.y == 28091649524129975855673249115644895380082395569265826631567705939331162643040 + assert p0.x == 8864805491392651408849860969071502422330330403516577719645408615048305804698 + assert p0.y == 3141991639291324936022779954882288522159181883763336030616570191472121730763 + assert p1.x == 15951435375274270238335190310049552077179069857488797671983601737994539723286 + assert p1.y == 7365450909453271239422049886386045513107923934059991464613784473257165293593 def test_e2c(): @@ -65,6 +30,5 @@ def test_e2c(): u = BandersnatchPoint.encode_to_curve(data) - # Test vector from specification - assert u.x == 26037012954893424526367048031037997009889535281273781660989300420960588198291 - assert u.y == 2904166584983200306316763312322681981821413355244066354672834649878949825050 + assert u.x == 41706851287321768980670436615954402659160947743433584884323702829779219804533 + assert u.y == 45261115535002764022712885934321790255618221679857277845409327068867281988279 diff --git a/tests/test_keygen.py b/tests/test_keygen.py index 8ff47b7..558720d 100644 --- a/tests/test_keygen.py +++ b/tests/test_keygen.py @@ -9,13 +9,13 @@ [ ( 0, - "5e465beb01dbafe160ce8216047f2155dd0569f058afd52dcea601025a8d161d", - "51c1537c18eea5c5969cb2ae45c1224cc245de5c5b8e6e25f48fb99f2786ee05", + "dff68d8158281c3ee65e678d75c7f5c007de51d0c3a800675208b7c61d2e6f98", + "cc1a43aef9a710b8def623da1eae8f35d7992f46302c08242e0a2bb823ccac08", ), ( 100, - "caf7eb70d84e27511179c83ac352f8d3e9b9661371520c54c9ad56781f374a32", - "ad20931d3f8cee57206bc1c3e5dad50677afb9fb712217c6a980867d3a56451c", + "84c569f6371c182164b6ca1b94097274c7071d3a005050df39c14275f60b01cf", + "0d28a81b0a4b8d197c7c10d60472d9ab9c5b7743803c4b68dc1a274d34009104", ), ], ) diff --git a/tests/test_verify_ring_sig.py b/tests/test_verify_ring_sig.py index 5841a8e..2c10562 100644 --- a/tests/test_verify_ring_sig.py +++ b/tests/test_verify_ring_sig.py @@ -13,7 +13,7 @@ from dot_ring.ring_proof.pcs import srs from dot_ring.ring_proof.pcs.utils import g2_to_blst from dot_ring.ring_proof.verify import Verify -from tests.utils.arkworks_serde import ( +from tests.utils.rust_serde import ( compressed_g1_to_uncompressed_bytes, compressed_g2_to_uncompressed_bytes, deserialize_bandersnatch_point, diff --git a/tests/utils/python_to_rust_serde.py b/tests/utils/python_to_rust_serde.py index 150f6b3..01358f6 100644 --- a/tests/utils/python_to_rust_serde.py +++ b/tests/utils/python_to_rust_serde.py @@ -1,8 +1,8 @@ """ -Arkworks-compatible serialization utilities. +Rust-compatible serialization utilities. -These helpers mirror the formats expected by tests.utils.arkworks_serde -and are intended for exporting Python-generated proofs to arkworks. +These helpers mirror the canonical formats used by tests.utils.rust_serde and +are intended for exporting Python-generated proofs. """ from __future__ import annotations @@ -35,7 +35,7 @@ def _is_zero_fq2(value: Any) -> bool: def serialize_bls12_381_g1(point: tuple) -> bytes: - """Serialize a BLS12-381 G1 point in arkworks compressed format.""" + """Serialize a BLS12-381 G1 point in canonical compressed format.""" if len(point) == 3: # Jacobian x, y, z = point @@ -68,7 +68,7 @@ def serialize_bls12_381_g1(point: tuple) -> bytes: def serialize_bls12_381_g2(point: tuple) -> bytes: - """Serialize a BLS12-381 G2 point in arkworks compressed format.""" + """Serialize a BLS12-381 G2 point in canonical compressed format.""" if len(point) == 3: x, y, z = point if _is_zero_fq2(z): @@ -119,7 +119,7 @@ def serialize_ring_proof( agg_at_zeta_proof: tuple, lin_at_zeta_omega_proof: tuple, ) -> bytes: - """Serialize the ring proof in arkworks-compatible order.""" + """Serialize the ring proof in canonical order.""" out = bytearray() for c in column_commitments: out.extend(serialize_bls12_381_g1(c)) diff --git a/tests/utils/arkworks_serde.py b/tests/utils/rust_serde.py similarity index 90% rename from tests/utils/arkworks_serde.py rename to tests/utils/rust_serde.py index 270ecf3..de8fbb1 100644 --- a/tests/utils/arkworks_serde.py +++ b/tests/utils/rust_serde.py @@ -1,8 +1,8 @@ """ -Arkworks serialization/deserialization utilities. +Rust-compatible serialization/deserialization utilities. -Functions to convert between arkworks compressed format and Python types -for BLS12-381 (G1, G2) and Bandersnatch points. +Functions to convert between canonical compressed formats and Python types for +BLS12-381 (G1, G2) and Bandersnatch points. """ from py_ecc.optimized_bls12_381 import FQ, FQ2 @@ -10,16 +10,16 @@ def deserialize_fq_field_element(data: bytes) -> int: """ - Deserialize arkworks Fq field element (32 bytes, little-endian). + Deserialize an Fq field element (32 bytes, little-endian). - Arkworks uses Montgomery form internally but serializes as regular integers. + The canonical byte format serializes regular integers. """ return int.from_bytes(data, byteorder="little") def deserialize_bandersnatch_point(x_bytes: bytes, y_bytes: bytes) -> tuple[int, int]: """ - Deserialize Bandersnatch point from arkworks compressed format. + Deserialize a Bandersnatch point from canonical compressed format. Args: x_bytes: x-coordinate (32 bytes, little-endian Fq) @@ -37,11 +37,11 @@ def compressed_bandersnatch_to_uncompressed_bytes(compressed: bytes) -> bytes: """ Convert compressed Bandersnatch point (32 bytes) to uncompressed (64 bytes) for transcript. - Arkworks compressed format for Bandersnatch: + Canonical compressed format for Bandersnatch: - 32 bytes: x-coordinate (little-endian) with flags in high bits - y-coordinate is recovered from curve equation - Arkworks uncompressed format: + Canonical uncompressed format: - 32 bytes: x-coordinate (little-endian, no flags) - 32 bytes: y-coordinate (little-endian) @@ -105,9 +105,9 @@ def compressed_bandersnatch_to_uncompressed_bytes(compressed: bytes) -> bytes: def deserialize_bls12_381_g1(data: bytes) -> tuple: """ - Deserialize BLS12-381 G1 point from arkworks compressed format. + Deserialize a BLS12-381 G1 point from canonical compressed format. - Arkworks compressed G1: 48 bytes, big-endian + Canonical compressed G1: 48 bytes, big-endian - Bit 7 (MSB): compression flag (1 = compressed) - Bit 6: infinity flag (1 = point at infinity) - Bit 5: y-coordinate sign/parity @@ -129,7 +129,7 @@ def deserialize_bls12_381_g1(data: bytes) -> tuple: return (FQ(0), FQ(1), FQ(0)) if not is_compressed: - # Uncompressed format (not typically used by arkworks for G1) + # Uncompressed format is not supported here. raise ValueError("Uncompressed G1 points not supported") # Extract x-coordinate (remove flag bits from first byte) @@ -149,7 +149,7 @@ def deserialize_bls12_381_g1(data: bytes) -> tuple: # Choose correct square root based on sign/parity bit # The sign bit indicates if y > (p-1)/2 (lexicographically largest), not if y is odd! - # This follows the ZCash BLS12-381 spec and matches arkworks serialization. + # This follows the ZCash BLS12-381 spec serialization rule. y_is_lexicographically_largest = int(y) > (field_modulus - 1) // 2 # If the computed y doesn't match the sign bit, use the other square root @@ -193,10 +193,10 @@ def compressed_g2_to_uncompressed_bytes(compressed: bytes) -> bytes: Convert compressed BLS12-381 G2 point to uncompressed bytes for transcript. Args: - compressed: 96-byte compressed G2 point (arkworks format: c1 || c0) + compressed: 96-byte compressed G2 point (c1 || c0) Returns: - 192-byte uncompressed G2 point (arkworks format: x_c1 || x_c0 || y_c1 || y_c0, no flags) + 192-byte uncompressed G2 point (x_c1 || x_c0 || y_c1 || y_c0, no flags) """ if len(compressed) != 96: raise ValueError(f"Expected 96 bytes for compressed G2 point, got {len(compressed)}") @@ -215,7 +215,7 @@ def compressed_g2_to_uncompressed_bytes(compressed: bytes) -> bytes: x_c0, x_c1 = x_fq2.coeffs y_c0, y_c1 = y_fq2.coeffs - # Serialize as uncompressed: arkworks uses c1 || c0 || c1 || c0 format + # Serialize as uncompressed: c1 || c0 || c1 || c0 format. # Each coefficient is 48 bytes, big-endian, no flags x_c0_bytes = int(x_c0).to_bytes(48, "big") x_c1_bytes = int(x_c1).to_bytes(48, "big") @@ -241,10 +241,9 @@ def legendre_fq(a: int, p: int) -> int: def sqrt_fq2(a) -> tuple[int, int] | None: """ - Compute square root of FQ2 element using arkworks algorithm. + Compute square root of an FQ2 element. Implements Algorithm 8 from https://eprint.iacr.org/2012/685.pdf (page 15) - This matches arkworks' implementation exactly. Args: a: FQ2 element (can be either regular or optimized FQ2) @@ -327,7 +326,7 @@ def sqrt_fq2(a) -> tuple[int, int] | None: def deserialize_bls12_381_g2(data: bytes) -> tuple: """ - Deserialize BLS12-381 G2 point from arkworks compressed format. + Deserialize a BLS12-381 G2 point from canonical compressed format. G2 points are 96 bytes (2 × 48-byte Fp2 elements). Similar compression scheme as G1 but for Fp2 field. @@ -350,7 +349,7 @@ def deserialize_bls12_381_g2(data: bytes) -> tuple: if not is_compressed: raise ValueError("Uncompressed G2 points not supported") - # Extract x-coordinate: arkworks serializes Fp2 as c1 || c0 (c1 first, c0 second) + # Extract x-coordinate serialized as c1 || c0 (c1 first, c0 second). # Each coefficient is 48 bytes, big-endian x_c1_bytes = bytes([data[0] & 0x1F]) + data[1:48] # c1 in bytes 0-47 x_c0_bytes = data[48:96] # c0 in bytes 48-95 diff --git a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_pedersen.json b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_pedersen.json new file mode 100644 index 0000000..f72feb8 --- /dev/null +++ b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", + "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", + "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", + "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", + "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", + "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", + "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", + "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", + "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", + "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", + "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", + "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", + "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", + "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", + "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", + "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", + "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", + "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", + "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", + "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", + "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", + "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", + "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", + "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", + "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", + "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", + "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", + "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", + "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", + "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", + "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", + "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", + "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", + "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", + "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", + "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json new file mode 100644 index 0000000..e2e1671 --- /dev/null +++ b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json @@ -0,0 +1,135 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", + "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", + "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", + "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", + "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", + "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249906c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "8c70bb1f00851b158af9e9dae73708b9f45eee4256c7465df0c7e21fb6a0881f6514da270928137e6ec8ec856e95505988a2566bb5b8b211ce512706577c1907da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "f9a870a23b1fdf1f483ca4835de8cd6c7ffb6a4a6685370eb8eb92c647ba11a287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16557befdaf649a92c4333257b279c463e299f1932ac92322bd4d925d4fc4d7826b48f42b6cff2c2103890bc09a9d02da15aa08e85aa641e286fb729562271780479052269827ed3813237500d22b66683b73a28a9533b5163bb65e059cf6e480ea195ee129559ef01be7b3ba53124e4105bb02595d9ee3a5d2156b7c051fc9d197cf6db735dcd2cd3d2d7845836e2e5fe65925c6d68f4318294008136a9b5e80efd945d684eeee33db6f0dc714954d18cd6b9651f59047714d724a74f0bd75622f7128f1ac69c29a78f563a4d293d1fef156c1029ea684495d1371c15e6fbf02d1a6f96989d7b9741dbc460a16dcf16d2f50c456490471dd5c5088296743a2c09115919ee5a9592af04c33182b1f47ec110a57ec2762d688ed0c6e1f9c167f3166aaa6ec0de0f9ad2af10a3629ca8ccb2d078fadfba98bb929357124e5d8dc9047abfa891f35000893dc616551584388b87efbfbebc0142b2f7cd7af25a9bdd2faa30b4d907347a63215c2bca7c19c23ec205dd14422fe6619d3c18542bda1096dc6d442bad4c5e3d5e64eef78bcd1b8cbae8b05a50b99d650b8a78bcaf608f8e" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", + "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", + "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", + "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", + "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", + "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6aec054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "dd5be8dec224fda0729eb1d3fb96cd3f48051aa1b4d5d34d8caab01b0d7bb92cf1c8e44a8a205c509c1f7665a97aa34daba181c2401a03d8c66260ab310b81a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "dd90ec2b3cd494c96d94bac1646292dd1fc07d6850ed567f50cee86b35a285a787b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161e5138357243a1b8c6576780706bf64e7c9532bb70e802a5429ed7ecceabdf981d803a7a414775707c35e688d011731819ee0c7df456479d352d01c568ebe2a8db0bedeae1d767ccec4fbe90431185edc4304b7d2796d4d85a6162dcf3244922558fa9d8b824bf7ffbf795c01838d7b6b2330d27c12f922cafb42425ab83fd223e78c0136771f57c9f037b7f92d9ced342e276d125b977f5eee81cf6adfce804283d984993ed33525fcdb56311e947cc9dc3995a458255eed97f0ed8830e3d0c2ba428e1b33498c25d26ea6d0e99c4cd6e4d5dbaa1491a1aa499c4f44f0a792f0de348765d1e7c38b808824a9cdac91d26a0867d9e7866f2282852b8d18bec095f20b1ab4e635182567e0fdb2fd317ada1d081721238e6dcf65a56b475591225011e3eb5d1c14db462f625924c55b25eb2afcfd4f207f4acee3afc227a157916e318df5c3d6c6070a2cda06bece28e3302f9b9409133a49b5146b24db9ef3c2ebc687a33ae0d2beeea954d18bb55f5f66bda5b14a2908d8de0d730b82bdeb91ed7a7236cd427798326b723653acd4a0189ca134d392250a09433a72ab3dadf98" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", + "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", + "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", + "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", + "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", + "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42caec054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "2b7bd9be066892749d8099f3d6bf094d148cce0f69e221a0fe5fce9989ff5b307049bf1d5f36ce918533f7328e23ef77b9140095c5c06a1f91eb4655cae2efaada317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "b8766db2369fe13a4d3b6b4d525c054c51a36b6f91cfc87592e1cb228eb9e00287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16e11765f73130ac9cfac4ec7351eac9d008ba59fd23aa1a321f7c17878eb9db108b3a9836ce1365d49b50c5cec15ad87c9a94d510c2a08d0c07281f89273a742aa0508fd4676803a6f76993af9391d8e03140ea4db09b279dc9d92ad181746f0b597867657e8b7320acdd7ecee921035c30982effb4e8bab347e57f162875721236600bad5959993e80febdd869a9b13d51e032f8dfd8ee4063ecaa2fe2a7f5192c97398be7f78847dc5616c1e26320c6c5b98076bad8e97309efd23b66f6e211e8345c7d62b44ed675f9257ca0bd1fb6a51f26170a0100d276914f9eeb95722b074394b1b1569a8e828013500b1d3a4e626d82643206268704efe428876cf3240d025d017532fb20de4473208d08eed85db83f710e5c4b88a186ab47c2e0611e8e0845b5d26c60137bd59f02d1f530eb860f81cd9764212d131679e58e96fa9329a71dc16395a17672ec4a9ba7dbd9403788c2b203c75f54b0701477f2692a0f438c563b306b9e22c7442c780ae8b59a90b5e2c807ccd07da566eeac2836a02c9cbf9d919e964ea17229c1d8da14518ee2560dd50e9fae7358a7c2328558d0ad" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", + "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", + "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", + "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", + "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", + "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "c090b59415fda5b5d56480bb15612d2c0581cc2e250a9fd1cc11a5ca6cf75e2becfb046f3e954eb592d18ac20a1f240deb3bd02cc7e08d163cf200dc981864afda317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6deb42de7ef3b5f2b77fe4897d5101a6af66ecbe984ce49806952c048734b69a87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161f151ef843b2205d4f220617cea8bd935b3a981298feaeaf86dbe50eae9b181fd73005b022d755de9fa36cfc53c9c1594e8cd77693191679caa05a8713f1fb859ad7ad7a63b7251cadd91aca0b823a93e83d43b88070e02fe92db0f00eea4e26fdaff2ae586be1848e212d8648af3532588715f7d3952027ff3e7ddda911430421dc2095a3dea539afd8ac02880751d83a2aa3e1469ea0bfe76ea1208d421b25eb8477aaba550c7b3dab40c9c43f63a0ee5b9df36a68ca54586641d4f54367270d2f9ebddb01ce178bd0efb5326fbefe2f85721cc8562d117bdada060cc20124136976b219d9373747c1f4331a708715a5fbf8c0c353fe71aab7aa41153a4c2d1d5e42fd8c3d080994074687da4f63985972a6276e8de049fa6d905333b48415e54bc15c7a44316551a474b58a3016145ade5ee3255111238d34125f79e9b426d1928a89355391a8d81cf874e0a1e91b335e2b76c5c8c30d0bbd7e82b898cc2504395cbc2b64e65a53f00e1cdd4ffc295783a5608fcdcce845fe1be69795560bd62911088b296ebe5325ca5dd7a574aef40da0072702658b9956b73bca1e6ea1" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", + "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", + "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", + "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", + "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", + "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249905ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "226d5e8397f7d1e7669a302b0f21744ca5a72eff12ac3c2e90201bd5576dd929bf9fe4a64ffa6a9e524c786384acb461e058bcf762b014bd6edfe9839c249c96da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "51bd9b960c09879d634914f14dc10badd69a52fd4066208aadcef0b4f55bf7ac87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16731ec27f7f83d53b72ad03d3dc58c3577a643ee06fc8a1c8dfec21d01131a5910380ed20e9dd96482988303151e20af5187eebbcb7c754bafeecf7e1a947472c361e8c8526d53b8c1c5d2fb9439ba640176f8ef70593555e94fb8b5c56a7ad041384b3e7819bc468e26882f5bcd1214510cdb351e881f4ce40827e84cfa7751e84c07f55defd454618554d90ee954026f10f7055a9b068bc8a435bfb6576372dd462bd25be390e9fa3af8d1393ae63c390fcaecfc37d2b941a42fc519a2c942bce81c8f6e3082f549e8b0ee3e4d7326b19b7d288bf97a96c1ad09b14786cdc0c395dff374d4e2650dfe14dbbce1edb844da66d0329bc93e5c96ce1e6ee18cb19439598f19033cbf788c032dd2b59e8fa9bd8db096bcb7fcfa8ca29ea70041d1f876109f36c4e57c097a0b5e794929f40e955d9b18226c452ea3bc48eb43bd30dcc121d12014b2088d47da08208216a1c0ee3839954d63d4d64a920916a8fae1304ed4a5f8be4ce3f9084b365a37e84a26bd61533e567ec603e3045e1b42876198488e0d854ff3671d31f8c2a74a16beb725767d5321d2a5506bc315d57de6389" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", + "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", + "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", + "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", + "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", + "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249905ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "226d5e8397f7d1e7669a302b0f21744ca5a72eff12ac3c2e90201bd5576dd929bf9fe4a64ffa6a9e524c786384acb461e058bcf762b014bd6edfe9839c249c96da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6ce421727f29371d9a332d1180d3c04be61a1370f8cfc34a06f0a5272aa2c29287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16c9deb39b90acda84aac8c1888991f458e86093ddb6173c9453e8aa3e456ed5a3e3506e65a63192ac6dda1cd836b4eac1d3ee7ebb1276dd7744fa7f37a3c1bb14b8b137b5d5237aaf4a8a625b92066eb65d98556c265142191bc11378a6fda9167db5267fb8c265fb1ac98ab6a249459f8ca6cd686c10676eafc06696b76e3a02fcf5bec5336e60770ff345f726a9d72ffab19ff74232072aba4eadbf5e2e9b2e47dc1ba8d4ece4b37a3d66465ba60d4a46deb979af6bc84bf3c62a48d10a6b2d085fb4b5a7270dc80cee6080762910c65a9055d711658a028969f3a0c8ca1811d4cb151541deee365c7445526818dd086632df5837d93db4cc73c55bf4d46a2df24b624e8dcac6a0ecd78f1b7553ef42db37e033c7fd13154afa74f40288e72d11eb5baaa9cc910bbc615c776299cadc2a971960288f9f7aa186929831a5599b212f07073d26ea2106883fd0fa6882480843760fa9eae2159b79270dc59f3d141ac892cc1879461a5a074ca69e09f12029c686add3b820679c0da8c8212be22ab49133780ec83d2c55efe00d8e3a054d0e40ef1c446066a5da940af05d8a6d9e" + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", + "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", + "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", + "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", + "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", + "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401", + "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249903db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", + "ring_pks_com": "16ca4899f97ae8fc410cbd241537f59458338892f9c556f21250d3204c0d8d880444c6f49adbd3a89adbe8f46b6736915f930ab09914f5f1e069b726ee8d7e16da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6b037a603dca77d7a2afa8228479b7c67770b9aa8b30a5051c43f5cd0268bbad87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16cb9d594fbe506e4b3fb33be87b4db9ea4cf8f55f791c3b202462c78b9c9fef9429c9f901e9e340da5ba29fbdc38ca3558b37deeba102b4b8df79b72abf9aac9b848fc111c3baa0eb5771c13eebf2b6333d2beb391e096928c011f7231c403d18f3e2bedf1bf755a8269940859fe6ddb0eae6f78ae5292d57dae7877351db7508acddaef8de9097ff761a6f2f847a084dc4f40dc09f8094a30c92d8862752512964105542141fbd64806f8763b270fe14109b4aca3511aba27b9baf5f04965f2a33e1903bd289bac5152478465d5f0aacb2697067505c58b003b9111542d0c00c29831a431b2b1baf1df7910884cf8abe8fa53b0e9567969d212dae4cd3f59220c7e7e2d24a827835b46fa4bea26e1c8d333505c5554fcd3576b2942858a6d602917742ddc6648768873fe45753d9292e64eeeb9e15c856c9ee9ca8c6e83b5ca35c6da3f7108f546c38d9212b6e6ab765a2f78506406bb1c5e02d1de4dbf66504f4bb2ba3c90112e93b8c617426719b7f23ab9a1d7d809b5c074f1deb90b7490e2c8e1042fd12f6ce7ec016aa47f88d8cef580224a58dac8843c7690eb9b45c87" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_thin.json b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_thin.json new file mode 100644 index 0000000..5727c6f --- /dev/null +++ b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "proof_r": "5c51d2d4206550cc271b92e84559ac0e77c86868570ce2bef19b542a3e224198", + "proof_s": "27aaf6c9b2f199330fc94cc8563a0e7a985532738c685eb897727f38a0dc2a03" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "proof_r": "949b3f36307170f4a1ebc91049b02b3aca4296a0bf3ed6d63b5223b8dcf3d1a4", + "proof_s": "6af733946286c0e523617d33dc69a2ebadec880a5216ade18f24a0a7bb767d00" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "proof_r": "0312558c1881cf60d8981607dcc28be481f0f99e4c638991982d0c572a0bd9a1", + "proof_s": "4d2c1990f552d8b894051dcbcbedde4607fc5bfca896b49f7eaa35548e1a1802" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "proof_r": "ad9bda7ee584b308db23133776bf39363bb9d4ce57e23ed66c6369b20dc0fa10", + "proof_s": "d2dc4037da4b24e6a9ee98cea2cc3796382705a6ca3eaaa27aa267656a0ec100" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_r": "c52877bfa7b282f40255ab8f84c95e30ee41e2ef648a742e23a94d37d6d45f90", + "proof_s": "e1e6a697dd866e137269d9e46cd9b88a5d65bac03fecf7a6f3ffe8c9af9c5f05" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_r": "68c4d71df0b535907fc12d6ee1684b6e4dc9edfba86cd2f2e4362c53da6dae05", + "proof_s": "97ae93e34fac032734f6b8ab5c74229077a5d2b44d09859fe042fdc3b0e6dd00" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "proof_r": "f976c046262cd6e7a5d92861f6a50537a0c13960c6eabeef321c4157fe7b1ba7", + "proof_s": "b8c61b40793e6cebe5d79c1e2ed57c524be638fe729a606a73ab38a3f4a36b02" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_tiny.json b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_tiny.json new file mode 100644 index 0000000..514ba45 --- /dev/null +++ b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "proof_c": "561493e9f5218706de62898426047a57", + "proof_s": "d7da066400d586e7171abbccdb0605f525101e9619d95c60a06a6e575a087f03" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "proof_c": "43dbd59fd726dbd0050a18db789aaf88", + "proof_s": "75bfef482126840f5133ee5686e4c127ff8e133eb916a74d0f9a4830bf90e001" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "proof_c": "882c0b9414feda55ff1bd7b706d47734", + "proof_s": "59f85ea9059570ee772c00362eb8c9ab986336bac7680008e0d968e641b36c05" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "proof_c": "8c3b8a0bf4f0ca3836f18253051fa307", + "proof_s": "1e826e624d7200b13846747780ffc1c10adb34ff2c36884126be1d87a153d200" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "a6e767f8e99c05057658975c5e642865", + "proof_s": "6270b0c5ceb52ba5b037ed5e8bb6e0e1102d21f35af868ef9b79fee61eb66704" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "7c9c59ea80b457ad4103dff28bfd5213", + "proof_s": "831ef85c1a67b89dc681b01323e82903caba2fbcf427479aba9fbe984706b000" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "proof_c": "fcefee1fd633471d137e21430fbdb472", + "proof_s": "34d32470eee6076281c340aa27324804131873e36a4ced979fc53113ec13ba05" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/babyjubjub_sha512_tai_ietf.json b/tests/vectors/ark-vrf/babyjubjub_sha512_tai_ietf.json index b983882..514ba45 100644 --- a/tests/vectors/ark-vrf/babyjubjub_sha512_tai_ietf.json +++ b/tests/vectors/ark-vrf/babyjubjub_sha512_tai_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-1", - "sk": "a5a0067dcb48c87c7a0a374b9adc68cbcc1372779a625a39bbaded228b619501", - "pk": "355788aa5f7bdf89352c957833b217c025a43f96ec996d0f4c5f90cab6457f1e", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", "alpha": "", - "salt": "", "ad": "", - "h": "9934ed0d523de780958a399db20785f40b88bf02554d02d3f0cbf09d1229d8ae", - "gamma": "6d6d84991e4270270bd3715bce82d16b4353cb4ef03399b03aec6aa35c51db95", - "beta": "ca6f041756b6b06fe2219c3c2eee56b78518747195f220da55d1d4eb45705d36b0a46031303bb77c2592a548b228ad59a8ce935508272ad66f31ff7e3dcda63e", - "proof_c": "86dc1791372f9f5f49bbbf3366fdb09897208c7e4be37a741680e99bbc8b4400", - "proof_s": "2e593aaab04123e7c7524194750f74b0bf4110d81235838d0a9b1e0bc9586a04" + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "proof_c": "561493e9f5218706de62898426047a57", + "proof_s": "d7da066400d586e7171abbccdb0605f525101e9619d95c60a06a6e575a087f03" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-2", - "sk": "c957077bb0f54610b10bf39356277939a9019276331e2f81a0e452cbb4a49f04", - "pk": "213c69de36f3c7be3ac27926cfd4afc1765cfde3eca46d009e9186278c77c6a0", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", "alpha": "0a", - "salt": "", "ad": "", - "h": "0f40dcaeeb10fd155b2f466c14834050693d57c57488f36ff62bc8f69392c706", - "gamma": "5bd19036f58b16e0d9ff81e105ac8d4135a13ca6d5727b97361deb015ed5fca8", - "beta": "7cfdcd003ad38820afc0b3aeb2c351204a9ffa57bb80010c38b16b20c474bc8257d769d2709909b056446873b1eb0a39535b0fbdf4197a53dce8f9befa692af1", - "proof_c": "d555c4668e6b841a3e62bb1635a050fe45f3954e9bb21b6656d52e189bb17002", - "proof_s": "d97312a28daf602ad28f4dd7dc621cba39d5659ffb47c25b9b9087112b3caa01" + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "proof_c": "43dbd59fd726dbd0050a18db789aaf88", + "proof_s": "75bfef482126840f5133ee5686e4c127ff8e133eb916a74d0f9a4830bf90e001" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-3", - "sk": "b7aff90c90c1da0e6e38f9edb0d339f259cf9d9c49fed5c7f55eaa4c33f6bf04", - "pk": "139d1a10c290ef941c83a0ba631f5cc6beb957cc89718aee9f28a6e9274d682f", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "9934ed0d523de780958a399db20785f40b88bf02554d02d3f0cbf09d1229d8ae", - "gamma": "75ca617c6b6d0a29ad431da6e811f33ba5840b95418bfb4c980e97889fefd411", - "beta": "215c240053c78553189bbb231635c5d4707168a0d13a828353bb44d67812f349a676cf0d1500e39e45a581551bb869153aa23e15387c22cd3b52b42892dbd6b9", - "proof_c": "a06dc9b2f64cda23cb7a8f210935bff809418245ca3f538c0380cf115bb8ec02", - "proof_s": "09a32f66c289180c3819c7b79d07e2e2bdf7da3f29ca2f6b612ffaf4a76ff305" + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "proof_c": "882c0b9414feda55ff1bd7b706d47734", + "proof_s": "59f85ea9059570ee772c00362eb8c9ab986336bac7680008e0d968e641b36c05" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-4", - "sk": "93474f400cbb6658cdb72e7e4148895a6e5f0e3564fcd903c5cc67a735b95900", - "pk": "1a5eaac41a58dce551502f72c938aed1eebc03f6031e6d04ddbf9ca16ae0b829", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "271e2e54eecc269f5efdaabf7e85925defe8a47f4fad1fca840088dc20b5dd96", - "gamma": "1cf923752ca1945378888fe158ae4e867a84fae7a40afb39792b77745e4b8203", - "beta": "47afe056ab4a0847a279f8690ffb164ee40ff29ee3e8d011db241a39b7d2c2dff4a112765d82c479b65c7afc67c0b55417d0582e6122bc69692e1f7a2f4c2d5f", - "proof_c": "b976c5da83b95d742b5fa5b29d3746d5c24a23d48f221ad4f43afbac75156f05", - "proof_s": "d2b711e9e71c1ab8ee5483f9d2472c3a4104f9363501e291b4fa73be17d5ba00" + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "proof_c": "8c3b8a0bf4f0ca3836f18253051fa307", + "proof_s": "1e826e624d7200b13846747780ffc1c10adb34ff2c36884126be1d87a153d200" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-5", - "sk": "13f12827f71217eef3592bb64344b5258eaf5d72c975ebf24f392d740ae47a01", - "pk": "00534b3924ae4a2f5046ca585865d9431ce2b13841043353c1a4552bd762fb27", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "a574e0585ee8bddf3af186a846954269ba3ab8bba10fda73b76e834340cfb11f", - "beta": "29ef2add1558870a142c7af7fde01af8be1ba0add346cb1ac3b077871895f523966964a47e78c4d030928a66499f8a3170f5efd1597671eca528fd671e0148e8", - "proof_c": "a6514d50ee8665d8bc12b8312d3936a1ceded086a31a812d3b30ddf5990b5c04", - "proof_s": "d90c7645ad16c05e38b03123ec7aeb5df15bc3f959a611ba98ceb0a91eeb6c02" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "a6e767f8e99c05057658975c5e642865", + "proof_s": "6270b0c5ceb52ba5b037ed5e8bb6e0e1102d21f35af868ef9b79fee61eb66704" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-6", - "sk": "13f12827f71217eef3592bb64344b5258eaf5d72c975ebf24f392d740ae47a01", - "pk": "00534b3924ae4a2f5046ca585865d9431ce2b13841043353c1a4552bd762fb27", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "a574e0585ee8bddf3af186a846954269ba3ab8bba10fda73b76e834340cfb11f", - "beta": "29ef2add1558870a142c7af7fde01af8be1ba0add346cb1ac3b077871895f523966964a47e78c4d030928a66499f8a3170f5efd1597671eca528fd671e0148e8", - "proof_c": "1819c2f6c4f95407a661da88643a78f8159a01d919af339d8a86019d447a4800", - "proof_s": "a4d0fb5e50563d4c0ea8e92725a4d8496b65aebd7ee94564e5ec5773adaad003" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "7c9c59ea80b457ad4103dff28bfd5213", + "proof_s": "831ef85c1a67b89dc681b01323e82903caba2fbcf427479aba9fbe984706b000" }, { - "comment": "baby-jubjub_sha-512_tai_ietf - vector-7", - "sk": "f24058cf847cd6e1575077d3abf169294f322d0548d1252d6126695e65493c02", - "pk": "502ed44cb78819b23ee869b3368875b09a181b393652f9b3739a2f4eb611431b", + "comment": "baby-jubjub_sha-512_tai_tiny - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "d0aa5ca83981f5065c9c3b9987b173f1c2744725967ebf98dafe2cd618a70d06", - "beta": "2cbaf43571e24ba8644a0f973d3d48cde04464a69f840bc853e6b09d8a6bb9a396d79f163cc06e7b8caa02293fa6b2f2223a02a69fb8cbbdd0a5c0444ba1865f", - "proof_c": "1366f6b7dbbcc9b6ecf8f210b09ce9caa6bbf7241408652861809917b83b2c00", - "proof_s": "f0a2659cef1a400e215220c340ff65f2e3553a5d92baa124bf91c7b21b80e002" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "proof_c": "fcefee1fd633471d137e21430fbdb472", + "proof_s": "34d32470eee6076281c340aa27324804131873e36a4ced979fc53113ec13ba05" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/babyjubjub_sha512_tai_pedersen.json b/tests/vectors/ark-vrf/babyjubjub_sha512_tai_pedersen.json index 2b54fb5..f72feb8 100644 --- a/tests/vectors/ark-vrf/babyjubjub_sha512_tai_pedersen.json +++ b/tests/vectors/ark-vrf/babyjubjub_sha512_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-1", - "sk": "a5a0067dcb48c87c7a0a374b9adc68cbcc1372779a625a39bbaded228b619501", - "pk": "355788aa5f7bdf89352c957833b217c025a43f96ec996d0f4c5f90cab6457f1e", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", "alpha": "", - "salt": "", "ad": "", - "h": "9934ed0d523de780958a399db20785f40b88bf02554d02d3f0cbf09d1229d8ae", - "gamma": "6d6d84991e4270270bd3715bce82d16b4353cb4ef03399b03aec6aa35c51db95", - "beta": "ca6f041756b6b06fe2219c3c2eee56b78518747195f220da55d1d4eb45705d36b0a46031303bb77c2592a548b228ad59a8ce935508272ad66f31ff7e3dcda63e", - "blinding": "75d486c29ce64486ff67d20d123d6af11c9ee548f54ed57dc6b5cb588458bf04", - "proof_pk_com": "5ebd05cab58921b689158c401e59d2ce8d4449d311662f962c0f673206663ea2", - "proof_r": "6e56ab4e041fc960bf029bdbf0f4471c9bca30b5a59c61a69acfb8ac31eaaba4", - "proof_ok": "607da64f244c46e95539ff9717c03511e92f8ebfae6364d9c034153098dc3a9e", - "proof_s": "cacf3eca4df0cb0fe3626ee733344749dabdd81bd5e196a47b593a91c5963600", - "proof_sb": "16e2ddc28e36651125b43ad3835d01ca78373f332fec8a7ab284c1e9d1843c03" + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", + "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", + "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", + "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", + "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", + "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-2", - "sk": "c957077bb0f54610b10bf39356277939a9019276331e2f81a0e452cbb4a49f04", - "pk": "213c69de36f3c7be3ac27926cfd4afc1765cfde3eca46d009e9186278c77c6a0", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", "alpha": "0a", - "salt": "", "ad": "", - "h": "0f40dcaeeb10fd155b2f466c14834050693d57c57488f36ff62bc8f69392c706", - "gamma": "5bd19036f58b16e0d9ff81e105ac8d4135a13ca6d5727b97361deb015ed5fca8", - "beta": "7cfdcd003ad38820afc0b3aeb2c351204a9ffa57bb80010c38b16b20c474bc8257d769d2709909b056446873b1eb0a39535b0fbdf4197a53dce8f9befa692af1", - "blinding": "d3ec8950b4ff8326a32d7de0e4e79f4867f05b84ea8104a993c0cb5a913d9d02", - "proof_pk_com": "95be7ea0d859f8f81358afcad8dccdc1fae18a5c458be0cde5f99582f5a9b401", - "proof_r": "2986918df588d48c8e5f541d76fe1c4d9d34a0f27424c197f24116bd640a4b0f", - "proof_ok": "a25e0b618fe91fdfca9017aa562461d91459f9c9636e720fb7fa404e41bda19e", - "proof_s": "ffbfc71efe64ac4ea31a7f33e5077997aaeaf852a0790dbb2933fe96d5877d05", - "proof_sb": "633ed5f017bb4787b1406f3fb8d78e39333abab75889e2eae8b8f55003b15c00" + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", + "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", + "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", + "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", + "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", + "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-3", - "sk": "b7aff90c90c1da0e6e38f9edb0d339f259cf9d9c49fed5c7f55eaa4c33f6bf04", - "pk": "139d1a10c290ef941c83a0ba631f5cc6beb957cc89718aee9f28a6e9274d682f", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "9934ed0d523de780958a399db20785f40b88bf02554d02d3f0cbf09d1229d8ae", - "gamma": "75ca617c6b6d0a29ad431da6e811f33ba5840b95418bfb4c980e97889fefd411", - "beta": "215c240053c78553189bbb231635c5d4707168a0d13a828353bb44d67812f349a676cf0d1500e39e45a581551bb869153aa23e15387c22cd3b52b42892dbd6b9", - "blinding": "c6d49afd346e1c5a0a9c2e3f823b8aa5368d35bd541df59453de3a6732eb0d05", - "proof_pk_com": "8981402e3c28b9006d9cbe784167a2f6f6ec3b10a8d8e527fecc835de3ec8087", - "proof_r": "d692d5e572fb0f11097ec70bca7f4acc7426e1dd3c6c8e24f57be85b6ec97a8d", - "proof_ok": "95a34ea5d7e0baeb46e6924c62e84c4da3f567a7d1f92636b6149ad6d77c9da5", - "proof_s": "3a6aff911ddb3ed1c026f56f2e6b6abc11c27a8a2c17e07a66e0434f20f83701", - "proof_sb": "e84693d6dca056334fb2bda22f436eec54b758db9f60e23248d8a4002f3e6d05" + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", + "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", + "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", + "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", + "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", + "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-4", - "sk": "93474f400cbb6658cdb72e7e4148895a6e5f0e3564fcd903c5cc67a735b95900", - "pk": "1a5eaac41a58dce551502f72c938aed1eebc03f6031e6d04ddbf9ca16ae0b829", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "271e2e54eecc269f5efdaabf7e85925defe8a47f4fad1fca840088dc20b5dd96", - "gamma": "1cf923752ca1945378888fe158ae4e867a84fae7a40afb39792b77745e4b8203", - "beta": "47afe056ab4a0847a279f8690ffb164ee40ff29ee3e8d011db241a39b7d2c2dff4a112765d82c479b65c7afc67c0b55417d0582e6122bc69692e1f7a2f4c2d5f", - "blinding": "b2c2eca6744dade3da658d4ba81b369c16806793cf18a0d891a842bb3facb700", - "proof_pk_com": "9ac3de2981787be78be595c2b539099815d149e38e7229e576bd58d69120af17", - "proof_r": "c2d9258344dfa662b8bf23f86dc0d846b89636d65c5c66a425d5e382f9f9a982", - "proof_ok": "838498802ab15f143eec44de51a902162b89b1a723c4ca9b52884d88d17758a6", - "proof_s": "09ef339e49436e50c2693882ac05483ab3dcea33717ec8de9bd36fb64df52b05", - "proof_sb": "4be4e1432309c69ba85f0806a7c8e485c5e81dc912915a01b81d28a29db0b704" + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", + "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", + "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", + "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", + "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", + "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-5", - "sk": "13f12827f71217eef3592bb64344b5258eaf5d72c975ebf24f392d740ae47a01", - "pk": "00534b3924ae4a2f5046ca585865d9431ce2b13841043353c1a4552bd762fb27", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "a574e0585ee8bddf3af186a846954269ba3ab8bba10fda73b76e834340cfb11f", - "beta": "29ef2add1558870a142c7af7fde01af8be1ba0add346cb1ac3b077871895f523966964a47e78c4d030928a66499f8a3170f5efd1597671eca528fd671e0148e8", - "blinding": "a9cf6a3c30367c6548440d095f62f6094a2a4ac0cd99ddaf321d9a1fb9c43505", - "proof_pk_com": "5ba0702f2754a02b79d5036d37ad8ed59d8202375cbf7a09b43a39ddc2108210", - "proof_r": "fcf39435b2f2139de277b2a2b96562e74cca099bc37c9ace9bcfd0f9ab535320", - "proof_ok": "46318428fca47f1f855e2a1d2866dda759f1bf7f07836ecd542794f0f6ac1582", - "proof_s": "bff012d68ddc0782fbea7b65174299691fd3de08d26f75d7e345c27a3d41cf01", - "proof_sb": "2486680e055d34b725c7c59ad91453eead9adedb7a8ce479a42002eb50d13b05" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", + "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", + "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", + "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", + "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", + "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-6", - "sk": "13f12827f71217eef3592bb64344b5258eaf5d72c975ebf24f392d740ae47a01", - "pk": "00534b3924ae4a2f5046ca585865d9431ce2b13841043353c1a4552bd762fb27", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "a574e0585ee8bddf3af186a846954269ba3ab8bba10fda73b76e834340cfb11f", - "beta": "29ef2add1558870a142c7af7fde01af8be1ba0add346cb1ac3b077871895f523966964a47e78c4d030928a66499f8a3170f5efd1597671eca528fd671e0148e8", - "blinding": "19ff183955cd2eba5ea97b6d0f548a297fd92cb61f96d95d1103311b609aca02", - "proof_pk_com": "ffb25e7ceb49d58d95e809f144e20ba272f241b4ac932a7ab64e907e70d79715", - "proof_r": "d0bf52caa83f8b7f6258b9863c458f2548b6ee04aa2866c91ea2fe0ba938b2a8", - "proof_ok": "46318428fca47f1f855e2a1d2866dda759f1bf7f07836ecd542794f0f6ac1582", - "proof_s": "be4699c5e7e91d9ed6c03a8fed7a21b83321f435f46107272fa886e20fe7b202", - "proof_sb": "9c1bcede6668d65e5fcf9ddeb26ac06a3d5ff615bc2fa19003389803cba8d501" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", + "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", + "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", + "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", + "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", + "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001" }, { "comment": "baby-jubjub_sha-512_tai_pedersen - vector-7", - "sk": "f24058cf847cd6e1575077d3abf169294f322d0548d1252d6126695e65493c02", - "pk": "502ed44cb78819b23ee869b3368875b09a181b393652f9b3739a2f4eb611431b", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "6574f9a5ae548992b727b94b2c0d55065732333bdbb3cc4a1e832b0a14d554ab", - "gamma": "d0aa5ca83981f5065c9c3b9987b173f1c2744725967ebf98dafe2cd618a70d06", - "beta": "2cbaf43571e24ba8644a0f973d3d48cde04464a69f840bc853e6b09d8a6bb9a396d79f163cc06e7b8caa02293fa6b2f2223a02a69fb8cbbdd0a5c0444ba1865f", - "blinding": "f6233057bc6bf9ee04f62c1a0027841bd4fd4abf3c9770fe83b387aee068df03", - "proof_pk_com": "3a1522a20a87db09a205a00d8dbde870721b6497f5c2634c4006d528b80db422", - "proof_r": "353bbaa0a29cfc8bfac91128700665ce223fd7ad4f67deb8fcf344d6dad96211", - "proof_ok": "a58334c3dc323f52c06d1d0b10afea416ea89d97fc18397fbd5f1ba434e25896", - "proof_s": "bd9f2b093acdb6fdfcc42cce20193f9d761b4f6ea923384342ca9496b7616702", - "proof_sb": "30fdb3f7b9a34e411a4e36aa57ed2273f1669ce23c6b5e3b3956650c88b84300" + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", + "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", + "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", + "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", + "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", + "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ietf.json b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ietf.json index 3488a8b..f8f6543 100644 --- a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ietf.json +++ b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "a1b1da71cc4682e159b7da23050d8b6261eb11a3247c89b07ef56ccd002fd38b", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", "alpha": "", - "salt": "", "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "e7aa5154103450f0a0525a36a441f827296ee489ef30ed8787cff8df1bef223f", - "beta": "fdeb377a4ffd7f95ebe48e5b43a88d069ce62188e49493500315ad55ee04d7442b93c4c91d5475370e9380496f4bc0b838c2483bce4e133c6f18b0adbb9e4722", - "proof_c": "439fd9495643314fa623f2581f4b3d7d6037394468084f4ad7d8031479d9d101", - "proof_s": "828bedd2ad95380b11f67a05ea0a76f0c3fef2bee9f043f4dffdddde09f55c01" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "proof_c": "5685489f948058d1ac34ffc87d6adc09", + "proof_s": "be881519a8790c307e3997b9b7905061b82009bc9d73ee5c5363319b1c884915" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "5ebfe047f421e1a3e1d9bbb163839812657bbb3e4ffe9856a725b2b405844cf3", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", "alpha": "0a", - "salt": "", "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "60f32f5ad3e9694b82ccc0a735edb2f940f757ab333cc5f7b0a41158b80f574f", - "beta": "44f3728bc5ad550aeeb89f8db340b2fceffc946be3e2d8c5d99b47c1fce344b3c7fcee223a9b29a64fe4a86a9994784bc165bb0fba03ca0a493f75bee89a0946", - "proof_c": "8aa1c755a00a6a25bdecda197ee1b60a01e50787bd10aa976133f4c39179330e", - "proof_s": "18c74ffd67e6abc658e2d05ecd3101ddc0c33623823f2395538cf8d39e654f12" + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "proof_c": "8d56976c451b512a88a28704cd62546e", + "proof_s": "68a6a31d6ce21ac9ea4e6eb688e19e08fc30a50a0dcd7c459dc0be49daaad415" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "9d97151298a5339866ddd3539d16696e19e6b68ac731562c807fe63a1ca49506", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "67a348e256d908eb695d15ee0d869efef2bcf9f0fea646e788f967abbc0464dd", - "beta": "edde0178045133eb03ef4d1ad8b978a56ee80ec4eab8830d6bc6c080031388416657d3c449d9398cc4385d1c8a2bb19bcf61ff086e5a6c477a0302ce270d1abf", - "proof_c": "aec4d1cf308cb4cb400190350e69f4fb309255aa738fff5a6ac4ced7538fce03", - "proof_s": "54e5d38a76f309ce63ca82465160abd8d75b78805a0b499e60c26436de4a8e01" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "proof_c": "7d7a08eed618b81bef1ca85695b93d6d", + "proof_s": "3a1107ff4361fd10bf5b891d5554cc76e56502d9f034e921eb2cac0aa73d9206" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "dc2de7312c2850a9f6c103289c64fbd76e2ebd2fa8b5734708eb2c76c0fb2d99", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "4d3e0524fc59374f1fdad8e471c695469b45ecf69c1de85c6c1230e888dd4cbe", - "beta": "36127f8aee7c61048984f0a208bf6d334db9dacbeeeef9ff2d17117e812328321462eb3ef602f5911d77ab11f815eb4154ba95c934e414198ef000a61b4de31a", - "proof_c": "b72598f235145a377911caa794ba85820173c4c49b7be3b05d847b2c753e0311", - "proof_s": "e8e34ad3131388a88eb7f80bd874f3421c378d4ad45911c4bc16e4cdc17b5716" + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "proof_c": "e44e5064895730cb04907d46262e63fa", + "proof_s": "393f12e07c2ce66c08855bcf64a9c3b1dc29e455af7e235500f04076edb0c70e" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "proof_c": "ad6af59b4b84f18187c694ef374687d13517cb53508ff9dafa37d0c759e9601c", - "proof_s": "4c1269d9d161dabd082fc606af979eca7f6c3ab68e78261dc6fb9fbbb98c9704" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "c43dfc9ad3e72ab427810f5879683725", + "proof_s": "08484f31d398c8f27b8ff363354bb14a5b5c23caf1436b321f5ea30b7f555e13" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "proof_c": "4fa53519bd9d17acae4d1021416557d11b84dd4670b563770c14eb98161eaa08", - "proof_s": "0f7f9bee9077427f547e69b919cf8d63823c14b20085fd9516768e0f5e3d3f0e" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "53613c4e23191e4bc2632927f7fd4316", + "proof_s": "84e4072747518ad201335c841fac19222f8ee895f288da6304cd89b3ab4c7708" }, { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "b0e1f208f9d6e5b310b92014ea7ef3011e649dab038804759f3766e01029d623", + "comment": "bandersnatch_sha-512_ell2_tiny - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "6d1dd583bea262323c7dc9e94e57a472e09874e435719010eeafae503c433f16", - "beta": "09106f062ac07846f3f841f64765527b333575143483855d633f99ccc2e8e306e6239ff79a1272cff931e8d0ac6c390328486329118ad40a18b85184da1837ff", - "proof_c": "6dbeeab9648505fa6a95de52d611acfbb2febacc58cdc7d0ca45abd8c952ef12", - "proof_s": "ce7f4a2354a6c3f97aee6cc60c6aa4c4430b12ed0f0ef304b326c776618d7609" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "proof_c": "365b235ddb44510f484f14d5d19a1cd6", + "proof_s": "9f5f9515507c84924f87b5cf549da8b177879cd2f835f94b91e36543e0cfbb0c" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_pedersen.json b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_pedersen.json index f22b451..3d20675 100644 --- a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_pedersen.json +++ b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "a1b1da71cc4682e159b7da23050d8b6261eb11a3247c89b07ef56ccd002fd38b", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", "alpha": "", - "salt": "", "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "e7aa5154103450f0a0525a36a441f827296ee489ef30ed8787cff8df1bef223f", - "beta": "fdeb377a4ffd7f95ebe48e5b43a88d069ce62188e49493500315ad55ee04d7442b93c4c91d5475370e9380496f4bc0b838c2483bce4e133c6f18b0adbb9e4722", - "blinding": "01371ac62e04d1faaadbebaa686aaf122143e2cda23aacbaa4796d206779a501", - "proof_pk_com": "3b21abd58807bb6d93797001adaacd7113ec320dcf32d1226494e18a57931fc4", - "proof_r": "8123054bfdb6918e0aa25c3337e6509eea262282fd26853bf7cd6db234583f5e", - "proof_ok": "ac57ce6a53a887fc59b6aa73d8ff0e718b49bd9407a627ae0e9b9e7c5d0d175b", - "proof_s": "0d379b65fb1e6b2adcbf80618c08e31fd526f06c2defa159158f5de146104c0f", - "proof_sb": "e2ca83136143e0cac3f7ee863edd3879ed753b995b1ff8d58305d3b1f323630b" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "5ebfe047f421e1a3e1d9bbb163839812657bbb3e4ffe9856a725b2b405844cf3", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", "alpha": "0a", - "salt": "", "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "60f32f5ad3e9694b82ccc0a735edb2f940f757ab333cc5f7b0a41158b80f574f", - "beta": "44f3728bc5ad550aeeb89f8db340b2fceffc946be3e2d8c5d99b47c1fce344b3c7fcee223a9b29a64fe4a86a9994784bc165bb0fba03ca0a493f75bee89a0946", - "blinding": "99ff52abf49d67c4303ac4a8a00984d04c06388f5f836ebd37031f0e76245815", - "proof_pk_com": "c1322e7a65b83996c25e37a84e36598333b0d417619242c0cb3d9d972edde848", - "proof_r": "7a4363e0bf9cd18317287d681ab05704982b0088ce373f696dbdf3909a902b36", - "proof_ok": "fc8770c209212640742d53e2f40e5c30fffae574f90fdc670ff11a1127586c03", - "proof_s": "93f7c9d73eec05e500b758f645a2967e62b2206e57eff5f9b99bfc71812e620d", - "proof_sb": "c864de36e0b428f6fb4ef470f94ec9601716cb26ad96f3359e4a1ec110794a0b" + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "9d97151298a5339866ddd3539d16696e19e6b68ac731562c807fe63a1ca49506", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "67a348e256d908eb695d15ee0d869efef2bcf9f0fea646e788f967abbc0464dd", - "beta": "edde0178045133eb03ef4d1ad8b978a56ee80ec4eab8830d6bc6c080031388416657d3c449d9398cc4385d1c8a2bb19bcf61ff086e5a6c477a0302ce270d1abf", - "blinding": "e22ec3e4a2a4132237eb8a62bcc5ed864593cfde08e53b1632ecd3245761c808", - "proof_pk_com": "54c04f259f9e40ee086031d29960b12b6b6407e9de14985001c7265587941831", - "proof_r": "9200b650a0c20b0ef73ccd7651ffc7af154e5e02879dc8666025c245aa547f01", - "proof_ok": "35f8dc0f744d1850513c46b6b4640716cbb4643da26cfe67f8c701486e0b4cae", - "proof_s": "5faa89369589174f4202d6e53e8b4ef10a49b2ad8face60d7cb28bfc8f43bf0e", - "proof_sb": "017093ff8d22ba2f3852141365a1452fbb5ab8cf6f20cb04555e3163f8d88f13" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "dc2de7312c2850a9f6c103289c64fbd76e2ebd2fa8b5734708eb2c76c0fb2d99", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "4d3e0524fc59374f1fdad8e471c695469b45ecf69c1de85c6c1230e888dd4cbe", - "beta": "36127f8aee7c61048984f0a208bf6d334db9dacbeeeef9ff2d17117e812328321462eb3ef602f5911d77ab11f815eb4154ba95c934e414198ef000a61b4de31a", - "blinding": "755610da34cc224fbe60ce5e42add2ea6b272ef466aef18c13497363116d1c03", - "proof_pk_com": "d26274e014ebfc19a9c1a951193858b972eae3360ed35635e89f1f9dbe432be5", - "proof_r": "26202144ba4c4cb7ecde831c9e9662bec519493b29a098dd5803a8b4d261fc12", - "proof_ok": "b9fa51c75d278d95f2ccace9609b28ec137b244c8b7d1523b16ed07c8e24b8e4", - "proof_s": "e42423127a2ca12d4f199287c8fa07784eacf9fc9b86a6bd56ee364cc352c009", - "proof_sb": "5371d6f9c76b560b4e42b9154a395bed60924d8de31284e926d06af382f5ad1b" + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "blinding": "fb0123dd6317dbd379afccded247f75b3c1c2e32b86eaa9d6c9d0eb5bef07919", - "proof_pk_com": "a91807f0ee57d2344a8942808bf35c65b5bd4fde16752a98f3e3dc67be8c103d", - "proof_r": "dbc69ea4dd299deec2f29845e98a17700f07842cf2f6c5e9d88f388fa3a2831c", - "proof_ok": "311f94e886825c80a30fd44535be37218501bd072afcbc1298f8fba6c3e3c96d", - "proof_s": "15f1562046078a7c0d152ef1b56bf3078c763089bf08790f10ff1cf3b9a5030b", - "proof_sb": "f962cb2598032cf5b0a21b4c253514d75c91ace15acde9a3f716e40f70f06804" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "blinding": "0752c5b639dffedf9a66ac111a765d3e9c4cfac9c8b26cc5af6d524967afdf0a", - "proof_pk_com": "d03caebf8577c1d2ed30a09708683195f11883411dc170e3ea9f09a2cbf86bab", - "proof_r": "8b16f0abb2873d6d56199280aeee9e02ce0274a9ca06a3194d6a72c25516ace8", - "proof_ok": "311f94e886825c80a30fd44535be37218501bd072afcbc1298f8fba6c3e3c96d", - "proof_s": "9671cdae8b4cdeea640c24993ccf7e571fcfb3344d81d3cc6f36d03496777c1c", - "proof_sb": "624e25cd6eccec59b09f0893ef9eab877b55c757b9e9c81260255145bffd9a0d" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "b0e1f208f9d6e5b310b92014ea7ef3011e649dab038804759f3766e01029d623", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "6d1dd583bea262323c7dc9e94e57a472e09874e435719010eeafae503c433f16", - "beta": "09106f062ac07846f3f841f64765527b333575143483855d633f99ccc2e8e306e6239ff79a1272cff931e8d0ac6c390328486329118ad40a18b85184da1837ff", - "blinding": "462ae9ad651e5caf11247b989fecb5f2b1729479c33b9133388d14fa35dbbd0c", - "proof_pk_com": "91f1ca92eeaa0b604faf3e4811c12b44991ea33cf582a529a4bc4429a3b6cc5a", - "proof_r": "b69946f270c46ccc59557bd40288a0a27607281da1892328fdb9da2dcb6c73cf", - "proof_ok": "5a02419120b814a5c81d67096aac728ee9bda5ddf9451cf554d871462a04831a", - "proof_s": "bd8c0c1e5e04577c8836e45fb64131d1275309fe28e1d4334b230e3aa639da1a", - "proof_sb": "d93ccbd393ed88c8165b0a01aabe28c56a53b43e527e7927eeadff006dd22114" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ring.json b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ring.json index b51e985..735f8b8 100644 --- a/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ring.json +++ b/tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ring.json @@ -1,142 +1,135 @@ [ { "comment": "bandersnatch_sha-512_ell2_ring - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "a1b1da71cc4682e159b7da23050d8b6261eb11a3247c89b07ef56ccd002fd38b", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", "alpha": "", - "salt": "", "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "e7aa5154103450f0a0525a36a441f827296ee489ef30ed8787cff8df1bef223f", - "beta": "fdeb377a4ffd7f95ebe48e5b43a88d069ce62188e49493500315ad55ee04d7442b93c4c91d5475370e9380496f4bc0b838c2483bce4e133c6f18b0adbb9e4722", - "blinding": "01371ac62e04d1faaadbebaa686aaf122143e2cda23aacbaa4796d206779a501", - "proof_pk_com": "3b21abd58807bb6d93797001adaacd7113ec320dcf32d1226494e18a57931fc4", - "proof_r": "8123054bfdb6918e0aa25c3337e6509eea262282fd26853bf7cd6db234583f5e", - "proof_ok": "ac57ce6a53a887fc59b6aa73d8ff0e718b49bd9407a627ae0e9b9e7c5d0d175b", - "proof_s": "0d379b65fb1e6b2adcbf80618c08e31fd526f06c2defa159158f5de146104c0f", - "proof_sb": "e2ca83136143e0cac3f7ee863edd3879ed753b995b1ff8d58305d3b1f323630b", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c5a1b1da71cc4682e159b7da23050d8b6261eb11a3247c89b07ef56ccd002fd38b4fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "afd34e92148ec643fbb578f0e14a1ca9369d3e96b821fcc811c745c320fe2264172545ca9b6b1d8a196734bc864e171484f45ba5b95d9be39f03214b59520af3137ea80e302730a5df8e4155003414f6dcf0523d15c6ef5089806e1e8e5782be92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "98bc465cdf55ee0799bc25a80724d02bb2471cd7d065d9bd53a3a7e3416051f6e3686f7c6464c364b9f2b0f15750426a9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd93818c0c1c3bff5a793d026c604294d0bbd940ec5f1c652bb37dc47564d71dd1aa05aba41d1f0cb7f4442a88d9b533ba8e4788f711abdf7275be66d45d222dde988dedd0cb5b0d36b21ee64e5ef94e26017b674e387baf0f2d8bd04ac6faab057510b4797248e0cb57e03db0199cd77373ee56adb7555928c391de794a07a613f7daac3fc77ff7e7574eaeb0e1a09743c4dae2b420ba59cf40eb0445e41ffb2449021976970c858153505b20ac237bfca469d8b998fc928e9db39a94e2df1740ae0bad6f5d8656806ba24a2f9b89f7a4a9caef4e3ff01fec5982af873143346362a0eb9bb2f6375496ff9388639c7ffeb0bcee33769616e4878fc2315a3ac3518a9da3c4f072e0a0b583436a58524f036c3a1eeca023598682f1132485d3a57088b63acd86c6c72288568db71ff15b7677bfe7218acdebb144a2bf261eb4f65980f830e77f37c4f8d11eac9321f302a089698f3c0079c41979d278e8432405fc14d80aad028f79b0c4c626e4d4ac4e643692a9adfdc9ba2685a6c47eef0af5c8f5d776083895e3e01f1f944cd7547542b7e64b870b1423857f6362533f7cd2a01d231ffed60fe26169c28b28ace1a307fdc8d4b29f0b44659402d3d455d719d896f83b7ee927f0652ca883e4cfa85a2f4f7bc60dda1b068092923076893db5bd477fa2d26173314d7512760521d6ec9f" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d85a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc7172ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b0b98118b398c44a391065d9ff7d1afb3b7d3ce2c4d81db787ccf6f7d90cf36c8e15688183f5e4c1d2688aaaf289a4448c2b9665fc06b8d1fbb42010f1c7b7c61669f4a2922bd9ec90ba95f5bc8e4cc6d3faa1af74a2a83f3a5870c15f4e18cf92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "a88f3e5041ce58a9b730391fdf31004a4eceec666a600ccf443b0ba82c478c88fea31f106453618afb82ae847d905cad9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd9544919af606752d54cabaa6a28c30762a42fb27d7347aa3a7d95e6451d735c7db7395793dc4ee1e2a63ab02c0d112baa66816f64eafad9351b9e11386a79abd3652805e5244bd13cf00e5d949c252d2977610d9721e05d699bc73b14bf2b5d4d9491f72803a1165a82f7ec4c02e177dfec115f10c24980fc1148b20d188c839da5923eed4679b688d28225fc9fa01d0b4c8c84c96d318cee216c718a35aef047587f52fb5ff09acf3e109feb54b0d0f34fbf8fec8465b37e478edb5a055cc2dd4f01d358a5b3c5f6ba74973e363eb3f58e5b9b59342fb5d34516fc360b9500cedc3e7e7182e2800d8599daf5f198c8dd21bab8faf01ba6d7567f26c79b3063a621181498e89b13e4954db72cb5a7c5179ef74424a6ccd2b34d9919361565236cc3a3d0dd91d680f84d19c9f78a4619bdf127c52e636b5b56726af7644f6c553a72102f6b8d96baed533806d665d961a6a999fb8b563c2af2b99381067a2ca2748c328ab92bf96d34200d14a3032f46f27106ce65d6fbe17cc47d0574e080536da27459afccd64dbbe7d45ba41ed4a64a63604043ca130ed5bf0ab1a179cd7d59c52cc8c088864bb16f9bc2256329448044860fd850c337eb162ce909ca80b89aa5e660e1563aaa6c1cc007b5070711fa55d4976548e56c9657c973aae29f3fe3485705690bb6718a47a6b1b175b879c" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "5ebfe047f421e1a3e1d9bbb163839812657bbb3e4ffe9856a725b2b405844cf3", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", "alpha": "0a", - "salt": "", "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "60f32f5ad3e9694b82ccc0a735edb2f940f757ab333cc5f7b0a41158b80f574f", - "beta": "44f3728bc5ad550aeeb89f8db340b2fceffc946be3e2d8c5d99b47c1fce344b3c7fcee223a9b29a64fe4a86a9994784bc165bb0fba03ca0a493f75bee89a0946", - "blinding": "99ff52abf49d67c4303ac4a8a00984d04c06388f5f836ebd37031f0e76245815", - "proof_pk_com": "c1322e7a65b83996c25e37a84e36598333b0d417619242c0cb3d9d972edde848", - "proof_r": "7a4363e0bf9cd18317287d681ab05704982b0088ce373f696dbdf3909a902b36", - "proof_ok": "fc8770c209212640742d53e2f40e5c30fffae574f90fdc670ff11a1127586c03", - "proof_s": "93f7c9d73eec05e500b758f645a2967e62b2206e57eff5f9b99bfc71812e620d", - "proof_sb": "c864de36e0b428f6fb4ef470f94ec9601716cb26ad96f3359e4a1ec110794a0b", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c55ebfe047f421e1a3e1d9bbb163839812657bbb3e4ffe9856a725b2b405844cf34fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "81ff2ae0324ba81dbc5f511fadd27d6fa23ff83d45a84ea96ed82f09ad73114a79349c978a86386c1a33c09f60c5362a99b73de3fe7f609d6f5f35736a6eb82c739943ad4a3d1fe3f1b589d5b173ad3351786b08e07a1369f82fee25b4a1600192e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "a57818b60d8fc54695a66b49a627b158a2f4141c696f0ac41b16831021e0ce5604aaa76fab504c106e4a50621adcbeeb9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda5d0e0b9c8dfabd2a88713ea7448a6afed58c035994f52a06a37045b7fe9bc8800939475c3beae30ee28aabdb0932bacb7967c476a0b2aaa9bc536fd18b487a65135ae128d4c6fe14dc98160c841a9183ac4a31adf99ad98c4f18368eed0733b7b5126d767299e72a086d9cd9fda84ef8392425404173b80a430a9b320c6cf46f203e0b7214333ab49b43bd68bef7db51fd7d55f3332122aa7e65dd990eb5c36fdef18cfe2ef8624e1372d4ae51fb115572e4a67ada192739a8eddfee2f88c53e9072a320d73c78176f8572b8021f5aa2bfe82834b546cd93295bf05d7b6b81ac56d1a3c8cdcccb575ef8e6865d0b45a2e684e3d03cdfe941d823450076a7229fa17e1d1a92e8a4e12672837f603e0b782235fee0b4f3f2673972730c14e224f0b6cd6e8a2e24358539a2cc242cf792d9b85cd784a6496192404c6ecc68ee370b75f373ee9d9ba48a2de51d3b3f0a923a9385444eb6396f2ec220cefe3113bf208f2697fb1625da3c8d12e7ab8d405c8c05cc70074a7e2b76d73e9fc2e05e95b303920abf93139baaadba3911e3d2d63ae5335a8be8fb028df0052aded98f2d81234fe608836b4896b2e080b9b9fa306be342e1aeb95368beb3099a97f2dd0c1b10e54e38efb04c2b8977da7da8dfa801d6997de31337ea2c4ca2ddc77ad43566a614cc1742a24285ff9da590746aa6d" + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "94f0f18ec9f0593fa1bfff259f33600653fbe62a665e9345bcf5149fbf0cf52d9748bfc8b7c40c01a11736a7cf55239dac1956d482d4cd466e1f921e52ae5f0fce92c3bc53844977507630d6add31b2674046761607c3131567fac4ca610e98592e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b1db26d63b3147361513641ced7124d32642eb1246aefd0c25495387ec3e8a1dfd1ffe4870e37ce5e3a5ac300a507bc39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda9af4c62f6d00e23d85b14c83e8d8ec6948a30871c2171ec2c98b78bd3c542d1ad95025cf9e2fdfda6d6926e42e6a2478b80aeedf10a587fe26c1feb19481f7a043eab9d3bafdda49ee2b32079524f9c5277df8ff2e976341b009ba470fe04a1b720d6cc520eb9b6e15d7d590ce8105ebbd9064bb8c6f10aab02ed91dc1c0430f70441bb8375bcf61ecb1b93b1ea8661634b7ebcf379fb16f7565e1fcb889971c38ae9824ce3afdf4b0253bcafd1da0240c67a07516e4966fba334cbbb7d5b73233c8b0fbdbb229398ebbf0d2d5b993733d2e288d02555b0bd1c78c297d0b62fac56252e2080bcbc1a62f3de4c601995b976957fd1e60a69a8eed5d47040535879a4c050ac105812d55ed2d4ec454f1d3ad8b59bec359e2d596f222d837cce31efd614b86268d1290911c46c6445590ee5eb7b08f7aef20cbc0ee2c210e24503900314cd1cec12f509bebf7256c83d810014348189272f076b2ef91384257827899f7c5971c37b8f0f2796979770999a8a4c22e7bdbe138dbd37941b09b1649c30e5484b3e73a08bdf92d3a928546722a1d9570ff2a7a6688dac5a7687d48021649b9ec86d648f1094cb5fad173cc88b5aad0ff3c26606aa6af9d8c46c491bc691bae6269d202b0fc85bcfe806ffff37a0e9fd9d91d3c75e9be7952042c5d08790b01bf994ec674b531323d263a62678" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "9d97151298a5339866ddd3539d16696e19e6b68ac731562c807fe63a1ca49506", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "67a348e256d908eb695d15ee0d869efef2bcf9f0fea646e788f967abbc0464dd", - "beta": "edde0178045133eb03ef4d1ad8b978a56ee80ec4eab8830d6bc6c080031388416657d3c449d9398cc4385d1c8a2bb19bcf61ff086e5a6c477a0302ce270d1abf", - "blinding": "e22ec3e4a2a4132237eb8a62bcc5ed864593cfde08e53b1632ecd3245761c808", - "proof_pk_com": "54c04f259f9e40ee086031d29960b12b6b6407e9de14985001c7265587941831", - "proof_r": "9200b650a0c20b0ef73ccd7651ffc7af154e5e02879dc8666025c245aa547f01", - "proof_ok": "35f8dc0f744d1850513c46b6b4640716cbb4643da26cfe67f8c701486e0b4cae", - "proof_s": "5faa89369589174f4202d6e53e8b4ef10a49b2ad8face60d7cb28bfc8f43bf0e", - "proof_sb": "017093ff8d22ba2f3852141365a1452fbb5ab8cf6f20cb04555e3163f8d88f13", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c59d97151298a5339866ddd3539d16696e19e6b68ac731562c807fe63a1ca495064fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "b2c08aa40031c9f2a66353b9f3b4e30440704e46f277167fc04fdb7812953cc6bcbca4beb8288a2585a26d786c313c8699b0dd6b2e008ea7aa3b00c273da21ba02bb6d51e39c8a2caf826bc2860d5b8e07b829647b1164c53ab307b9ca0c2bf592e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "a28c6420603f4cd2efd457092ef74585f78eeae389e2ffabf58b9f9dd14ec4db9ffe14be02b7376f6ae7959e11ce1e559107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda06a150e462634cd8e2a43f4a377bf503f37896a5ff3994e5dfe0d76d7080cab3c616ed081e9f13ba653904fce67a0e08476090b55153972ea2b7609d437cdeecce2475bfd26aede213d5878a93ace5f99620bdabe5970d32feba4b425e600a8f53b81887987ba0c9401bb8fd21d5219fc054674e8f6eea7824b5643e31d0d55da7c33c6610cb183ed9fedca5321be353303da40b63bfc00719c8d253009520f67a8f11107b915e404a4b3066632c858314a3c2de105cf69d4ca584ae02c3e4a60829ae05278cfcd8854e1eff8af8a3bda9d92797d404578011368e726052e226ad658bafb6a5e5a51d1dd9ae7dc88598420f2d17146abd268471191dff53b6ad7267bd8bd716c004d0984b4bdb4c5d5295abe978f53cd716a43777658fe91407c4e6f164ab9eb72050b7ab4845068ec48c4ef4933da4eb9f7bda8cbaee06d4590d328064880d590e9cf3933d96a6c15d2f2cb1bb2d6247d522089587646f8de386429e448d962155b5ebfcb4962e1ea3186608ce8a0928ae0eb90ebc38fac9e0a78cf12d236efb73ecb943636ec911a8aed1c634ab079758eb1b68aaf76585727dc23bc29c5e05797d7e1c0fc30a5eb00b0a323d8fe728072821dbf1c903394932472e99a82bb70e69505a27ed9ace5862c1e247530904bef58d4ae28f9d8d82d61a5b7f8c92efeaaaccbe1ba0c9c97" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "92c975ae43de7bd1bbbcb3ebf6f04e99136472c8e665c48618ab06171f96fe693f2f12089ac66e5f79a68b1bff5b7076b1584cd580607934a1d1e0feb12c9bcd60440b83a85d8f142a9a3834b3da1c157fc80f1684ab0eb21e70b8b1ae57176892e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b5aad40d12b343366083f9a84c186d2dfbd6ebd7573f1ef7ff14e49c9e1baaf64b62f751e821188ed13b329dfd4e4d799107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8e0ac886ab6a40f4e54281fc4698174ddfd2621259950c659c102629b7dae6d05e47219066b2725d95df23e8dee5b1d683e8d9403bdafa74979c83014c7eaad03c96a5eb7966e44f2a46f291fcde1b8074f3a781bdebcf0a16da471c90846f5eb7a9bef19b200f0adaefe85adee6cab46adce74aff1133c853cce2ce2a96133ce996256dae7b294ff36a6ee0841ccc622f9799c6835ef8c3456232963d1bcf68d22d33ca2f73826b94bb365582baf7c69eaaa14f7ae54ec6a28f6a232b34cb5d861d411d57a1df938f88ed517cb109c4c8daa4d678cf5aacf3bb391932e9ce65111fd51782ce82119806a18fe799238ca4753517ddc2fdb3557fc51d809a9b2b8d72462b09ca852d5c146de0f8b3feeaf0528606ef096164af793423b7830342234fa6a96a895e27f9ffb37ba9246b29ad4c2db52acde665bd22913b56bdb216b2fa26e5b91dfa8d6a2ec1c6f002cb4596d65784e4b283b011f2c9bd1b47ea5c4b662b6db72fe2f53cea7ae575ac7c7f9008e7e1f70ad6f29b702fd147e44a15c1901edad5b587830e2a847a4fe72264b983933c1ac9cdcd09d5bd7cb6e61600c191acf69113095c03dbbe17f6c1d50ba8aca3afc64f9f8cebfddbb05347a760a1763564cfb18d1313f48f6484f763ca499bc68b43057969a9ca1e27fb0cf781a8f48bcfd3b25b83a5738a35039854e2" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "dc2de7312c2850a9f6c103289c64fbd76e2ebd2fa8b5734708eb2c76c0fb2d99", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "4d3e0524fc59374f1fdad8e471c695469b45ecf69c1de85c6c1230e888dd4cbe", - "beta": "36127f8aee7c61048984f0a208bf6d334db9dacbeeeef9ff2d17117e812328321462eb3ef602f5911d77ab11f815eb4154ba95c934e414198ef000a61b4de31a", - "blinding": "755610da34cc224fbe60ce5e42add2ea6b272ef466aef18c13497363116d1c03", - "proof_pk_com": "d26274e014ebfc19a9c1a951193858b972eae3360ed35635e89f1f9dbe432be5", - "proof_r": "26202144ba4c4cb7ecde831c9e9662bec519493b29a098dd5803a8b4d261fc12", - "proof_ok": "b9fa51c75d278d95f2ccace9609b28ec137b244c8b7d1523b16ed07c8e24b8e4", - "proof_s": "e42423127a2ca12d4f199287c8fa07784eacf9fc9b86a6bd56ee364cc352c009", - "proof_sb": "5371d6f9c76b560b4e42b9154a395bed60924d8de31284e926d06af382f5ad1b", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c5dc2de7312c2850a9f6c103289c64fbd76e2ebd2fa8b5734708eb2c76c0fb2d994fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "89804d665064e57f7de11fc3eeb50fcc7432972e344d14e9fe0f1c4a62ecb1a80bf0927ac19553787590c3a834feec1083c8f1104bb55779216dd5051aefe92873b36f331309eaa429bb1bc14f8b31270964bdc061ae6c856e05b1911750811292e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "84c37dff677bda19d7ce202500196edeabb794b0e0970b52a76061d9fc9c396f5d6671db8da091886f4f894775b49a549107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda8c53db306c4d0d30a509b2d3b48706279629cd604a3e220fdcd49c44876b6a0455419b418f22b18969cec9423e540dd89dd715839826e4aede1002bb651de8968c528cf5d2eb41f17e10494c89f3b475aedfbfd5b5de126f7f22c2fe83e2da00dc1af96ea799fe2dcbd9f0985bc69dc6ce5d23c8c51075af7aa2062927f4d73b76d01e3ca5b67377c420afac1850d6e053114594e7680222b06c6d1ce239d13e69c933daa0b6671896b92456a4856b9bf92dcca0329bd1743ee6dbf0912b3477b1b03294a9b1553d7c79e6bc82448ee76f9dc8e9c869cca60346f0cc2700d200bec0aa5f9d4f1b91acc3d0e21456779b7a0abcb1a6e52e8126410131c63756c2f161d49f441fdfaf719feeeedcd7b8c9f327eac0c279b42e251b71c104b280d8d53a5dfd6be57c646fff62151f43372f768375472ea3bf5f23a23c040b4c26f9460ee2b0f8b2c3946d8ce0909b80e7ee5608c1735ab06cc5bed2425fd823ce68938a4b576f245ad0fcab22a6609a9ebfc5c3716d67148dd570f64f2da5cde1f801c0d421eb4f978026ff58aa8cb03118bd50badc10a9ac76c27579d7b074bffcdfe592547df5db7b7cf36de8b7ac43ea2329196d2daef4faf01cafaf016f927a376a1bcb5a8961c8135b8df70b98d34b6d32eade381a55595a3ba9a324d596b5ea2a1f01565430d7ba3965ad07450c0" + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "8f3bd322dd602f42115519028e1261ee1dd456a7a8a92363b4ce733d53a80ba8d0414bdbd3634ac75487ec03a4f4458d975014f4832268b15b43cf23c2fb09f6056f3a8eb1881ed4d7e2d5e1f0e07dbc0bd7686c04c1a9ab906578736efbbf7e92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "98e9ae043bf407a800c73f16585b9fa74acd7c09038d04af53da2583d62c3b2a1ad6ccbe263f6f62547067f35c7bae909107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb41e837b06a596792ebe4847f044a1c9992b00d425cefd37e7ae8cbb99b8fe8d25ef49e0dca6b4b354921967a6f9e03c956a9a5558912ecde5539854c9958692566557a9f9a81546baf91617fd41e3c7960848e584e2d1d35a8be5ac5911086dd7d1f3b32411b9bdf92b9b9ff47fc0ad5c172662e9eaaccf7300f350b740b239c9213c270f1ab0f7b7fd115b4fcf30ddbdbe14b21872cdd4cc3f6260f92e2621555fa06619f164d0116d58670925e74e5579140c388b463a39cceef4734ae321b7b9c79ec2d4b778c457aff910e27296956483eb1c00fb5df65b6032a7962d718ce87bc2ecfdb519f71a2ef2cf37ce234c065fd820e7d46aa9b4910fca9fcb0fe887bd3d99ebdbdaee2dd94c00e1ba286d71da97d804f2eb9e90045974548f1859546e0e5603ec3acb11f20cf3aa513ebb45af6e501329654076b8be4eba5f08aac72327767b0be36cddd535a4aa866d317cc441c3cae05ab36510aa0080ea4e24e1e2e837880ca0cae490f7e9866e92a5ae3904b7cf79a8de01fdc3b2d292840b4b8bb0a50c6d3f84e59c18131db932872ac971a15e0a883e0de6ca79795e0fa04adb0d3ce7e2b7517ee8b12a6348f267439319cd1fec9d5977a7d9091b0f34aafeae086a35677fb741acb5437d8bd9d64e2649ad0aa62244b90d106d068fc7e0859d7bed816a3bef6dcb33dc009459" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "blinding": "fb0123dd6317dbd379afccded247f75b3c1c2e32b86eaa9d6c9d0eb5bef07919", - "proof_pk_com": "a91807f0ee57d2344a8942808bf35c65b5bd4fde16752a98f3e3dc67be8c103d", - "proof_r": "dbc69ea4dd299deec2f29845e98a17700f07842cf2f6c5e9d88f388fa3a2831c", - "proof_ok": "311f94e886825c80a30fd44535be37218501bd072afcbc1298f8fba6c3e3c96d", - "proof_s": "15f1562046078a7c0d152ef1b56bf3078c763089bf08790f10ff1cf3b9a5030b", - "proof_sb": "f962cb2598032cf5b0a21b4c253514d75c91ace15acde9a3f716e40f70f06804", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c5decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac294fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "8a73624024659145d99046f7c25332c0427a1a419b90c9c31f11dcb43bfa5dea85fe0551e6e344c777470a6a75665195980c655ed0b3fe459512a901981313ce23003968a3487ea5b5de2f9f4372727ac137ab04fec24c03a4ebd7705585165092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "a7185a7a63812926137b53a4776569fe2323e84689e9e2523e03d3c61beb04277bde2c4a2a5e6acecbbfe1c09f16f9899107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb2c7a1bdeee0b96e5636a6bc0e91bf3a2309e9a396c1a079f1be004fb8f01c58a559c750c46b100666e10f841924c3aaafcd90d79db59ea96d9f519cd23e85e1498b18f2a1b5fde389db657178e77d55c946cfcc485343d2e14ed37fb5d39e7bbbccea9103664414fe5eb0b4224721d790674a15b386d31f1d6b4ed8ccc74536305372529273c12476382842eb14722352276ab05e1cdab867fe43b6da8d114941f5dd46bda9b29681e6210fbfba89c09e123ae6aa788845d4f87dbc42228b6639a51dc61bc79ff3e53da217679648be2d6d4e4a0277d05afa98dd73786723373c3d8d4932f6258f174793f9597b9ef888e0e8bcb37ce0f79669411163c51570e6ffd7afb89a3d8f43e1e9fdfce7c583c9a6bb32414c6787d83ad3d46812a43ebf758c58417d169b0a8e7f08befd8beea50699e786f54037f8859c362a7b4a1796c40e76ce0c05c0920f0d092c78123470cfc80e13198bca67c003bf554642fb1cfecc2cc5c6a418a628e2d16152c8dadd9e069e3c3ee9f6715766760d4cdfabd545348c67f22e576d6ab90184843e49989ff102693018ca1256aa1d20d73416b6a2cf93aeaf2a985b109aef311e94f1b2b9951a341496a3584d386fe998b9eab014b5d452cf9489672089388ff5927e37593773ea18e316fd2bd0a8b0566f09f7d36173103dc41ceb82e4dc9fcfc5f9" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d86be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b4f43f81bf0b79c4b144d8ba8f7975bb12b512aa15ddc53e5eef73647b6fbcde47697176d801d29de4270293a68d328891798ba7a2d045a4c403f647619ab99754f593d73de9b09e1d65d29cc60a083b9677f9b2c8f6d66142908e6778d29a3d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "87b659658d78c2d7154006f0592e159386828c8ee3955ad4795ed2751cd061d1238b1bb57c402937481854ab9a0e52a09107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8d75886422670dd4bf46279c1914efae398dc1cd5724b1bfc8780470ed67db63932f28ea195e4d836ee4cd58d054ab88a2d5bf2660bb37b0756c9fc8a8bd5d81699b208bb25689c9d90142c774f4310404ef03af8a41301c5a7be003edf060f966037ce81d3526a84e86a417df80bb6910f77b801ee82bf2f1a7d593d024ec1be4feacca619ab375e247a6cab09c64dd221d567e649c5735c738d10450388a2e72e8d49a4006cb45b0e32439f61d6578cd6700abb863da52a2f078526aa42669b524d4626b936ed0e957c711cddc03584a22a2b0fde46f7c3b7a60160c72ef354b1dccbb231d4a2b6a26f28713fa7ffab4deb1141529dede42fd066fc929755a7e1c1ef71ed98a5d44ab20176f8c120215444058ac0b8521472a664a33c0d8431edfe789f6221ccc33644748d9347bd900f8e1ea008b7cf83ed931bdfd4d9d63aab1165d23aa23bfc0f49aba962080de70ff5c3457343858799d7b7a12c48945cada1ba8c461586e367f125fee0ec7b0c02efd913b89b80e633bd59a3c6a1d08ef98f1aea1f5528b07408981b15a606cb14596953ba42f5b18d681922c3bc3f0773d1cf7566d543202c3db456f1f95fd460081ff6759da8c0caf6f90a431df7f9171d5ad8046ec651e76c019ae1c5f94e00cc3889dcdc1115cc53fd0fd548c79af1ef586376f6447f1224d879a1d393c" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac29", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "9508104b820469687488d83f729288d9f70fc0523318beff44a47da10d490b3c", - "beta": "4ee61f3c000544aa48c565e143e05c6501a623bdbf02a0a408b97433660b4907715f75890cc0e45cdd7116e3da15b15c3c637782e8e05d05c0d5895e5fe583d1", - "blinding": "0752c5b639dffedf9a66ac111a765d3e9c4cfac9c8b26cc5af6d524967afdf0a", - "proof_pk_com": "d03caebf8577c1d2ed30a09708683195f11883411dc170e3ea9f09a2cbf86bab", - "proof_r": "8b16f0abb2873d6d56199280aeee9e02ce0274a9ca06a3194d6a72c25516ace8", - "proof_ok": "311f94e886825c80a30fd44535be37218501bd072afcbc1298f8fba6c3e3c96d", - "proof_s": "9671cdae8b4cdeea640c24993ccf7e571fcfb3344d81d3cc6f36d03496777c1c", - "proof_sb": "624e25cd6eccec59b09f0893ef9eab877b55c757b9e9c81260255145bffd9a0d", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c5decb0151cbeb49f76f10419ab6a96242bdc87baac8a474e5161123de4304ac294fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "8a73624024659145d99046f7c25332c0427a1a419b90c9c31f11dcb43bfa5dea85fe0551e6e344c777470a6a75665195980c655ed0b3fe459512a901981313ce23003968a3487ea5b5de2f9f4372727ac137ab04fec24c03a4ebd7705585165092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "a8455660f642a887ca7bced683e7c5315c6ebff1d7d047ca43f5b5c7b34c244a3902f6ca62346b638ed58e4aa5b2c1c29107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd85a6e99a7824dc5f71bbe12cd251236e62139b0fd1f2f17e979f098520fb37c4cee0af5e1fa9f328d351bbe59044eb3f8156b5f8f7ee79d502b573f752c9d913fdb27c803162ca2b0711a1544679c8c40247a02d5c555d5c1f70cc6c98e82adb77224cedf52fe9093b6dea787a5f04c67344bae1bc8a73ec67839de08202a95f3156b0923bfb4de17fc2fea219af465b11f1285c32008b9ca59a88d7b085e23f95f99f59c457e2ecdffb9dc7fc3c8c169bc1195e70f5e7c49c56329297d0c1339411db06e43c66b2fd1cbe3807d1225f8eb9ca6a5b19653271a51a737224204d55e9e06cb7eb1748f7770d9cdcda4b0030d84c0213b76ad5fbc9c36da4c8e21edaa6e8dc51fa80dfb938ec531e5ef248867c9d02f3604cbd8172a80a241feb4961ae6a954dc81e266cff5a651b3127700848ae83e88246034d18a55f2e9eba26b2cb13d93cb704fff63665b87bd0ccd595c8561ae10a4484af3a070a20537df679737410b648dd6c187cdc0277690b882497d53af3a8fcab1243a0bab5a49d6922a4e3ae917b19959a085708d26cd90898763d698a75332d1aa817534af67420f58c4126d7710ff7075d9a78bf4eaaf9ec30a9d4562f6db0f14ae82cf384179b8bd41390d2340377998f0ab8fb1547ebd4b45b36110a582db3e0d19ecb8cac6a8d35ba072f8176100cdde64c53a14b53" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d86be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b4f43f81bf0b79c4b144d8ba8f7975bb12b512aa15ddc53e5eef73647b6fbcde47697176d801d29de4270293a68d328891798ba7a2d045a4c403f647619ab99754f593d73de9b09e1d65d29cc60a083b9677f9b2c8f6d66142908e6778d29a3d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "875e6616f4d39dcb1bd8dbac51fd6fbf54e2fc24ee641cadd283138cff4dd31ba336f7ecc1d29923a6330679a822cc4c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84e134c22a168085546b5d1e770cb1c578a253a6344209aa346a242ff62459fdab5a3024f74ba6842ab2aedbdd25f47d951f863a44c3712e65ee38496cbcacf173560adec3b143ceac2e42cd0ed1beebecbc59ee1bfea5346144b2f9ecc9acd2c7be098013600886cf8f8dfecf5775918c843a72ff0d5123161b0baf7f827d5300e134fa0e9c02810d598a665c28a69b470c0bb3049f29e2e21604d403d05365803065bd1b8887d6f7d73a8bce144fe03c8573978b3891ca67dd254f61e4b444b2e54e83e53a0beefb24ec34eba6886fa21ffe3de8eca9a5f8a739f56f5a873d85b99984380564f9992b105c5dd9f5df1d5586748abfe52be134071de4ffef23368afdc6fdbe75587780b667108599aa4ea4051fcacd83fdc52f31a26fe60d71f614c50746be5d68abf785d2e13656744c1463a32f55f6604568d50f9b4af52c8ffc4cbdb78b88ccdcc826d32f1b1bb2f44b3fde506dd6379a5bd6dc642202e0b220ebcbf7a60fe6045489717bb2b7cef209be9642142e3f88a54d70d12e228e9d70c0e400ed72b39b8fa0805bd4a76a8aba88ca324dedb1aa1778ce5852790035885e4a2982da28a28044c50e7f2b2e18e473ed76e13c0778bb88128b206bbab59b7a2fb6ac7c8b852ec8af45c84bb95f8fa3736d57addea168c2381668067cae4e3cb4c72bb3b2b724e582c62faf45" }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "b0e1f208f9d6e5b310b92014ea7ef3011e649dab038804759f3766e01029d623", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "6d1dd583bea262323c7dc9e94e57a472e09874e435719010eeafae503c433f16", - "beta": "09106f062ac07846f3f841f64765527b333575143483855d633f99ccc2e8e306e6239ff79a1272cff931e8d0ac6c390328486329118ad40a18b85184da1837ff", - "blinding": "462ae9ad651e5caf11247b989fecb5f2b1729479c33b9133388d14fa35dbbd0c", - "proof_pk_com": "91f1ca92eeaa0b604faf3e4811c12b44991ea33cf582a529a4bc4429a3b6cc5a", - "proof_r": "b69946f270c46ccc59557bd40288a0a27607281da1892328fdb9da2dcb6c73cf", - "proof_ok": "5a02419120b814a5c81d67096aac728ee9bda5ddf9451cf554d871462a04831a", - "proof_s": "bd8c0c1e5e04577c8836e45fb64131d1275309fe28e1d4334b230e3aa639da1a", - "proof_sb": "d93ccbd393ed88c8165b0a01aabe28c56a53b43e527e7927eeadff006dd22114", - "ring_pks": "7b32d917d5aa771d493c47b0e096886827cd056c82dbdba19e60baa8b2c60313d3b1bdb321123449c6e89d310bc6b7f654315eb471c84778353ce08b951ad471561fdb0dcfb8bd443718b942f82fe717238cbcf8d12b8d22861c8a09a984a3c5b0e1f208f9d6e5b310b92014ea7ef3011e649dab038804759f3766e01029d6234fd11f89c2a1aaefe856bb1c5d4a1fad73f4de5e41804ca2c17ba26d6e10050c86d06ee2c70da6cf2da2a828d8a9d8ef755ad6e580e838359a10accb086ae437ad6fdeda0dde0a57c51d3226b87e3795e6474393772da46101fd597fbd456c1b3f9dc0c4f67f207974123830c2d66988fb3fb44becbbba5a64143f376edc51d9", - "ring_pks_com": "a359e70e307799b111ad89b162b4260fb4a96ebf4232e8b02d396033498d4216305ed9cff3584a4c68f03ab3df87243a80bfc965633efc23c82ca064afe105baacccbf23e47b543d16c3c4466a83242a77acc16f79b8710051b5e97c85319cf392e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "88a7fc8a8ae7d295bdd26553b06d298c7d7fdb3f08746aba8e3312d78254a2013d4cd3bea7b62156b5a5b0a42e7e45179107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd807677ad0f02386ef4e6eb99d230aca4c9a73ee3f68397a65c98d6377bf32e1ce36ae082eafb81393786ab07f7a461798e0d87a09cf3d6ea801fc22d65c907dcb30ffb60128a307105cdd5db58f93967ec1c0497eba4db9dac64eb2dfe43baeb30cbe5d2184c998b9070dcda0c8170286d793d583982adb77f01f2ddd295a16161399bcc4b0505757b49074173aca578544d676a44d37fc01321feec35e974376e2080c28a9bfd963566febe2aac12fe51326001b67abbbdc2d67a24c7605b56b4c12eb37e41fbace1e45bf37750057ca1bea272955a82bd2b9930d1a4c2f03d7dc19572954c84f2175706be08a9e7b12445f50bb4bcd6ed77c3d8f3356cc0346bc0a6359ca9384239bf09b160a5845710e688c4dbc7cb7e27494f0746818221f6b6e7af92a58c68f29415fdea5f75ab3e45b1138a7ecde2ff6eac44772f100fa4e6f4b97c4a401e919dbfd1386253a1dde71a3d6bbf24de9a01e1adca917d84e3f10888680e2b736f51a1336d7f6099000777bd3a6de5636654012b5873d1094994887e59f7a6565999b922cafac10782ba51d1290e344ddd3ee84a324ba241b8c9a32c6fb85bb7164878f2de12c9cece5ef7b92860a55fb25ccb65839a66b1a73d4975f06823422cbe4aac82eef13e88eb6f6fd363bb19436f34b40825438dc55d3812bb0485d4ae4f9cc67af7074d" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b3982b5838293b8fb58f52511cda9ebcab48a5f0932edf817630dccc230eb651a8f97ce4fd078b7af816329f5739ee12a2fb2031f85fac1ef27137ad68e5ae0abefb844f516a67641847b265eb1a353652fce946342f487d95b0a538e229d6e092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8048dbacf65522f4ad29d64c1eaa2e914a308bd0a69de81499436e9b516fd8ccec4512abea7dcf27175f52b9be39f03e9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84989a9448a7393c6bf91ee89971748708c643a365b42421005e76c9eefaf0245b0d762f85a9fccd4900ed88d4a4ff85b7272746c0f19acc2c729f4b6ac50df5f6f3dc464fd39b4c4b8311b87600d9073bcba43577b41970105c75634edd2717d74a8975ace0661a7b1bd57765b5a430f6b38eed1f8eaa6e2440537e99f48a2c9237209d43328fd9ae447a610e0e4becca103208ee7b4e0bd6acf9ac267b0f132700412904e580ec794b7af75f7d30224502b4facb29ff44ebb4d2fd80db9902edbdd1dc1e65466a356f46b1d2132103f86039f13114f3e06c98bab560d2e71d2f2dbd01e6b32a220f7ecefd812aed740bc1ef95bbd5b7d49a41ad56de8f36352f2edf247ee7169a5927232c66a889aa11084a36b890582d9951168628cde057ab0290cd106583fe09a6c40c581e4b1f269eac69ac5e277911d0d3a41a701b2aa8cea5c7bc96efd03bd968045c1aee5e9cfd55c191256aae97445370e7d52ec5910a208a47b8cd30ac784acbc0e46cc33321cd7e3ba824206e7ee4de69bcb718b02b7514a55b89765a3743de28818b1f95901c7f1465e1287c83fe0f983200474eda63eab68a605438860fe1d8ce88992a04b7bf621fd3c63a462917404d909181c6a5a15e7c8ef381a59b22c3acfd5c66320a413ea393f08bd755e2a9463c3da85ab2ff7e6311f41dc80fe6d0c798c1" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_pedersen.json b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_pedersen.json new file mode 100644 index 0000000..3d20675 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902" + }, + { + "comment": "bandersnatch_sha-512_ell2_pedersen - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_ring.json b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_ring.json new file mode 100644 index 0000000..735f8b8 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_ring.json @@ -0,0 +1,135 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d85a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc7172ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b0b98118b398c44a391065d9ff7d1afb3b7d3ce2c4d81db787ccf6f7d90cf36c8e15688183f5e4c1d2688aaaf289a4448c2b9665fc06b8d1fbb42010f1c7b7c61669f4a2922bd9ec90ba95f5bc8e4cc6d3faa1af74a2a83f3a5870c15f4e18cf92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "a88f3e5041ce58a9b730391fdf31004a4eceec666a600ccf443b0ba82c478c88fea31f106453618afb82ae847d905cad9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd9544919af606752d54cabaa6a28c30762a42fb27d7347aa3a7d95e6451d735c7db7395793dc4ee1e2a63ab02c0d112baa66816f64eafad9351b9e11386a79abd3652805e5244bd13cf00e5d949c252d2977610d9721e05d699bc73b14bf2b5d4d9491f72803a1165a82f7ec4c02e177dfec115f10c24980fc1148b20d188c839da5923eed4679b688d28225fc9fa01d0b4c8c84c96d318cee216c718a35aef047587f52fb5ff09acf3e109feb54b0d0f34fbf8fec8465b37e478edb5a055cc2dd4f01d358a5b3c5f6ba74973e363eb3f58e5b9b59342fb5d34516fc360b9500cedc3e7e7182e2800d8599daf5f198c8dd21bab8faf01ba6d7567f26c79b3063a621181498e89b13e4954db72cb5a7c5179ef74424a6ccd2b34d9919361565236cc3a3d0dd91d680f84d19c9f78a4619bdf127c52e636b5b56726af7644f6c553a72102f6b8d96baed533806d665d961a6a999fb8b563c2af2b99381067a2ca2748c328ab92bf96d34200d14a3032f46f27106ce65d6fbe17cc47d0574e080536da27459afccd64dbbe7d45ba41ed4a64a63604043ca130ed5bf0ab1a179cd7d59c52cc8c088864bb16f9bc2256329448044860fd850c337eb162ce909ca80b89aa5e660e1563aaa6c1cc007b5070711fa55d4976548e56c9657c973aae29f3fe3485705690bb6718a47a6b1b175b879c" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "94f0f18ec9f0593fa1bfff259f33600653fbe62a665e9345bcf5149fbf0cf52d9748bfc8b7c40c01a11736a7cf55239dac1956d482d4cd466e1f921e52ae5f0fce92c3bc53844977507630d6add31b2674046761607c3131567fac4ca610e98592e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b1db26d63b3147361513641ced7124d32642eb1246aefd0c25495387ec3e8a1dfd1ffe4870e37ce5e3a5ac300a507bc39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda9af4c62f6d00e23d85b14c83e8d8ec6948a30871c2171ec2c98b78bd3c542d1ad95025cf9e2fdfda6d6926e42e6a2478b80aeedf10a587fe26c1feb19481f7a043eab9d3bafdda49ee2b32079524f9c5277df8ff2e976341b009ba470fe04a1b720d6cc520eb9b6e15d7d590ce8105ebbd9064bb8c6f10aab02ed91dc1c0430f70441bb8375bcf61ecb1b93b1ea8661634b7ebcf379fb16f7565e1fcb889971c38ae9824ce3afdf4b0253bcafd1da0240c67a07516e4966fba334cbbb7d5b73233c8b0fbdbb229398ebbf0d2d5b993733d2e288d02555b0bd1c78c297d0b62fac56252e2080bcbc1a62f3de4c601995b976957fd1e60a69a8eed5d47040535879a4c050ac105812d55ed2d4ec454f1d3ad8b59bec359e2d596f222d837cce31efd614b86268d1290911c46c6445590ee5eb7b08f7aef20cbc0ee2c210e24503900314cd1cec12f509bebf7256c83d810014348189272f076b2ef91384257827899f7c5971c37b8f0f2796979770999a8a4c22e7bdbe138dbd37941b09b1649c30e5484b3e73a08bdf92d3a928546722a1d9570ff2a7a6688dac5a7687d48021649b9ec86d648f1094cb5fad173cc88b5aad0ff3c26606aa6af9d8c46c491bc691bae6269d202b0fc85bcfe806ffff37a0e9fd9d91d3c75e9be7952042c5d08790b01bf994ec674b531323d263a62678" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "92c975ae43de7bd1bbbcb3ebf6f04e99136472c8e665c48618ab06171f96fe693f2f12089ac66e5f79a68b1bff5b7076b1584cd580607934a1d1e0feb12c9bcd60440b83a85d8f142a9a3834b3da1c157fc80f1684ab0eb21e70b8b1ae57176892e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b5aad40d12b343366083f9a84c186d2dfbd6ebd7573f1ef7ff14e49c9e1baaf64b62f751e821188ed13b329dfd4e4d799107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8e0ac886ab6a40f4e54281fc4698174ddfd2621259950c659c102629b7dae6d05e47219066b2725d95df23e8dee5b1d683e8d9403bdafa74979c83014c7eaad03c96a5eb7966e44f2a46f291fcde1b8074f3a781bdebcf0a16da471c90846f5eb7a9bef19b200f0adaefe85adee6cab46adce74aff1133c853cce2ce2a96133ce996256dae7b294ff36a6ee0841ccc622f9799c6835ef8c3456232963d1bcf68d22d33ca2f73826b94bb365582baf7c69eaaa14f7ae54ec6a28f6a232b34cb5d861d411d57a1df938f88ed517cb109c4c8daa4d678cf5aacf3bb391932e9ce65111fd51782ce82119806a18fe799238ca4753517ddc2fdb3557fc51d809a9b2b8d72462b09ca852d5c146de0f8b3feeaf0528606ef096164af793423b7830342234fa6a96a895e27f9ffb37ba9246b29ad4c2db52acde665bd22913b56bdb216b2fa26e5b91dfa8d6a2ec1c6f002cb4596d65784e4b283b011f2c9bd1b47ea5c4b662b6db72fe2f53cea7ae575ac7c7f9008e7e1f70ad6f29b702fd147e44a15c1901edad5b587830e2a847a4fe72264b983933c1ac9cdcd09d5bd7cb6e61600c191acf69113095c03dbbe17f6c1d50ba8aca3afc64f9f8cebfddbb05347a760a1763564cfb18d1313f48f6484f763ca499bc68b43057969a9ca1e27fb0cf781a8f48bcfd3b25b83a5738a35039854e2" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "8f3bd322dd602f42115519028e1261ee1dd456a7a8a92363b4ce733d53a80ba8d0414bdbd3634ac75487ec03a4f4458d975014f4832268b15b43cf23c2fb09f6056f3a8eb1881ed4d7e2d5e1f0e07dbc0bd7686c04c1a9ab906578736efbbf7e92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "98e9ae043bf407a800c73f16585b9fa74acd7c09038d04af53da2583d62c3b2a1ad6ccbe263f6f62547067f35c7bae909107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb41e837b06a596792ebe4847f044a1c9992b00d425cefd37e7ae8cbb99b8fe8d25ef49e0dca6b4b354921967a6f9e03c956a9a5558912ecde5539854c9958692566557a9f9a81546baf91617fd41e3c7960848e584e2d1d35a8be5ac5911086dd7d1f3b32411b9bdf92b9b9ff47fc0ad5c172662e9eaaccf7300f350b740b239c9213c270f1ab0f7b7fd115b4fcf30ddbdbe14b21872cdd4cc3f6260f92e2621555fa06619f164d0116d58670925e74e5579140c388b463a39cceef4734ae321b7b9c79ec2d4b778c457aff910e27296956483eb1c00fb5df65b6032a7962d718ce87bc2ecfdb519f71a2ef2cf37ce234c065fd820e7d46aa9b4910fca9fcb0fe887bd3d99ebdbdaee2dd94c00e1ba286d71da97d804f2eb9e90045974548f1859546e0e5603ec3acb11f20cf3aa513ebb45af6e501329654076b8be4eba5f08aac72327767b0be36cddd535a4aa866d317cc441c3cae05ab36510aa0080ea4e24e1e2e837880ca0cae490f7e9866e92a5ae3904b7cf79a8de01fdc3b2d292840b4b8bb0a50c6d3f84e59c18131db932872ac971a15e0a883e0de6ca79795e0fa04adb0d3ce7e2b7517ee8b12a6348f267439319cd1fec9d5977a7d9091b0f34aafeae086a35677fb741acb5437d8bd9d64e2649ad0aa62244b90d106d068fc7e0859d7bed816a3bef6dcb33dc009459" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d86be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b4f43f81bf0b79c4b144d8ba8f7975bb12b512aa15ddc53e5eef73647b6fbcde47697176d801d29de4270293a68d328891798ba7a2d045a4c403f647619ab99754f593d73de9b09e1d65d29cc60a083b9677f9b2c8f6d66142908e6778d29a3d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "87b659658d78c2d7154006f0592e159386828c8ee3955ad4795ed2751cd061d1238b1bb57c402937481854ab9a0e52a09107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8d75886422670dd4bf46279c1914efae398dc1cd5724b1bfc8780470ed67db63932f28ea195e4d836ee4cd58d054ab88a2d5bf2660bb37b0756c9fc8a8bd5d81699b208bb25689c9d90142c774f4310404ef03af8a41301c5a7be003edf060f966037ce81d3526a84e86a417df80bb6910f77b801ee82bf2f1a7d593d024ec1be4feacca619ab375e247a6cab09c64dd221d567e649c5735c738d10450388a2e72e8d49a4006cb45b0e32439f61d6578cd6700abb863da52a2f078526aa42669b524d4626b936ed0e957c711cddc03584a22a2b0fde46f7c3b7a60160c72ef354b1dccbb231d4a2b6a26f28713fa7ffab4deb1141529dede42fd066fc929755a7e1c1ef71ed98a5d44ab20176f8c120215444058ac0b8521472a664a33c0d8431edfe789f6221ccc33644748d9347bd900f8e1ea008b7cf83ed931bdfd4d9d63aab1165d23aa23bfc0f49aba962080de70ff5c3457343858799d7b7a12c48945cada1ba8c461586e367f125fee0ec7b0c02efd913b89b80e633bd59a3c6a1d08ef98f1aea1f5528b07408981b15a606cb14596953ba42f5b18d681922c3bc3f0773d1cf7566d543202c3db456f1f95fd460081ff6759da8c0caf6f90a431df7f9171d5ad8046ec651e76c019ae1c5f94e00cc3889dcdc1115cc53fd0fd548c79af1ef586376f6447f1224d879a1d393c" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d86be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb472ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b4f43f81bf0b79c4b144d8ba8f7975bb12b512aa15ddc53e5eef73647b6fbcde47697176d801d29de4270293a68d328891798ba7a2d045a4c403f647619ab99754f593d73de9b09e1d65d29cc60a083b9677f9b2c8f6d66142908e6778d29a3d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "875e6616f4d39dcb1bd8dbac51fd6fbf54e2fc24ee641cadd283138cff4dd31ba336f7ecc1d29923a6330679a822cc4c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84e134c22a168085546b5d1e770cb1c578a253a6344209aa346a242ff62459fdab5a3024f74ba6842ab2aedbdd25f47d951f863a44c3712e65ee38496cbcacf173560adec3b143ceac2e42cd0ed1beebecbc59ee1bfea5346144b2f9ecc9acd2c7be098013600886cf8f8dfecf5775918c843a72ff0d5123161b0baf7f827d5300e134fa0e9c02810d598a665c28a69b470c0bb3049f29e2e21604d403d05365803065bd1b8887d6f7d73a8bce144fe03c8573978b3891ca67dd254f61e4b444b2e54e83e53a0beefb24ec34eba6886fa21ffe3de8eca9a5f8a739f56f5a873d85b99984380564f9992b105c5dd9f5df1d5586748abfe52be134071de4ffef23368afdc6fdbe75587780b667108599aa4ea4051fcacd83fdc52f31a26fe60d71f614c50746be5d68abf785d2e13656744c1463a32f55f6604568d50f9b4af52c8ffc4cbdb78b88ccdcc826d32f1b1bb2f44b3fde506dd6379a5bd6dc642202e0b220ebcbf7a60fe6045489717bb2b7cef209be9642142e3f88a54d70d12e228e9d70c0e400ed72b39b8fa0805bd4a76a8aba88ca324dedb1aa1778ce5852790035885e4a2982da28a28044c50e7f2b2e18e473ed76e13c0778bb88128b206bbab59b7a2fb6ac7c8b852ec8af45c84bb95f8fa3736d57addea168c2381668067cae4e3cb4c72bb3b2b724e582c62faf45" + }, + { + "comment": "bandersnatch_sha-512_ell2_ring - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b3982b5838293b8fb58f52511cda9ebcab48a5f0932edf817630dccc230eb651a8f97ce4fd078b7af816329f5739ee12a2fb2031f85fac1ef27137ad68e5ae0abefb844f516a67641847b265eb1a353652fce946342f487d95b0a538e229d6e092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8048dbacf65522f4ad29d64c1eaa2e914a308bd0a69de81499436e9b516fd8ccec4512abea7dcf27175f52b9be39f03e9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84989a9448a7393c6bf91ee89971748708c643a365b42421005e76c9eefaf0245b0d762f85a9fccd4900ed88d4a4ff85b7272746c0f19acc2c729f4b6ac50df5f6f3dc464fd39b4c4b8311b87600d9073bcba43577b41970105c75634edd2717d74a8975ace0661a7b1bd57765b5a430f6b38eed1f8eaa6e2440537e99f48a2c9237209d43328fd9ae447a610e0e4becca103208ee7b4e0bd6acf9ac267b0f132700412904e580ec794b7af75f7d30224502b4facb29ff44ebb4d2fd80db9902edbdd1dc1e65466a356f46b1d2132103f86039f13114f3e06c98bab560d2e71d2f2dbd01e6b32a220f7ecefd812aed740bc1ef95bbd5b7d49a41ad56de8f36352f2edf247ee7169a5927232c66a889aa11084a36b890582d9951168628cde057ab0290cd106583fe09a6c40c581e4b1f269eac69ac5e277911d0d3a41a701b2aa8cea5c7bc96efd03bd968045c1aee5e9cfd55c191256aae97445370e7d52ec5910a208a47b8cd30ac784acbc0e46cc33321cd7e3ba824206e7ee4de69bcb718b02b7514a55b89765a3743de28818b1f95901c7f1465e1287c83fe0f983200474eda63eab68a605438860fe1d8ce88992a04b7bf621fd3c63a462917404d909181c6a5a15e7c8ef381a59b22c3acfd5c66320a413ea393f08bd755e2a9463c3da85ab2ff7e6311f41dc80fe6d0c798c1" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_thin.json b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_thin.json new file mode 100644 index 0000000..3fe9e81 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "proof_r": "d969c67f8fd97fcc8c0f8b129810ef5427786e319e304a056038caf32286d15e", + "proof_s": "374e93cc0df1aaa3cbe7a66397213275e852717462f8a9b775ed8bcb68f45b00" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "proof_r": "22be463a480dfadb3beb93ba75890a612fb65e206fecdd9fa9ec130ed16db268", + "proof_s": "2eb15dc87c37022290606c34db23fdd7a80648e6aa0dce076ca3694000a72c01" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "proof_r": "2d726da5bc791c511e7b5629bde266151d66f9f36d276dbe16dc1c900f4913dd", + "proof_s": "743aaadff73eaaccc8e6cc333542faa331a132012e9cb2112a1e681546152d01" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "proof_r": "9a638cee379161ecf1a9269801631303359a531b035076369617c42ef46afcb1", + "proof_s": "223446d4fd1450c1fef095c3fd3d5a52ae9f3a8b07a88d7e7ddaed089b235504" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_r": "fdfae23d8d8c9a5225a7bd49b65cc276abf7ce0f48a3730ca74eb57fe0b27467", + "proof_s": "d55860b9c11a56cbe92b5d9c992fd95756fe1845be0be7eaa2a291b524cd111a" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_r": "d415fcd6eff582440fab9c62819b3bb82dd7158a2faf0465512aa095e9e885c2", + "proof_s": "f2f59942f122f9566576ff9cfffbc803f1229881044aa69cca96193106b08c0c" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "proof_r": "9f9f6670d06734e319a01c6afb72e0e3647085dae2216c707a22643a3ef1a5c2", + "proof_s": "74544f931a955eb7f38dab224988f37425b1275b78ed4340f6d5b7c4bc91a60a" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_tiny.json b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_tiny.json new file mode 100644 index 0000000..f8f6543 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sha-512_ell2_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "proof_c": "5685489f948058d1ac34ffc87d6adc09", + "proof_s": "be881519a8790c307e3997b9b7905061b82009bc9d73ee5c5363319b1c884915" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "proof_c": "8d56976c451b512a88a28704cd62546e", + "proof_s": "68a6a31d6ce21ac9ea4e6eb688e19e08fc30a50a0dcd7c459dc0be49daaad415" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "proof_c": "7d7a08eed618b81bef1ca85695b93d6d", + "proof_s": "3a1107ff4361fd10bf5b891d5554cc76e56502d9f034e921eb2cac0aa73d9206" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "proof_c": "e44e5064895730cb04907d46262e63fa", + "proof_s": "393f12e07c2ce66c08855bcf64a9c3b1dc29e455af7e235500f04076edb0c70e" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "c43dfc9ad3e72ab427810f5879683725", + "proof_s": "08484f31d398c8f27b8ff363354bb14a5b5c23caf1436b321f5ea30b7f555e13" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "53613c4e23191e4bc2632927f7fd4316", + "proof_s": "84e4072747518ad201335c841fac19222f8ee895f288da6304cd89b3ab4c7708" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "proof_c": "365b235ddb44510f484f14d5d19a1cd6", + "proof_s": "9f5f9515507c84924f87b5cf549da8b177879cd2f835f94b91e36543e0cfbb0c" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_pedersen.json b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_pedersen.json new file mode 100644 index 0000000..0db5ed1 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "blinding": "9196197366ec51cd0a1d3a24937b21beac30be36fb433c3923a228ca2797d808", + "proof_pk_com": "fde8df7a70ebd32b39895c64088dcfa22fd436d6d3e742acd4256f043061b8bb", + "proof_r": "4d980be01c90bed8b4853f828a636dc696a8d50b9477497cb1e2094d57ff5f56", + "proof_ok": "e98b9316f516f484a95f321b64891439baefbc65a5753d0a7688d34882867db6", + "proof_s": "7d98653a4f43df951f102df7df22861d17b46b9712e47d8b29c01499a4832d04", + "proof_sb": "372ba4e0f91941a40164dec5ee9842bc2f593dc7197e1590219b7b61f4772610" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "blinding": "e4f55d97d3e32f2519977249fef33131dfc46c69161aec05c20533df88919e00", + "proof_pk_com": "f5b453f86340b9e0fbd4ee1e741e4ea5d0ad89e19d3c170033c9e2ff29172bdb", + "proof_r": "cd5aca4564db7107e7e8f3d7b2b83f0f6003c98cbc68631b3e620c3d28c0b941", + "proof_ok": "d514b61e5d476e25bc0df7001b1e1e7af4ccc08f65c7cc7dbfb160bb37f5391e", + "proof_s": "5426e46493e59a1e561ba0c32631bdd7a45208ad99e54f15502b058cc1ca080e", + "proof_sb": "58dc6f40ab4766fc3b565d8d3b7206405763bca13be2b6f1b156cefa24da3714" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "blinding": "af9e73d2cffa7afdad01f6a984862698d33539394a696a8b1ef6007058c13d1b", + "proof_pk_com": "bd5587e13bc14792ed79b486eaf03db299930e2c3f84d1afc903259b43c9d0ef", + "proof_r": "920fed35b9cfa5a511cac008cc4abafe0fa887b78a8f7276d6ba88c3c8a0f122", + "proof_ok": "8c183f9e206847b3e2cfaea15da2d6283174fa459a26874fff93e31ecce84d9c", + "proof_s": "2e547d41099f8cd5350bf9786c10026723e7ea7e6c79fcd21643b0aa971cd410", + "proof_sb": "0084e899ace5292a80d7c165d4ad82c4954410b606097d4fc7e14dfa6f5e8806" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "blinding": "c146f1514188878d604101eab6b1e62657a64f256b27857da964e6761a71a103", + "proof_pk_com": "0e6e305a6a229b21fb7b009301a9f85eab3d6626c5bfb72deeabdcb4425592d3", + "proof_r": "14a340ca1d171dfbfc8cdda32c2c29c11cde9ec5c4e16c3675622be34c6204d3", + "proof_ok": "7c951ea43b3d98c6711fabaf1ff5d4fd1582e6e8a9f0dba74a7f27a4756bc849", + "proof_s": "0a091a88b4f0b8ee01304cc67c465dd0f9d1012daa94c05f7b69c87e1c2b410e", + "proof_sb": "f45765a06a6a952b0795e49f88cca9d20b40cfa23fc018d2f541667003f07819" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "91c6237feac3f60e443d2e356c88183c8d4c2764e3ba9dc485d0269c74c36e15", + "proof_pk_com": "1ed615e63fbc32b8ada345212d0ce8f67c98df6beb500df2ac1a12a7037898e5", + "proof_r": "c39bc83fee747f4198eb9e0fb00d719718fb77e50917b811da7fa6a7f490103a", + "proof_ok": "f3e5bfc0b64eb718e78b7ebabb65059a7222e142f5ac216b84be59b87fe78135", + "proof_s": "841b92b7a3d93940ef80163dadf4970816b346d9b7e6a42cd9420b4bc6fab319", + "proof_sb": "9d73dd3dfd43c2db0c938cbae59c801d0fb38a54b5afa7d128936e64f2c36614" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "2388860590f4d3dfa1024dfa44cf6f933bd4e800824183d01127ec6fc4e12e02", + "proof_pk_com": "cdc5b4d7ee3cc01f014891daaaec17e7a75790cf2410fa4da678d38f6d2ed2bb", + "proof_r": "d39d69f05353c0ede83a070c1248b684d34e035862a7d1acf7a06f7c37e6e5ac", + "proof_ok": "06d9e043a1e0fcea5c4016b86858e688b26254c5f0c9c94083a402ee89099608", + "proof_s": "99e263816fe24599c150216c79f249aef620d6c0d5c3b1185621223423d59e14", + "proof_sb": "44522fbe6cb85568f5147840f112635bf375092afca51dcfd2569b6b3017ac04" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "blinding": "b15a8ad10d2ab3ba1c6edeab5d6667f2bd08a49bbfc8158aceab8d0f8698ef12", + "proof_pk_com": "bcd5b20e9ef57095cca6f259bee3bdc538464a915475a59e035c2181a0dd10eb", + "proof_r": "9252fec49607ae15f98dc2a05c0675f99c4d9b2943c4f2ac863d90432bddf2d5", + "proof_ok": "e7a8f89c36b295bad4dc1d267af453de8c4b9f3aa7136e29052de58aa24acc06", + "proof_s": "4250fda4b0aff93409d37f7ab499d645c7cf9ba56639ff6947857e10ba615506", + "proof_sb": "401f45f75557470cfa4267099f5cbf6a89c01753a628ddaf41fa16cc7abe7907" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_ring.json b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_ring.json new file mode 100644 index 0000000..4e85593 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_ring.json @@ -0,0 +1,135 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_ring - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "blinding": "9196197366ec51cd0a1d3a24937b21beac30be36fb433c3923a228ca2797d808", + "proof_pk_com": "fde8df7a70ebd32b39895c64088dcfa22fd436d6d3e742acd4256f043061b8bb", + "proof_r": "4d980be01c90bed8b4853f828a636dc696a8d50b9477497cb1e2094d57ff5f56", + "proof_ok": "e98b9316f516f484a95f321b64891439baefbc65a5753d0a7688d34882867db6", + "proof_s": "7d98653a4f43df951f102df7df22861d17b46b9712e47d8b29c01499a4832d04", + "proof_sb": "372ba4e0f91941a40164dec5ee9842bc2f593dc7197e1590219b7b61f4772610", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "ac7a8becb61103835b24b39a70692183799090ad6d464546ff699be396dfabb12fd35e9108820768b2d86f44c1a38b4593f716e73c1d3b698eae8e188c8fcd6fdb137ff53398fe41b000a4db5f42fadd9ec645770774470efe20f621a8017bbc92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8e8cb130a517b13438d4fd49d792a920cdc521ac827ec9d7879bc21d4c8c35fd076fde9ac604b2d3d8ec4d1167e27d529107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb1c9a472387174696ff39fb49428f962accdb0cc857ae96b214d5ab4a0e8a25e935dbef995a1be99976abfd4fe5ee3828be7df11e0abe3a021e1d589228acb72138ae8cd9293c11462f5569572a34f18fe36ec66d81dce62c25a6c6a9c225d1d65516aa000f79515e87cf3e436daede297c1112786bae111429a24c93282d50ef9e2fc6f1a674134e87fdf1ee3a7834f9b0ee4fc5ad855b939c720d0c6ef7a665226802a5a3ef0fb0185125c93703ffe150cc7712fb4d738e9927256d9d79f0be6d95d219c47ce6552df452c2b6a41a3208592e91de4401cb94950c8261d461f25f6e33179c78a848f8b47aa2c534726ddab1d559f7b70e30722ae8b07a15939c301fe7f7c2d0acf0703df2c58132e727af3d447e7ce24db5f1830bd47698f0e2acce5a22b1070f129fc0a07c562cb9d224afae8e3e81332c4a832ff4b0e4f0eaca520dac5789855c3c234259ffe19105e882e05faefa469b3cbcb64ddb7cb36ec54fc97d7b7f82546ed447bed2a2d3c67c66c25976b187cecc43250350931644aa5e8b2023d61d1f33154afb3689b2fb773ec0eaa35e0159b119c65f4d3fc592a54fb02f76b56281db907f28decaa878b90eb99d1ffde9f18cc5b57eadfb9d7999bb937b2b32b9232d74b93f68a3908739241dbdad5352303327a583d4a3e2d7f54e936a58478b00a50263431bc7f2e" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "blinding": "e4f55d97d3e32f2519977249fef33131dfc46c69161aec05c20533df88919e00", + "proof_pk_com": "f5b453f86340b9e0fbd4ee1e741e4ea5d0ad89e19d3c170033c9e2ff29172bdb", + "proof_r": "cd5aca4564db7107e7e8f3d7b2b83f0f6003c98cbc68631b3e620c3d28c0b941", + "proof_ok": "d514b61e5d476e25bc0df7001b1e1e7af4ccc08f65c7cc7dbfb160bb37f5391e", + "proof_s": "5426e46493e59a1e561ba0c32631bdd7a45208ad99e54f15502b058cc1ca080e", + "proof_sb": "58dc6f40ab4766fc3b565d8d3b7206405763bca13be2b6f1b156cefa24da3714", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b572ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "88299eb0f7699f01680afa14d327ddae7cd4ad64a9c6741282b11a1857121354f11eebdca467e471817a0b2d3b383bc399791815cb93a5225ccd14f383b9067d3a2dc2e45bc633894028298cbbba36481892431948fb86c94d7f978df92f811d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "91c669d20bbd5ebd0b61cbd00eb8198b15046a310e49af96c1efe585703f313398a5d48a6c4bc392bac1042a322ec41f9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda907cead8084ea8774c822f0cd04f36fd4078e89d6022f7f53abbf14fb9337d7c940ada82a6862ae2583cca8949e22b1a12008b9d6076632261988538b29b7d7a51dd632efa0420400b51342645585dfdafd56151db22b01ad6256fa1964648c4f940c251cc2825fc0697ea20abf0e4decc1e621c7021319b6948aa99704d832ac72e8dbf840c5258e019b284f7fccbe130074d3c09fec2eddb51cba46511a1b55b13daf421be838485864ee9c4d7e65f8fde6f782b9c10b3e37cd7d1d06641b01052d8c1e76226203cfeeb68a61d396b134063b8505b4506044eb7d35eda012ac2a289d9331336a39fe06bc054eb8ccc24491b87028ed11c500d699ef9b574c4eea9c039f35055242a41be6d45efe5b2174383dcb61f030a84120ce9ce0b819dd40f72e6f24f0cfd39b3ea729aaaa01d7e53cea5d930bb2baaee276181d7346a11b6411f1d0c7dd35eb96a01197f9dc873921e853a6a04b9418c61c1a1adb47eb1f3e241bd14c86c547b7a4fec9beaa0004e1cd01b1902c36aa580aba758af5723ba681a101f24b17914f603eb8ab11b5499219274be27aac972e4744af5c58a65d01e328c93c7e9152bc46fe85b1f44ec5003de348c991e985f3f67c8ca723b1beb56bb666f222aa8d1c6c3dc4f38b904cedb87269bc1570302a75f85a950f1dbb4d46cbe7fd02307db34b5b934ce9" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "blinding": "af9e73d2cffa7afdad01f6a984862698d33539394a696a8b1ef6007058c13d1b", + "proof_pk_com": "bd5587e13bc14792ed79b486eaf03db299930e2c3f84d1afc903259b43c9d0ef", + "proof_r": "920fed35b9cfa5a511cac008cc4abafe0fa887b78a8f7276d6ba88c3c8a0f122", + "proof_ok": "8c183f9e206847b3e2cfaea15da2d6283174fa459a26874fff93e31ecce84d9c", + "proof_s": "2e547d41099f8cd5350bf9786c10026723e7ea7e6c79fcd21643b0aa971cd410", + "proof_sb": "0084e899ace5292a80d7c165d4ad82c4954410b606097d4fc7e14dfa6f5e8806", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f76272ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "8ca2f2b37d0b6a623fd931301be25b712399e1b1c22d270ce4c3caf69a3850c594ba8d3737ef496612661d6de37fa74898843b091447c887e0d8a64f0d1e32114b3e76b086848922f6d23f059bff80795d5ee1f99e4a42a71794a52e29072d0992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "80fc4d2d0c04da81c40a137b0dd781e62751de3db1ebe8edc658eecb3604d6c85da34430e6ac13ae976bd85c2d8bdf0c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd98780e8d2db488c7ad61ea94a91143aaf9ad117a4a13ae82ac329b393dc214dad42319a1be26970e3dac288ae9bdc130b03e761acd8f6e9634fa6c81012340c9e1802a1e9027fb8a890409114e347ea2565228f31a06e3fb90d7336271117753ff55245e1230661edf8b806b83399fb1831544ce51b780d4b5e221078dbb6b72ffc4c0b7d964ceba7f37fadc01fd91e2af225759dbe8a57c52a0de751b6ecb1f146d1394c2dec924c981a0818ad06602a7c2eccdb40dfa771b731f83bf4eed637b47a779565ada6c2d0f36672922cf9dcc5272f33b6ad5e4abf3af0eedc26c357a23ef91a34b04787a1db191889b70f1863769e39a849d2c9e5159bf144b3e0e6473253810aaa6ed4971947a3a0eec553f6847fc1fc4ab12138795e59244b1620d87aa55cacaae5ab950901ab76158bac87b80fbc24bbdd643bb18fce093646c8eaa533b6d0b54c3627332d5d1567fe0b9d5b0434e87cbd23dec7f9d5c05398fd41f5b221ec40aef93a4695211daeb9a229f7165b787a2ffdca67ef10a086b4a4a7376d8cade79e0555aeb09e637cc0180c31435566729400d12f32bde55c8b8ea67d91cbbc0280167372e510f861bd0c80fa5e0ac2846eb5ff2112a23aaeb61a2a024f9e6195b666104424359de7aea61959f87ad417891ee8fa84640f21f09d15f56f0a9d3cc71b621e2937aaa2edf" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "blinding": "c146f1514188878d604101eab6b1e62657a64f256b27857da964e6761a71a103", + "proof_pk_com": "0e6e305a6a229b21fb7b009301a9f85eab3d6626c5bfb72deeabdcb4425592d3", + "proof_r": "14a340ca1d171dfbfc8cdda32c2c29c11cde9ec5c4e16c3675622be34c6204d3", + "proof_ok": "7c951ea43b3d98c6711fabaf1ff5d4fd1582e6e8a9f0dba74a7f27a4756bc849", + "proof_s": "0a091a88b4f0b8ee01304cc67c465dd0f9d1012daa94c05f7b69c87e1c2b410e", + "proof_sb": "f45765a06a6a952b0795e49f88cca9d20b40cfa23fc018d2f541667003f07819", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a072ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "b681c19e8af0660ee2cdd4a8e0cf84bba38c8b5bba1ce5be4dfe49fff6a2460e347b9ea40900daace68a3525e9e7488792a6de508892110efd5633b354b121051807e4433c342a51bd326d8ac24d4e0e674377ac308da8df6cd731050e080ce792e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "abd5c7fc2ee1d5feaedc536de9c0b0ae3714042a85660d5d1b588ddbd40d7e66342ad8d6f03d7ca8ecb9632ce48556569107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda844ce10ed4be35c10d2ae5bd1556625242a88e6f6130f44fd06650c69580541424c264d8932fd0c62d1301b876915288dc9dee3d05c3bd17ee58a3b3f31d8897292d16cd91beb90aebf588877205b9302bd101be8335f26b075e3d0a4ec40d6bf7ec53557db650cf63f1f751b8668f18883578374e05a6bbf63d6a68122d315d41b79eaae1b26bfd7ba3c67e7a444dd323c45a87515cec458523bbeac0df02f0b2ddc65154e706034299fb6588652ced5211c3d7b5cf934c5067bf2e8cbb35be71d5dbb8f0cbb7ebfb129b114dc9af8b0c32908901117288e732ebe25d83e0145bc85877e3ff14ef3ace0e708fefec662172d4b948a671528c9927b34644d2bba07e23e36db7e1cb87c9afe8f5a833832d3ef1e5753b151b9962292c5513c0fee71c0f4d089194d7edf9472a4e79d996fc0553f66db2bdaf91e9af49ab70e5bb887557a851e4677913590facc5ebc03bcbb3e7fb86fb89d445f509e9866475a5e5f17ae351573eaf2fa96a5693acbc57455ae94dec2b43f7d58479d7d4d4382e0cce8941fdbabc6b10b3ec598fb3d65a37b5e26c7f3de0f2a6dfebca0b855cbf925185434f6469a82169e55567e6db5c3c999d9c6d8edfb67fdca6e7d06136faf4066086e385cf12d977ec5950212126209a1494016ea5bcf7ac8fa0938f74e4b90d25af7674db5d4d5b114383e777f" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "91c6237feac3f60e443d2e356c88183c8d4c2764e3ba9dc485d0269c74c36e15", + "proof_pk_com": "1ed615e63fbc32b8ada345212d0ce8f67c98df6beb500df2ac1a12a7037898e5", + "proof_r": "c39bc83fee747f4198eb9e0fb00d719718fb77e50917b811da7fa6a7f490103a", + "proof_ok": "f3e5bfc0b64eb718e78b7ebabb65059a7222e142f5ac216b84be59b87fe78135", + "proof_s": "841b92b7a3d93940ef80163dadf4970816b346d9b7e6a42cd9420b4bc6fab319", + "proof_sb": "9d73dd3dfd43c2db0c938cbae59c801d0fb38a54b5afa7d128936e64f2c36614", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad972ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "91d78fd3f075ca2d17022908d80eab5c14993f32315b451b2fb9906e368e6c6680c0dbfce36117f008ab70c8952f3590a9cc7a38b1a25b926f3a10b1b232531be85bb0dc3692d8c3407ed94ac9d5560eba533321f31e66477e0ee40809fbebc292e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "96062422d3c2209b52e3e2485b29ab1a54401053418befd026d425b669ae002364d7ce61268ba8a6d7c6d7cd91ff57389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda5836792cfeb621f9b50efc6fd72678bc85d2c512b6fbb77c090b427e3205c356d4c7fdc191f10f459efb7f74ed5c9f7b133dbdf0b20597af725c7e04636b5185e11eb14bee9cbca1679c971229d2a29013a9056ad764dd73dcc9727b7ecfb5284e70b840ced99b6dd3a8aa7c4a15a80e1f1f1b36535a22524172b33be755d016e0733aec3ba76cec5e5ead0016cc04b971f9fa310c374150c258f86072b581dff6e6de63a4e596628de9ba617f48f982b69c7371d52b86abcd5ee63dd77e070a45ae1591e7ba2fb393410acae895c6c82322e83dfbbe149c494d87616928834c1c495c263de1d03d2b39a38db4ded1a58e51f3395404ed0e7a8d07bddaa5f0ee4351b9133c4346f9e6bb738557f4df582e5f62333c9426258387aa2906550071926c37ac7ddf1b97fff862f93eda43c5b1304146ca2fda53907af2a00ab826ca54047f5edafefb0c7adff16c7d143d2021f76acf2aba21a206efa2b400603378e5cec1b89b17f8fd6e3ce03d814de3a04c6ceffba314167c259b694d4293dad8f949953e4b236990129f0d4fae3903291499d4d038046a4f7b2eaadfb6ced3392ab8a2442e9518130a23e7fcc416dfc59a7b128324322dd09c3718d06802578a23747d4b8125b1a715a822b55f9ac96653dda5529e42172ce4a233b3b8915e072998c5d11ee4a21d6a9f31c438c0c67" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "2388860590f4d3dfa1024dfa44cf6f933bd4e800824183d01127ec6fc4e12e02", + "proof_pk_com": "cdc5b4d7ee3cc01f014891daaaec17e7a75790cf2410fa4da678d38f6d2ed2bb", + "proof_r": "d39d69f05353c0ede83a070c1248b684d34e035862a7d1acf7a06f7c37e6e5ac", + "proof_ok": "06d9e043a1e0fcea5c4016b86858e688b26254c5f0c9c94083a402ee89099608", + "proof_s": "99e263816fe24599c150216c79f249aef620d6c0d5c3b1185621223423d59e14", + "proof_sb": "44522fbe6cb85568f5147840f112635bf375092afca51dcfd2569b6b3017ac04", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d8cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad972ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "91d78fd3f075ca2d17022908d80eab5c14993f32315b451b2fb9906e368e6c6680c0dbfce36117f008ab70c8952f3590a9cc7a38b1a25b926f3a10b1b232531be85bb0dc3692d8c3407ed94ac9d5560eba533321f31e66477e0ee40809fbebc292e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "850d18a3e6c3de203d30e5066cb2e555e8d81d96d70fc03dfb902e476a895ce476535b515d9e8bebe06639495ae94ed39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda400a45a21a7d21b89c865a37d5e532aa52e996add908863ae556891302da843dbd19e16ad377da566efe298219785d4950a613734db1a1b1777413b3995e07aee96bd2ac6df030191a3d7f1aedf2ccb7e57a50b97c1647c662dba723accaaf986bfe0bf42c88c1f26eca9d5fa2b3d79874dc2557720a41f1b6d165f3cda07182115370ddc2b9cdb528044f988a70ce0814015b006eb26a9825daa423076a112197a34f3d8ba6263986bdc5c4d014afb8edd1c54f212964804a852847d808e6829217c2a9260f9a204af9653c2e329484598b4541266afd9d00d8331160dd8078a00b1cd66c270975549a73f79fd2dc14a365cc5a165ba40c35178ab0b15bb5b7cca9dadb09b097d9124ae1e5941df3677435a95a4b0f37c6f699408e62384291138b27350e311a03385eac21b55edb0adb9c7e47b058115b533f87bb56ce347b896f0bb5d9b9affb10c7ca447bc36822923b68b2589927726f0ad5774b5b26c2694fefd305a8ee4945209ca65ccaa16f91c1fc2908c1b741f11dd52db5cdd862e4ce82c7d7e316e1f59eb36c30a2a43ad8b2167a0e8f6a97e857c814f12228e25d6b262a268a99913ec238c4f73f74a161364917bbdd73b0432492ae7f0fd478b321ab859097afafc6e812463ca57c3c3ecf59cf25df30a23f8c9c47c33f2a6c62ebe8b824402e0dad9c3ea3ee113d8" + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "blinding": "b15a8ad10d2ab3ba1c6edeab5d6667f2bd08a49bbfc8158aceab8d0f8698ef12", + "proof_pk_com": "bcd5b20e9ef57095cca6f259bee3bdc538464a915475a59e035c2181a0dd10eb", + "proof_r": "9252fec49607ae15f98dc2a05c0675f99c4d9b2943c4f2ac863d90432bddf2d5", + "proof_ok": "e7a8f89c36b295bad4dc1d267af453de8c4b9f3aa7136e29052de58aa24acc06", + "proof_s": "4250fda4b0aff93409d37f7ab499d645c7cf9ba56639ff6947857e10ba615506", + "proof_sb": "401f45f75557470cfa4267099f5cbf6a89c01753a628ddaf41fa16cc7abe7907", + "ring_pks": "8b3031022897595ba3a280d6af047a46498f6fbfd62081d1df6a2e4b713014dfbd7fcb7c0956648c043bc345a8fb3e1a2c73815bf87f6c3cb985a00c9e95e991307d3bef5e82f857b97f987da190e5fc6d77a9d24ac2068b701df1ab0487a1d865ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af72ce1e7899c633b92709702bdfbe74347d9bdcd1ad62b13f960ade3d1df899b373154c7a701d5d6d38216c4be09800c024c35e4739e43f2b7aa5e0b55a281757a4cc78b2e3eb6a19a777ff20bb801c043f73d836aab81038e286560e260922b40c5335e7855f8a38e9ffada8eadb2415f82b5822319fa53b66ef1c0469048ec3", + "ring_pks_com": "899a03fb5c20a2c8edc9aad05d9f976a5c51fe352cc568572f46237e55281aeade702ee6b4baf503da63cae0b150f25a95efd7ed57f9c691d0f241653395b6da8ea7b421ec61dc65ad3a1c64d304e05e9089feb6e2338bd122edf58f5ec1811c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "add770e84672cf11b3c4abfa456ae36ba6eac4314d5849f5e0e8a23af1243108c02e70d1bd2d1da04f0e302854c865ec9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaf5e9cf3d3adbeea0ab804871f868afb29b8af72fcafd599aea1724f65f44b8a95240b76f21d4421400bbf76d73ac11f9580d555f27eb652448014409f7a4b47f835c075f782ade1f9a7c9a2c50af38bdf753f791684bb179f14845934b7bbf4773c28e36dd1bb6b0cbac536bf5c80a72de7b047fa7a8528e69d185b69792f6daf3cf079108342b0cd85a907d3fa62b65b636115bdb092d3afc77c857aabd7308956f8a60d55d765d93fbc2f55ebc2ad0b9e616c473732bc036baf647252133686f601cd07488b3b88abadee92116947aa091dccd78c108039dcf06225a5fc1da6f5fa6881850592d99711ffb107db34926265f6de7acad66862c004fb268c37f9aeea6d24ff7f6120d4f3cb2a6d98dc1fb58c65fed4f594ed1803b1eb92113da96db462d19e9295bc0c93965e7686959b36014512bf22a24da518399e332b46905dc7661a7378c4c234207eb103378f05109d94273507efca8881639f2227e99fddf5d6b27a1622349bc174d8e2cf3a22c98cb81a3fc3f3fa1135ec2bf92e0b19ad7b16d4988b737644ccc628fdaa5484a2c9c3253d5804fdf17fa1c3799002e0a4853723ab8008e5064f66f4dbe2d9ca9ed2130c8f7e6029e8a71862bbd7ea8768d314594582062dd0db29a0bc877b648ead269bec30a7461ce66c8aad270be1b03f10fadd35a3d14cd860257c3fdf" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_thin.json b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_thin.json new file mode 100644 index 0000000..c210f1f --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_thin - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "proof_r": "d3d7c7f4479e345739df433e76b32efb8e57fc08f3c0b8f27bb127949eb11964", + "proof_s": "63968b87f066889851b08e73fe19089660b49b6bbde1a10e61f25f8cebffca06" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "proof_r": "5b6eab9bfbcbe68b6f9ec329db3027e71b3d441ddd2ea0079d670dc4219d4ebe", + "proof_s": "99760e714a7a1beaebeffd885a6375f86b0391af72cb8619f9fa020ee37b490e" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "proof_r": "f9161743cd3d7438d6a16397bb5f74a90a8f2d5525b4cd9c5b7b5f698e03f40f", + "proof_s": "8be0b86c7c3f2acc0389aa47e65c902cb19e5a1fb02d080c99b4d35a9ae2bb14" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "proof_r": "cac0aad60611fa74b6c71f6635dbede529b87efd19245eff27c3876cfabe5333", + "proof_s": "7d7fcd913d28caa36fd671602d7431653cc6c1afa7fe112e7fe51f375e52fe11" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_r": "285c435d928cb3548a44ebf5f527da30f97763b12c059af365aa75d070a12542", + "proof_s": "64ab2cb7f93365d9c4de43a883d5eaa9ba721946c33b88e4c3ab7e80f7a0d216" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_r": "c03ea2f0015e8ad3786ea2c88313ab6ff93e8df2f4469e73b020e77f39c16e31", + "proof_s": "c2c9d7c57d47d7bf2d4ac101396536d7d7e37cbde6a74f6e985ad1f85a1ae20a" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "proof_r": "f39ac083d7a350a7848bf9688c9be284ec83bd77cb6c28f4c0ed1209019bb630", + "proof_s": "e40c7763383e099af6bfceb6d5c6f2cca00444186ec661a170d1070d4654e711" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_tiny.json b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_tiny.json new file mode 100644 index 0000000..c56f335 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_shake128_ell2_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "proof_c": "bd81d36b18801d85c80c98eef57ea639", + "proof_s": "33a609847861279f528b4cad7269f1313bf39d6b745f544811885fa5e2841101" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "proof_c": "64cdcb750008ee9922a0548c4e5a314e", + "proof_s": "6e5011c2cefd5eb995864ef2c04ed0923a04da14bd70d5a071e81a373f8a8b03" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "proof_c": "8b77b290283fc21c44d2d8c9b0a2392b", + "proof_s": "a7bc8fc05b2a18274b9e0b9108dcf4f1cd04f21c394a69cf9c6d058b011c7c10" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "proof_c": "caa2cbb33d1bc6b2c06b56e0a26f1e30", + "proof_s": "16664b84cafbbf50d49c3ea06e6005395d94b83d305b7236187786a561260f07" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_c": "6c1f153773b04d04ea270b3760d9ec98", + "proof_s": "678561187a92cb8060f816e8ce798501729aa085fb75f3011be14d5bb9b88f0f" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_c": "2f1b673690396e20805066dc5de26163", + "proof_s": "20a2864ac68a085430d967870c2c1ed62efd2f7c51e1096848f1b44b3c1d7511" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "proof_c": "46d06d9a752a4b6b0fa53ecaf8472f63", + "proof_s": "806b5504c2823d60c717ba153e48e70e18ca1d772ad74ed71df2fba5532ebd1a" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_pedersen.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_pedersen.json new file mode 100644 index 0000000..cc0fbce --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "blinding": "101ce60d21f5c6f590ddef98a4bf7f476baca00564542ac41838459e1bd3b816", + "proof_pk_com": "4b3349f30ffd34496951344d9c312dc2f9d656115c258ddcb74fdf84594c243900", + "proof_r": "802c8905555241465e7b7a9d4888373ee6e01fac23528b429cdef9bc12f3c93000", + "proof_ok": "b028f7c1966914ae3c9439c43a60d8b09278526a8e3ef5bca46217d55791fe4a80", + "proof_s": "a482ef87b37ece01ae3acd22a4074db24243db8ede369a0d926656f8f95c2b04", + "proof_sb": "d7db22d81aa9f66a9bc5554c48d972de3dc162e50f42376acbbb7ad7209f4016" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "blinding": "3a2cad30984bd4f7f87867a26925361dcd66317bd503298e3abed37774aad108", + "proof_pk_com": "a85601fa16306e57062208f6955390e2a0b046994274786d3c2bea6abde4380400", + "proof_r": "00d5981b5b24ec1bee09563d904304f111bdaf4eeda9144b0aca8ecfe0a7430380", + "proof_ok": "33cd57b070427bdadebd115fb04f2c5dd6b7985eb7fcc2744d744d28772c831100", + "proof_s": "e28ed027aa4f90b503a741b8bb3d92be9aabd38b2688566603a8cb5ff7ba4113", + "proof_sb": "a1b3aca030b593e5c5097566d584ecaeeb9f72a5dc010deae18e66f866b27204" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "blinding": "f06965d9912d89aa26b24de126d9d72519308ce4b458f58d0f27d55b59ff8c0e", + "proof_pk_com": "1b3834e85c6c1edb035b73d1c62fa8219a6e6b39fbf7619b69e261f02bc93d5080", + "proof_r": "39c2ba376b065b8fd89fc4d89bdd8b52073f5f4fc35fe8f67ee83d73d1f7a34200", + "proof_ok": "eec00f5462c3acc58cdc4210e770b12830a3a29b390fad3edbeb6ec6af3b373e80", + "proof_s": "4b7bf40c6514edfa8b15dba03bf2f96e4f8b32ea56faf69e9f10e4ff01bcac06", + "proof_sb": "79bbfda713a59183dddbf4744cc67e3f9c3e4aaf8fdb7a34c7a17d818e37cb03" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "blinding": "d197c4d1733d7fd97afb6d8e9d18d61b470e2818a52124aee01e8196a3770615", + "proof_pk_com": "fd70ec5877d0446e5cc9364dad6c2d2d4172b039226ccc74f98a208b8cc78d2800", + "proof_r": "ac535a8c53c7e33fd72116b9c64cce71158e6f9cf15bfe9c574c4a572a04ff4480", + "proof_ok": "9c1555853acb686809d7827afebf6de9c5c98312436410afc272ebb868d8aa5600", + "proof_s": "41a987008d013ebd10030ca5994d85cd8bd957f2940c9c3d015342315b3d4700", + "proof_sb": "573aeaea8072de85eef603a0c44c7f07ec212086cbb91b672e2456fe4a218809" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "0cf83dce52d08b7798eb9144d912b7b31204fe92896af3e792236be115f76919", + "proof_pk_com": "ac3b3a47430e1a5cbe997df2951e5fe13fed96d613522b9ba385b256191ada5400", + "proof_r": "e53915bcfc3aaf3ffde07b0e9d7fbf7d1d7f34bffb8ab82cb827028c596cc70700", + "proof_ok": "6876624d12db6c30c864fdbacb718ec0e6385108b11539f16bf2d3456316d94500", + "proof_s": "a1b971d14062080f7a5ead1ef8a19877823447faa9fdf34141fd9eac6c10531c", + "proof_sb": "53fd9abae2688ddd003f40a79229b4a24790dd02245e0111e676b39ea5424713" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "20537ef560d5a8d7483dec55e0b680797376e61d0844bf6971135cd5811ee203", + "proof_pk_com": "1db1d10f797d6208fa724359100b6297c74a98a3f35626ad8dedf891d362ac5780", + "proof_r": "2d6643ffbdc2d4b11024be73349dda34fb1922c7d67a617471bf05ce93f29c1e00", + "proof_ok": "db649772438941f07e1cb4d92ab387aaae22fcb0389d32668a54385a0bd8461580", + "proof_s": "505e46a525c967e840254400b59e75fb40ea21d3c132bcc5032df23bf3dde514", + "proof_sb": "f71544be7ce3ce1a4473d30070c427f84be2f80c080a008c1a4280e40754ff03" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "blinding": "2cff0f18f6fa445491223aeff1e117c6180a555aa23dd710f1d785c24abf870a", + "proof_pk_com": "caa63e87aed8ca3880a2d63d97dfb9f47c8add13dbf5fc2c80cf410d7c66ed1500", + "proof_r": "ac7737523563fc44092829f1d20a02c50e02380cefc6c871fd32337bf62da15c80", + "proof_ok": "e8a1911928b58371fe29722b83ac3721bb74e85a5a85f549edcaac082293373780", + "proof_s": "0a228726dd7e3350d84d7b6d6e5f8a0b91cf6860d91157f1612df05a2a8f3c0c", + "proof_sb": "87e7717f25017e1855e30decb2bf83029e8a56fa166872ee9e408a581cb61b0a" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_ring.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_ring.json new file mode 100644 index 0000000..790e0cf --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_ring.json @@ -0,0 +1,135 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "blinding": "101ce60d21f5c6f590ddef98a4bf7f476baca00564542ac41838459e1bd3b816", + "proof_pk_com": "4b3349f30ffd34496951344d9c312dc2f9d656115c258ddcb74fdf84594c243900", + "proof_r": "802c8905555241465e7b7a9d4888373ee6e01fac23528b429cdef9bc12f3c93000", + "proof_ok": "b028f7c1966914ae3c9439c43a60d8b09278526a8e3ef5bca46217d55791fe4a80", + "proof_s": "a482ef87b37ece01ae3acd22a4074db24243db8ede369a0d926656f8f95c2b04", + "proof_sb": "d7db22d81aa9f66a9bc5554c48d972de3dc162e50f42376acbbb7ad7209f4016", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad860980ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "82ec7c0c76dd39b37b85002cfa00056fcbf9a9a54890c8f1103c939c5eea01bc1c21784256c99c87ebe853bee37ccd6696807828f1fb40ad5b211ef6b8e124eebccd1898f9c1c586737a96754dec8dcfa2c092e33a7d5878065c681d7a69dcdb92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b15de96be694ed3587bca8dc9c414e16bd26a96aa797051187d70dffd004740cc874a5ddaf0c3a80bf24a51b68a90fef9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8ba010b8fcc764fc8613b1db8d2853b9a0e69a20df78949de07ccc2ad2770f14cc314b5837fd1b5cdb5b52b8fbe0e92aa5d4c8752e24929871c003937a6c43d403ba4e9b0c9c569a0a0c1a7402130d5842ce9eb49f578a3911c899b8e16fb7455c60bcaa69ef629730be1945f95ab2479661b14b00d54fa63e030f0cf9e17e07df3b9a6c5e6aebbe823efa36410ef6f53578620244eaf4b3790521362131835e248e86bc2fa12be36a40a280c30fe14bd950021f49b313f571ff54568f07c54dad119c84e98dfc07afefffcc7f7bee87e78eb998386afc3ad4bf73a0bc81f01a2038031be50e260411406e3b40563e71d78981eb573aee87c808662bd6d8b02bec33309ca8b975cde667ce3153e7c8b4ec7aaae30ff4feeb2ae0804489df446711c79a33463d42b4cbeb14abc977e1c9204e77aa7028d953a6cedccf0715cd0e92906b69816feb41b0cd9083bc880ca7ec855ffd924fe46241b438150e62a107523c1f095c1a0fcee9387dcdcc251ba0153ef022f7c30e28eb037e1808ff514c872070be6cf6120055d8b551321036658a6a6be3db9a8040fd914490b7ac00a0a7e486638835f0ce3965f32ecf8ae88b2ebd392ec9429defbbe77798e2bb588aaf9b747ca8f670e41ea70db736fa49ca5e6cffc3d51a4770172189277a8a105a7a50d8f55cfc46b268e32ca4fdd23275" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "blinding": "3a2cad30984bd4f7f87867a26925361dcd66317bd503298e3abed37774aad108", + "proof_pk_com": "a85601fa16306e57062208f6955390e2a0b046994274786d3c2bea6abde4380400", + "proof_r": "00d5981b5b24ec1bee09563d904304f111bdaf4eeda9144b0aca8ecfe0a7430380", + "proof_ok": "33cd57b070427bdadebd115fb04f2c5dd6b7985eb7fcc2744d744d28772c831100", + "proof_s": "e28ed027aa4f90b503a741b8bb3d92be9aabd38b2688566603a8cb5ff7ba4113", + "proof_sb": "a1b3aca030b593e5c5097566d584ecaeeb9f72a5dc010deae18e66f866b27204", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad8609807b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "8f3123b2261e4a09508ff8ee33256cf243d6db0451c6ef90ddf82b4470092214626f9564968507e9778313110ae58c7390cb472265c709870ef0c819ba9e24d27ec037a37d6dfec4f9a2ea36bdf335f758549096f65406d3442c8673bb95c0c292e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8a4a091855e35f3661319ec351cc6f1048bb151bd3cc3ee9fc49d063653a01cbc95b165db39695408652ec4e904657d99107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddabb53ef3435264067b5e5252ad348a2c3232a171c4a4c2730b5fac0cb66786251e953e41fd22c7c56b2e9dc69a524d008bb3d5f011a22839518ff2ed7593951b651cc0b65f4cb12903e4dbb27253d893c81fef82bce7bd130b48e2b30b32828fbbee9a4423e229da282d97ca3d99e4c611500c3c6195bceb83adaa558b61ee0a1265dda98fdcd568aeb69529e686f36a649cb7cc13e8974eb3bfdfd9f4b56d086290eb4628ab4deaf50bbfb1e5a4d685c97fd68c7acb4d6c0ea074dd57122a4c06bef45b13f97fa8ec7893696c000129844d31f220be2d6ec1ea2e2e8482514a3af99357d7012bf668617e9cb6a32e4e56081ab46f6abe2c523f72842eb7103d278e8d617aed40de9db6083ed3f8539e7ecd7373cef0efbc82d01afb34a844278080b63b0e4275b70b82a524c45710a0c691d6b5147f516e279f4d7394e56d55b84db600e789cf473720f501faee2f00d06eee7dc9caf49a4884e41a6285b534c79e54b7668cc1eeb1147e5035836108eee02524669b4738f5e406b797e7713eb6d994dfee6e3bc08ebc6d7d8f83d06e97d8903ab4bd5333bb6b7814879d635554207c9c6bdfa7e20ce0068705154b6cf6530562e12da160e164c396a766222f826de67f43114b1d59d6c48a5753aa30e54ce01d3794660829a0810a924ac7c8ef6973d77c5afdbc3f48291886220469" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "blinding": "f06965d9912d89aa26b24de126d9d72519308ce4b458f58d0f27d55b59ff8c0e", + "proof_pk_com": "1b3834e85c6c1edb035b73d1c62fa8219a6e6b39fbf7619b69e261f02bc93d5080", + "proof_r": "39c2ba376b065b8fd89fc4d89bdd8b52073f5f4fc35fe8f67ee83d73d1f7a34200", + "proof_ok": "eec00f5462c3acc58cdc4210e770b12830a3a29b390fad3edbeb6ec6af3b373e80", + "proof_s": "4b7bf40c6514edfa8b15dba03bf2f96e4f8b32ea56faf69e9f10e4ff01bcac06", + "proof_sb": "79bbfda713a59183dddbf4744cc67e3f9c3e4aaf8fdb7a34c7a17d818e37cb03", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad8609805f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "a09e4172efad8849b48b4040d514438735521b53d9b6201a87448f45b71bdbbc9af4b36f0ad3d81f7ee4f8e6bf4afc948ea71fb87ff63cafbfeef864d7a314b71d2633bf092aa6d28a16df81b98a013b627f27499d80608409176df25a5014e192e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "af22e9bf883f46fd330815ae1b735054c4cd1b6b910bef436a66812038de2436198a52f0f8b89e66bcd6c0554d903c389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd95cd797a54156f5e2ed94d66f8785c0c7c01688337fef8b42d11695904c96dec1024df34b9e04703888101939f85f56b8e7c357f6bb524b91106f8e27db467eba6811ff8d52651c778aa545fa166ee3b9c6fdc5d147503afb3cf432a6fe81db08f13b6e8b5fc03ac16876fc638b7d7fe9cfa575728da16105cb4df239ee0a20fd3be4b59abcc9a74ecb0dcd99b30d97f48ce4f38983c513ecfb7bc2eda62b965b4a67036d0ea0d2c1e82f2f9a461f3ea2c9f4c9ae68043fc7e0eb6b72aec980c48e440472cb07ada0de61718e0c75d2612745125831c0b28d5718376dbcd4f6bb6381fcabb6b0705055db5111e665e91dc1f3b91bda4e2a230046efa9f3c286f1a5a3221bf80f0ac83fe5b5e3e9c6e087e2875e185096ddece39d9ccdaefee5172683871783453c25fb994359ffea2ee1f993d822b5c4f0c5291dc627f55cd0bb1e1b6a200e296a0264ee391c9559b2f748f8a321025ab231aa195ed1b0e15c44085b3558ab68d08f4f86c2e37a911da76f4bf09390ce1a17d6c399d0cbebac08da37a89b6925810204e3f3aa80a2b52b5c493fdabc4372e463381b4ce8a7df9ec85e9d9f116202699ec410d80a95978de1bbf2a506a876908401e867727b61898e6b475a7f174fc3fe7baf04d1dac31d125f9baffc6a5b9884265487d67a27749c3dc44915a1f61fe983fd94bc6c7bb" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "blinding": "d197c4d1733d7fd97afb6d8e9d18d61b470e2818a52124aee01e8196a3770615", + "proof_pk_com": "fd70ec5877d0446e5cc9364dad6c2d2d4172b039226ccc74f98a208b8cc78d2800", + "proof_r": "ac535a8c53c7e33fd72116b9c64cce71158e6f9cf15bfe9c574c4a572a04ff4480", + "proof_ok": "9c1555853acb686809d7827afebf6de9c5c98312436410afc272ebb868d8aa5600", + "proof_s": "41a987008d013ebd10030ca5994d85cd8bd957f2940c9c3d015342315b3d4700", + "proof_sb": "573aeaea8072de85eef603a0c44c7f07ec212086cbb91b672e2456fe4a218809", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad8609805a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "a1d78bc514ff88ebe235a580ce406f482aa98ef3efde2055c41a1cf7e7ea7860fe2c2ac9bb263e79370db0eb899937ff9304664aa1bd3a2ce06fb1683068481a28ccfbb77250eb80c959c9d0d6f477f7c403f54f168f972ebd6c805960ee34a492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8e9177efdb113ef2d96a56fb65e57ddf8a520150b227a649f0b7f5bf754b0c9f63709f8f6f3b7c35a9f658c2aaa6350d9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb5efc1fc797b375945197f9e2e75afbcfa3457611f8da81900461332000fd7dde8ddf42b9bc493256926e390b2f2a8aaa9ed78d7c9ac6a5d8241b92a9f34b84fc1e315806ca43cb95066125837b89eadb18e695405442848075cd717f742e0567cd55f899aa3a072896f2e52d43a1eabeb0df1275e20765bcbc907b5caac1c21525c0a3f988783ab98d71e7ddd90a373fac54099b3ecbd428607c5c52c06631b811bfab93d0f1226633f5653b850a6e3cd4362a4f8a0f28ca7c8613ff48c2508767b267ad55e1dc6d2ffbf84960e3208edbf5cbb2a7db0e247cf576b048c6305130d1e7608bf2b18d6772687ce1f627f6b63259fec4af02b13cc310cab85c7148f3b151e4d277c21e39e9160222f1f00e6479304bdf5e01506b87249b5117253b1f57dd305687e24a29ea297dbd2e99b17ca09da03b7e5d82480939017a85c4cad332cfeaa5be72e118544d8f73301e98ec5fdc49019462d8443e618cbb33e1dea624c5b741b860cddf16efd7224b4d54ad323d0b129e7ce28ba92655acec84d624dc070fdc635b27f7614d225e2116688933f83ff43256a307eeaac719f4f6bc463fee9a007184aa37c59da0bc16a4d315f8f87aefe94cc078636d39bbe16e69162a0fc8cfe086cc61a92cfa8622d7c4c3cfd640fbe4285d005bec6db4089036035b89e6727348b2306ae2b4cd3d822" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "0cf83dce52d08b7798eb9144d912b7b31204fe92896af3e792236be115f76919", + "proof_pk_com": "ac3b3a47430e1a5cbe997df2951e5fe13fed96d613522b9ba385b256191ada5400", + "proof_r": "e53915bcfc3aaf3ffde07b0e9d7fbf7d1d7f34bffb8ab82cb827028c596cc70700", + "proof_ok": "6876624d12db6c30c864fdbacb718ec0e6385108b11539f16bf2d3456316d94500", + "proof_s": "a1b971d14062080f7a5ead1ef8a19877823447faa9fdf34141fd9eac6c10531c", + "proof_sb": "53fd9abae2688ddd003f40a79229b4a24790dd02245e0111e676b39ea5424713", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad860980cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "a571e8ef956c80ff323656d6ebd4ee9cf3c3dfbe155d1546310b442fc109e7382918bba95df7afb24190b0442b0ba8e9b0043d90e8e2db8ae575ccd57911176c80ba66034460c27e16254fd796b7d3df00e20ff0a509bf92c4c6f53ebe08f23c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "98d740fbf01f88496783222e510cf94145bd8f8b9aea5fdf2b363add9d0fb6ad7a68ab6abe59a91a6f69708783f671389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda80e66b0614769e66d0693aeae1f81e98bb96a540ece93675e3dce9d5884fd1ee76bccd0402ea6c7d0fd8684fe4f53bbb3e323d5ab16d695eb141ce885cecc2dc595c55422dac72fd4ee68f0042df087696e4b7578096cb22e4cbca6dcb22eb1a2a658e5281e9d1d207cd7f6c6f1a5b05792ed14322c0ec602c0c97a6dc6d12dc42bedebd9a545803d61a8a7ba5bbbb90d20e08dc13ae58413aaa6d803cb076282d8981fa6bc5c1d1119e8ca88562261f5d8d499cef845ff3ce79e33be937b34264023994c699373c7e9c176094a0d8d2c342226de2259a365c345f9d0ec3d3ca6ed14279ecd33b36cadc43c5faf549a60111548f2ac2171256ee7fecb69d15fc0a4c29aec2f93a32420972c746aa0780f2aa6020d5399cf27ba7377905ea252618759fb4b97054c78161b07a365341b0c4434fe2acdc1d59a32a8acbed1c9248bf5e6f9d828a97580378eea4bf16a97293e35ae22d00df6993191a41cb0f64c3691fa59ffc1e8f07772f938d19436c70fde5f7836a5f66d10208128b3f6ec2b403076f4eab2e3cbdcfbeb1a7f62d968b6989fb405cd0e93cd738cb16ce8ae3926f6cff8f5cc07ad8b46aae7bc5dadedaa64c117855d3501af2d89db10b10c0ca1f7ce8182c61f4211b9fc280475cf2c8c6bcdc1c0ff17add695839b5ecc4bcd433e9c0c99b3788da562d9b8937395f1" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "20537ef560d5a8d7483dec55e0b680797376e61d0844bf6971135cd5811ee203", + "proof_pk_com": "1db1d10f797d6208fa724359100b6297c74a98a3f35626ad8dedf891d362ac5780", + "proof_r": "2d6643ffbdc2d4b11024be73349dda34fb1922c7d67a617471bf05ce93f29c1e00", + "proof_ok": "db649772438941f07e1cb4d92ab387aaae22fcb0389d32668a54385a0bd8461580", + "proof_s": "505e46a525c967e840254400b59e75fb40ea21d3c132bcc5032df23bf3dde514", + "proof_sb": "f71544be7ce3ce1a4473d30070c427f84be2f80c080a008c1a4280e40754ff03", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad860980cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "a571e8ef956c80ff323656d6ebd4ee9cf3c3dfbe155d1546310b442fc109e7382918bba95df7afb24190b0442b0ba8e9b0043d90e8e2db8ae575ccd57911176c80ba66034460c27e16254fd796b7d3df00e20ff0a509bf92c4c6f53ebe08f23c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "ae666c81112ae53e1f1c99a407ec99101fc065e1ec45bafc7e1cc158fdc0ae20fbefdeb7d9ed20753f65413eff532fa79107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda8b466de672aae10748e9da7e61ebf22c3bf1bb99f4deb5918aa57b7c302e82a9d2604cddf28eba6f8bdecb5b56c92f7870f54b5dc6d48a661c04fd7b22ade4370b146d9f9190869487064f2c45e55c3c743d85af87d44c6a325d080116ef6d92660429f5cb98b879281826f2592afc566f6490c5ad636d18e133dcacee76d448d4d4b8220463c692d3470bc7e7eceeeebcff9ad5e6032922d2e2a8d8c003a394546983bd299f72a29c722b56cf100e1c7cc89e720d5c348db1ad365c031715fe9acedf9b345d7b1903ae20e4222128f04603d3aab82389fd6cf10549db6b133cff9f1e3da701353d6984dc95afedb4a44820cb854eaecc841b249909096ce006838dc1b0bc3204a59d3dd83f6feed477de577331f1d36407a8b841dea6f331933484e97e7ac99db464e204b48577a7457f7e6bee3c030ba28fbf1354374ad3eaafee6c6b48b4f62f88ba93cc47b578ee3213a3c5dcfe5fddfc3501d5fc017a3326c89adff61f7a1179eac029eab28a761ab5bf78fcdd75d60e2a6d293cd08178c251f2d1e32e2f32171ee9c7c06b10b9851691aff7e829419758cc6d5fcfa909ffac954c53f2a9c39e0edb1a758d9058908d68e23b7c3d28756bd5138128eed99502b3657cd057e5441f354de23cb26366d1e6db848ce5386372c32ca48203a29753575577163f4a014a0ffdec874be" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "blinding": "2cff0f18f6fa445491223aeff1e117c6180a555aa23dd710f1d785c24abf870a", + "proof_pk_com": "caa63e87aed8ca3880a2d63d97dfb9f47c8add13dbf5fc2c80cf410d7c66ed1500", + "proof_r": "ac7737523563fc44092829f1d20a02c50e02380cefc6c871fd32337bf62da15c80", + "proof_ok": "e8a1911928b58371fe29722b83ac3721bb74e85a5a85f549edcaac082293373780", + "proof_s": "0a228726dd7e3350d84d7b6d6e5f8a0b91cf6860d91157f1612df05a2a8f3c0c", + "proof_sb": "87e7717f25017e1855e30decb2bf83029e8a56fa166872ee9e408a581cb61b0a", + "ring_pks": "2a33f0ef115c00a4cc79a18415af4b67ad74d17493dbd8f62112389a300cdb52001f547e91c60502ff20f353bfcf5f163d13ea92a84ed1e26bc3e2538c5661844c000510bf087970c1f4b1971bccfdf7e08d9602b5a0c9409b324e5c75d24aad860980e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00da46d89c4957bf836505c3bc5690ada8a827bbc6fa68781c73f9f4713493282f0025863c6a761ec1891e20097a7079774015685214eecbcb976eef51cc209f0268009b54e3c8b37027dfd7bb0f223d2523037775b7610214d8ca18b638755a759d5d0050a0ccb4e59c65b4972c806e850f53ae4d7c9307852e2b3858e32ed2ea1a0a0980", + "ring_pks_com": "a5a7af3800d5c1919ccf4a8824c99534ec21397e976c2181172a4a834d1fed9b2815eb6380b4a5085e032bc83474aee198359675974dd4d9cde6b4c8ac1e4e28750625b3f81a9bd419cd0c6a3ecfe72bdbefaffe2638cca7d0fb9dad25e7e52a92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "a199113fa3b5e6bf315f600fe31fd8b9f8aff50d9dbd878e277b3d8f443689fdbebc216e7404e8eecb4b6b2e0487bc219107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd90490cfb5cf225366d8bbaa6520149d47dd3f036a1ebfe01c6d379c96055f4fe23e2b615973a24b7002d57885726b8aba2925f7ec404a7c3321e7cd5e9f1670f8fd3a055f314796ae34146d4db5180f3c9caa95d926203194ac81c6642a81c4943d53a95249952148073ad28303fe0347d9454fa6718524ce568987439af2b45b920fcd0458611ce2926d53be6dee1ab82b38e68b07e30a84be9b8b6a5151024e6887bcec76855b922a540efda026b3ffc19126287bce885343700c43d30780b81c25d75654bd7a67af67f733df137bf6a04d94dc8f795a6a8d0cfba54c8240711307ad6af9fa66395a56e0a3f42825a934b3927d49e5ee4116ae76fe2ea3970403b2bdf89b7a6e6cfa98e1b019259f896c3ac4e62c5b12eea16c3643fbd117048623ecc75b839186abfe0ccbbeb4c89418ab9d0892ec143e1aaba5ede668463849510968f1249929205d68c8151ffd4ebc88fa4d77bad2532611528f3524b10ff3712d132dc35d5aa3dc6f22a78400d2840a3e49ffb065f5f24f50d2d2a00e71e5d91329254e52defb3989836f3a428a44b597276701465f0b75f4eb37ffe3fd4bd1944fcda169bf34efd991f65ad596c4be0b3a0e3d08462bd721ed2292ffd8d3f126ccb7fe6c98b41c02725350133542823a2b688a049f634e98d05481d996a9af47ef340b324604bf2f2d5bc1d01" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_thin.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_thin.json new file mode 100644 index 0000000..aed6e6f --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "proof_r": "410f2976323fbfa8f205f954be49b79f84de6914a8c11274f850c732a78aa22880", + "proof_s": "2cfa234af5add41ef307472982f1bbf5deeb70fae3852cfb8e461a56d2bbc008" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "proof_r": "430e6c38aac86988bc3e67e11472189d0206cb81dbe813410ea4af50b8afad6a80", + "proof_s": "70729a36aa86d143491c1fc5551d7f1507dc2523a3c7accd05defa525a97e018" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "proof_r": "5b738191f00557dac56032e41ce8aacfd9880b5912a64101811192bea5502b4200", + "proof_s": "b605e8b4c8821300a4bfe99ef02cc329680d5a21912f3f23558f6d3e11618606" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "proof_r": "d63b7748e9d71a5b5f5c37040a198011cde8ca1a62883116351f8b300ab2a73400", + "proof_s": "7a6274731ac669ad52d4f91276358e5f9d7445d7d10f3e00e1c8d5a2b7f40905" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_r": "1101599929d24a0273e7691fa1ed8e918136bcee8f921a6b4a254c526232836080", + "proof_s": "ec729e0af8083b5fd051282db89f0d46ea502427f1a4f2f3ea1f2789e0685102" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_r": "5cc14a294f4e6790962cb625006fa8a6272ff5337cdd17cef4579c141130721d80", + "proof_s": "03b440a7cd00f29aa1a9f029ac223b344f6fd0cd0f3f83f43ef537cbc9a4ab12" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "proof_r": "033aba18a8cb9eff70792334874589b49589af8616d21ec3ba759d9b3af6d26600", + "proof_s": "710f4329e3516a9b777c3d15987802e783d8f2ee33f8306e0bc6d7599d8ff90a" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_tiny.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_tiny.json new file mode 100644 index 0000000..78a22c3 --- /dev/null +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "proof_c": "e9ff8e32c127d5d5c7948d7d61b6fc4b", + "proof_s": "fb2a8c0f65a696a18c2979cc04c228ffadd4b808e4a6a77ed61845eb1762bb13" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "proof_c": "83f8c1a4b92cedf08331f336da2bc72f", + "proof_s": "5dcc5b1e9cd455ff5554bde5b4662feb4c5ea605f326eb2587d210c7b38fd514" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "proof_c": "e2e6ee29ceacf91640fb722367f2772a", + "proof_s": "3f0dccb7b75c486413ddde92c39cd5f8180843a124e7864c19f69438bdafae09" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "proof_c": "86fad36b9433fd11ed17618578f29002", + "proof_s": "9b9c235f3b735e05df061b571a7341e0423e9953baa83a5841f29982df6d6908" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "48bda9ade99f2795e982ee006b9b4bf8", + "proof_s": "58e5b18ce4b907efe1dd0b4a08da77b3bf2008409316dd3cd96fb753e7e0370d" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "9d2539f65e96ab4879563cf0187e6de5", + "proof_s": "60d99174ebe75354588fc11dbe37a9af5cfb35e8a05cf29dde47ba71e0c3df04" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "proof_c": "eaec9c0a51b266729e2d095499f79666", + "proof_s": "a022d1eccb90af7f08e68893b38c6dbb54318ea2c8a0dfdff76805a68298bf0f" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ietf.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ietf.json index 83fb90a..78a22c3 100644 --- a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ietf.json +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "fd72a90d1eeba6733824e76bb31991b8108d6562756b85f244333e3c7205225200", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", "alpha": "", - "salt": "", "ad": "", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "8bf194739010d89b8557bd03cd4b4e8196034d036ae90804bd6cd29e3c3d066000", - "beta": "a2100f37b8970e5908ef9b75dd28cc99a2155e1a5f0f5502eadf808dd0efe300df84402a976682b29dca5356846fe6fc93924877d802adeba148bd5c0aeca445", - "proof_c": "d3148c20dce454d99b11f59095d58a5fa45af94957f51177d5934ae1df12ce05", - "proof_s": "5598a73c1298f425177eb560fa3cdccf80c54257c232164e07dac45c32df6003" + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "proof_c": "e9ff8e32c127d5d5c7948d7d61b6fc4b", + "proof_s": "fb2a8c0f65a696a18c2979cc04c228ffadd4b808e4a6a77ed61845eb1762bb13" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "e30eae606d21dff460cdaecfc9bfcd2e319628ccc0242f3ca21f2d5c940ba41680", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", "alpha": "0a", - "salt": "", "ad": "", - "h": "4238eeb9ea60bbd22d149b155d734660d1198ab7f00635abc7538b23bc17144e80", - "gamma": "1cad4ef9a7079c17504a3b91431dd8cb338f361f3bdbe7afaf9d4425448b822d80", - "beta": "00b8fe7ec85b7a6ce40423a025dd2be167251a6a5b59b6d621be5dafc7792891692b6b8de2c6dab85833fb513eb00b62ccef81d903ef331001ee12c48b92ed55", - "proof_c": "970ffb1b0f5cc29c444c1fec1b60f9928f1813987a485c0d6a08aff2998c1201", - "proof_s": "d35b8fb512fd5d2964c7f60d73127790f17f538f480c717b2ce93cd8c7b0cf11" + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "proof_c": "83f8c1a4b92cedf08331f336da2bc72f", + "proof_s": "5dcc5b1e9cd455ff5554bde5b4662feb4c5ea605f326eb2587d210c7b38fd514" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "2a00e5a32e2f097858a1a4a73cf5c2fb4e6d375a4ea4cc3ae3e91660eade850c80", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "4bd4b20f72a6ac4074dab2da20bbcb518da52ce10abcd98552c192b9ebf3012b80", - "beta": "157ba81d5f3fc0732d4638919ffe4a66257b6b9701bc8ce39095c25bf819dd42f2fcd504b3bf04262c8d8b7b22c6c5bcec1e482a5092356cd54c1055622e4332", - "proof_c": "969d905ed35dd38c1b6c760c5b6de535e48277323c7c6f1233abba64550dd412", - "proof_s": "aad3e78c8a6b5eeb5daf6bb4656988e6d37f94a8bad9f7860eacafe914ddc801" + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "proof_c": "e2e6ee29ceacf91640fb722367f2772a", + "proof_s": "3f0dccb7b75c486413ddde92c39cd5f8180843a124e7864c19f69438bdafae09" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "4f29d79a27b9545d7223431eb6a63776949454b16e2ac0b7a959304ce3e52b6a00", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "f4f63b975b725bc090b7d741597be3d03fea7e4cba3f6a95a177fd63bdc2060280", - "gamma": "537086b351db10424ee65f6e30c3b35ef8207ec050e48eefc0113fb6df56552880", - "beta": "332892f4150b85ef1da2766d0461c8ac391f5f1fc03e888a8222c567a8f19be3b090e852d3cb25a51788c1e502cfe7944def39d6253bab9604a2b6817caef023", - "proof_c": "bc9a0efa4c36da697f1b22085441443d7b1c913b97ca435727566e559a51ac17", - "proof_s": "ad2e2a03a59302ff93e2e5da4f23e0bc4b930fb886a990a026a16e50a097df10" + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "proof_c": "86fad36b9433fd11ed17618578f29002", + "proof_s": "9b9c235f3b735e05df061b571a7341e0423e9953baa83a5841f29982df6d6908" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "proof_c": "f6cadcdbd2f6d3a971719154ec1bf06b700df71667377e420830343dc7b5a50e", - "proof_s": "c50ba9c3fcce329970c73fad17d4fce0d81f44838bedafb9f7406ee4f712de1b" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "48bda9ade99f2795e982ee006b9b4bf8", + "proof_s": "58e5b18ce4b907efe1dd0b4a08da77b3bf2008409316dd3cd96fb753e7e0370d" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "proof_c": "65870dfdc489ce27a92afb455d23e9b43e1300811d1dad9f55c6def39906461a", - "proof_s": "42cc993aedb2f7f0de1f769759ff5905763c8c73dd841b4ba6c67ca15bb0fb09" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "9d2539f65e96ab4879563cf0187e6de5", + "proof_s": "60d99174ebe75354588fc11dbe37a9af5cfb35e8a05cf29dde47ba71e0c3df04" }, { - "comment": "bandersnatch_sw_sha-512_tai_ietf - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "7cba529a807bf602c84625fff28f4d1d836cf40a83f42d95c412f7b62ed9192900", + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "6896518ab25de4c4985ee693210a9d2cf1cef60cc792f5e0eef408c3db11b02000", - "beta": "dd0ba3c69fc9842563a7b84bfca12a5c403b1771bc660e8d0012897f1dac2f8e72e501b861ead231372ea6ab2723f47f9997ac00b215c80e383b5482c579060d", - "proof_c": "721e0ba6fcc4cded30efaa9e1bcc65fd2589505308eb526e1ecdbe1411543510", - "proof_s": "5b82bdfddf3fe1d704f8c788a43cbbeb1a86da36d0bf1d1e304831409e53a317" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "proof_c": "eaec9c0a51b266729e2d095499f79666", + "proof_s": "a022d1eccb90af7f08e68893b38c6dbb54318ea2c8a0dfdff76805a68298bf0f" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_pedersen.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_pedersen.json index 1cafc2f..cc0fbce 100644 --- a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_pedersen.json +++ b/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "fd72a90d1eeba6733824e76bb31991b8108d6562756b85f244333e3c7205225200", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", "alpha": "", - "salt": "", "ad": "", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "8bf194739010d89b8557bd03cd4b4e8196034d036ae90804bd6cd29e3c3d066000", - "beta": "a2100f37b8970e5908ef9b75dd28cc99a2155e1a5f0f5502eadf808dd0efe300df84402a976682b29dca5356846fe6fc93924877d802adeba148bd5c0aeca445", - "blinding": "55f71122f86a3e731bb762514308811c3f590d931428df71cb3b37097025fa06", - "proof_pk_com": "99eed57ceff5b7afe0636479c35f41bc137f8afc4c04e17df934a71cee4d5c0200", - "proof_r": "58e41eb2860893c90c258121c16d3de87b886e9d4db5ab1925090b77b597270500", - "proof_ok": "3aef7094d9223853d76322eaea721f9130577a33aaaa5fda1570b8df7509052e00", - "proof_s": "aa7b6008f94e58234e3756daa6f1278636bba73193fd57b0afad2423722e5c01", - "proof_sb": "15b50551d8bcc9ab2acccf46704eb3068fba04d997bdc3f14026be1ad74e9e0b" + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "blinding": "101ce60d21f5c6f590ddef98a4bf7f476baca00564542ac41838459e1bd3b816", + "proof_pk_com": "4b3349f30ffd34496951344d9c312dc2f9d656115c258ddcb74fdf84594c243900", + "proof_r": "802c8905555241465e7b7a9d4888373ee6e01fac23528b429cdef9bc12f3c93000", + "proof_ok": "b028f7c1966914ae3c9439c43a60d8b09278526a8e3ef5bca46217d55791fe4a80", + "proof_s": "a482ef87b37ece01ae3acd22a4074db24243db8ede369a0d926656f8f95c2b04", + "proof_sb": "d7db22d81aa9f66a9bc5554c48d972de3dc162e50f42376acbbb7ad7209f4016" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "e30eae606d21dff460cdaecfc9bfcd2e319628ccc0242f3ca21f2d5c940ba41680", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", "alpha": "0a", - "salt": "", "ad": "", - "h": "4238eeb9ea60bbd22d149b155d734660d1198ab7f00635abc7538b23bc17144e80", - "gamma": "1cad4ef9a7079c17504a3b91431dd8cb338f361f3bdbe7afaf9d4425448b822d80", - "beta": "00b8fe7ec85b7a6ce40423a025dd2be167251a6a5b59b6d621be5dafc7792891692b6b8de2c6dab85833fb513eb00b62ccef81d903ef331001ee12c48b92ed55", - "blinding": "0ce2977115916538f4cf6c75b7c90a9ea76898f0f704e573bdc4834505ef0907", - "proof_pk_com": "a172b7b516845763a6187aa1c838f312c0f9eca9035415413c2e27cf1d9e0f0b00", - "proof_r": "5ac844affc580c63973acffe00657c63275ba22e67319e33a4b272294798974c80", - "proof_ok": "fa262ba3fe3784dc99afdd38ecd24f232eb6e2b08a8d45d15936cab9ff878a6d00", - "proof_s": "8dbb109fd72f19dd17942aa44f28f73db656639106b39b834659e9c495a59b0e", - "proof_sb": "89d3d8cd84eeeb31cc99431135411b17b3e197ecf7f0e4027348211102173001" + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "blinding": "3a2cad30984bd4f7f87867a26925361dcd66317bd503298e3abed37774aad108", + "proof_pk_com": "a85601fa16306e57062208f6955390e2a0b046994274786d3c2bea6abde4380400", + "proof_r": "00d5981b5b24ec1bee09563d904304f111bdaf4eeda9144b0aca8ecfe0a7430380", + "proof_ok": "33cd57b070427bdadebd115fb04f2c5dd6b7985eb7fcc2744d744d28772c831100", + "proof_s": "e28ed027aa4f90b503a741b8bb3d92be9aabd38b2688566603a8cb5ff7ba4113", + "proof_sb": "a1b3aca030b593e5c5097566d584ecaeeb9f72a5dc010deae18e66f866b27204" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "2a00e5a32e2f097858a1a4a73cf5c2fb4e6d375a4ea4cc3ae3e91660eade850c80", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "4bd4b20f72a6ac4074dab2da20bbcb518da52ce10abcd98552c192b9ebf3012b80", - "beta": "157ba81d5f3fc0732d4638919ffe4a66257b6b9701bc8ce39095c25bf819dd42f2fcd504b3bf04262c8d8b7b22c6c5bcec1e482a5092356cd54c1055622e4332", - "blinding": "2b21be394a108c03a0b86142e6460119b8968eb54d763b49ebe0aa35a05d441b", - "proof_pk_com": "6569809f0968219fd0d87ca33df071c8ad6c599fca44565c33c84db0c5b5c80600", - "proof_r": "8770cbd4c1afb90063a50997b8ca6d0a4e2ee99981afd2aa0f4851468f8abb0a80", - "proof_ok": "410f22934442e290d8158d296f6db464d7c5cff5f63f86abed5a0c24b725331400", - "proof_s": "7642d814d5d1ecb8bb2d2f1a4f9c2e91cc3dde968595dc2d2d5f29a77bd93018", - "proof_sb": "c8be64577daf258976e0d93144cd00abdfa5f0d522474bcf71833d64e0b1bf0a" + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "blinding": "f06965d9912d89aa26b24de126d9d72519308ce4b458f58d0f27d55b59ff8c0e", + "proof_pk_com": "1b3834e85c6c1edb035b73d1c62fa8219a6e6b39fbf7619b69e261f02bc93d5080", + "proof_r": "39c2ba376b065b8fd89fc4d89bdd8b52073f5f4fc35fe8f67ee83d73d1f7a34200", + "proof_ok": "eec00f5462c3acc58cdc4210e770b12830a3a29b390fad3edbeb6ec6af3b373e80", + "proof_s": "4b7bf40c6514edfa8b15dba03bf2f96e4f8b32ea56faf69e9f10e4ff01bcac06", + "proof_sb": "79bbfda713a59183dddbf4744cc67e3f9c3e4aaf8fdb7a34c7a17d818e37cb03" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "4f29d79a27b9545d7223431eb6a63776949454b16e2ac0b7a959304ce3e52b6a00", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "f4f63b975b725bc090b7d741597be3d03fea7e4cba3f6a95a177fd63bdc2060280", - "gamma": "537086b351db10424ee65f6e30c3b35ef8207ec050e48eefc0113fb6df56552880", - "beta": "332892f4150b85ef1da2766d0461c8ac391f5f1fc03e888a8222c567a8f19be3b090e852d3cb25a51788c1e502cfe7944def39d6253bab9604a2b6817caef023", - "blinding": "535bf70009cdb7d543a87870e614b10eebfbcf88831235bdb73f715bb4b1ca14", - "proof_pk_com": "abcb05616c7f6646d0320c0347bb6c49aab0e4365991ddb6df7927b1e1d5181080", - "proof_r": "d2e713f18c489c78722637343c34ce07329312940990afeadff20c9d868f505a80", - "proof_ok": "14811d17183dec2e0cd18b0847944502f6b749462bd2d7807284f3f9ec39f46b00", - "proof_s": "3e76548777cd038f793eb5003279c7a145269d272607867c238c4e3e1907430a", - "proof_sb": "4cfbc3d573fe5af54dcfc86906fb551f54a6ecebf1525c79420fc081d8972d1a" + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "blinding": "d197c4d1733d7fd97afb6d8e9d18d61b470e2818a52124aee01e8196a3770615", + "proof_pk_com": "fd70ec5877d0446e5cc9364dad6c2d2d4172b039226ccc74f98a208b8cc78d2800", + "proof_r": "ac535a8c53c7e33fd72116b9c64cce71158e6f9cf15bfe9c574c4a572a04ff4480", + "proof_ok": "9c1555853acb686809d7827afebf6de9c5c98312436410afc272ebb868d8aa5600", + "proof_s": "41a987008d013ebd10030ca5994d85cd8bd957f2940c9c3d015342315b3d4700", + "proof_sb": "573aeaea8072de85eef603a0c44c7f07ec212086cbb91b672e2456fe4a218809" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "blinding": "11019f4bc50575d8fa851b6a507b15cdd9e95a8f68e0dfe87114cf7df8a3751b", - "proof_pk_com": "f19d2e76d3ce1897208d3faec8858032a7aa8ab87e8d58cfebee7db4b0ec864380", - "proof_r": "956de50d716e0a5e36bff0ee4cf37aa4549c18be7604f8cf42ea4cc8782cc37180", - "proof_ok": "df425b123de0be0eceb2f3bb4165d53764cfb1d76089d7983b1caead2b251b3880", - "proof_s": "e469e64ac2dafd4593f6fe9e499632b17fa6b2439ba737f561eb6692b917dc07", - "proof_sb": "2eebd3d61621bf03870754d77ba1b02df1aa586964fe710f4735a2a13a5b071c" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "0cf83dce52d08b7798eb9144d912b7b31204fe92896af3e792236be115f76919", + "proof_pk_com": "ac3b3a47430e1a5cbe997df2951e5fe13fed96d613522b9ba385b256191ada5400", + "proof_r": "e53915bcfc3aaf3ffde07b0e9d7fbf7d1d7f34bffb8ab82cb827028c596cc70700", + "proof_ok": "6876624d12db6c30c864fdbacb718ec0e6385108b11539f16bf2d3456316d94500", + "proof_s": "a1b971d14062080f7a5ead1ef8a19877823447faa9fdf34141fd9eac6c10531c", + "proof_sb": "53fd9abae2688ddd003f40a79229b4a24790dd02245e0111e676b39ea5424713" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "blinding": "bd91ce4bc2029413d70d3cf1a7df866fb63a922060cc57c34a1f565bababef0d", - "proof_pk_com": "ad5e5cee98cfb26b37b652cc066efe2589de155a8cf5f6f2c4b9a190f699b35100", - "proof_r": "a553ea123f99426c5f5cc05d50cd8c9b9b3c28255ed9de443668dfad67f4ac4f00", - "proof_ok": "df425b123de0be0eceb2f3bb4165d53764cfb1d76089d7983b1caead2b251b3880", - "proof_s": "5b4feb8eb8bb0223f7ba7934cb8e3d1a4c76d07e9c2d764c429e9d2060a7d70a", - "proof_sb": "4595f69e8b303755b8ee99051fc9eeb26e2ab9eb302a1aefdaeabbba6917980e" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "20537ef560d5a8d7483dec55e0b680797376e61d0844bf6971135cd5811ee203", + "proof_pk_com": "1db1d10f797d6208fa724359100b6297c74a98a3f35626ad8dedf891d362ac5780", + "proof_r": "2d6643ffbdc2d4b11024be73349dda34fb1922c7d67a617471bf05ce93f29c1e00", + "proof_ok": "db649772438941f07e1cb4d92ab387aaae22fcb0389d32668a54385a0bd8461580", + "proof_s": "505e46a525c967e840254400b59e75fb40ea21d3c132bcc5032df23bf3dde514", + "proof_sb": "f71544be7ce3ce1a4473d30070c427f84be2f80c080a008c1a4280e40754ff03" }, { "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "7cba529a807bf602c84625fff28f4d1d836cf40a83f42d95c412f7b62ed9192900", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "6896518ab25de4c4985ee693210a9d2cf1cef60cc792f5e0eef408c3db11b02000", - "beta": "dd0ba3c69fc9842563a7b84bfca12a5c403b1771bc660e8d0012897f1dac2f8e72e501b861ead231372ea6ab2723f47f9997ac00b215c80e383b5482c579060d", - "blinding": "8df74965c9d0b35a62f3e7a6a6e6cb2f21d8979ff9818d44f7881e2e819d680b", - "proof_pk_com": "1f7623ae31a9738de5ca1f8ccf59ce8e6a5a34767a41bb9d7d0c7894a79e4a0a00", - "proof_r": "1e6e21d4e8064da94db21ad527c44f86fb4f865bbd4368f7f8d09984fcb4472600", - "proof_ok": "65d53e3baacfce0c81fe8cb644f882713659941feb708158bd86e3374fa6144500", - "proof_s": "efba9e4a6c28651252073d4754f3544dba6d3a499e271b532e73995e2afa210e", - "proof_sb": "acecea09cc7ed1e53bce65c5a08cc1174daf7f63b21dc40dd0e2d04c34ff430e" + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "blinding": "2cff0f18f6fa445491223aeff1e117c6180a555aa23dd710f1d785c24abf870a", + "proof_pk_com": "caa63e87aed8ca3880a2d63d97dfb9f47c8add13dbf5fc2c80cf410d7c66ed1500", + "proof_r": "ac7737523563fc44092829f1d20a02c50e02380cefc6c871fd32337bf62da15c80", + "proof_ok": "e8a1911928b58371fe29722b83ac3721bb74e85a5a85f549edcaac082293373780", + "proof_s": "0a228726dd7e3350d84d7b6d6e5f8a0b91cf6860d91157f1612df05a2a8f3c0c", + "proof_sb": "87e7717f25017e1855e30decb2bf83029e8a56fa166872ee9e408a581cb61b0a" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ring.json b/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ring.json deleted file mode 100644 index be1cc8d..0000000 --- a/tests/vectors/ark-vrf/bandersnatch_sw_sha512_tai_ring.json +++ /dev/null @@ -1,142 +0,0 @@ -[ - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-1", - "sk": "3d6406500d4009fdf2604546093665911e753f2213570a29521fd88bc30ede18", - "pk": "fd72a90d1eeba6733824e76bb31991b8108d6562756b85f244333e3c7205225200", - "alpha": "", - "salt": "", - "ad": "", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "8bf194739010d89b8557bd03cd4b4e8196034d036ae90804bd6cd29e3c3d066000", - "beta": "a2100f37b8970e5908ef9b75dd28cc99a2155e1a5f0f5502eadf808dd0efe300df84402a976682b29dca5356846fe6fc93924877d802adeba148bd5c0aeca445", - "blinding": "55f71122f86a3e731bb762514308811c3f590d931428df71cb3b37097025fa06", - "proof_pk_com": "99eed57ceff5b7afe0636479c35f41bc137f8afc4c04e17df934a71cee4d5c0200", - "proof_r": "58e41eb2860893c90c258121c16d3de87b886e9d4db5ab1925090b77b597270500", - "proof_ok": "3aef7094d9223853d76322eaea721f9130577a33aaaa5fda1570b8df7509052e00", - "proof_s": "aa7b6008f94e58234e3756daa6f1278636bba73193fd57b0afad2423722e5c01", - "proof_sb": "15b50551d8bcc9ab2acccf46704eb3068fba04d997bdc3f14026be1ad74e9e0b", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd0795036100fd72a90d1eeba6733824e76bb31991b8108d6562756b85f244333e3c7205225200e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "91a6de4e5efce2e74b530a9af9cf63c3dec80198c2cf46ee3c61394a95ba2e24dd1bb8c40fa77305db6f5acc753ad646b54da89696939f320b47579c10fb372270dcdc2bb1734ed4f5d1140d92a822d0209baf687c5d9ca1133a5a5aeee6fcba92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "a641807aad87d0f6d689e3bcb2f2f6e6b1660e4a95729219a0da69d4a45d173930e7ae3077702e937186f4f1ef33df6e9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd99e17dd62f42645ea38bbb7cd6aea92e9be4633216c9025e7fe501639d12ff07dfe938992d0cd6059e15533ef9e4353e8f27bfbff04f1a0ae2f66eda465d5c3684b00f8908fc1643fb0287668635be6b0806906e225ff746483c0575aa1af0f7c6c021e8e2294431da3e560e75c4e605a7fe6be05f35ba3807a34a6db346350aebb22d69a1848bdd93e3e7e129dc775b7e5c04b9f0eeff6997af1ea557171a1fd64d141d62af291cfc538297974fac67ad3602cf3769f23b7467adde379e1171b6fba892d5d1fd83d7746c3583ac29af8930d3366eb090b0ea9595ae16df311beb389fa29e024252219644eaf6b2ad062d60e4b1b297004f5530ebbc8ba5a869145d85872e3b85d03e894ebf472ca577dd77f101f5e2741bb8e35289415e941cbe5267ff247aabc4ff2f3d6e874f839e4d219dc49a123bfced3245e69ed1ff20ab7caecea9e0251b1d855d0617e6e6bd0fd8025a6cfe1c55e24d8b43e23ce35d09edfb92ffbab2cfad7efc18be1dd593dc749fcf669b13290840acecafc0310e94ef910c638ccc3d301567c82e190073b459cb4901b32217fa3984c0cc3be2c106ff6561ebccbfeb7388f64bc489545d7a2e5cbc0103b3a2ffb7c078e7a3e66e910bbb12e7e43d7b1c10cb6a686e7cdd28189fb8dd3be75f7c257493c9cce22555a6dab8d9458cc82eaa9a628be0a800" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-2", - "sk": "8b9063872331dda4c3c282f7d813fb3c13e7339b7dc9635fdc764e32cc57cb15", - "pk": "e30eae606d21dff460cdaecfc9bfcd2e319628ccc0242f3ca21f2d5c940ba41680", - "alpha": "0a", - "salt": "", - "ad": "", - "h": "4238eeb9ea60bbd22d149b155d734660d1198ab7f00635abc7538b23bc17144e80", - "gamma": "1cad4ef9a7079c17504a3b91431dd8cb338f361f3bdbe7afaf9d4425448b822d80", - "beta": "00b8fe7ec85b7a6ce40423a025dd2be167251a6a5b59b6d621be5dafc7792891692b6b8de2c6dab85833fb513eb00b62ccef81d903ef331001ee12c48b92ed55", - "blinding": "0ce2977115916538f4cf6c75b7c90a9ea76898f0f704e573bdc4834505ef0907", - "proof_pk_com": "a172b7b516845763a6187aa1c838f312c0f9eca9035415413c2e27cf1d9e0f0b00", - "proof_r": "5ac844affc580c63973acffe00657c63275ba22e67319e33a4b272294798974c80", - "proof_ok": "fa262ba3fe3784dc99afdd38ecd24f232eb6e2b08a8d45d15936cab9ff878a6d00", - "proof_s": "8dbb109fd72f19dd17942aa44f28f73db656639106b39b834659e9c495a59b0e", - "proof_sb": "89d3d8cd84eeeb31cc99431135411b17b3e197ecf7f0e4027348211102173001", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd0795036100e30eae606d21dff460cdaecfc9bfcd2e319628ccc0242f3ca21f2d5c940ba41680e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "a6571d71eff73590d962a22568f464b959acc5e2c14dd7608ee9a11ec01ea5b1aada4629331201c93b2dd224b961c800b0544e094725a074285a7dcdcb4ca4eca5d4273ba7aab47499133298e6f3cac6446af3f54105c776a4f2867d523ca98e92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "80c642a1a2bc671af70de73c14edc980acd0332f502eb6923e006210d481a01e69cee1b7575b184400c8b15952d59f169107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8e5759448d2ef00468953e1bc3f11984d84b04ff9b0cc8dd70b6ca2def3f98819beb3b650703f3c16cdb273f0d52cfaf97e49862c8608d68eba051d22034da26a2e1dc1321886b0b739ce7c01d5f33317016324c41ef343f35147e229e93c02cc18878e9edd2dea70c135208b9694ef9a3e822cdb03bbe39ce02f437de5d415198dedb6c7500e1d91a1d687cc9e2468fa38f36c069488620c783c6b884ed730513a36d584e9933340bf2f62c66b14052ef7d63d70f6ef00c04d0d17c0dc6045e9c701c4babdb8b548fdc5ff06a979e8bf2416dd7cb9a8fbaaef79c04912f873e273288e81b3242024ee2752d5e100a37ca7a2fb8015fbaa103267c6e84416106dd1d58a78ce2cb206aeadc5759e74a43b6ea4aed1a52f66b4285cfc94df5f953610a1d1d1f2f6c88c3d0b4c9041e0968ed68ea15b131a0afff1e1ce84fd5e23e8be9444c0f2370c08ac85703eb62883b445ed3be23ac8e5ba716d2c7fdbb4f88bd9091fc191e3a90dcdd3a54075415ede80d376c5fb3bcebd056b8a1e1e20201af7cb25f44c7d62550f10d722289e00ca7ebc316ab2563e88c7592ed7ad5f3d62dc98b9b8976401481c0155fba8b146a080abf852ac5b9abad5e43b57ea04a46a6c717a70aa1799149baee11c907deb533c0eb312d92f59bb6bb6ceadff885aed400d5f116318b71733ffe071aa64cdd" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-3", - "sk": "6db187202f69e627e432296ae1d0f166ae6ac3c1222585b6ceae80ea07670b14", - "pk": "2a00e5a32e2f097858a1a4a73cf5c2fb4e6d375a4ea4cc3ae3e91660eade850c80", - "alpha": "", - "salt": "", - "ad": "0b8c", - "h": "89824ba3aa7eefbb79cd5734a659adca078ed6851e067e2c7f5c378de142dc2600", - "gamma": "4bd4b20f72a6ac4074dab2da20bbcb518da52ce10abcd98552c192b9ebf3012b80", - "beta": "157ba81d5f3fc0732d4638919ffe4a66257b6b9701bc8ce39095c25bf819dd42f2fcd504b3bf04262c8d8b7b22c6c5bcec1e482a5092356cd54c1055622e4332", - "blinding": "2b21be394a108c03a0b86142e6460119b8968eb54d763b49ebe0aa35a05d441b", - "proof_pk_com": "6569809f0968219fd0d87ca33df071c8ad6c599fca44565c33c84db0c5b5c80600", - "proof_r": "8770cbd4c1afb90063a50997b8ca6d0a4e2ee99981afd2aa0f4851468f8abb0a80", - "proof_ok": "410f22934442e290d8158d296f6db464d7c5cff5f63f86abed5a0c24b725331400", - "proof_s": "7642d814d5d1ecb8bb2d2f1a4f9c2e91cc3dde968595dc2d2d5f29a77bd93018", - "proof_sb": "c8be64577daf258976e0d93144cd00abdfa5f0d522474bcf71833d64e0b1bf0a", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd07950361002a00e5a32e2f097858a1a4a73cf5c2fb4e6d375a4ea4cc3ae3e91660eade850c80e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "99b82f3977c171684ad1a7add8b8ead0a3565f58ab16cdbd1774faaf73860f8cc1dbbdf66f7eba767acfceb2aa1e8ccfa2e3a6811006cf2591c15712fc6a56775a0542659f8bdf30889ce115e805f455dc4464da2468a3cbb6e7893dee70b70192e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "96ff1a58557e371bd7da544b5aeedbfa5754827b14e7072ba83de2208d2873720bb50ba97ed57f62c58ccd4c32757ab59107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaa301ddcee581401c09e7368c64a372413b8f78645096bdc799c5aa3f5f78c65526f7f85c2396f7a45baff85cb1f8c28b3f648c4d4af293e91e93fd3106c7903405ca9d044df25d15ec007d57362be5ffc23bde6e46a01df66ae420a450b9477a3c30c5b27d8655e166627f1e9748bbcd9e77900d81369e2f739410d1fd9933b08660218820311bcc39a89ca93f4621e04a78922b463b2dc5b0e96d089ab6072a69982580abf97cea06c0a90e1afbe3ac77724071c301852471408c5c7dc7244e5c05d4ffdaef37d332de059556f1e62d33d72f273dcb79d79fb41a60a74201b1d4c0ee3fe71d2a8e9140d4aefcb1e19aa758862b0d06f9869b2c20f32109d320be4bd16ace522c3d5d25fa2eebc68f4b87b264efecf830c96dd31953cb2a909f67f2f565c5e5d0849edc006ee05c886037afe6a5175d29fc3d0c86c14b68b34a3e92ec7d9199fc39974237d229fc0ce45753fcdd6f3612b6a63abe9ebb83159edcfca5d4f2d639dc9f62a3887f615a2682ba225c641a13bd78b1af963859cc44674b62c0f490c900371f57c44a20926b9f07a7846e7e1a59da461c38c6ddac14a673072bac4a6be135e7a1e30a22aa6da494cc9a786cc6950caf564d12193a5b6caff4863867823e2c4d3b197ea93dc791545ec29daab4fb36699fcc6709ebb982bd9ffe05aa99b0f35bc87c97d3986" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-4", - "sk": "b56cc204f1b6c2323709012cb16c72f3021035ce935fbe69b600a88d842c7407", - "pk": "4f29d79a27b9545d7223431eb6a63776949454b16e2ac0b7a959304ce3e52b6a00", - "alpha": "73616d706c65", - "salt": "", - "ad": "", - "h": "f4f63b975b725bc090b7d741597be3d03fea7e4cba3f6a95a177fd63bdc2060280", - "gamma": "537086b351db10424ee65f6e30c3b35ef8207ec050e48eefc0113fb6df56552880", - "beta": "332892f4150b85ef1da2766d0461c8ac391f5f1fc03e888a8222c567a8f19be3b090e852d3cb25a51788c1e502cfe7944def39d6253bab9604a2b6817caef023", - "blinding": "535bf70009cdb7d543a87870e614b10eebfbcf88831235bdb73f715bb4b1ca14", - "proof_pk_com": "abcb05616c7f6646d0320c0347bb6c49aab0e4365991ddb6df7927b1e1d5181080", - "proof_r": "d2e713f18c489c78722637343c34ce07329312940990afeadff20c9d868f505a80", - "proof_ok": "14811d17183dec2e0cd18b0847944502f6b749462bd2d7807284f3f9ec39f46b00", - "proof_s": "3e76548777cd038f793eb5003279c7a145269d272607867c238c4e3e1907430a", - "proof_sb": "4cfbc3d573fe5af54dcfc86906fb551f54a6ecebf1525c79420fc081d8972d1a", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd07950361004f29d79a27b9545d7223431eb6a63776949454b16e2ac0b7a959304ce3e52b6a00e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "8da8855eb25f855cbae8ead191842f3b118b75894f849ae74d6deaf96927ff2123222dbd9f369e8ade9abaa9b3617c08a68a398a9b3226928a5c51960ec8fb22f2300ab5de81ed5425c6ef23586551ac296179330d13fcdf284a772bed62fd6e92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "8706c5f4c8a67e18dfcdbef13dd19d746c418b98277c3b59aea04e81ea7f5dc8454b09989582ca995d6b287a6c2963279107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda2f8ad2ade6c2cfc85dfb08a50f991cc6ded06d754646eb6f1d639bf11bce14e41f167e3ff1ebdab3a54afc39002e8e588f12525a0fcae4e681ec7da3609d7cc7aa2b65f3ed017b8056d08c08cdc668e80c8be073c50b0c68dee66b283404da55149d12aa61b0483c41adb5188dfcc2cce377d8827a9f413d7b30aea9c60f724266d59fb791f26466f98f334686019f177aa12acc9b204cf516a9447f39f830dc81c04dab1ff342b79bd981a6a72a4ae4bc9f9432f26c024c99dbe4c1578a250fde9a39c128d8300c334fb8e0bd86cea1d54c6960816ce0de0c381497936960b6449052c850e3c37fb880874dd1f18703611be942f275dc1c87078c287e6b9173f78f1c41786167f1e1751b379a806f2f3f273cf61563433ee3562c9187ef438979adeadb891924f4b2ec72dd8ece127f5416e8b419409970c425dec89942128afa6d72532d1102becc1499f6466e86ec34c650efbf9eec2b65bf4ca071403b9cd848f2fcef3d23a1d458bbdaa44ac533d85c923cc2e783d8bda8345c6d1f925c94d3d8b1c670268eb3462399dc46442b92e4b30aa8b0475616fcae1f1d09687dcf512acbf915de17bf4131d04a9d6ab550e9e2b3c300b11b5d332c9ba392d2c80af994e13f96561f360c1f3fd1384cf697b81358a57e2ed4b703972eaff559ca49754db91d74c93acf3223968616fef" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-5", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "blinding": "11019f4bc50575d8fa851b6a507b15cdd9e95a8f68e0dfe87114cf7df8a3751b", - "proof_pk_com": "f19d2e76d3ce1897208d3faec8858032a7aa8ab87e8d58cfebee7db4b0ec864380", - "proof_r": "956de50d716e0a5e36bff0ee4cf37aa4549c18be7604f8cf42ea4cc8782cc37180", - "proof_ok": "df425b123de0be0eceb2f3bb4165d53764cfb1d76089d7983b1caead2b251b3880", - "proof_s": "e469e64ac2dafd4593f6fe9e499632b17fa6b2439ba737f561eb6692b917dc07", - "proof_sb": "2eebd3d61621bf03870754d77ba1b02df1aa586964fe710f4735a2a13a5b071c", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd0795036100e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "846108a631d0a560d5ec8da66b1e8fe393b022a30ebf325458ab948853d6c29a995eec78884fdf63ad9e5471d7f30c5a976aa5dcb25170c504f516e6ac4000246cf417c139aba77339a637c879471c74bbfb870285f80a75b30b7545e1a8945c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "85ce565be999865c61c89eab6c443d7a4f5a424e95dc8bc7fa5219d1124ac8387bf4205bc0cac12683287d8289a230dd9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd88dcd2a591af90735ace338ca8e44486201fec52bd65f7b19b9b50645f5816b0403c38144f83d267be8040d2cc86235fa284b55be2869fcb064184f0eefa6eb155cbca2f81c0d6283bbab7755250454005946d2a76a128187dc93be38ac96ecec4a6209466f5538977271d00da602032ef66a040bd5c22c026dce0e608464f649db2367050ecf218d45a2e05c8888686a40de9fda9eb966c149269fe7b150418c679cd269a5fc9a188438a72f8a9b0be172cc62f5ed75ede9f69bf8f114a62480d517932ff15d28c9c229b50474c862f17c7144b20f5166ac9421a0c79ecbc306128706eeb10791c88e4f5f8500ca9e979e9253e9f6fd13437d32f49235a026d28e3f196da7dcc46ab7f7876d0e7a619acabdf259f0a5b92a60a5727b9f06e49204bdb51e33c2d0b236ed6e16c23faa9dafdab4a5b602dc4270da44179ab124aa82fcb3d4e1f100e1ebd8d47e4313a06ab6f2a22f1b41c53c38d0a39631149898599e73986a402f7c21146ad8a0d80439159d66e508fd075e2ebb019e871fcc429c138131d830a7e6e92bcddfab40d109247c81d4d01016bc68dbf6f6b0c4f3b288885224c85e9aafde07a13ac8f392d5a84a6656d21cf8580beb631a89f070084a46ac0de725ba71adcd447dade37451196d4503d57fc2385394ef8def4076694d61d5e4b356f002c85863e8c759714" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-6", - "sk": "da36359bf1bfd1694d3ed359e7340bd02a6a5e54827d94db1384df29f5bdd302", - "pk": "e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "4af5381d91e64fd514a4efe23acb3beae6a87d549cf12fc7c51d27e63dadb17380", - "beta": "ca0b4e8537f33e28ae8c4ac2b53ce5784e3abe846ad4aeb3fa330a5836e483da3d8903e5f227679e228e6ae2949748b2b7c5cbbec44ea06157f613767ee2c09f", - "blinding": "bd91ce4bc2029413d70d3cf1a7df866fb63a922060cc57c34a1f565bababef0d", - "proof_pk_com": "ad5e5cee98cfb26b37b652cc066efe2589de155a8cf5f6f2c4b9a190f699b35100", - "proof_r": "a553ea123f99426c5f5cc05d50cd8c9b9b3c28255ed9de443668dfad67f4ac4f00", - "proof_ok": "df425b123de0be0eceb2f3bb4165d53764cfb1d76089d7983b1caead2b251b3880", - "proof_s": "5b4feb8eb8bb0223f7ba7934cb8e3d1a4c76d07e9c2d764c429e9d2060a7d70a", - "proof_sb": "4595f69e8b303755b8ee99051fc9eeb26e2ab9eb302a1aefdaeabbba6917980e", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd0795036100e58e8ba2e99035fb7ae11fa14e2a609d6d13679278dac63ebee64ca8612ffa1480e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "846108a631d0a560d5ec8da66b1e8fe393b022a30ebf325458ab948853d6c29a995eec78884fdf63ad9e5471d7f30c5a976aa5dcb25170c504f516e6ac4000246cf417c139aba77339a637c879471c74bbfb870285f80a75b30b7545e1a8945c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "b2ff083e9be8cbb73aa5476ec9fb9c2bea4bd012d92b14bac79755be01c22d09a1819871b64b85c4bf295b774adcf2b09107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd977b663d06152c9858ac3a509dba85177ce7675286b0d651cf74dbe8f1b7c0fc3e01c1b6d82dbf75349d00a5bc50cd918f4a1eef9821b89db91089a22bfafedd00dfee18f9af6c3922be84289150b5799014c630e57596eb4d91486df7c769219e577da523d1ef115ea6ad3e1f181f963d55d68ad88fd669533c51e0f622054fdc021201ee8eaba20c99c3d8b3aa9407020417fa86c00e2484844c9f150da70d343896f9a00dcefecdb8e10c0ad1c269bf85c5ad0599d70dd337dd750fe8ec09c3f908b26c7014b4fb1834b5cd52346cf2ff39a676986ce98dadb68ad231190ec9e86e05ea61d714d86db46ba4edc4ef6eb4a478de095806b3a26c23bcce492e3fb416eecad5af5aa89151496682d4fd2539ac6b2b9041d67e8b7fedff8dd716ee7cc767eabf93afd020a689267c94cfa102f0555fc73ee4ecad6ef0a647af36a6e310332179660d98b51d99681870a331ebcb21a383b2858d149f368de64b438880cd19eec00c1fd68ac40163a6cab782d97f194e66a3b7eedc664822bb9c70120b9859b0d804a8bdf23ed87381590794459173892565665ee4becc8430d2ee2bf1ac907c52e110140895ba7beb94ec28907e2b9b512ff5e99207ee745a7313880e7aa576a5e0b60c93a34d05449aa94ca83981ed93bf64aa7b0e1774e443be273bc6a162f4330e26233176139a2b58" - }, - { - "comment": "bandersnatch_sw_sha-512_tai_ring - vector-7", - "sk": "35b877a25c394512292b82bdf8468e98eaf03c79c7fc9d53546dadc5fb75b500", - "pk": "7cba529a807bf602c84625fff28f4d1d836cf40a83f42d95c412f7b62ed9192900", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "52ae936f6c5d4f4ddf6dbdf337f9925f2b481be3e6a01af1f62e3996f7a2ce0b80", - "gamma": "6896518ab25de4c4985ee693210a9d2cf1cef60cc792f5e0eef408c3db11b02000", - "beta": "dd0ba3c69fc9842563a7b84bfca12a5c403b1771bc660e8d0012897f1dac2f8e72e501b861ead231372ea6ab2723f47f9997ac00b215c80e383b5482c579060d", - "blinding": "8df74965c9d0b35a62f3e7a6a6e6cb2f21d8979ff9818d44f7881e2e819d680b", - "proof_pk_com": "1f7623ae31a9738de5ca1f8ccf59ce8e6a5a34767a41bb9d7d0c7894a79e4a0a00", - "proof_r": "1e6e21d4e8064da94db21ad527c44f86fb4f865bbd4368f7f8d09984fcb4472600", - "proof_ok": "65d53e3baacfce0c81fe8cb644f882713659941feb708158bd86e3374fa6144500", - "proof_s": "efba9e4a6c28651252073d4754f3544dba6d3a499e271b532e73995e2afa210e", - "proof_sb": "acecea09cc7ed1e53bce65c5a08cc1174daf7f63b21dc40dd0e2d04c34ff430e", - "ring_pks": "c49043883ab2b5022e6b6050ab32b1b5fac32d8feac60ff5a2a0f300de3b027380df89abf403256d66b848ed97286997396ae4a36b4efb871cee426db00a31a73100361acf81e906a57a1b015afa7d755957597a187ef5b759f07ed49cfd07950361007cba529a807bf602c84625fff28f4d1d836cf40a83f42d95c412f7b62ed9192900e0d311f75e84c5ab6df5752b52be1d445d21f26b82736f1f7eb95a4ee2e3cd1480780fe2fc4cf2ade0c4a8b209ea790a7359bed1903c623d6c5a3e6fa14ef8cd4b00b2c0747a0e2e633055203066476aafee699e60c91500924c13910997f727670980d8bccfa3a9f11d7da69f44f8a50fae932c24172e1cf1cdf331abdf8777932d0400", - "ring_pks_com": "a2e5263fc874eaad1c8c818243cc65ccb037e4ecac8111222b3c2fec01ee9d26508400e111dc414ac04518a420579f378f38950a3a2855d899f1ccabcc01f5a7fedc9bb6355200cffd06e82ad190a0f919dc073e2132eb96f8daa3e652b1c99492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "9460eb2984e80881222cfdf60501debce963942d1e2ef210f636ee3937037a0bde2e8addf6a99beebfcd0428fc6b473c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda6f7cde7543b823297057ba904020a302f2981c31f0bc2396abe8c56c9102d9893722f1017a73b56219f1ddbedfcb53db1ca0ae4a6f8c432229ab2b2fea819a63f94d661446d14cd5a113396959540fb5e78c88e1b8f5eef40d7f871296afd210a242df9fae161c4652ab90fa5702cab395376d9673247df0978f454894e173e4f9a82ebbc7ab5f9c08b3a3f63d65ef03e5b8a50766c12ebeddbc29e6242411faead66fb45152356ad044ea9419376fb020973c09f85bf59a15823ef813bd068ed4fe7b158cd418b5dc31068ceda015d55ebbae823304c3cb74feae2c32527738d3083dbbf95f90904a602ffaee9f549f041f699d4d31d0cdcf5e3adcb31b217c7dbda3aae1b27a970bcca29eb5cc04cb3faeb8ea56496a43177d9e4fa87386e498a9489f72eb7ea5f78962147644a6b5b39594c3d9f5495befeb9adf33ffe68b84e7a03fcb31249bfe506f72ce4e304da14f4b77400b14a2fa2adf8b7f52b1efe79b901a3fa5e2e2a70715834b271b09008b90d2f633a1aafb0e4565a6263e9b334adef4343093699419c3603612704b851d2f54eabcd6f3d76af1684ecbc590d8292489d37bca938f9a1f46447d733c930c6dda30643add0ac1f668244e7e5b75906aeaa5212befe0e369fdc44ade120ac10988181672dd0bb0427a1dcad2d45ebeb9392ed2ceff2dae4c03b1183cf" - } -] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/ed25519_sha-512_tai_pedersen.json b/tests/vectors/ark-vrf/ed25519_sha-512_tai_pedersen.json new file mode 100644 index 0000000..a1c2a2d --- /dev/null +++ b/tests/vectors/ark-vrf/ed25519_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "ed25519_sha-512_tai_pedersen - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", + "alpha": "", + "ad": "", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "blinding": "4a22498de4c3e17d9ddf99ceafd9752bbe301a6510b5645894117cdb5d72e303", + "proof_pk_com": "22f723db530cf1acfa5f34ea78944c357f308423776c1966f13eda6379d107ca", + "proof_r": "6e6e2e0c237f721d70ed57af4ef530ab5cfe6f37520747cdfe9f4d91db5f4e76", + "proof_ok": "3ea1f7d543a114e7d72517754186da044bfa2b25c3df1b9722f29594a9a3427a", + "proof_s": "e8491718dd89be322bcb56780048d3414cde042b88ae278fae074ce6f2d8750a", + "proof_sb": "93edbc92a00957d0ccb6a209737b5b24a89cd0be74ca95476e9a5745184f8d0f" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", + "alpha": "0a", + "ad": "", + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "blinding": "5d37f7af4f2a830e3da4ffd6435e369c39ac34516b3153cce697a205bed07c00", + "proof_pk_com": "c3bcd67a8d7bbaeaca28b6369df02af694a773f193c2456e10a329f31e8b855f", + "proof_r": "7ba53378c5ba70ac190ddbf40a0acfe9f05b7a22dc8fcd99c0bf40ca7c4cb47c", + "proof_ok": "f912427e0d829d8c31845aaac079b03d6e4c7a4bf46f195b70a9c1508f5a1b5b", + "proof_s": "4bb196ceb832943a136805ca6a4737581e7253f18d7cabf2c6cdee7600ebfc00", + "proof_sb": "07f7f0f937a195a993a182baa296224ad5de5aaf8951c497a89e6f5532b95505" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", + "alpha": "", + "ad": "0b8c", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "blinding": "ba0db55791e6749eb315f31da7cfa060035864b1c986b802c4a05e2fb9751c0a", + "proof_pk_com": "1b3a3614beb7112ed8de6b70b22ac56677735749178afb3ed2cbd8019e913060", + "proof_r": "12f7e7f49003ef40c4bc1d699e0d754c0984272728b95309ee57cea8507bb5de", + "proof_ok": "84a33f30613b37fb2c204d949ccecb89f903e5c4011731d037d45b1616dcb811", + "proof_s": "a5674b91eb102a1ce9c3b75525aed367df75b74350bd6258bd829a6001324306", + "proof_sb": "b0b99c1b55379dc8a30ba01d607ce7f31776d5c286384717a3cd4173ae97b00c" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", + "alpha": "73616d706c65", + "ad": "", + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "blinding": "2212ac769181892e29a5a249e0e613f739695f917d0e014dfdffb6fdf170b50c", + "proof_pk_com": "0282657fb005dd982950eef22ef5f9ed3d13fb224c983758a9501a62459cc0f2", + "proof_r": "5e49ea3612a52a5a142eb3f47fa35080dae7958ff22c8b54fe3c2de462f4f928", + "proof_ok": "d797517b29708c6c804584407fb4a857f6ffea7046df027cee8adf13dd93191c", + "proof_s": "b0b0ce660112d1b6bfa8d9c5768dc6ca3402d1c45246ccab718032a8b67eb609", + "proof_sb": "776d144386b3c0fe91e6263f433ce3f61ec9135b890b6d6fbe1300bc49634d0c" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "031707f37dfe8ef4837a376320d6420e71724c66d0d8391746228d19ef6bc70b", + "proof_pk_com": "7344a2db6d51163b1d8042f11258f230af3adb0722eb228246c9e8951a5b68c2", + "proof_r": "2471ecf11e758e80a8854f9811b323bfe579cbbfba0234d1ff26839c38606d16", + "proof_ok": "80019d3fc955d254a092014358c71cfc57e60fa543ad3f0c5e1526b88c3b33d6", + "proof_s": "cde860d02df38ecc5241a308c0dcb32273a514b74cdec679f48eb6097dfe2000", + "proof_sb": "4e6a6c18bf9ec98eaafb16d958539edb29a924762773818042ab7614c75d2507" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "6f4e473d9391539e5bc21696a434287db0df24f0630de99f22c238f94eb32b04", + "proof_pk_com": "5b3a2e50f9711c889e7b97cebc91c11c1d038cf1f0e59017fe2c40671203982b", + "proof_r": "a8f57c79b2bfc5f2e33739ae2d7100d299b032379b014ce5fe671e3e18c1a0a0", + "proof_ok": "a446fd73943c1138dd2ba4433596287c0dc72e09953f412d0e34a87606b34c80", + "proof_s": "a9d177a177cf3bb87411def3a5fcad728f06f98a910c8b31fd8dca45be455206", + "proof_sb": "27da39875b79eb36e38359d483cba9396f726092567de795020a5fb7c83bde01" + }, + { + "comment": "ed25519_sha-512_tai_pedersen - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "blinding": "dbdd83ee1fff976aebcc282d42398b7d9dd7b94d1e7af4621279f6df69de180c", + "proof_pk_com": "cea64ca9f0ba93036b4f3c397a263d70d888f2c250637d9b9db492dd8a25d85d", + "proof_r": "1e9493b522e564b6eefba836da96f6c3ce17bd2c36d247776e50ee2d66c0a6a1", + "proof_ok": "075ddb875f12b6db5fe2eb75593c1e88d46b1e645e8144651d59763e4844f031", + "proof_s": "323a0d6cea3affaca38718015d313e1901744c592573732a58c4964d1eadb90f", + "proof_sb": "1174ba0758094f616888a442f1208a36f55db9bf869e44404445da277758d005" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/ed25519_sha-512_tai_thin.json b/tests/vectors/ark-vrf/ed25519_sha-512_tai_thin.json new file mode 100644 index 0000000..9b1c520 --- /dev/null +++ b/tests/vectors/ark-vrf/ed25519_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "ed25519_sha-512_tai_thin - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", + "alpha": "", + "ad": "", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "proof_r": "a010ef8b17cef692e4adf9390344dcd8712e5d757d8a90a13aa98bb64940331c", + "proof_s": "0db90352e13f441815b9ad13a9039984e3033c713f74cc508aecf27a68f17400" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", + "alpha": "0a", + "ad": "", + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "proof_r": "be988d69654aba82f451e0a2e92b5952819be26dc9d760cd43aadd074bc89048", + "proof_s": "7f589707c3112a9a8eff9506c45f72a6ece2f77ccce21df3a3751c5e90ddd10f" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", + "alpha": "", + "ad": "0b8c", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "proof_r": "bf51e65697f85ca3730c71c4f35341dc26a9419ca3b958495bb110dca1cf52ae", + "proof_s": "5dd7557a170fdee82e6125c540097a8e86f461037dd13d6e3bdb9b1d3de4ee08" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", + "alpha": "73616d706c65", + "ad": "", + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "proof_r": "51a2ccefe2d4e0ec3e37b6ea914f0531c2cb5c746edd23007c3af0baeb368061", + "proof_s": "425fb6df6637738889ac1508d78d91460a100cb474c9a5b167e29474867a970f" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_r": "90262f7a47c165dfc375c40a34de2b31511f5864bfccf386da6229bf5194dc04", + "proof_s": "85d34aa12673ac6c0b42eb9420b1b2d437d589adecef8c461c055e6212b30c0c" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_r": "fdaf0be7ba509458d79e5a4ee97a91dece316038b3566a11bfdc2da017df8605", + "proof_s": "587840eb4c5685cc93bd6c50dc7ea43010015d37f76242d63acef2805c6adf08" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "proof_r": "47f1e3c256b03929a9649459e17127bcdbaddaf59fd2da16cb14b681a9bfa260", + "proof_s": "8fbd55ac1464578e7d95ae21bb8baa1da484c2e4c82b6bd3fa89c49a1eacd304" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/ed25519_sha-512_tai_tiny.json b/tests/vectors/ark-vrf/ed25519_sha-512_tai_tiny.json new file mode 100644 index 0000000..ed31fdc --- /dev/null +++ b/tests/vectors/ark-vrf/ed25519_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "ed25519_sha-512_tai_tiny - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", + "alpha": "", + "ad": "", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "proof_c": "44444e2aaf9b5e7ad9b3c0758912f996", + "proof_s": "247b1bc06e2c5ecc8d85f5f185b6e0e6e8f5381c1a4fb59b4cfdfb5c63f97907" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", + "alpha": "0a", + "ad": "", + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "proof_c": "a8eea0031ab89afa3840ebd28f1d4526", + "proof_s": "b67d2093dd5aabe794c0c8e5c8a8b2791a44f7714237859cd414db1ba830c202" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", + "alpha": "", + "ad": "0b8c", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "proof_c": "88401f18baf04278e73ca352eec5805e", + "proof_s": "13652f819865e7d994dcc8e226a92653e16fa563fbc250766b51093d5b13c602" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", + "alpha": "73616d706c65", + "ad": "", + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "proof_c": "c30ac26f9961ec5cd94661325db6d803", + "proof_s": "52eb8200c459cedc4ceb13d55df4812f456add6f02b795b6f1424a04f34edb0b" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "c260de688db7869e58a9de51347045da", + "proof_s": "0b4440c53777ba1ef38e53341bc1a9e53af067c799c589e34898cf8f2fbd2c07" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "82d8649618791877c054a7f0613aca57", + "proof_s": "b8915a96ffbf0fb91a960c7ebdc51c9ce9aad0f3fdbca6d660b49b389c389f03" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "proof_c": "bd260e6063fdb6f89edb964196a89879", + "proof_s": "68633c206bbd865be1bc6ce6f5f32c3c0f6bae6d74a95f493a3f4d4485f0c70e" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/ed25519_sha512_tai_ietf.json b/tests/vectors/ark-vrf/ed25519_sha512_tai_ietf.json index b22f22f..ed31fdc 100644 --- a/tests/vectors/ark-vrf/ed25519_sha512_tai_ietf.json +++ b/tests/vectors/ark-vrf/ed25519_sha512_tai_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "ed25519_sha-512_tai_ietf - vector-1", - "sk": "fc4722b3efeadcfa8b437f6e34f71c50a240bd2e66b958c5c17519cc9d936906", - "pk": "bf512469e7f9b766bf5c75e47e84fda3ec887bd24d79b9165c19e01a88fe5ff2", + "comment": "ed25519_sha-512_tai_tiny - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", "alpha": "", - "salt": "", "ad": "", - "h": "cd2988ed28d9873b80e90ccae0f5767af0ff8cb3b8d22f257f217e3736165ac2", - "gamma": "efdbb69c46c055a5a4ab2a0b5961c6c797095844044f1ed71a463c495d4e581a", - "beta": "bf4523b08aa50cc3b98f2fea4251d4ce909ac52746105f1377bc6905909966e0dc65aab98243a2b150bd889d267d6edcf6fc0b0268aa9e0bfb3e1ba8a972330c", - "proof_c": "7005e08500c28ff5c9cf8881c3667a3c", - "proof_s": "f6e5185341262ea10d5d8b2bd8325099e690941c82f8be67d981c3da4ac27a01" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "proof_c": "44444e2aaf9b5e7ad9b3c0758912f996", + "proof_s": "247b1bc06e2c5ecc8d85f5f185b6e0e6e8f5381c1a4fb59b4cfdfb5c63f97907" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-2", - "sk": "3ed3f4d0fadb42cb77d42a7ea57def2bad464bd1ae0c896dd077c04358415c04", - "pk": "a9a4b61b52db164253147e0c5bd20852d7ab4763e996d0117febccbc7a1abbd6", + "comment": "ed25519_sha-512_tai_tiny - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", "alpha": "0a", - "salt": "", "ad": "", - "h": "97c2415dc230172a09e08966971dde6dfe4a5a5f43a1384bdafaadae3ab867cb", - "gamma": "458a601806dca1bfc14767378544a60d3d45a10c461ba266008ff735ff3637cb", - "beta": "6321a49e106c673daeab8ca566696fb458bd0303aa6acd9e5435d08a70b7b6dc1a26db26cd65d357aca67741dad61ec04cf742df80d3bb0141f093aff3682091", - "proof_c": "6c5d557034da6f3550f4ed348b7b9281", - "proof_s": "6861d92d783add90383ee376a32550facc645fcf5af8b6c1986f86eaa3d7d806" + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "proof_c": "a8eea0031ab89afa3840ebd28f1d4526", + "proof_s": "b67d2093dd5aabe794c0c8e5c8a8b2791a44f7714237859cd414db1ba830c202" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-3", - "sk": "a665fa5af45ab71a60a6b667bc995423d5ee43197a5fd93b16f761cf0bdf0407", - "pk": "0b33325148327641856fde36d56aaf2cb1e4959824bb0916517160d402973bc5", + "comment": "ed25519_sha-512_tai_tiny - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "cd2988ed28d9873b80e90ccae0f5767af0ff8cb3b8d22f257f217e3736165ac2", - "gamma": "7e56c17639f1b58b3d19326ce82a5b0f8da4ebd29241913251d238dab5e5b416", - "beta": "478b9e54561d15f82e1ba6cee6c015039b65f377b8f5350de7bb1fef65392aadc06c439e8fa0b0139094f1c67235f7946fb65c109483ff266860a4f4b02ea1f8", - "proof_c": "2735f77d8c7162cef499314b69baec44", - "proof_s": "af2ffa762dd13e7519d4a0b06145099a83a2ad163457eda34c189b3e4aa40805" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "proof_c": "88401f18baf04278e73ca352eec5805e", + "proof_s": "13652f819865e7d994dcc8e226a92653e16fa563fbc250766b51093d5b13c602" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-4", - "sk": "9a150e5caf622cc949d95af8980d458b551ab394d52ba49759433b4d04d9f703", - "pk": "cf2d35428c1e329dfbd5ce3a6aafd8cffe31c6a0685471f59f5cddec0725483a", + "comment": "ed25519_sha-512_tai_tiny - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "12e502cc03e6f887e4ea8eb284bed41d8d5a2e9460db785a92e06431d6e1b533", - "gamma": "c6bf46adff3376d9c74d764af3daf4a0edaed8c0fcaa848e46f2c41d94b0e938", - "beta": "243445998cf25977db99fc5a5c22a79f143bf1a0ecb89b59632a2161cfbf90cd183191bcc943e34b32dc81e96e7b66f2ef9f60dac1877bdd3cc416231e9f617e", - "proof_c": "873339ea94ef5065bc4cc487ee59a190", - "proof_s": "9c90820cc75ce12d26ea07eb8b9d9207cc72976056f96c2bf6cac3756818060e" + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "proof_c": "c30ac26f9961ec5cd94661325db6d803", + "proof_s": "52eb8200c459cedc4ceb13d55df4812f456add6f02b795b6f1424a04f34edb0b" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-5", - "sk": "03cb98147455ef9db48d9f1ca758d8047f43ed4320909800d849522444b52502", - "pk": "465a022ab19f37d3586489ae08808b23d39844fd321735d6573c089b12202d1a", + "comment": "ed25519_sha-512_tai_tiny - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "1b6dbb2e1ea7f31f0674df9a9086d1eeda4cdfc32a0c8855836866dc30b80f04", - "beta": "2e7b21d022916e75e16e17dd806199849f6ebe5d257398593bd477d491750d4d17fff734395c28c6c98e1f623c21b780e7eac32723f895f3feab0ba07b6d7923", - "proof_c": "bd961d3d952a794e43df0e605a8060b9", - "proof_s": "34c96762240d57b5e81e1f3be085c3076de91b12f4489a5fb22c3a56cda20006" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "c260de688db7869e58a9de51347045da", + "proof_s": "0b4440c53777ba1ef38e53341bc1a9e53af067c799c589e34898cf8f2fbd2c07" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-6", - "sk": "03cb98147455ef9db48d9f1ca758d8047f43ed4320909800d849522444b52502", - "pk": "465a022ab19f37d3586489ae08808b23d39844fd321735d6573c089b12202d1a", + "comment": "ed25519_sha-512_tai_tiny - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "1b6dbb2e1ea7f31f0674df9a9086d1eeda4cdfc32a0c8855836866dc30b80f04", - "beta": "2e7b21d022916e75e16e17dd806199849f6ebe5d257398593bd477d491750d4d17fff734395c28c6c98e1f623c21b780e7eac32723f895f3feab0ba07b6d7923", - "proof_c": "f6f65a2f0b8733dfa7f21acc70fbf27a", - "proof_s": "da87b9116b84d5678fceeefb252337fd8a8dbd23edfc3d5e9971842463cd9302" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "82d8649618791877c054a7f0613aca57", + "proof_s": "b8915a96ffbf0fb91a960c7ebdc51c9ce9aad0f3fdbca6d660b49b389c389f03" }, { - "comment": "ed25519_sha-512_tai_ietf - vector-7", - "sk": "0f6ed8b686b6ef0b2552344803cb0f92a84a654fd46f0e98782c6de71f98d40c", - "pk": "ea18949e73be86cf0c7de5e688c782689b72c49741baed797ee1a128d7cf1bd7", + "comment": "ed25519_sha-512_tai_tiny - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "d7e4fb52f1edce33ba45ee2c1f526891c23a961017e55cc77434a70d6958bfbd", - "beta": "6441894c8ed2517a0a5765163994c761672d4d69f735bedec1f4ed2a62922564fc5522a3571b5d2b1611a35cfdb153d3eb78336d0533a31d55dcc1e5a63949be", - "proof_c": "bb49291cf5ac7bcd6686736a7f0c7df3", - "proof_s": "b020a87fa08b4333b16a1abf81d752a10c5191b4f239ac8a90aa65a4526b4c04" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "proof_c": "bd260e6063fdb6f89edb964196a89879", + "proof_s": "68633c206bbd865be1bc6ce6f5f32c3c0f6bae6d74a95f493a3f4d4485f0c70e" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/ed25519_sha512_tai_pedersen.json b/tests/vectors/ark-vrf/ed25519_sha512_tai_pedersen.json index ac2c8df..a1c2a2d 100644 --- a/tests/vectors/ark-vrf/ed25519_sha512_tai_pedersen.json +++ b/tests/vectors/ark-vrf/ed25519_sha512_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "ed25519_sha-512_tai_pedersen - vector-1", - "sk": "fc4722b3efeadcfa8b437f6e34f71c50a240bd2e66b958c5c17519cc9d936906", - "pk": "bf512469e7f9b766bf5c75e47e84fda3ec887bd24d79b9165c19e01a88fe5ff2", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", "alpha": "", - "salt": "", "ad": "", - "h": "cd2988ed28d9873b80e90ccae0f5767af0ff8cb3b8d22f257f217e3736165ac2", - "gamma": "efdbb69c46c055a5a4ab2a0b5961c6c797095844044f1ed71a463c495d4e581a", - "beta": "bf4523b08aa50cc3b98f2fea4251d4ce909ac52746105f1377bc6905909966e0dc65aab98243a2b150bd889d267d6edcf6fc0b0268aa9e0bfb3e1ba8a972330c", - "blinding": "491b2129f9bc2a1ac10f3c817e69fa5e6f476b79f0deb3981008e9f79486ea08", - "proof_pk_com": "ebc5b64a635cd8595475889b77c1b1f84ce108374396f9fc0376fab89c7b0835", - "proof_r": "c073a53e3b469c7e69455df371073e882ae7907c56969bb23e9909d58014e2b0", - "proof_ok": "33843c5f4d2e1dd5e5dfdbb8b9d9ff26452fe7523507320ce3158158bf1966d5", - "proof_s": "440c890b9ae16d89de1d6f64dc923d6af04e95510667e48de352cd06e489370e", - "proof_sb": "97c88ab7050f7eea3bd10c92559ef4743c09b53c163a4ef2ad13e719698fd50d" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "blinding": "4a22498de4c3e17d9ddf99ceafd9752bbe301a6510b5645894117cdb5d72e303", + "proof_pk_com": "22f723db530cf1acfa5f34ea78944c357f308423776c1966f13eda6379d107ca", + "proof_r": "6e6e2e0c237f721d70ed57af4ef530ab5cfe6f37520747cdfe9f4d91db5f4e76", + "proof_ok": "3ea1f7d543a114e7d72517754186da044bfa2b25c3df1b9722f29594a9a3427a", + "proof_s": "e8491718dd89be322bcb56780048d3414cde042b88ae278fae074ce6f2d8750a", + "proof_sb": "93edbc92a00957d0ccb6a209737b5b24a89cd0be74ca95476e9a5745184f8d0f" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-2", - "sk": "3ed3f4d0fadb42cb77d42a7ea57def2bad464bd1ae0c896dd077c04358415c04", - "pk": "a9a4b61b52db164253147e0c5bd20852d7ab4763e996d0117febccbc7a1abbd6", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", "alpha": "0a", - "salt": "", "ad": "", - "h": "97c2415dc230172a09e08966971dde6dfe4a5a5f43a1384bdafaadae3ab867cb", - "gamma": "458a601806dca1bfc14767378544a60d3d45a10c461ba266008ff735ff3637cb", - "beta": "6321a49e106c673daeab8ca566696fb458bd0303aa6acd9e5435d08a70b7b6dc1a26db26cd65d357aca67741dad61ec04cf742df80d3bb0141f093aff3682091", - "blinding": "719d0ed9f57dbacfb3fd5eea07432f00e1d60ff7000720d5f0ea494d0109350a", - "proof_pk_com": "b6034f833e371ddf8e36def77a483c543c2fd9bc6f4aad41dc67377c61e991c2", - "proof_r": "d2ffecb79a4e3b9b38b8f828ceaf0dc7411fdcfd404a33ab0c95d0df80f82fae", - "proof_ok": "211fe27e1f2e951fb2f6293cf7b488571e545af9758b9a207e92d1669c6c813b", - "proof_s": "041521263d93844034db7f96b872fd882da33eb51d6bdca2980e2f5dd2b0a704", - "proof_sb": "fada3f1318399292287cf3606ffe98f59a384b863f6c8c1ccc2870ee14f91407" + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "blinding": "5d37f7af4f2a830e3da4ffd6435e369c39ac34516b3153cce697a205bed07c00", + "proof_pk_com": "c3bcd67a8d7bbaeaca28b6369df02af694a773f193c2456e10a329f31e8b855f", + "proof_r": "7ba53378c5ba70ac190ddbf40a0acfe9f05b7a22dc8fcd99c0bf40ca7c4cb47c", + "proof_ok": "f912427e0d829d8c31845aaac079b03d6e4c7a4bf46f195b70a9c1508f5a1b5b", + "proof_s": "4bb196ceb832943a136805ca6a4737581e7253f18d7cabf2c6cdee7600ebfc00", + "proof_sb": "07f7f0f937a195a993a182baa296224ad5de5aaf8951c497a89e6f5532b95505" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-3", - "sk": "a665fa5af45ab71a60a6b667bc995423d5ee43197a5fd93b16f761cf0bdf0407", - "pk": "0b33325148327641856fde36d56aaf2cb1e4959824bb0916517160d402973bc5", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "cd2988ed28d9873b80e90ccae0f5767af0ff8cb3b8d22f257f217e3736165ac2", - "gamma": "7e56c17639f1b58b3d19326ce82a5b0f8da4ebd29241913251d238dab5e5b416", - "beta": "478b9e54561d15f82e1ba6cee6c015039b65f377b8f5350de7bb1fef65392aadc06c439e8fa0b0139094f1c67235f7946fb65c109483ff266860a4f4b02ea1f8", - "blinding": "d7df2e4ca69d04bb804d76c03b08d58b1864e29ebc4518649933325380fa760c", - "proof_pk_com": "96f3f09d88192d3581d903576407bce0a496208b0c789b8009e4c31e182eba6d", - "proof_r": "84df27733245d62543f0f047bab93e3b88f82c3579c77ab66a77d16b6edc8c04", - "proof_ok": "a925817d8f5adc4febfa5fe50beab0945fa98b5bd21bb4c38b95394f96a44fe4", - "proof_s": "6465dc8521b23a262ff283edeff4adb0acd14e006616e351622ef1226f2b8603", - "proof_sb": "0f562688376c742b8ca03823d937f9905abb09f463d1806beb0eedc61fda7400" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "blinding": "ba0db55791e6749eb315f31da7cfa060035864b1c986b802c4a05e2fb9751c0a", + "proof_pk_com": "1b3a3614beb7112ed8de6b70b22ac56677735749178afb3ed2cbd8019e913060", + "proof_r": "12f7e7f49003ef40c4bc1d699e0d754c0984272728b95309ee57cea8507bb5de", + "proof_ok": "84a33f30613b37fb2c204d949ccecb89f903e5c4011731d037d45b1616dcb811", + "proof_s": "a5674b91eb102a1ce9c3b75525aed367df75b74350bd6258bd829a6001324306", + "proof_sb": "b0b99c1b55379dc8a30ba01d607ce7f31776d5c286384717a3cd4173ae97b00c" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-4", - "sk": "9a150e5caf622cc949d95af8980d458b551ab394d52ba49759433b4d04d9f703", - "pk": "cf2d35428c1e329dfbd5ce3a6aafd8cffe31c6a0685471f59f5cddec0725483a", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "12e502cc03e6f887e4ea8eb284bed41d8d5a2e9460db785a92e06431d6e1b533", - "gamma": "c6bf46adff3376d9c74d764af3daf4a0edaed8c0fcaa848e46f2c41d94b0e938", - "beta": "243445998cf25977db99fc5a5c22a79f143bf1a0ecb89b59632a2161cfbf90cd183191bcc943e34b32dc81e96e7b66f2ef9f60dac1877bdd3cc416231e9f617e", - "blinding": "bc45e7904e6002177e7b7a3b5b5288cf135a472c02b8fd7cad120400858bdb02", - "proof_pk_com": "fb69166429a1460eb6a5936aefb682f344b767e195a59559094aef6c234a0fc4", - "proof_r": "9bdb4a6b8696c5926125e862e04988834cded1f348b5659f7ac9332e147fb45e", - "proof_ok": "aa23de5cbfb36559c064a97839ef76dd7065e6f8f78bbd71be93e8678c7cd066", - "proof_s": "7aa1eb955fda69f85e3fd465042c3b90f55753b67e06f61d741abe4d6877f00a", - "proof_sb": "a90751aef1c794c86470167a2816b0281f2664a5a8a0b0c5e26125fd9ab6ca00" + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "blinding": "2212ac769181892e29a5a249e0e613f739695f917d0e014dfdffb6fdf170b50c", + "proof_pk_com": "0282657fb005dd982950eef22ef5f9ed3d13fb224c983758a9501a62459cc0f2", + "proof_r": "5e49ea3612a52a5a142eb3f47fa35080dae7958ff22c8b54fe3c2de462f4f928", + "proof_ok": "d797517b29708c6c804584407fb4a857f6ffea7046df027cee8adf13dd93191c", + "proof_s": "b0b0ce660112d1b6bfa8d9c5768dc6ca3402d1c45246ccab718032a8b67eb609", + "proof_sb": "776d144386b3c0fe91e6263f433ce3f61ec9135b890b6d6fbe1300bc49634d0c" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-5", - "sk": "03cb98147455ef9db48d9f1ca758d8047f43ed4320909800d849522444b52502", - "pk": "465a022ab19f37d3586489ae08808b23d39844fd321735d6573c089b12202d1a", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "1b6dbb2e1ea7f31f0674df9a9086d1eeda4cdfc32a0c8855836866dc30b80f04", - "beta": "2e7b21d022916e75e16e17dd806199849f6ebe5d257398593bd477d491750d4d17fff734395c28c6c98e1f623c21b780e7eac32723f895f3feab0ba07b6d7923", - "blinding": "d0e5bd829b5b463263bd741f1aded24663383621ae64b86cea035ee5d30e390b", - "proof_pk_com": "ad5aaacc5cf9925d32081d0c8822954ce2005bc55ad2c111d71d6d29a791980c", - "proof_r": "b90fbaf3f37889af63821a1acc5d363d2a3a51a213150d311691c4f96f76d050", - "proof_ok": "114483fd610de396594280a3ff6d870370ed552618da7267f52afe3f7a6a42a4", - "proof_s": "7e77874a618472db2b6bb54ef13c5e6ec75a567ed64b075548596776d8546109", - "proof_sb": "c4dfdc49e8ec1fd3cf441159c3998a7daa9bd2a6617aec409f3c335e22e6080a" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "031707f37dfe8ef4837a376320d6420e71724c66d0d8391746228d19ef6bc70b", + "proof_pk_com": "7344a2db6d51163b1d8042f11258f230af3adb0722eb228246c9e8951a5b68c2", + "proof_r": "2471ecf11e758e80a8854f9811b323bfe579cbbfba0234d1ff26839c38606d16", + "proof_ok": "80019d3fc955d254a092014358c71cfc57e60fa543ad3f0c5e1526b88c3b33d6", + "proof_s": "cde860d02df38ecc5241a308c0dcb32273a514b74cdec679f48eb6097dfe2000", + "proof_sb": "4e6a6c18bf9ec98eaafb16d958539edb29a924762773818042ab7614c75d2507" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-6", - "sk": "03cb98147455ef9db48d9f1ca758d8047f43ed4320909800d849522444b52502", - "pk": "465a022ab19f37d3586489ae08808b23d39844fd321735d6573c089b12202d1a", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "1b6dbb2e1ea7f31f0674df9a9086d1eeda4cdfc32a0c8855836866dc30b80f04", - "beta": "2e7b21d022916e75e16e17dd806199849f6ebe5d257398593bd477d491750d4d17fff734395c28c6c98e1f623c21b780e7eac32723f895f3feab0ba07b6d7923", - "blinding": "6df36c2d2e9ecfb0bb0cac264fe82fa4c2734e8e98e476bcca9a3ed25f693103", - "proof_pk_com": "eda9f13b3af9d09ea7847331c622970cfbfcf38cc4b05aa97cde2b4f6715a221", - "proof_r": "e251d7a72c11f491e3be12a23d2ce93955d2c4a4265b3ef976030d2b8c06476e", - "proof_ok": "114483fd610de396594280a3ff6d870370ed552618da7267f52afe3f7a6a42a4", - "proof_s": "c8d6c2db0f869f278e7e13c1c32ddd306161ff6e3ca992714e6a36176ab6ae03", - "proof_sb": "7b38f90fabe9ebb5ea3eaaa27a613574f632b7369cd215424da4cc64df3c710e" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "6f4e473d9391539e5bc21696a434287db0df24f0630de99f22c238f94eb32b04", + "proof_pk_com": "5b3a2e50f9711c889e7b97cebc91c11c1d038cf1f0e59017fe2c40671203982b", + "proof_r": "a8f57c79b2bfc5f2e33739ae2d7100d299b032379b014ce5fe671e3e18c1a0a0", + "proof_ok": "a446fd73943c1138dd2ba4433596287c0dc72e09953f412d0e34a87606b34c80", + "proof_s": "a9d177a177cf3bb87411def3a5fcad728f06f98a910c8b31fd8dca45be455206", + "proof_sb": "27da39875b79eb36e38359d483cba9396f726092567de795020a5fb7c83bde01" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-7", - "sk": "0f6ed8b686b6ef0b2552344803cb0f92a84a654fd46f0e98782c6de71f98d40c", - "pk": "ea18949e73be86cf0c7de5e688c782689b72c49741baed797ee1a128d7cf1bd7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "865bf48f6abf5b36c287f4d1ea6f9f13019892e6f1ea13e4ea85af4c850dc628", - "gamma": "d7e4fb52f1edce33ba45ee2c1f526891c23a961017e55cc77434a70d6958bfbd", - "beta": "6441894c8ed2517a0a5765163994c761672d4d69f735bedec1f4ed2a62922564fc5522a3571b5d2b1611a35cfdb153d3eb78336d0533a31d55dcc1e5a63949be", - "blinding": "3434f642c32d6195c07e81ac2b6a27ad4248265519413d051497edb8c29ac304", - "proof_pk_com": "744cbc3ddaf6f31f1fd6f550a73fe7b3892f0718900a00f617de02a838506e87", - "proof_r": "284887ba4c1d282105bed0db4ff27d7850698ca752a33560c34ed55ea4f44a10", - "proof_ok": "d7e96d7bf54d0767611fab3e06e45c8aadd76042dc72951ef27acad5dbddb31c", - "proof_s": "4500cc32cf8948538c855a0088fd638fa11a2276386cfd040dbfe26f46ba5804", - "proof_sb": "e259074535b8a86dfceb46406fa91bab9949728f13ee150a13923c269bd6b103" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "blinding": "dbdd83ee1fff976aebcc282d42398b7d9dd7b94d1e7af4621279f6df69de180c", + "proof_pk_com": "cea64ca9f0ba93036b4f3c397a263d70d888f2c250637d9b9db492dd8a25d85d", + "proof_r": "1e9493b522e564b6eefba836da96f6c3ce17bd2c36d247776e50ee2d66c0a6a1", + "proof_ok": "075ddb875f12b6db5fe2eb75593c1e88d46b1e645e8144651d59763e4844f031", + "proof_s": "323a0d6cea3affaca38718015d313e1901744c592573732a58c4964d1eadb90f", + "proof_sb": "1174ba0758094f616888a442f1208a36f55db9bf869e44404445da277758d005" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha-512_tai_pedersen.json b/tests/vectors/ark-vrf/jubjub_sha-512_tai_pedersen.json new file mode 100644 index 0000000..546b94d --- /dev/null +++ b/tests/vectors/ark-vrf/jubjub_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "jubjub_sha-512_tai_pedersen - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "blinding": "b411f199a190e143365b0100b6056affb910d956cbe203fa1f9d9ca7eba93b0c", + "proof_pk_com": "aa37fb22978457ede1d5c9a1cc146ee3fb16eaecd4badcffe9340dc3f1e1e19f", + "proof_r": "e0af508af99628925dc9b548430cd25c8580ce36df323dbc1822ae3297aa069f", + "proof_ok": "e34616acb7f465489575355fc52b58e2a846c00f976c442975571d1dc9679442", + "proof_s": "2918db8cf1d38144ddfff87cc170b5bcfbeabf421fe0f29a66529fb5b0257405", + "proof_sb": "805862bf392db5499502dbaa8e92e4b1f8d65f43de4b3012290e38d5e3756208" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "blinding": "84616b1b767b43ff53b1902474b9deca4416b45aa488928fe128719555505708", + "proof_pk_com": "138f9a70acc072087cd47ef5dab86b120636bcfb6a8e3879d46d614d2736c844", + "proof_r": "17ab4dbd47f7889cfb50e82594b38f5b24b6fe90517f2a11d3164932c7ea0f53", + "proof_ok": "005df5bc38e095b575f4e80307e92bf493dd9e6931b3eac1e86b8fa66e8d8773", + "proof_s": "fd721148c4808f0a518560eae6f7f7ff757f890855ac63d5c8346240aec3490c", + "proof_sb": "3559963241ea508e2deb30ceb9dc77b07c1a599604e1b8696b6d8d6839529f0a" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "blinding": "86f342277fe5f88c92337b4e79594c6335dee5868b0a5c4542a0321dfa4fa608", + "proof_pk_com": "7536c30b71337821df6afa9887e769687ed89759aaa7f147898921ac6ac31b47", + "proof_r": "5d9cc07b9b9a98c193cdfe58e9e0e550cb26210ecf435ce1ad76c59511613234", + "proof_ok": "ec71d389fda2e9ee50c61b91acc704c5348039b47872419dadc9a25cb6ef8693", + "proof_s": "743e695720f3ceb5b52c978f5be8ecac8bd4389ff8c83e91a2a572df3af9ce0d", + "proof_sb": "a5e34e4383a074a21bbe09cef2d0424e8030f8e9543d45e02b6a3d6ed5d6e80b" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "blinding": "f2ac62314b4e1a23394c6547807a4ed6f658c056c2f13119f72562b45d324f03", + "proof_pk_com": "f0118d7b17f69f42a238d8f7ffd1b1d8c05a85cbe0c324da60ff2cb60b10c705", + "proof_r": "9bf9f2d0045fcaa887a9e31c852ccaec583df32bcb82a722dd1ce7cf106099ed", + "proof_ok": "c28d503817a895359e8d86c4dcdc5bfeeb02845936b05d6b01022d54a5104fbf", + "proof_s": "15a2b2036994955aebede90b405f0a5971807235a429390e48f2f851c1a61a0b", + "proof_sb": "bf72d5850b00d002707e2300a27801cc7f22c84fe6e1053cef66e19a4e98e103" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "24ca3f6a30adddbec78a9e468f1eb09f214f06d086db71abea944f70555e0d03", + "proof_pk_com": "1ff2f4afef54d46de90292564b60c454a48132c806168a976061aa6223d35972", + "proof_r": "aa5c6254903a5050350581c1a9a54589cbb108cea0d4043a189fe6fc8c215904", + "proof_ok": "3b7a223f757f8fd4eeedd1fe28eb72852d4f007f383733f960726c4bcfcf9792", + "proof_s": "f4437660312f5331dabf71b3003e012fa6857bfcebc3b9bd84b734fbfd68ee06", + "proof_sb": "672158bac1e24e831816e417cf692452c8305f1b42a8f23eac4a49d10051d20b" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "187e68d7c3d3664eada848c3edfc19defe50ee099eb430cea8933b67a6d7440e", + "proof_pk_com": "bfd25e18e4818ac6cdf07336c162412c6a07770cb2d9598bb9b3c4338f889e71", + "proof_r": "ee9ed1962f50ef09495cf5ed0cf0a8fb90f0fd70437d1e5a9f918ee1112f56d3", + "proof_ok": "c7473bff4b6ca801c0d4f0da828c20257dea8d7f39c7b8e2e499db5e791c3091", + "proof_s": "5dce37237fc16f96b2f850121e65711092c0ca1a78a68034876717f7e95ef704", + "proof_sb": "c77bbefd18f04f6a65ebd6a6f43e3a88416069e84a89893923d57c29d5835707" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "blinding": "cc07c57642d40b84be0c2741dff44254f2ebe46d506f6a19e6c5499edca31300", + "proof_pk_com": "2556f967c10bf28e415e2edcf6c0df0f02373117a00c4abd4f59690906348e5d", + "proof_r": "662b1a6a1acd770ebd8e78893cbdc0ba2b22d3377a4665bb2dde06bf0f5f7dbe", + "proof_ok": "c758b260c926cc5bc12c6d0532ed451bcdbe877e094fa065c906743cb0f5bea4", + "proof_s": "e444981f96a004a00d09ddb83c284468ed23107a6d92f20467427aa3caa7470e", + "proof_sb": "99c41f609962f251be9de67860727d57bca0d758fb0bc45ef07bd8d24c7ffa05" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha-512_tai_ring.json b/tests/vectors/ark-vrf/jubjub_sha-512_tai_ring.json new file mode 100644 index 0000000..93ebad2 --- /dev/null +++ b/tests/vectors/ark-vrf/jubjub_sha-512_tai_ring.json @@ -0,0 +1,135 @@ +[ + { + "comment": "jubjub_sha-512_tai_ring - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "blinding": "b411f199a190e143365b0100b6056affb910d956cbe203fa1f9d9ca7eba93b0c", + "proof_pk_com": "aa37fb22978457ede1d5c9a1cc146ee3fb16eaecd4badcffe9340dc3f1e1e19f", + "proof_r": "e0af508af99628925dc9b548430cd25c8580ce36df323dbc1822ae3297aa069f", + "proof_ok": "e34616acb7f465489575355fc52b58e2a846c00f976c442975571d1dc9679442", + "proof_s": "2918db8cf1d38144ddfff87cc170b5bcfbeabf421fe0f29a66529fb5b0257405", + "proof_sb": "805862bf392db5499502dbaa8e92e4b1f8d65f43de4b3012290e38d5e3756208", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "b701d3e476fec9d15caa8157b3db3bd45756a30e12af0fbbdd4c0fe462330553fe827879a13e4752d99adb48d0e0a96ea3e92b666229010a14b2ab0e2325e72fdf796c68172ac8c7cc01054d5b8694bea10bf56d60953ce09f2bf29f42433eaaa40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b8c827f20e69575051dc213a68b75f88a3d90d8b7dac9651d9b57d37c33c201ce251ded3c1bf53f0dc40f8690efc5ce89107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd870bc74f4428da688ab8bdc336b514b07f7a33d6a5d1b04fc949296e346c1c63b8eaa9baf148914752c114f9d5895281989d253d23f2331db3d341dd7182ac6fd2a557e7911bc91efab62999f9dcaa6e14a780abe866c1ef022cc850e88f2d88496667b04174a84f975de165acd69a69a49333426e8e4eba122463e9569ec2583a9f74218a11e45691324477b8579b4d87312339840117768fa724139765a01945d919b3a35d7eb17a935d05bea29c2e374d055acc550a406178cb7feae69811a9abeff64a8f5e58852fc6f1d5d668901c83bc2678ccd5df028162a3ea82b6395e6443f794bd943f00745834ba5164ee048bcf0abc6b3c40ab1461292f576f6411cd82c94c40f90e123c03aaad9d275fdb1594287a14a85a61a9166aa4189e2c74b0a894e77ddcf98830f1addd2e7fc2faba7805fd8f9d46683a54dd57cfc8258c058d29c4ba226707c6d7d3ed1bf29cd5aca25ae2897b2d32342e086899a4faac2b7e4d3a824335ad651ca45390d42a0c8e241c21c5c3ac9124e469a116c06af6eebb5c80fd103175992b58d8d1fe29a0b8409ea72d19e25d8f8770a849c74b6cd09e63c75a820d9ccfaa96d9430d339198d1eb4be575266b6ae2792ff06100a18c4209e086c11a0085b4a1396777960142df9d0469067576f6a5c55616f7b26c502f5cf594889db51839d1dbddd439" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "blinding": "84616b1b767b43ff53b1902474b9deca4416b45aa488928fe128719555505708", + "proof_pk_com": "138f9a70acc072087cd47ef5dab86b120636bcfb6a8e3879d46d614d2736c844", + "proof_r": "17ab4dbd47f7889cfb50e82594b38f5b24b6fe90517f2a11d3164932c7ea0f53", + "proof_ok": "005df5bc38e095b575f4e80307e92bf493dd9e6931b3eac1e86b8fa66e8d8773", + "proof_s": "fd721148c4808f0a518560eae6f7f7ff757f890855ac63d5c8346240aec3490c", + "proof_sb": "3559963241ea508e2deb30ceb9dc77b07c1a599604e1b8696b6d8d6839529f0a", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "afe98e02cb8f9a086c294a6101b7b7318b0cf93a23d02c6db78439dba9f94b6fab1147be33b43d0b56b87067631c1dceb8e673afcfb2b3a59858a5805be199baa272bd11d1e7d9d4b47d1505714bb136f0b714ea399e02040001c6cbd5f74f3ea40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "a7efcea88a8011b720952e2fe243aa74a7cc4fefcc7037c35ec873639b744013182bfbe2867d422381d0fd99154971769107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaafe8bba5d293cd32b3fdb9a20f49ff37c877f3adf75f55e5857039cd95613b08cedceda13970ff86c27175259de551d984734beccc780c397f31879f21ce773b43eec89bcdb31a445808cff7af303d2893227370ce1f0eb31e9aff7345bca14eaa0c64598dd5e75907f844ee641527ab01003c8a88c0a3bae1864a157b7c962480aa1a97f7c3ebaa908034bf0e3f783e86fd8ab99534d19ad03002548895b08f4b778b3c50639b7ff93cb9f31b9034fd8490c6f23d4381732de213f3d907b604e67ad4af007879c268fc24ecb8cb6a82ba784a97c8c57576f1eaa4ecb279557db92a7de2d7c59f5b057e3a33a3d81071b25c9bad489bb280ff4e374bf33f73129d57082d69c0ca6f3a98874865d1afe0ec4db4d494e83094c79b97651295112b9e1cdbe80c2434316c4ecbbae78681ced1c707b4ac1cfe9e6a490e51f60424f8ad52b26163de0d3de9974c084d2aae209899a7aa4a20cecd324375005169cc4ebe84010de8bcac25ca014689f6ff4c15004d1a13b52db0c891b67eba5f8d68a5552e92a6c8fdbcc23ae9aa9225ea805a75a4a56ea18e5f22587c5be42226889bc2948c3232171163627f054d09b8bf8b4186ca4b3a7772ecea49ad1703840f1a4fc0e5227638ebe82b72259d2ae282ed01c83a38c05906890476cd1eb5097015e04b5852b2ca1e42926eee7671d99c9" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "blinding": "86f342277fe5f88c92337b4e79594c6335dee5868b0a5c4542a0321dfa4fa608", + "proof_pk_com": "7536c30b71337821df6afa9887e769687ed89759aaa7f147898921ac6ac31b47", + "proof_r": "5d9cc07b9b9a98c193cdfe58e9e0e550cb26210ecf435ce1ad76c59511613234", + "proof_ok": "ec71d389fda2e9ee50c61b91acc704c5348039b47872419dadc9a25cb6ef8693", + "proof_s": "743e695720f3ceb5b52c978f5be8ecac8bd4389ff8c83e91a2a572df3af9ce0d", + "proof_sb": "a5e34e4383a074a21bbe09cef2d0424e8030f8e9543d45e02b6a3d6ed5d6e80b", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "840ef06a6fdbd6d3d4545cca9bff8aae306dffca00a32fe34933eb074bbb05a24d1a00ccbcb8a08c7d1f53fbc2c4006f92d9ad7861f496f6762f7c7d5f3a07f71e25e91e5d737532b890db4298f4d2570200be9e0974f443dfc771b85bd40995a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "94aae3648d69b05b136c59c8391f6a0a8d8f138e591ec254d9096da442bf7369e3101787599aedc87b0415b87b094ef59107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb4de0636b732197770d915eba2b54cbd02d48862ff87907f921b62166035a37d8e27f08afad9b3f234314f91d2a354bf84018eb9116a53e45a490b7101ac40bec41b3d353726d07bed987f8500f6c8fd1989f413b7de9a29a5386a924492dadeb590f9f3ab81f4f41e48ef4607b9072cee1f06babee459c3596f1b5a94d7d4611c423ee3c7b779d4f87c4e13c617f29557954f228a884ffc7b407d0162953837e5b91554dc8f66718e03aac0bed597e95e59bc9036f32f711cff6a9cabdd666ee545e4f59f08e72ce26bae3101b8dceba709d104006fc3150863e1b6f85eff3975d7bcf4dbe5fb464826b319e953b55bee63320c50893b100a41f930fe651e2ffd87a1b717b9d159d6e62904a2b445a0fa831646547dcf8170086bb018ec780fd6e6ae5f32697d3e3af36fc57eb1d50485862c0f2bdecbe945f22fc082a85e3084bb789bca7b3eaa49ae0de8b29adf39c267eebbfe2f86fc045cd75db506512bfae89b19f9edeb25965a50e431a942831a1f0944772a8a0fad68814760394f1ec3faf571f78f2fafa6fca3b549305268b711c562f78dda6c2219d1a30cdafb5a17bea414e5fd97133630975b1f547eb47fc23dddbde3eed8361765c307511a028f8ce1d81c85cb4b888acec17b12cb8f568b5870dcd3f2ba9481234f83510a04ed9964a604e56ad8869add8ec81270c5" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "blinding": "f2ac62314b4e1a23394c6547807a4ed6f658c056c2f13119f72562b45d324f03", + "proof_pk_com": "f0118d7b17f69f42a238d8f7ffd1b1d8c05a85cbe0c324da60ff2cb60b10c705", + "proof_r": "9bf9f2d0045fcaa887a9e31c852ccaec583df32bcb82a722dd1ce7cf106099ed", + "proof_ok": "c28d503817a895359e8d86c4dcdc5bfeeb02845936b05d6b01022d54a5104fbf", + "proof_s": "15a2b2036994955aebede90b405f0a5971807235a429390e48f2f851c1a61a0b", + "proof_sb": "bf72d5850b00d002707e2300a27801cc7f22c84fe6e1053cef66e19a4e98e103", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "91e73e4a9ce454fccce2485c08fdd8eaf04f61ed8e3115f371c4e1b9229c98c3d5601d4250a6133d54be9f05e53cca44a981c22ca60e239c67a6572bbe14c63ca347ff7a144df1aa0acfefde08a3843ed0fcbfe670d3f94e7c422f73e816d9e6a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b14c815249e440c2408945b62a84dbc2a84d2f490edcfbf345697fbb0646a74a54c67d07c0e7e7a04637dc78fb22ce3c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd9922a6bd9cd97ad6f4eb9f2352ca952ced064f866da1929eff978295ac897d5f42a3d9fea7f3af0a40216bcbbcc66903abfae29f22a6d8dd08a14ff7afa21c640a57b690d50d7ac73371e06a7aaee1cd9010b6a1d0de92f4357c33012231817f065f1f136883044d267d1e16f9a873e5e9a1aab085625c66cf8c103c56c91b3a3e7e1ec9d388c27e2c5777d26169dcceaca1a06ff1c82e73dbeb1e9d2245511d3a7f1c772acf6b4c1ecf4b92c2d6550f201655f398e178f904ef43bab2088e3ec294e99b8f2e1e51ec3fc54c8c570d0325cff7dc1ac4f2b8f8f3b82cc257e65e37873a9e400ad979e49e91c052c302e5ee47042324a04cfc8923b59bad15951b0470a486d69b91723f65ba849da4e52daf13e782748a4af810a0d4f689bf3344f65b728ec28427bb82065dd84834eb49bab45790a08c397e6b3b21766a58b618ae59129fbf20764282b735dadf5a9b15df61c3b938484d69cf27e15c5d35306d82d55c41f01f96d485d9afd7233e173d6fde822759e50822dffa2cede7e231f8e87543b7734dcf2829ad5ff98f03da55a976f41df97259e79a1a69dd16fea8131b7b40da3d79e669bee365035b47e6d102dc8f7c52bec69f2b862723de550ead903ab05792e0e19f18562ba79269a012f59e99cefe7c754a6d962f98d400355277b2b1044263310cbf0f03cac79962cc" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "24ca3f6a30adddbec78a9e468f1eb09f214f06d086db71abea944f70555e0d03", + "proof_pk_com": "1ff2f4afef54d46de90292564b60c454a48132c806168a976061aa6223d35972", + "proof_r": "aa5c6254903a5050350581c1a9a54589cbb108cea0d4043a189fe6fc8c215904", + "proof_ok": "3b7a223f757f8fd4eeedd1fe28eb72852d4f007f383733f960726c4bcfcf9792", + "proof_s": "f4437660312f5331dabf71b3003e012fa6857bfcebc3b9bd84b734fbfd68ee06", + "proof_sb": "672158bac1e24e831816e417cf692452c8305f1b42a8f23eac4a49d10051d20b", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "8ab4f2e1d93031a84b5dd83f3ab4e186a9b40316ad2d259ae237543dd6c7a86c50ca66ef3964edd35bf7a3a7e22b8c1baa988145aa6d81e40361364dadb6022b43ec0ea3cf68bfda3763746535b4afb4dae1b1488a87b2dc76b144e695ce9f58a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "83cc16095cbac0edac32e68cfca2d43875767de6b8844f0cdc3c5a2bd22e90693f5438dce247427e5e8602309cdcba7b9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8c2c358128b17cf6d3ce46487dea071bce3141b9273fdf37a67a652c7183fed30ab3cfaf5b3e24136f310d7700cd1fecb924fc82ee8ac156abbd83de06a5890e122501bc27a16e2ab718fb8d1bf1a02e8c64da37c018b84ebaec8386691acbc89552b23f4af629e71a035c49616fcee09433add2ac660f8ee8832bee594d7c2680619d8a0a1adf3eaea062f5a9bcd7a45d2be2545125c8ae2105678e7bf5d62bed603fd62defb0c1ed67a76c71122cf8bdecb2fa1dfc18df5305ba7a6d9e147252f4ad30fd540f615fad470f073979a67cdb1d5a4225ed0470f6d172ffd08c5eb0b8514867d025b64d18ead0f959f63ab07237fddd125d49e75ef0d981167370acab4469058bf14a9ac02085e227982a3cb9be387e5ca0f80eb196f8517c580d02ad7ff41441fd8a61d9d1dbcb535bd7b2437b05c04564d1716e95c8cd2d40529586966d210befce5750fa5983bbd0060d550f65b54ffe7390d45b23b10f77dac3990d8e15ac6495382cda6b5b002cf0f3c388d7763d031caaa4b42007752a9b720b6620189d27aa981f2f556bd2b13a891144534babc68972b9a16276865133e02c4864a574b384c8c4f9e68e7bcb95b0a1211e865ebbaa38d2dadf98cdaa12a64dac198bd256e541c6cb045a4d3c3569d1158cb81f69bbead18a7addd7b4ca2d2c5330f15e9482f532898f31c8d744" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "187e68d7c3d3664eada848c3edfc19defe50ee099eb430cea8933b67a6d7440e", + "proof_pk_com": "bfd25e18e4818ac6cdf07336c162412c6a07770cb2d9598bb9b3c4338f889e71", + "proof_r": "ee9ed1962f50ef09495cf5ed0cf0a8fb90f0fd70437d1e5a9f918ee1112f56d3", + "proof_ok": "c7473bff4b6ca801c0d4f0da828c20257dea8d7f39c7b8e2e499db5e791c3091", + "proof_s": "5dce37237fc16f96b2f850121e65711092c0ca1a78a68034876717f7e95ef704", + "proof_sb": "c77bbefd18f04f6a65ebd6a6f43e3a88416069e84a89893923d57c29d5835707", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf1f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "8ab4f2e1d93031a84b5dd83f3ab4e186a9b40316ad2d259ae237543dd6c7a86c50ca66ef3964edd35bf7a3a7e22b8c1baa988145aa6d81e40361364dadb6022b43ec0ea3cf68bfda3763746535b4afb4dae1b1488a87b2dc76b144e695ce9f58a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "a1387f6fe03ae28c6a74a991abc7141d2ac54365f22a0657c06f6fc7f8a5071862d732da566505281652417c71dc73b19107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84560e91584148d71fffb0ce57e7f9466c752841d5429fa7845c3d991fa46e1aa7ead037226ac082cc75ec9baaf86e6e97714905c461d7a716423b497819cfbb9f682568c0b4d8c292b9838f2ef8b0f56d10c8bf607ea84293412b8b949618f0764633c67b1e46f5052b497eb3bd27b8633e8b4e7c4288884b4f1e20ba29af6c372e2e1694ca15da900da6ee0020f66d5341f0d1e314505887942602995dab43d4a72c9ff68114073bbffec07fc317fdd71791b15c62100218af8af48fde665c97e3c04270e9755e592148b8a8e96770abb282d98f38f363120f2f6db43aed2a1872fd78e26785a2279dd307e3c5fb21887af554b41652b07604b3356a2709647f27cc604729fa82ca0843ff0ea80b81e7975765f1ee329ea66c27792acccc55112d51bf14f33d24b42ae14f44f86857399ecc301f100297e7f712d0ca53bc70ac8d8218e589a07096b97a09e5597d000656c7d2fefd109b545722c5bd9a390b18f815cc21295fd46253ed9591bbe1e1f1eb480aa72d5d1e8e2127a65937d9b9d2f607c87f0bcc553274fb7bf2307a418a42f9d138d0f2dac00f85a114de704549390f2f7b9ef6eb7c5eebb8200319ea71c94411faa34741ed633c563e8484b88cef3f0ac8ec9fd855a2e23657c339b99c090d3cd8f3a4df61c147a86fd7c7c82d7c0ef14b592be0be599abc42dedb71" + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "blinding": "cc07c57642d40b84be0c2741dff44254f2ebe46d506f6a19e6c5499edca31300", + "proof_pk_com": "2556f967c10bf28e415e2edcf6c0df0f02373117a00c4abd4f59690906348e5d", + "proof_r": "662b1a6a1acd770ebd8e78893cbdc0ba2b22d3377a4665bb2dde06bf0f5f7dbe", + "proof_ok": "c758b260c926cc5bc12c6d0532ed451bcdbe877e094fa065c906743cb0f5bea4", + "proof_s": "e444981f96a004a00d09ddb83c284468ed23107a6d92f20467427aa3caa7470e", + "proof_sb": "99c41f609962f251be9de67860727d57bca0d758fb0bc45ef07bd8d24c7ffa05", + "ring_pks": "a38c11dd4506b61ec3e10f0a333b4d63a394ab857ef63d3b438a2ddacbc51e53ba4d0766b48399d0c1be10101d25e77cbac06fa02bbfdeb0b8679417988549e923b7ca429a4842add257105d7dff1e445f03ccd6ad470bee8a72c16a23a84bf10a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf5feccfa5c9378d3620aca5c468fae4395a97aa64162274dfea0a311d9219dfead21f871f185ead62932bb3c5e7b92691447d0e342e4e4b703b7606fe31845929200c774be188623c9530985f7528b006ef0ffc08c25df3189f0a2415ceb4ea44378a413102a6c245a84b5ace37335724a04faae590e3bed6fa39082b3d5da82e", + "ring_pks_com": "b455ef5e11bc0823e41d302efc3cece1ea86d4a69fa5a706027fb8e795caa9fb691e28440a9f6ef1cbd5858b5a8477bdb91470764027057aae907b2b6567e787b84184949ed11eb5a3a17610f88af5257147e362b6ffd504240fb0bf41aae1c1a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b207cb16e97433954600bf42f1c9c6160cdf0fc064eca2ac8dcf6a41fe8969147a9cc8b3f1aba6680f9bf7fabcf97aae9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb907b180c459eebf99fd49349ebcaa17eda4de7d4a994d21962037d83acdf3991a93b23aacc3eb6f75bbb3aab5af78388664ee178a311f2a1d242becb503cdf65140efe7c11decad4478efa136f2a6ccdd47333101d88acc67bc7ce8091c6f9c9f06133f76fc101d6e2fd2309566148def97b451614225c1770c54922b407f331455e8714934e941697979d38d960bfc8775d68cc37a7c9bdc9f581478cbb551261a7f33fe55e796addd91144af12386aee4b3d9fae2abff9e006c72b81d266a41816e8adb131f64ddebad340b18e301e556e6a5e26a54cd3c4385ff96f2f3643f9c97dec5b3a62c59a99a926004381627d00d4cf0cc82f1a0f2300f9610e955afeae0d965cbfcbc66a124dad066411aabad57a0c0089fe9ea18f2f2b4c2cd4582d9f7e62b8e4caacf9a47c59ed1d32cbbbb401f121a6b88965204a47909f268afc26651596ceda53b15301c15d2ba57072ef61636f40db6a903f9e6a3a59f100254d4375a00f8a79bed773d0974d4fbd669a48f6f1a3b28bc523411fb868d250b77e8d7b11667093216bcad79805f0497a10c2918a8ad2f8f8fc58202c65bfff423a14cbb5cf5bd26208afa9f5a2d4b6874aeea79b06d03e974697213c80cc380acd3c989da3c0ffffaacd1cfc80bfb360d85a5ea5625ff4a223c0ed958f8f313bba4d977a9bd383db6d1b1b54b9267" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha-512_tai_thin.json b/tests/vectors/ark-vrf/jubjub_sha-512_tai_thin.json new file mode 100644 index 0000000..cc1cd41 --- /dev/null +++ b/tests/vectors/ark-vrf/jubjub_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "jubjub_sha-512_tai_thin - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "proof_r": "7e54a24273da1570053421a614988ffb6cb0b8e24ab4a66c169f32cd44a71692", + "proof_s": "a42f3b65b9b8c65451a0be2794eeb86c18776d62f11c55eaafbe69ced98b8b0d" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "proof_r": "ab2d2548c852aa9ec77744fc1f60234f709bbf609096410087faba7038912548", + "proof_s": "9ff42b229d20c64c7d9b13e5e2daeb4cd3b0d7c11ae3da94ab1e50784ec75108" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "proof_r": "fbcb6817e0eb5538e6950b84498af2ec5cebfa093153505c88b7b3af33a987b0", + "proof_s": "ada5918baeabac6222489accfa589972d03d6c4defafab161ba1e58523fc9e0d" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "proof_r": "4168db297f150523b64291215a5ac911855d2db50b5e9f179008ee555df208dd", + "proof_s": "56a8e685e1830e14cdcea99c87957e6df65bd0cd800aa562ceae1dfbb3e68307" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_r": "8d6d5e1d9701418504550c829bed07e271b367365c7ee47c3c15313b38fd84d8", + "proof_s": "4a0ea02c72504cf58472d0b5a716b1e1bc73528a3026056386e69ec1d267ba09" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_r": "a9e8ec9f65f47d7b8ac184c5c05417de02f172a585d31d7973fde1f2fc61f037", + "proof_s": "a4426bee50b4d06332240f6c0b5a1b46f4e47c507c0d6b0c89c3ca15d634940a" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "proof_r": "e2941c813264e454707e849b635f47fd81554843b91854b67caa9210d2eea089", + "proof_s": "a1de4f8e2ed19a8effe3a7e795152a334bdd21c2fbcb7a91efb807f091b92209" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha-512_tai_tiny.json b/tests/vectors/ark-vrf/jubjub_sha-512_tai_tiny.json new file mode 100644 index 0000000..9d358ae --- /dev/null +++ b/tests/vectors/ark-vrf/jubjub_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "jubjub_sha-512_tai_tiny - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "proof_c": "b2269d3fd834e21fe3506f3b44e61ee5", + "proof_s": "c8f85cb28e95b080b007ac0886552c9ead5687d71ba48442dd4ed2c16535fe01" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "proof_c": "8fd0ccaa2fbbf2657014bd57ddbf75bc", + "proof_s": "955227f7a1158f01dbc5089e599e4d63ac6132ad00c130d748f5e1379e3a9905" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "proof_c": "114362601668f4bfc59ea37b0c889042", + "proof_s": "6909e3b58008382d0e431be97c86d0ff07eb9a6cd6e58a4a4c338af9ef06ec01" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "proof_c": "983b3e5082b9b0f5f4d91617dee60dcd", + "proof_s": "b66b1f77cd46eaaa1b70ffc35394d57054241b6c9c2404697177e549ef7abc04" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "8837150613e8d5dca81e016bb76769b0", + "proof_s": "3f12d2589633c89100a44d260a4b1bf454e0ad009810b27827769c83cc040508" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "5c721d287ac42b0d3a06c6ec11204e71", + "proof_s": "42fabf57e2e146fe0ea0765e3e909c6ce9b7b801e024949f2a25838a10022405" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "proof_c": "f14e38cd954ed8440892f237a1c0ffe9", + "proof_s": "af179e80e9a7b854828a0b049cfaff27862690612fe8224dc7bf40bbdb85690c" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha512_tai_pedersen.json b/tests/vectors/ark-vrf/jubjub_sha512_tai_pedersen.json index 4a65932..546b94d 100644 --- a/tests/vectors/ark-vrf/jubjub_sha512_tai_pedersen.json +++ b/tests/vectors/ark-vrf/jubjub_sha512_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "jubjub_sha-512_tai_pedersen - vector-1", - "sk": "2e5fcb767f3f6528c6ff5304168c79bff667f830156433d7660fb98d72bc5a01", - "pk": "30f478ac3c74e78188193194fed53fb95ab9a5e3bb0b5177540d0e3668bfb4d6", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", "alpha": "", - "salt": "", "ad": "", - "h": "deb13729d88f7003e094dcef981aa8e62ce35d7dff20143c1ece67335dd23257", - "gamma": "33d5f173ef131ffa7cd137ced05e2374a78d085f8e0d0a8c2f64b5488e535ceb", - "beta": "0b5d3619b606a30f43d0b80f9b72d619caca4ae6c86afd6a7a54dd1a892ff34559279ca1341f177a2909cb97a2593ffc92399507867d29cb86615b83a4061d31", - "blinding": "3b7e8f330b99205fd5300cf18a094d3298df60cf5b4945107146d6407a2d2400", - "proof_pk_com": "97da69b45d5cd751fab9bda5271cd38221400531d0e16d3b2565f942bc81f05a", - "proof_r": "c040a9081484b38594a1c469c506267cc1fa45efcd9d8baeff621ad9bf3df72a", - "proof_ok": "b20a6c4458a41a16e6894f42fb3958c11864c8b75245f6bd031517ee9662111b", - "proof_s": "6187858dc1653c6f55bca63f16ef56291ed853a182bcfbdade22fa6c6603f707", - "proof_sb": "9d3ee6ee1936e1b8ea6f9707c7792bc29f1fa541850f42978043d66b465f7f0b" + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "blinding": "b411f199a190e143365b0100b6056affb910d956cbe203fa1f9d9ca7eba93b0c", + "proof_pk_com": "aa37fb22978457ede1d5c9a1cc146ee3fb16eaecd4badcffe9340dc3f1e1e19f", + "proof_r": "e0af508af99628925dc9b548430cd25c8580ce36df323dbc1822ae3297aa069f", + "proof_ok": "e34616acb7f465489575355fc52b58e2a846c00f976c442975571d1dc9679442", + "proof_s": "2918db8cf1d38144ddfff87cc170b5bcfbeabf421fe0f29a66529fb5b0257405", + "proof_sb": "805862bf392db5499502dbaa8e92e4b1f8d65f43de4b3012290e38d5e3756208" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-2", - "sk": "ca48b2a3649f62e7c240e22851cb514b57ca1dfad8de5e6d79f06329357bd008", - "pk": "308996eb46e494d162f29bd6abacf14685bd204eeaeefd78d226da240dab9e3c", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", "alpha": "0a", - "salt": "", "ad": "", - "h": "0602a39a45b8b51b28f4489367e3215d8886e650a7dcd8a21e7365a2bfa1994e", - "gamma": "703fa7dce4f7acd51a92b5e845af77babee0c9cf9a680901c621ff98b0637821", - "beta": "282c8073be0faba5ec8d75b7c69790d4032be1af17b00d32d44b1d23a5b0d4935333e66481a212c42a0c6165c7ba568c6203a6b7e687bf513559b998af718fa0", - "blinding": "31d20c401eda5170115ec3a267d5992051f90cfec778b5d1adb9d2dc840ee80b", - "proof_pk_com": "d7cf9ddc584e24d147148fc39e30fda1c6dd11a85d75a9754fa9cb278164f709", - "proof_r": "669af5bc935a578ed76f6ab83a3ab13a7ed1a23c82cd0f9851c140da5c9bbc0d", - "proof_ok": "e0ac0b52a36d31ae196cc9b446f14128cd3070e933d64035547a517fdf406d62", - "proof_s": "a931e47c8d599ac10ff0fe5f0d5f5648ca413e322d9c997ad452fc1c8b2c3803", - "proof_sb": "a95dc0c0f41cf435b92930833b52e7d8c5ba4fe7ceacd59b071eb9a8963c040b" + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "blinding": "84616b1b767b43ff53b1902474b9deca4416b45aa488928fe128719555505708", + "proof_pk_com": "138f9a70acc072087cd47ef5dab86b120636bcfb6a8e3879d46d614d2736c844", + "proof_r": "17ab4dbd47f7889cfb50e82594b38f5b24b6fe90517f2a11d3164932c7ea0f53", + "proof_ok": "005df5bc38e095b575f4e80307e92bf493dd9e6931b3eac1e86b8fa66e8d8773", + "proof_s": "fd721148c4808f0a518560eae6f7f7ff757f890855ac63d5c8346240aec3490c", + "proof_sb": "3559963241ea508e2deb30ceb9dc77b07c1a599604e1b8696b6d8d6839529f0a" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-3", - "sk": "d0286e69edde875b7d5587068d03ffda50d2af165574c7b29136962b81796603", - "pk": "b818097374bdcec279c69356de3398a240f2542bfbe07b339d56d0b452c16cc6", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "deb13729d88f7003e094dcef981aa8e62ce35d7dff20143c1ece67335dd23257", - "gamma": "28e78b819a6449ebeaa94866dbb93290177bb4c58fe9ac9dc5a82a8c871afd1a", - "beta": "d882d5ca4549f669da6c325957914bb19f0b7ee9eda29a8012cc2fde2b0e2f21ad321f6e6c2096582cae71af42f2ca345fb2dc3bf55cdabda35b9d937f54f89e", - "blinding": "76f0df1c062d482c842d43b092af5544d7108559683bbe14a3265f16e84e6006", - "proof_pk_com": "7cbb7a527393d031377a0457dd4a605c491781261dd1f17273f918c16299535c", - "proof_r": "c6038e97ebe9b0d5339d4ce0a734a1c05e7725da800d218d9389bb8028e2ec02", - "proof_ok": "c218481a881a21bd0ad10271ecf77c5ac7ce3731ead92bef8b4d28f2dff14f48", - "proof_s": "bad33d0fe96476d13cfcfb329263275c8592517fde6bea0475c261eb9ccc9805", - "proof_sb": "8f426c0be2db2f58a9488d7690a49f00d2de5cd374595d5f45dcfc7568a6bf04" + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "blinding": "86f342277fe5f88c92337b4e79594c6335dee5868b0a5c4542a0321dfa4fa608", + "proof_pk_com": "7536c30b71337821df6afa9887e769687ed89759aaa7f147898921ac6ac31b47", + "proof_r": "5d9cc07b9b9a98c193cdfe58e9e0e550cb26210ecf435ce1ad76c59511613234", + "proof_ok": "ec71d389fda2e9ee50c61b91acc704c5348039b47872419dadc9a25cb6ef8693", + "proof_s": "743e695720f3ceb5b52c978f5be8ecac8bd4389ff8c83e91a2a572df3af9ce0d", + "proof_sb": "a5e34e4383a074a21bbe09cef2d0424e8030f8e9543d45e02b6a3d6ed5d6e80b" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-4", - "sk": "0e141badfe43931575e134862426bc47b86440d2b2e643862c1250a1464a2203", - "pk": "205db8f7d735e7df59410f974e39d0c6797119034b090f749f0306ce7c27fb40", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "d9151cdd64a5d16bf3149694f13dfcbc7df5b3657f70e3aa8daa2f67b0e75ccf", - "gamma": "5014e883d3c7994ecac93af9e6a95aeca4d5fc3be5e77fac8d8af1f313b0b12a", - "beta": "6035d22c8348614fb393c2910e81412711ff1defcfd1939e5744cbec10600489979bbe25a47f1738cb4937d9a3a16914cef65563a57b9142638a151d4ea459ff", - "blinding": "cb35e984e03253ea63bdcf1ce52c54d60c85a9e0e6d6b771fae5148d5b0e4302", - "proof_pk_com": "bcae3d49b89cf385d2fc6edb899194c2f9938d6b970f2a40a6efe426f5724784", - "proof_r": "8a5e6ce55f659c43848414270e558423b5a42ca8b8d67c9b5f269997c407461f", - "proof_ok": "6e2ce4a6f4cf3b924f65eb71c3719f709c3d87f0a1ca6e295fe1078a792b00b9", - "proof_s": "e4ef3397d02e9b9b10292e81e0d0e17e53437443d2a03fca0ef3ff8225883f03", - "proof_sb": "e66e8da6a4f4ba895d56e8e0b5fc006e361c843423e987eb7ebaf4bbee20b30b" + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "blinding": "f2ac62314b4e1a23394c6547807a4ed6f658c056c2f13119f72562b45d324f03", + "proof_pk_com": "f0118d7b17f69f42a238d8f7ffd1b1d8c05a85cbe0c324da60ff2cb60b10c705", + "proof_r": "9bf9f2d0045fcaa887a9e31c852ccaec583df32bcb82a722dd1ce7cf106099ed", + "proof_ok": "c28d503817a895359e8d86c4dcdc5bfeeb02845936b05d6b01022d54a5104fbf", + "proof_s": "15a2b2036994955aebede90b405f0a5971807235a429390e48f2f851c1a61a0b", + "proof_sb": "bf72d5850b00d002707e2300a27801cc7f22c84fe6e1053cef66e19a4e98e103" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-5", - "sk": "c5b89638ae4eb16237a1d6f39d48687e9bf12733eb74c6b21edf871ca576fa02", - "pk": "8146083de63a51ca4c8c28126e5bed6439e3a2ad6c23307b96c030d3dabaffd6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "b6014d3da5289c81014b6b5fb077183f4fc67370d589b04703f331d497c21b5a", - "beta": "bb1f90f574cac7deaac1a52fab802e4305cceababa4d874fcf1e0c122f13ee966c05b375f7d48df5ae7d286cb470f26a0f10ff19abe4f3d90d568c27621b4f0b", - "blinding": "4589927d0d90f1e3e801a1ffc6d5c576b18e890e9c72141d3f0203f2768a8909", - "proof_pk_com": "e4b8106ea14e5fdb290239787013f1476d1215125dfd81fc0e509f067aa9049a", - "proof_r": "b3f03fe8cd7ff11840cde63b36d99fd3ecb83089234ec3eb3ff877a73c9f1480", - "proof_ok": "d2a9679ab878d18dce9360b8f021ed10110d5962e3dacdd17a9178c36c233ed6", - "proof_s": "f7f68fb226b905e76c4b05d962afa176e3f734c445d47ead6fdfc29019235106", - "proof_sb": "beb64e4b98b7c08cf2935eeacbf255f42e5597050fc99d4df96c1f104fbb700e" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "24ca3f6a30adddbec78a9e468f1eb09f214f06d086db71abea944f70555e0d03", + "proof_pk_com": "1ff2f4afef54d46de90292564b60c454a48132c806168a976061aa6223d35972", + "proof_r": "aa5c6254903a5050350581c1a9a54589cbb108cea0d4043a189fe6fc8c215904", + "proof_ok": "3b7a223f757f8fd4eeedd1fe28eb72852d4f007f383733f960726c4bcfcf9792", + "proof_s": "f4437660312f5331dabf71b3003e012fa6857bfcebc3b9bd84b734fbfd68ee06", + "proof_sb": "672158bac1e24e831816e417cf692452c8305f1b42a8f23eac4a49d10051d20b" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-6", - "sk": "c5b89638ae4eb16237a1d6f39d48687e9bf12733eb74c6b21edf871ca576fa02", - "pk": "8146083de63a51ca4c8c28126e5bed6439e3a2ad6c23307b96c030d3dabaffd6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "b6014d3da5289c81014b6b5fb077183f4fc67370d589b04703f331d497c21b5a", - "beta": "bb1f90f574cac7deaac1a52fab802e4305cceababa4d874fcf1e0c122f13ee966c05b375f7d48df5ae7d286cb470f26a0f10ff19abe4f3d90d568c27621b4f0b", - "blinding": "0b16ae8001479a5887fd3710f388b6b35be9b04eb4d62ba5717d0452a06f0f06", - "proof_pk_com": "296d6eeceaaabaaba6849fd46fd7abdf33a90b8bf79b3199f851b795db3089df", - "proof_r": "93a39efa2a45eb8a6d104f50155cb7e049249395a8d252726b5c76d4fdf0564b", - "proof_ok": "d2a9679ab878d18dce9360b8f021ed10110d5962e3dacdd17a9178c36c233ed6", - "proof_s": "97ab45760f9a3fb33d4136ee024e324e853809497083f72251d0125eaee96d08", - "proof_sb": "1477e1966975343b0b75bec7b4bf208230df702a5de23515ee2df63f2d9bc806" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "187e68d7c3d3664eada848c3edfc19defe50ee099eb430cea8933b67a6d7440e", + "proof_pk_com": "bfd25e18e4818ac6cdf07336c162412c6a07770cb2d9598bb9b3c4338f889e71", + "proof_r": "ee9ed1962f50ef09495cf5ed0cf0a8fb90f0fd70437d1e5a9f918ee1112f56d3", + "proof_ok": "c7473bff4b6ca801c0d4f0da828c20257dea8d7f39c7b8e2e499db5e791c3091", + "proof_s": "5dce37237fc16f96b2f850121e65711092c0ca1a78a68034876717f7e95ef704", + "proof_sb": "c77bbefd18f04f6a65ebd6a6f43e3a88416069e84a89893923d57c29d5835707" }, { "comment": "jubjub_sha-512_tai_pedersen - vector-7", - "sk": "d742adcfce2a341263158af472d390d680eef01422290420cc61107497597005", - "pk": "a1014ce47c0acb248907b95d8cac8427d56ec88f6b88e6efa85e9953a2ef2e6b", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "ef00ace4b541a74d06b05d03aa8447cb66a3a11aea47f26a0197ab7ff0498bb8", - "beta": "9b5d9daf0ae53d37fa2d1458005194e14c20520ede7fb12cb76b4b27743104bcbc71e6815708bf6136360c667515f5707ad68037b6efd32e59e17df962c8d10a", - "blinding": "b25bb1af5615454485805239c496939748815cfa6a340095e771016018a7db02", - "proof_pk_com": "26b7098b64f6ad2ed1b22f3491380f79927a2bb720df5e938291f00e52cecd9c", - "proof_r": "dc9aeebbecce230138055bfeda361ec1ccac090c8761ed2b59cdde183ce80066", - "proof_ok": "34d4ec9b618684a9b2a6f3ad50e6e5f1df5b64c79dff52ad3d1a26ee90f91c55", - "proof_s": "6bcf6a736e7e567c6b789476b99f48e3d45d84e69f7bc0d686c8e00086760007", - "proof_sb": "8b42b21a12f024b00e0bc81f7280816eee8377f412bf401ed2b49863a6c78505" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "blinding": "cc07c57642d40b84be0c2741dff44254f2ebe46d506f6a19e6c5499edca31300", + "proof_pk_com": "2556f967c10bf28e415e2edcf6c0df0f02373117a00c4abd4f59690906348e5d", + "proof_r": "662b1a6a1acd770ebd8e78893cbdc0ba2b22d3377a4665bb2dde06bf0f5f7dbe", + "proof_ok": "c758b260c926cc5bc12c6d0532ed451bcdbe877e094fa065c906743cb0f5bea4", + "proof_s": "e444981f96a004a00d09ddb83c284468ed23107a6d92f20467427aa3caa7470e", + "proof_sb": "99c41f609962f251be9de67860727d57bca0d758fb0bc45ef07bd8d24c7ffa05" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/jubjub_sha_512_tai_ietf.json b/tests/vectors/ark-vrf/jubjub_sha_512_tai_ietf.json index acbedc1..9d358ae 100644 --- a/tests/vectors/ark-vrf/jubjub_sha_512_tai_ietf.json +++ b/tests/vectors/ark-vrf/jubjub_sha_512_tai_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "jubjub_sha-512_tai_ietf - vector-1", - "sk": "2e5fcb767f3f6528c6ff5304168c79bff667f830156433d7660fb98d72bc5a01", - "pk": "30f478ac3c74e78188193194fed53fb95ab9a5e3bb0b5177540d0e3668bfb4d6", + "comment": "jubjub_sha-512_tai_tiny - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", "alpha": "", - "salt": "", "ad": "", - "h": "deb13729d88f7003e094dcef981aa8e62ce35d7dff20143c1ece67335dd23257", - "gamma": "33d5f173ef131ffa7cd137ced05e2374a78d085f8e0d0a8c2f64b5488e535ceb", - "beta": "0b5d3619b606a30f43d0b80f9b72d619caca4ae6c86afd6a7a54dd1a892ff34559279ca1341f177a2909cb97a2593ffc92399507867d29cb86615b83a4061d31", - "proof_c": "2b870aabdbe252ed892a6e1c35a8e879f3fa900aa74b0158a3baef1888e54e02", - "proof_s": "d7dc8916c042374ed5601171f7c3db1ac65e7528efd56894fc0b6e9754449801" + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "proof_c": "b2269d3fd834e21fe3506f3b44e61ee5", + "proof_s": "c8f85cb28e95b080b007ac0886552c9ead5687d71ba48442dd4ed2c16535fe01" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-2", - "sk": "ca48b2a3649f62e7c240e22851cb514b57ca1dfad8de5e6d79f06329357bd008", - "pk": "308996eb46e494d162f29bd6abacf14685bd204eeaeefd78d226da240dab9e3c", + "comment": "jubjub_sha-512_tai_tiny - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", "alpha": "0a", - "salt": "", "ad": "", - "h": "0602a39a45b8b51b28f4489367e3215d8886e650a7dcd8a21e7365a2bfa1994e", - "gamma": "703fa7dce4f7acd51a92b5e845af77babee0c9cf9a680901c621ff98b0637821", - "beta": "282c8073be0faba5ec8d75b7c69790d4032be1af17b00d32d44b1d23a5b0d4935333e66481a212c42a0c6165c7ba568c6203a6b7e687bf513559b998af718fa0", - "proof_c": "b88f4c1462bc7bdd70e51a28e4bc8d9f34bbf0a9a7c12aedbbc7a34193890e0c", - "proof_s": "51b8f86737125e2f58ad1ea56d3c6cf09fcda65e6435fbed57e75bf762673f09" + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "proof_c": "8fd0ccaa2fbbf2657014bd57ddbf75bc", + "proof_s": "955227f7a1158f01dbc5089e599e4d63ac6132ad00c130d748f5e1379e3a9905" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-3", - "sk": "d0286e69edde875b7d5587068d03ffda50d2af165574c7b29136962b81796603", - "pk": "b818097374bdcec279c69356de3398a240f2542bfbe07b339d56d0b452c16cc6", + "comment": "jubjub_sha-512_tai_tiny - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "deb13729d88f7003e094dcef981aa8e62ce35d7dff20143c1ece67335dd23257", - "gamma": "28e78b819a6449ebeaa94866dbb93290177bb4c58fe9ac9dc5a82a8c871afd1a", - "beta": "d882d5ca4549f669da6c325957914bb19f0b7ee9eda29a8012cc2fde2b0e2f21ad321f6e6c2096582cae71af42f2ca345fb2dc3bf55cdabda35b9d937f54f89e", - "proof_c": "a6cd47823711a71bd8851d2ce0254f40bfa51d496067892b73805b50a9bda206", - "proof_s": "73e1521fdf993ce4f4b09d8d376509a65668102f1bb0b34e2fc5de58d9a24104" + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "proof_c": "114362601668f4bfc59ea37b0c889042", + "proof_s": "6909e3b58008382d0e431be97c86d0ff07eb9a6cd6e58a4a4c338af9ef06ec01" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-4", - "sk": "0e141badfe43931575e134862426bc47b86440d2b2e643862c1250a1464a2203", - "pk": "205db8f7d735e7df59410f974e39d0c6797119034b090f749f0306ce7c27fb40", + "comment": "jubjub_sha-512_tai_tiny - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "d9151cdd64a5d16bf3149694f13dfcbc7df5b3657f70e3aa8daa2f67b0e75ccf", - "gamma": "5014e883d3c7994ecac93af9e6a95aeca4d5fc3be5e77fac8d8af1f313b0b12a", - "beta": "6035d22c8348614fb393c2910e81412711ff1defcfd1939e5744cbec10600489979bbe25a47f1738cb4937d9a3a16914cef65563a57b9142638a151d4ea459ff", - "proof_c": "3fc38d46d8dcbc9827aedbc3e0febc2f6cc3bc67db31fb66a23e4167ee41690b", - "proof_s": "e270d00318f80a6b75253ec475685ecf73c748b9209fb189159309d160160f07" + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "proof_c": "983b3e5082b9b0f5f4d91617dee60dcd", + "proof_s": "b66b1f77cd46eaaa1b70ffc35394d57054241b6c9c2404697177e549ef7abc04" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-5", - "sk": "c5b89638ae4eb16237a1d6f39d48687e9bf12733eb74c6b21edf871ca576fa02", - "pk": "8146083de63a51ca4c8c28126e5bed6439e3a2ad6c23307b96c030d3dabaffd6", + "comment": "jubjub_sha-512_tai_tiny - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "b6014d3da5289c81014b6b5fb077183f4fc67370d589b04703f331d497c21b5a", - "beta": "bb1f90f574cac7deaac1a52fab802e4305cceababa4d874fcf1e0c122f13ee966c05b375f7d48df5ae7d286cb470f26a0f10ff19abe4f3d90d568c27621b4f0b", - "proof_c": "c27de8ec3e87d002e2721d77ebf51b6d96122be70b9558f24a72a40c9dfe9a0c", - "proof_s": "cdeda57bd86400ac1444ce44a0f6ab107bb8a4c26c24e9d35d91b1cf005c800c" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "8837150613e8d5dca81e016bb76769b0", + "proof_s": "3f12d2589633c89100a44d260a4b1bf454e0ad009810b27827769c83cc040508" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-6", - "sk": "c5b89638ae4eb16237a1d6f39d48687e9bf12733eb74c6b21edf871ca576fa02", - "pk": "8146083de63a51ca4c8c28126e5bed6439e3a2ad6c23307b96c030d3dabaffd6", + "comment": "jubjub_sha-512_tai_tiny - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "b6014d3da5289c81014b6b5fb077183f4fc67370d589b04703f331d497c21b5a", - "beta": "bb1f90f574cac7deaac1a52fab802e4305cceababa4d874fcf1e0c122f13ee966c05b375f7d48df5ae7d286cb470f26a0f10ff19abe4f3d90d568c27621b4f0b", - "proof_c": "c55a0a2f5da77f4df3c98d8971256481d49ce7e00eb6d0373c850b0ac6b91706", - "proof_s": "31f283a6d0b632a43a72e856f5c1c2d0db108611564966b354ec9f0c5dd4610d" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "5c721d287ac42b0d3a06c6ec11204e71", + "proof_s": "42fabf57e2e146fe0ea0765e3e909c6ce9b7b801e024949f2a25838a10022405" }, { - "comment": "jubjub_sha-512_tai_ietf - vector-7", - "sk": "d742adcfce2a341263158af472d390d680eef01422290420cc61107497597005", - "pk": "a1014ce47c0acb248907b95d8cac8427d56ec88f6b88e6efa85e9953a2ef2e6b", + "comment": "jubjub_sha-512_tai_tiny - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4faa81c497e007f695dceca4d92abddf2f9d79aa298b5fad477079907fabe033", - "gamma": "ef00ace4b541a74d06b05d03aa8447cb66a3a11aea47f26a0197ab7ff0498bb8", - "beta": "9b5d9daf0ae53d37fa2d1458005194e14c20520ede7fb12cb76b4b27743104bcbc71e6815708bf6136360c667515f5707ad68037b6efd32e59e17df962c8d10a", - "proof_c": "ae387564a8455de2aba2a274ed2f06291764fadf6687ce67b463c1e28f7a660d", - "proof_s": "8d0e61ed17d9e14a8e53800cb69cea7df6072aa3d5974c010f05ecd22c961707" + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "proof_c": "f14e38cd954ed8440892f237a1c0ffe9", + "proof_s": "af179e80e9a7b854828a0b049cfaff27862690612fe8224dc7bf40bbdb85690c" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha-256_tai_pedersen.json b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_pedersen.json new file mode 100644 index 0000000..99aef28 --- /dev/null +++ b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", + "ad": "", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "blinding": "a0fe3ca2941f67f07b21864eddf7eb7b3725a13e02b9ad292693ca7fe5965646", + "proof_pk_com": "76880ba4627b1052ce9c62341d3504c96cf57f10fc6a86fd7fb19cede470ef3700", + "proof_r": "f4fd80e46c233708aa57ca6722ca44f05564bfb201136b6de709a4de9d4c080700", + "proof_ok": "2de01f0087982dd9ff13b6c9c12159826027be607239712b47fcbbd9801177d100", + "proof_s": "81736cfa96966fffde32f1b39e7c7c6916fa4a242d0771b9a75551226f0df7e8", + "proof_sb": "9b6eeed6228a15cfb502c282ad0df78437f53c63b4c96a6e9063e7af9aaee8dc" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", + "ad": "", + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "blinding": "7e6df180e6fe8c763f3e18488b6610c09fba9835b1991ff025283d2fe78639a9", + "proof_pk_com": "c5fbf7d0f6c011c82ea60f2c492cd2ea780cc17a0849333b1b65660bd0b51b6680", + "proof_r": "cd188ce4805b8664b15524809b72ee50d1b73c4c42bf45fc7dda5ec1dfc8789180", + "proof_ok": "a3a22d5ac446998e1b99b6e4dea8defbba4cdab7377fa690c155589a974036e600", + "proof_s": "66579415dd689f22d59e6ac47511f5556e69374b909ab7bc98b1871247cbb0e7", + "proof_sb": "7849433f58a18b9526c57f0e632bf281fdb97bfc372a835b0e32e2a3a79d45cb" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "blinding": "988699ef8ea68f263c0e4a04d0acb91c7cadf7d9b2d14609fe63c939d5f9a05a", + "proof_pk_com": "55f477728e6a70c8163ffb5e10f58f9dea174360202172772ff3043381b8372a80", + "proof_r": "cc7aeb499bde19f5f2033977ba059e7aad32d409ed0d5caada91b9ae91dafddb00", + "proof_ok": "a5ac3d436f6f034e9fdb718b1239cf43dba8940a0ff135f675fd262450f1e41b80", + "proof_s": "194b1c88b2c2191a7f242ae76d2aa3bf28fc9c66e42df1ebd8c7dff7ad0a91e7", + "proof_sb": "4b866b363824d4afa6ab752a8170797daf87ee8c15dcdf2173585c0a062506ad" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "blinding": "04f4b3fa241b685771301b59eb5f58ef34b16212aba9a6e749c854e091c37edb", + "proof_pk_com": "be9c2b7a3a9ce7b66ba3e2484d486d33880ab01a28b9ce4de91a2a06d440938f80", + "proof_r": "00f03b28429fdb737bbcbd9b99abbef7c890a78debaab05e01ed5ae4a911bccb00", + "proof_ok": "7d2e4efb526d5031e1d60d51d5accc44d748fc21b14d40d2b4dff47796fbef9100", + "proof_s": "58e9d92f02bd0493d9ef7dee882d3f504bf045d90dcfd89f1b0ab73fd352de95", + "proof_sb": "c3f5974dc3b9a0b95e006897c43ec5ce749a2f37ca2140d340522057d84c3f1c" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "9fbd401437473a215fd30bd97a23438c46b58991b4a987967168a957ac290be8", + "proof_pk_com": "b9cca35eec17e7c8633cd4e0bb72a9cc49e2a567b1ebefa840f80ad4fc6c0e2200", + "proof_r": "b4c0e181852ff3960fd7ae9ee95890625974fcd9f468de96908732587f504b8c80", + "proof_ok": "b98d9ee96bbd7a32e949edc7dd5e2bf74432cbb79569f06fc74dac4cee9fd62d80", + "proof_s": "e801a9455307447780ed6925886c944f27b815cb3a50259eb6097e67c9fb77af", + "proof_sb": "42e291c5bd51c7e1e5cf7ca7e1778918aabea74453579f7e879a609353de44c6" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "281e228d0bb4305724a2ea3a1a6c97c6b91b8b63ccc2716bf5ff541c8c43e2a9", + "proof_pk_com": "e0f2271ec3d66defa1441918018055ddfc34083d0f95fc8f3013663db5f2322e80", + "proof_r": "d33f6c7a2dc4cc87644d24f4e13389cc585b14934f6d1c897fe9f0c0c6be6b5c80", + "proof_ok": "1cfe9a872ac7c88eea71dfe2e15d8dd921718994a6088ccbefd994932edcc31a00", + "proof_s": "3c8938aa49fa3047bedb3a66c33f71dcd75e5745c2d93878578ebc9fb4e6cab6", + "proof_sb": "466e9c2e02e9594e87c9002228838f356e326640229f05d40f857614b3ad26f8" + }, + { + "comment": "secp256r1_sha-256_tai_pedersen - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "blinding": "756feac9778ae51176097de2a902ba8dd6b3ed32f9fb18ca05d6731fe0380111", + "proof_pk_com": "f8b560dcf1ecab375c77acbcd8bb3171b616a63cb852b8eaa7ea6b8ce3aaff8900", + "proof_r": "e91fe573671cab6cdc2b407c7a6b62e418a5943534e4d4a7b99290fe053ff55480", + "proof_ok": "eaf9046feb444cc58b5ad18a4a6eabee567d541ab7a38bad8ed19f4c53b66d1280", + "proof_s": "ca71aa0fc67e4841741b12061ba1bbeab1c68583e4e723ae10505cdca468c4f6", + "proof_sb": "144072c4c69a00d1daa83af385c9f426e16a4760ed7d9b6e834fec323350b267" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha-256_tai_thin.json b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_thin.json new file mode 100644 index 0000000..6cadf75 --- /dev/null +++ b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "secp256r1_sha-256_tai_thin - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", + "ad": "", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_r": "424e91586f512ae0b5f06692340436bbea6e59fb52dbcfb0f41bd8721b7c48b600", + "proof_s": "c6df155f8b9dd28d7539e10c194c442d32e401c563710f99c11d3a2fb0a7d431" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", + "ad": "", + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_r": "0660e1f457f31eda17d1bf61cacc9623ed5d41ec1c97b7ba37a00ae16e1936b500", + "proof_s": "1cfae0cc88e4239bc758702375a580e7ba81c048cf8cfc46f1564d6b397ae41f" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_r": "02e7f057abd05d9894c8b17fa414e0010ad3ff130e5cd7a4e6f1162a2dcec35180", + "proof_s": "a3f3eb3f2ff76aabaf390f5483f37cf7169b881f6e2fc10e2da005bccdbc477a" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_r": "08fc37baaad0a2aa712faeb8a03bf669d6094b338df0c83ad1282f132e34072d00", + "proof_s": "69e3bc059f57ffa2b1383ce7f99ace9eaa2d8a33165bcedc9d204b60f94c0fab" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_r": "794c512aa0e0fc54fb9c432588506d4bf4fb057b1bd8854d60c000fabcfe09ea00", + "proof_s": "c87538f6265457c6a452b242267443fbd4d228abcaf3951e9bb1fe5f28960a40" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_r": "2b464138e9ae2c8646fb5bc071f16fbc79980b51edd5b799ba5339b42ada6eef80", + "proof_s": "0c65f1810be63a68b7b17d0deefc624eabb3082f8953ec41f93c2dc9abe187d6" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_r": "3ee948cd1b3c5c22345f221377803935676872536e19f0b6289e7feab37bd09900", + "proof_s": "68bb0eb250e0428194e4a861414e278e0e4e9e7e5c213637ff72de846defda9d" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha-256_tai_tiny.json b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_tiny.json new file mode 100644 index 0000000..3557e08 --- /dev/null +++ b/tests/vectors/ark-vrf/secp256r1_sha-256_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "secp256r1_sha-256_tai_tiny - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", + "ad": "", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_c": "c1f5f3ea1b0ca19250084a3e9dc5bd72", + "proof_s": "87273dc88eafbfe092126d6e624165db553932cd7a78779b1e06184a265e3c22" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", + "ad": "", + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_c": "34e572fc2a1655dcd547695555331de1", + "proof_s": "2ff7d8e383b96f8038848f2f971af825ee88521b720a03babae0ab8cdabae95a" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_c": "860017b8a4fde275edc469c3a61415f0", + "proof_s": "8b64f17113614156f21c8a858135d04e3af8b1d5d8a7f32b38720745e0f864b5" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_c": "a7b7b6afd0cbdfb8420978d345bce0d3", + "proof_s": "e0a78bd2da5fb823043f5cee9bb42dffe8c74e1615fe798c93a9a3125fb51b69" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "dae5ea449f898c7047393098c2f03b83", + "proof_s": "251511bab28d16f21ffd1fae3a68484d54492498d70bc2f3fbadcc0668571696" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "38eba01c864b7b6b35d1e747a640ba57", + "proof_s": "4ce01d01e8e0f3576182f086be61089f14dd2e38484ebfd758e3f6a353f6fa27" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_c": "a801990255dce4885c7ec1899a20ed5a", + "proof_s": "5bf22e88a3dcbe68272dc5bb703f4b06167eff9aad8816b4dfc6fd2ab9e67f49" + } +] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf.json b/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf.json index 9f98fdc..3557e08 100644 --- a/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf.json +++ b/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf.json @@ -1,93 +1,86 @@ [ { - "comment": "secp256r1_sha-256_tai_ietf - vector-1", - "sk": "9a4585773ce2ccd7a585c331d60a60d1e3b7d28cbb2ede3bc55445342f12f54b", - "pk": "0218e11f9ef92fa3929aa8f078d2e73b58f0ac7d6f274e222b8515c7b0140138be", + "comment": "secp256r1_sha-256_tai_tiny - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", "alpha": "", - "salt": "", "ad": "", - "h": "02efe91dd6237dda2cd6f1b9cc31f1a69161366b9e5fe14d697d4a3a6841a92634", - "gamma": "034edfe3670c4121b5c49b7baf4f849a2de30850af2901fa4b0c33d3ed127d4fdc", - "beta": "32fba2fe215602128cf8fa898ff2da6d117111ecae65d2d30716ffe98cd49dfe", - "proof_c": "5f27b05aad38af700f881819816111ee", - "proof_s": "18da92d89d92aae8f418ed54eaa7495f5e04cc8835bcfff2210ae6c1b9bf753e" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_c": "c1f5f3ea1b0ca19250084a3e9dc5bd72", + "proof_s": "87273dc88eafbfe092126d6e624165db553932cd7a78779b1e06184a265e3c22" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-2", - "sk": "86d9576498ea764b49243efeb05df625010438c6a55d5b578de4ff00c9b4c1db", - "pk": "03a47366bc18483e02261117304cdf77cc95f234c16d11a8356dc4514a7adaf120", + "comment": "secp256r1_sha-256_tai_tiny - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", "alpha": "0a", - "salt": "", "ad": "", - "h": "02f5e3aaae0155c8211f9d0a08ebc2e7ea915813083ee44c797ea22dab60515f47", - "gamma": "03e69a44ea08d63f03277a9aa53999481a8ff473211210a7099c2b713ee16863f1", - "beta": "ce206ce1edc7dc23bc8db515aeb6a8fbbfd3045536ea7d7b1254ac0f9bbc5685", - "proof_c": "ede1e24bbab7a3b05d3ca65e2da161a3", - "proof_s": "3060920a0e67c660f459e0bab6f3f71e865aae07296eb114434072f95210bdc4" + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_c": "34e572fc2a1655dcd547695555331de1", + "proof_s": "2ff7d8e383b96f8038848f2f971af825ee88521b720a03babae0ab8cdabae95a" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-3", - "sk": "c529ffad9a5ab61162b11d616b639e00586ba846746a197d4daf78b908ed4f08", - "pk": "02d984ff7d61ba3a11d8ade6cea6ba949fba3bbcf841506cf8c3a7b6e1bd67b926", + "comment": "secp256r1_sha-256_tai_tiny - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "02efe91dd6237dda2cd6f1b9cc31f1a69161366b9e5fe14d697d4a3a6841a92634", - "gamma": "03cc4c70e3e4a787e14b841a819ade3de908211d9a42de5817f080d023b6408805", - "beta": "58016dcca4640763bf1914ceaeb5bf8394efe6c0e00b654b9b10491e7d65abc8", - "proof_c": "4a88aca885ddf35b93255bdc59911c8f", - "proof_s": "3f9e45b6a85f0e77fd57697c1eb92d00088d84226bc717945e2fb9e45f86da84" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_c": "860017b8a4fde275edc469c3a61415f0", + "proof_s": "8b64f17113614156f21c8a858135d04e3af8b1d5d8a7f32b38720745e0f864b5" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-4", - "sk": "719ec881a39ca062f09262ff75fc8a06d6cb91ad078c4d344723508c509c2de5", - "pk": "02c8f77b17ec60330366b9e148dfa565a7c8d6a0768e5737786722e3c85a5ff054", + "comment": "secp256r1_sha-256_tai_tiny - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "0245156290599051538dda121499cc4db77ef1c967cd1684a2090393442e7748b3", - "gamma": "038960a1861a4bcaa84f10c168ce34ed9f6ca08a31fdfe2e10c164b185eafb1e9d", - "beta": "10afc31299c3b70c45fd39459517e9ddcd597a9af1907807c72ee2f054107283", - "proof_c": "337a0e2d9813aa677cadc1babff29dd5", - "proof_s": "77cdda834d27790d337f1fc6ff81d794fb399ed3bd98ab5b28f10d9a8383d568" + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_c": "a7b7b6afd0cbdfb8420978d345bce0d3", + "proof_s": "e0a78bd2da5fb823043f5cee9bb42dffe8c74e1615fe798c93a9a3125fb51b69" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-5", - "sk": "db43b75a9c05eb89ae926b7b1d5081e79def64a210f5b6bd0d0be3e99a9a7be7", - "pk": "03c443ab7bbb42585044551b7efac1d9c3c8f007bdf0c325d35fde5417bbb8e11d", + "comment": "secp256r1_sha-256_tai_tiny - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "027e94a9c67159fc1fe9bdc7e0e85ea2eeced075486e12705db94580901d997de8", - "beta": "95c290716823f0d5f49c7b3ae4efe29d4c0acdba1373c8b8193fc66f39920555", - "proof_c": "780f0e7f60e35fbf23bcf7622a7c87f3", - "proof_s": "fe9838493a71efff78d4f1d406fccb3e874eeb39b4e901b773ddfcab85050f94" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "dae5ea449f898c7047393098c2f03b83", + "proof_s": "251511bab28d16f21ffd1fae3a68484d54492498d70bc2f3fbadcc0668571696" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-6", - "sk": "db43b75a9c05eb89ae926b7b1d5081e79def64a210f5b6bd0d0be3e99a9a7be7", - "pk": "03c443ab7bbb42585044551b7efac1d9c3c8f007bdf0c325d35fde5417bbb8e11d", + "comment": "secp256r1_sha-256_tai_tiny - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "027e94a9c67159fc1fe9bdc7e0e85ea2eeced075486e12705db94580901d997de8", - "beta": "95c290716823f0d5f49c7b3ae4efe29d4c0acdba1373c8b8193fc66f39920555", - "proof_c": "1b49441781d0230908eebbd6672d8da6", - "proof_s": "098191c8fe8b2c814fc83e9570b1811916492312e7488b5d1cdac6d5f5c8b781" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "38eba01c864b7b6b35d1e747a640ba57", + "proof_s": "4ce01d01e8e0f3576182f086be61089f14dd2e38484ebfd758e3f6a353f6fa27" }, { - "comment": "secp256r1_sha-256_tai_ietf - vector-7", - "sk": "f6ecc50886479df8bea823e5b8b939c934efa139c08b96b9a07dd2fa986e5867", - "pk": "0224d2dd7a97395b03103acb7ec1ae80743f453ff0711e6ad81bd9d59b7217809a", + "comment": "secp256r1_sha-256_tai_tiny - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "038e34a8ece8200f2f10ffca34aaa0fef748a9fbe9e24c149b5f8328a66f80c4b0", - "beta": "de55cf8d4a266aa0ca69f00a223d52bdbcb96cb3b8d0d2830159c81494f72f4c", - "proof_c": "77bfd3c3c15720c9d2b55f79cb560481", - "proof_s": "f534dcbb6395a343bb3e49247619f693c2acfec0421ab5e0b155b57d2f9d8a2e" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_c": "a801990255dce4885c7ec1899a20ed5a", + "proof_s": "5bf22e88a3dcbe68272dc5bb703f4b06167eff9aad8816b4dfc6fd2ab9e67f49" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf_rfc_9381.json b/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf_rfc_9381.json index 4f9959b..3557e08 100644 --- a/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf_rfc_9381.json +++ b/tests/vectors/ark-vrf/secp256r1_sha256_tai_ietf_rfc_9381.json @@ -1,44 +1,86 @@ [ { - "comment": "secp256r1_sha256_tai - rfc-9381-vector-10", - "flags": "00", - "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", - "pk": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", - "alpha": "73616D706C65", - "salt": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", + "comment": "secp256r1_sha-256_tai_tiny - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", "ad": "", - "h": "0272a877532e9ac193aff4401234266f59900a4a9e3fc3cfc6a4b7e467a15d06d4", - "gamma": "035b5c726e8c0e2c488a107c600578ee75cb702343c153cb1eb8dec77f4b5071b4", - "beta": "a3ad7b0ef73d8fc6655053ea22f9bede8c743f08bbed3d38821f0e16474b505e", - "proof_c": "a53f0a46f018bc2c56e58d383f2305e0", - "proof_s": "975972c26feea0eb122fe7893c15af376b33edf7de17c6ea056d4d82de6bc02f" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_c": "c1f5f3ea1b0ca19250084a3e9dc5bd72", + "proof_s": "87273dc88eafbfe092126d6e624165db553932cd7a78779b1e06184a265e3c22" }, { - "comment": "secp256r1_sha256_tai - rfc-9381-vector-11", - "flags": "00", - "sk": "c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721", - "pk": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", - "alpha": "74657374", - "salt": "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", + "comment": "secp256r1_sha-256_tai_tiny - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", "ad": "", - "h": "02173119b4fff5e6f8afed4868a29fe8920f1b54c2cf89cc7b301d0d473de6b974", - "gamma": "034dac60aba508ba0c01aa9be80377ebd7562c4a52d74722e0abae7dc3080ddb56", - "beta": "a284f94ceec2ff4b3794629da7cbafa49121972671b466cab4ce170aa365f26d", - "proof_c": "00000000000000000000000000000000c19e067b15a8a8174905b13617804534", - "proof_s": "214f935b94c2287f797e393eb0816969d864f37625b443f30f1a5a33f2b3c854" + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_c": "34e572fc2a1655dcd547695555331de1", + "proof_s": "2ff7d8e383b96f8038848f2f971af825ee88521b720a03babae0ab8cdabae95a" }, { - "comment": "secp256r1_sha256_tai - rfc-9381-vector-12", - "flags": "00", - "sk": "2ca1411a41b17b24cc8c3b089cfd033f1920202a6c0de8abb97df1498d50d2c8", - "pk": "03596375e6ce57e0f20294fc46bdfcfd19a39f8161b58695b3ec5b3d16427c274d", - "alpha": "4578616D706C65207573696E67204543445341206B65792066726F6D20417070656E646978204C2E342E32206F6620414E53492E58392D36322D32303035", - "salt": "03596375e6ce57e0f20294fc46bdfcfd19a39f8161b58695b3ec5b3d16427c274d", + "comment": "secp256r1_sha-256_tai_tiny - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_c": "860017b8a4fde275edc469c3a61415f0", + "proof_s": "8b64f17113614156f21c8a858135d04e3af8b1d5d8a7f32b38720745e0f864b5" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_c": "a7b7b6afd0cbdfb8420978d345bce0d3", + "proof_s": "e0a78bd2da5fb823043f5cee9bb42dffe8c74e1615fe798c93a9a3125fb51b69" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", "ad": "", - "h": "0258055c26c4b01d01c00fb57567955f7d39cd6f6e85fd37c58f696cc6b7aa761d", - "gamma": "03d03398bf53aa23831d7d1b2937e005fb0062cbefa06796579f2a1fc7e7b8c667", - "beta": "90871e06da5caa39a3c61578ebb844de8635e27ac0b13e829997d0d95dd98c19", - "proof_c": "00000000000000000000000000000000d091c00b0f5c3619d10ecea44363b5a5", - "proof_s": "99cadc5b2957e223fec62e81f7b4825fc799a771a3d7334b9186bdbee87316b1" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "dae5ea449f898c7047393098c2f03b83", + "proof_s": "251511bab28d16f21ffd1fae3a68484d54492498d70bc2f3fbadcc0668571696" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "38eba01c864b7b6b35d1e747a640ba57", + "proof_s": "4ce01d01e8e0f3576182f086be61089f14dd2e38484ebfd758e3f6a353f6fa27" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_c": "a801990255dce4885c7ec1899a20ed5a", + "proof_s": "5bf22e88a3dcbe68272dc5bb703f4b06167eff9aad8816b4dfc6fd2ab9e67f49" } ] \ No newline at end of file diff --git a/tests/vectors/ark-vrf/secp256r1_sha256_tai_pedersen.json b/tests/vectors/ark-vrf/secp256r1_sha256_tai_pedersen.json index c8186cf..99aef28 100644 --- a/tests/vectors/ark-vrf/secp256r1_sha256_tai_pedersen.json +++ b/tests/vectors/ark-vrf/secp256r1_sha256_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "secp256r1_sha-256_tai_pedersen - vector-1", - "sk": "9a4585773ce2ccd7a585c331d60a60d1e3b7d28cbb2ede3bc55445342f12f54b", - "pk": "0218e11f9ef92fa3929aa8f078d2e73b58f0ac7d6f274e222b8515c7b0140138be", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", "alpha": "", - "salt": "", "ad": "", - "h": "02efe91dd6237dda2cd6f1b9cc31f1a69161366b9e5fe14d697d4a3a6841a92634", - "gamma": "034edfe3670c4121b5c49b7baf4f849a2de30850af2901fa4b0c33d3ed127d4fdc", - "beta": "32fba2fe215602128cf8fa898ff2da6d117111ecae65d2d30716ffe98cd49dfe", - "blinding": "49c359ddec3eb3b3ba1c3ddc6407711b1d7f0e593dbfe1b7c0b71a59202b8d2a", - "proof_pk_com": "03c05394e3a2053dc03cedbcae45fa2f40d98e4be67213cfbb534cfb84baf74b0a", - "proof_r": "0258d4057e13f390adf08ec2467daef51d5c486d081e6b4128ef10805ee3963160", - "proof_ok": "024062eafb0510001c4f06cd331c75a2c657821dd7551e24ddf2c4d40cc52364b5", - "proof_s": "a3c1d39f85f025a4b73ea39fc2fc319f049d750f2f31fb5b4f8ca6464870eaf7", - "proof_sb": "9b6405578b722ad1f4fe712d03bda143d22588ef0ce37b4fc806af8e3c48912d" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "blinding": "a0fe3ca2941f67f07b21864eddf7eb7b3725a13e02b9ad292693ca7fe5965646", + "proof_pk_com": "76880ba4627b1052ce9c62341d3504c96cf57f10fc6a86fd7fb19cede470ef3700", + "proof_r": "f4fd80e46c233708aa57ca6722ca44f05564bfb201136b6de709a4de9d4c080700", + "proof_ok": "2de01f0087982dd9ff13b6c9c12159826027be607239712b47fcbbd9801177d100", + "proof_s": "81736cfa96966fffde32f1b39e7c7c6916fa4a242d0771b9a75551226f0df7e8", + "proof_sb": "9b6eeed6228a15cfb502c282ad0df78437f53c63b4c96a6e9063e7af9aaee8dc" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-2", - "sk": "86d9576498ea764b49243efeb05df625010438c6a55d5b578de4ff00c9b4c1db", - "pk": "03a47366bc18483e02261117304cdf77cc95f234c16d11a8356dc4514a7adaf120", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", "alpha": "0a", - "salt": "", "ad": "", - "h": "02f5e3aaae0155c8211f9d0a08ebc2e7ea915813083ee44c797ea22dab60515f47", - "gamma": "03e69a44ea08d63f03277a9aa53999481a8ff473211210a7099c2b713ee16863f1", - "beta": "ce206ce1edc7dc23bc8db515aeb6a8fbbfd3045536ea7d7b1254ac0f9bbc5685", - "blinding": "41aee80966787a715d9b0c793a8f1b595dd153273fdf8f88a32aa2fa85ebbc9f", - "proof_pk_com": "02bcc733a50b18daa1b4054f308fbb34ae9eeaed6e5f00f7f2913e1b5813e1609d", - "proof_r": "023bc839d271ddf9fd9943e766373b14c1d5f2f2ecb9f58c707b89afa2101f09b6", - "proof_ok": "030ecafabe459b7ba212efa9af54cf0d4b81a68a1b8d1e3581ed13ebdf221da766", - "proof_s": "1329fa3ad770c8d233a0853fd41301af6562d77423ece14522d24b57c711b4d6", - "proof_sb": "c5cfce2167a1c4e8d3339710c2701bd6613c1d67232bdeab1cd76418f98d4559" + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "blinding": "7e6df180e6fe8c763f3e18488b6610c09fba9835b1991ff025283d2fe78639a9", + "proof_pk_com": "c5fbf7d0f6c011c82ea60f2c492cd2ea780cc17a0849333b1b65660bd0b51b6680", + "proof_r": "cd188ce4805b8664b15524809b72ee50d1b73c4c42bf45fc7dda5ec1dfc8789180", + "proof_ok": "a3a22d5ac446998e1b99b6e4dea8defbba4cdab7377fa690c155589a974036e600", + "proof_s": "66579415dd689f22d59e6ac47511f5556e69374b909ab7bc98b1871247cbb0e7", + "proof_sb": "7849433f58a18b9526c57f0e632bf281fdb97bfc372a835b0e32e2a3a79d45cb" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-3", - "sk": "c529ffad9a5ab61162b11d616b639e00586ba846746a197d4daf78b908ed4f08", - "pk": "02d984ff7d61ba3a11d8ade6cea6ba949fba3bbcf841506cf8c3a7b6e1bd67b926", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "02efe91dd6237dda2cd6f1b9cc31f1a69161366b9e5fe14d697d4a3a6841a92634", - "gamma": "03cc4c70e3e4a787e14b841a819ade3de908211d9a42de5817f080d023b6408805", - "beta": "58016dcca4640763bf1914ceaeb5bf8394efe6c0e00b654b9b10491e7d65abc8", - "blinding": "1270ded4474f2a6a55e5989a00fa9abd7509a886278ce3d04ceabaff691ed7e7", - "proof_pk_com": "029faa59b5a4e4040ecf5fb9c589cb7e7227f439048eee71eff58dffd3af9c2fb8", - "proof_r": "0278644cc97eb84fb3020eaa24d811ccd677ec0f0ac935210cdb0ce36208688eb6", - "proof_ok": "03d969d5150672b88c99c86e81aa0ee22a59cd1059e1bbeb7db43b529f5b2afd96", - "proof_s": "eac092e24eca44bb9e4b647999d127a68c836a588a43b7c61a6036956213f283", - "proof_sb": "611d08b75146bd1c22b6d5ce499fa31a2ee1b80acd284007951bdec8871b06f5" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "blinding": "988699ef8ea68f263c0e4a04d0acb91c7cadf7d9b2d14609fe63c939d5f9a05a", + "proof_pk_com": "55f477728e6a70c8163ffb5e10f58f9dea174360202172772ff3043381b8372a80", + "proof_r": "cc7aeb499bde19f5f2033977ba059e7aad32d409ed0d5caada91b9ae91dafddb00", + "proof_ok": "a5ac3d436f6f034e9fdb718b1239cf43dba8940a0ff135f675fd262450f1e41b80", + "proof_s": "194b1c88b2c2191a7f242ae76d2aa3bf28fc9c66e42df1ebd8c7dff7ad0a91e7", + "proof_sb": "4b866b363824d4afa6ab752a8170797daf87ee8c15dcdf2173585c0a062506ad" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-4", - "sk": "719ec881a39ca062f09262ff75fc8a06d6cb91ad078c4d344723508c509c2de5", - "pk": "02c8f77b17ec60330366b9e148dfa565a7c8d6a0768e5737786722e3c85a5ff054", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "0245156290599051538dda121499cc4db77ef1c967cd1684a2090393442e7748b3", - "gamma": "038960a1861a4bcaa84f10c168ce34ed9f6ca08a31fdfe2e10c164b185eafb1e9d", - "beta": "10afc31299c3b70c45fd39459517e9ddcd597a9af1907807c72ee2f054107283", - "blinding": "4df12778c4356ffc13d457a16852f82341dbc81f03716bd5b667902c299d02ce", - "proof_pk_com": "037f5fabacaf4f7aad20f3386979e5dc95c1f1dd359f025935011ae0f1a4f068c1", - "proof_r": "03f078fb7cf7b20683e89e6b1fee5d179fa5fc9b71b89bf68236ad49f677e3ac73", - "proof_ok": "03ba4a59bfd54fdbc2b422bed7ba0e6eef059dee8044b717f35b5de871a08e3fff", - "proof_s": "e08e2dee66f54f12a6e86484bf43accf34051d6a49cf58395f48dd7ac486e2d2", - "proof_sb": "9efbc04b1969c69fa52253b9a4a72bb99e623d229a47aa2277fb1672fd5d02b6" + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "blinding": "04f4b3fa241b685771301b59eb5f58ef34b16212aba9a6e749c854e091c37edb", + "proof_pk_com": "be9c2b7a3a9ce7b66ba3e2484d486d33880ab01a28b9ce4de91a2a06d440938f80", + "proof_r": "00f03b28429fdb737bbcbd9b99abbef7c890a78debaab05e01ed5ae4a911bccb00", + "proof_ok": "7d2e4efb526d5031e1d60d51d5accc44d748fc21b14d40d2b4dff47796fbef9100", + "proof_s": "58e9d92f02bd0493d9ef7dee882d3f504bf045d90dcfd89f1b0ab73fd352de95", + "proof_sb": "c3f5974dc3b9a0b95e006897c43ec5ce749a2f37ca2140d340522057d84c3f1c" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-5", - "sk": "db43b75a9c05eb89ae926b7b1d5081e79def64a210f5b6bd0d0be3e99a9a7be7", - "pk": "03c443ab7bbb42585044551b7efac1d9c3c8f007bdf0c325d35fde5417bbb8e11d", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "027e94a9c67159fc1fe9bdc7e0e85ea2eeced075486e12705db94580901d997de8", - "beta": "95c290716823f0d5f49c7b3ae4efe29d4c0acdba1373c8b8193fc66f39920555", - "blinding": "03ccde8db52a23a05622cb3de3b28fe163bea752f995ac76f27b1b4dd12c85c3", - "proof_pk_com": "025f1ad7ca0ea49ee76972247a4e37064fc04685adb04deb266a0111ac8acecbe4", - "proof_r": "02913e7047a25344a416e788ba6b9b613f2c43a2cec7fe05210b532c32d8e10268", - "proof_ok": "029ecdc5545f5638369f56e6438ca256e402ad5b1c3aecbeddd8687ed40e8b8222", - "proof_s": "11d7baad33cdbcccf1bdb36a88d6c79bd6808920ca4ca46b52760f4ae70f04fc", - "proof_sb": "961936fc56be01c4ab739cb7057af7716df4bfd5e3db2ab15060e9fdb9c7a1ef" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "9fbd401437473a215fd30bd97a23438c46b58991b4a987967168a957ac290be8", + "proof_pk_com": "b9cca35eec17e7c8633cd4e0bb72a9cc49e2a567b1ebefa840f80ad4fc6c0e2200", + "proof_r": "b4c0e181852ff3960fd7ae9ee95890625974fcd9f468de96908732587f504b8c80", + "proof_ok": "b98d9ee96bbd7a32e949edc7dd5e2bf74432cbb79569f06fc74dac4cee9fd62d80", + "proof_s": "e801a9455307447780ed6925886c944f27b815cb3a50259eb6097e67c9fb77af", + "proof_sb": "42e291c5bd51c7e1e5cf7ca7e1778918aabea74453579f7e879a609353de44c6" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-6", - "sk": "db43b75a9c05eb89ae926b7b1d5081e79def64a210f5b6bd0d0be3e99a9a7be7", - "pk": "03c443ab7bbb42585044551b7efac1d9c3c8f007bdf0c325d35fde5417bbb8e11d", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "027e94a9c67159fc1fe9bdc7e0e85ea2eeced075486e12705db94580901d997de8", - "beta": "95c290716823f0d5f49c7b3ae4efe29d4c0acdba1373c8b8193fc66f39920555", - "blinding": "3a8ddc7ff9fb95a4f749095bffd35f87296edf52138c62574f8978b0acd8ec5e", - "proof_pk_com": "03d10b350b99d8271749957dbf81ee795f15ad7ebec0c825fea748aeb640fa98b8", - "proof_r": "02ff5214a8fd58f518d7f479d4a89e94f285fdef225ab5f13ec080c88076417512", - "proof_ok": "029ecdc5545f5638369f56e6438ca256e402ad5b1c3aecbeddd8687ed40e8b8222", - "proof_s": "54e5c354a9bd219bc373a6718e37dff5b8e0f28e2ab6808c70cd7a44afd7d564", - "proof_sb": "7bd08b90b6dcabb5208fc7975e5b60ca10893de8c47f4e5872434dacd5a3a6e7" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "281e228d0bb4305724a2ea3a1a6c97c6b91b8b63ccc2716bf5ff541c8c43e2a9", + "proof_pk_com": "e0f2271ec3d66defa1441918018055ddfc34083d0f95fc8f3013663db5f2322e80", + "proof_r": "d33f6c7a2dc4cc87644d24f4e13389cc585b14934f6d1c897fe9f0c0c6be6b5c80", + "proof_ok": "1cfe9a872ac7c88eea71dfe2e15d8dd921718994a6088ccbefd994932edcc31a00", + "proof_s": "3c8938aa49fa3047bedb3a66c33f71dcd75e5745c2d93878578ebc9fb4e6cab6", + "proof_sb": "466e9c2e02e9594e87c9002228838f356e326640229f05d40f857614b3ad26f8" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-7", - "sk": "f6ecc50886479df8bea823e5b8b939c934efa139c08b96b9a07dd2fa986e5867", - "pk": "0224d2dd7a97395b03103acb7ec1ae80743f453ff0711e6ad81bd9d59b7217809a", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "02fee01f6fc82a668e037ae128a1075ac507888676eb50c5f1da3e6b6b91cba6c9", - "gamma": "038e34a8ece8200f2f10ffca34aaa0fef748a9fbe9e24c149b5f8328a66f80c4b0", - "beta": "de55cf8d4a266aa0ca69f00a223d52bdbcb96cb3b8d0d2830159c81494f72f4c", - "blinding": "92c4396187b806a6670fb020b68d0706fa649fd2f9713058b332c3b394e38cd5", - "proof_pk_com": "0367dfdcef249741cbe3407ef679b95259aaba58968b6be800777283828eaa91e6", - "proof_r": "0234a5a1d0ef72c1ddfb6b2d325efc82ace08e310e49cfe9c1d2e2471f852a8129", - "proof_ok": "0387862be1ef0ed5d72090c1bfcc80b822980ae04973281a8aee011908e818e138", - "proof_s": "f960fda0507cd500156b135aaf7225aeef6b1f3ffc9c4d68fe402352ee9dea89", - "proof_sb": "b0aad34cfe34c8d9af80dda7d53c31d0dd89020c0bd6d41829cda9ce533d672e" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "blinding": "756feac9778ae51176097de2a902ba8dd6b3ed32f9fb18ca05d6731fe0380111", + "proof_pk_com": "f8b560dcf1ecab375c77acbcd8bb3171b616a63cb852b8eaa7ea6b8ce3aaff8900", + "proof_r": "e91fe573671cab6cdc2b407c7a6b62e418a5943534e4d4a7b99290fe053ff55480", + "proof_ok": "eaf9046feb444cc58b5ad18a4a6eabee567d541ab7a38bad8ed19f4c53b66d1280", + "proof_s": "ca71aa0fc67e4841741b12061ba1bbeab1c68583e4e723ae10505cdca468c4f6", + "proof_sb": "144072c4c69a00d1daa83af385c9f426e16a4760ed7d9b6e834fec323350b267" } ] \ No newline at end of file From e6340b27cfd140832fa1bd6f0a8ce8f19db34f8f Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:47:00 +0530 Subject: [PATCH 07/33] test: generate dot-ring verification vectors --- scripts/generate_test_vectors.py | 862 ++++-------------- tests/test_dot_ring_vectors.py | 93 ++ tests/vectors/dot-ring/README.md | 22 + .../baby-jubjub_sha-512_tai_pedersen.json | 114 +++ .../baby-jubjub_sha-512_tai_ring.json | 149 +++ .../baby-jubjub_sha-512_tai_thin.json | 86 ++ .../baby-jubjub_sha-512_tai_tiny.json | 86 ++ .../bandersnatch_sha-512_ell2_ietf.json | 93 -- ...ersnatch_sha-512_ell2_ietf_edge_cases.json | 171 ---- .../bandersnatch_sha-512_ell2_pedersen.json | 161 ++-- ...atch_sha-512_ell2_pedersen_edge_cases.json | 155 ---- .../bandersnatch_sha-512_ell2_ring.json | 210 ++--- ...ersnatch_sha-512_ell2_ring_edge_cases.json | 107 --- .../bandersnatch_sha-512_ell2_thin.json | 86 ++ .../bandersnatch_sha-512_ell2_tiny.json | 86 ++ .../bandersnatch_shake128_ell2_pedersen.json | 114 +++ .../bandersnatch_shake128_ell2_ring.json | 149 +++ .../bandersnatch_shake128_ell2_thin.json | 86 ++ .../bandersnatch_shake128_ell2_tiny.json | 86 ++ .../bandersnatch_sw_sha-512_tai_pedersen.json | 114 +++ .../bandersnatch_sw_sha-512_tai_ring.json | 149 +++ .../bandersnatch_sw_sha-512_tai_thin.json | 86 ++ .../bandersnatch_sw_sha-512_tai_tiny.json | 86 ++ .../dot-ring/ed25519_sha-512_tai_ietf.json | 93 -- .../ed25519_sha-512_tai_pedersen.json | 161 ++-- .../dot-ring/ed25519_sha-512_tai_thin.json | 86 ++ .../dot-ring/ed25519_sha-512_tai_tiny.json | 86 ++ .../dot-ring/jubjub_sha-512_tai_pedersen.json | 114 +++ .../dot-ring/jubjub_sha-512_tai_ring.json | 149 +++ .../dot-ring/jubjub_sha-512_tai_thin.json | 86 ++ .../dot-ring/jubjub_sha-512_tai_tiny.json | 86 ++ .../dot-ring/negative_tampered_proofs.json | 20 - .../dot-ring/negative_wrong_inputs.json | 29 - .../dot-ring/secp256r1_sha-256_tai_ietf.json | 93 -- .../secp256r1_sha-256_tai_pedersen.json | 161 ++-- .../dot-ring/secp256r1_sha-256_tai_thin.json | 86 ++ .../dot-ring/secp256r1_sha-256_tai_tiny.json | 86 ++ 37 files changed, 2897 insertions(+), 1790 deletions(-) create mode 100644 tests/test_dot_ring_vectors.py create mode 100644 tests/vectors/dot-ring/README.md create mode 100644 tests/vectors/dot-ring/baby-jubjub_sha-512_tai_pedersen.json create mode 100644 tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json create mode 100644 tests/vectors/dot-ring/baby-jubjub_sha-512_tai_thin.json create mode 100644 tests/vectors/dot-ring/baby-jubjub_sha-512_tai_tiny.json delete mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf.json delete mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf_edge_cases.json delete mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen_edge_cases.json delete mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring_edge_cases.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_thin.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sha-512_ell2_tiny.json create mode 100644 tests/vectors/dot-ring/bandersnatch_shake128_ell2_pedersen.json create mode 100644 tests/vectors/dot-ring/bandersnatch_shake128_ell2_ring.json create mode 100644 tests/vectors/dot-ring/bandersnatch_shake128_ell2_thin.json create mode 100644 tests/vectors/dot-ring/bandersnatch_shake128_ell2_tiny.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_pedersen.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_ring.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_thin.json create mode 100644 tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_tiny.json delete mode 100644 tests/vectors/dot-ring/ed25519_sha-512_tai_ietf.json create mode 100644 tests/vectors/dot-ring/ed25519_sha-512_tai_thin.json create mode 100644 tests/vectors/dot-ring/ed25519_sha-512_tai_tiny.json create mode 100644 tests/vectors/dot-ring/jubjub_sha-512_tai_pedersen.json create mode 100644 tests/vectors/dot-ring/jubjub_sha-512_tai_ring.json create mode 100644 tests/vectors/dot-ring/jubjub_sha-512_tai_thin.json create mode 100644 tests/vectors/dot-ring/jubjub_sha-512_tai_tiny.json delete mode 100644 tests/vectors/dot-ring/negative_tampered_proofs.json delete mode 100644 tests/vectors/dot-ring/negative_wrong_inputs.json delete mode 100644 tests/vectors/dot-ring/secp256r1_sha-256_tai_ietf.json create mode 100644 tests/vectors/dot-ring/secp256r1_sha-256_tai_thin.json create mode 100644 tests/vectors/dot-ring/secp256r1_sha-256_tai_tiny.json diff --git a/scripts/generate_test_vectors.py b/scripts/generate_test_vectors.py index 074808d..df5dabb 100644 --- a/scripts/generate_test_vectors.py +++ b/scripts/generate_test_vectors.py @@ -1,706 +1,224 @@ #!/usr/bin/env python3 -""" -Generate test vectors for dot-ring VRF implementations. - -This script generates test vectors compatible with the ark-vrf format, -covering IETF, Pedersen, and Ring VRF schemes. - -Edge cases covered: -- Empty input (alpha = "") -- Single byte input -- Additional data variations -- Different key positions in ring -- Edge case scalar values -- Same key with different inputs -- Same input with different keys -- Maximum and minimum scalar values -- Keys not in ring (negative test) -- Duplicate keys in ring - -Run from project root: python scripts/generate_test_vectors.py -""" - -import hashlib +"""Generate dot-ring vectors using canonical JSON fields.""" + +from __future__ import annotations + +import argparse import json from dataclasses import dataclass from pathlib import Path -from typing import Any, Literal, cast - -from dot_ring import IETF_VRF, P256, Bandersnatch, Ed25519, PedersenVRF, RingVRF -from dot_ring.ring_proof.helpers import Helpers - -# Alias for clarity -Secp256r1 = P256 - - -# Output directory for vectors -VECTORS_DIR = Path(__file__).parent.parent / "tests" / "vectors" / "dot-ring" - - -@dataclass -class BaseTestVector: - """Base test vector with common fields.""" - - comment: str - sk: str # hex-encoded secret key - pk: str # hex-encoded public key - alpha: str # hex-encoded input - salt: str # hex-encoded salt - ad: str # hex-encoded additional data - h: str # hex-encoded input point (hash-to-curve result) - gamma: str # hex-encoded output point - beta: str # hex-encoded output hash - - -@dataclass -class IETFTestVector(BaseTestVector): - """IETF VRF test vector.""" - - proof_c: str # hex-encoded challenge - proof_s: str # hex-encoded response - - -@dataclass -class PedersenTestVector(BaseTestVector): - """Pedersen VRF test vector.""" - - blinding: str # hex-encoded blinding factor - proof_pk_com: str # hex-encoded blinded public key - proof_r: str # hex-encoded R point - proof_ok: str # hex-encoded Ok point - proof_s: str # hex-encoded s scalar - proof_sb: str # hex-encoded sb scalar - - -@dataclass -class RingTestVector(PedersenTestVector): - """Ring VRF test vector.""" - - ring_pks: str # hex-encoded concatenated ring public keys - ring_pks_com: str # hex-encoded ring commitment - ring_proof: str # hex-encoded ring proof - prover_idx: int # prover's position in ring - - -class TestVectorGenerator: - """Generate test vectors for VRF implementations.""" - - curve: Any - suite_prefix: str - - def __init__(self, curve_name: str = "bandersnatch"): - self.curve_name = curve_name - self.curve: Any = None - if curve_name == "bandersnatch": - self.curve = Bandersnatch - self.suite_prefix = "bandersnatch_sha-512_ell2" - elif curve_name == "ed25519": - self.curve = Ed25519 - self.suite_prefix = "ed25519_sha-512_tai" - elif curve_name == "secp256r1": - self.curve = Secp256r1 - self.suite_prefix = "secp256r1_sha-256_tai" - else: - raise ValueError(f"Unsupported curve: {curve_name}") - - def _secret_from_seed(self, seed: int) -> bytes: - """Generate deterministic secret key from seed.""" - # Use seed to create deterministic but valid secret key - seed_bytes = seed.to_bytes(32, "little") - # Hash to get uniform distribution - # The original line was: sk_int = int.from_bytes(hashlib.sha256(seed_bytes).digest(), "little") - # The instruction provided a malformed line. - # Assuming the intent was to use os.urandom for a random secret key, - # but for deterministic test vectors, the seed-based approach is correct. - # If a random secret key was intended, it would look like this: - # n_bytes = (self.curve.curve.ORDER.bit_length() + 7) // 8 - # sk_int = cast(int, int.from_bytes(os.urandom(n_bytes), "big") % self.curve.curve.ORDER) - # Sticking to the original deterministic logic for test vector generation. - sk_int = int.from_bytes(hashlib.sha256(seed_bytes).digest(), "little") - sk_int = sk_int % self.curve.curve.ORDER - if sk_int == 0: - sk_int = 1 # Avoid zero secret key - return sk_int.to_bytes(32, self.curve.curve.ENDIAN) - - def _get_scalar_len(self) -> int: - """Get scalar length in bytes for the curve.""" - return cast(int, (self.curve.curve.PRIME_FIELD.bit_length() + 7) // 8) - - def _get_point_len(self) -> int: - """Get point length in bytes for the curve.""" - point_len = self.curve.curve.POINT_LEN - if self.curve.curve.UNCOMPRESSED: - point_len *= 2 - return cast(int, point_len) - - def generate_ietf_vector(self, comment: str, seed: int, alpha: bytes, salt: bytes, ad: bytes) -> dict[str, Any]: - """Generate a single IETF VRF test vector.""" - sk = self._secret_from_seed(seed) - curve = self.curve - pk = IETF_VRF[curve].get_public_key(sk) # type: ignore - - # Generate proof - proof = IETF_VRF[curve].prove(alpha, sk, ad, salt) # type: ignore - - # Get input point (h) - h = IETF_VRF[curve].cv.point.encode_to_curve(alpha, salt) # type: ignore - - # Get output hash (beta) - beta = IETF_VRF[curve].proof_to_hash(proof.output_point) # type: ignore - - # Serialize challenge with proper length - challenge_len = self.curve.curve.CHALLENGE_LENGTH - c_bytes = Helpers.int_to_str( - proof.c, - cast(Literal["little", "big"], self.curve.curve.ENDIAN), - challenge_len, - ) - - scalar_len = self._get_scalar_len() - s_bytes = Helpers.int_to_str(proof.s, self.curve.curve.ENDIAN, scalar_len) - - return { - "comment": comment, - "sk": sk.hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "salt": salt.hex(), - "ad": ad.hex(), - "h": h.point_to_string().hex(), - "gamma": proof.output_point.point_to_string().hex(), - "beta": beta.hex(), - "proof_c": c_bytes.hex(), - "proof_s": s_bytes.hex(), - } - - def generate_pedersen_vector(self, comment: str, seed: int, alpha: bytes, salt: bytes, ad: bytes) -> dict[str, Any]: - """Generate a single Pedersen VRF test vector.""" - sk = self._secret_from_seed(seed) - curve = self.curve - pk = PedersenVRF[curve].get_public_key(sk) # type: ignore - - # Generate proof - proof = PedersenVRF[curve].prove(alpha, sk, ad, salt) # type: ignore - - # Get input point (h) - h = PedersenVRF[curve].cv.point.encode_to_curve(alpha, salt) # type: ignore - - # Get output hash (beta) - beta = PedersenVRF[curve].proof_to_hash(proof.output_point) # type: ignore - - scalar_len = self._get_scalar_len() - - # Get blinding factor - sk_scalar = Helpers.str_to_int(sk, self.curve.curve.ENDIAN) % self.curve.curve.ORDER - sk_bytes = sk_scalar.to_bytes(scalar_len, self.curve.curve.ENDIAN) - blinding = PedersenVRF[curve].blinding(sk_bytes, h.point_to_string(), ad) # type: ignore[valid-type, misc] - blinding_bytes = Helpers.int_to_str(blinding, self.curve.curve.ENDIAN, scalar_len) - - s_bytes = Helpers.int_to_str(proof.s, self.curve.curve.ENDIAN, scalar_len) - sb_bytes = Helpers.int_to_str(proof.sb, self.curve.curve.ENDIAN, scalar_len) - - return { - "comment": comment, - "sk": sk.hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "salt": salt.hex(), - "ad": ad.hex(), - "h": h.point_to_string().hex(), - "gamma": proof.output_point.point_to_string().hex(), - "beta": beta.hex(), - "blinding": blinding_bytes.hex(), - "proof_pk_com": proof.blinded_pk.point_to_string().hex(), - "proof_r": proof.result_point.point_to_string().hex(), - "proof_ok": proof.ok.point_to_string().hex(), - "proof_s": s_bytes.hex(), - "proof_sb": sb_bytes.hex(), - } - - def generate_ring_vector( - self, - comment: str, - seed: int, - alpha: bytes, - salt: bytes, - ad: bytes, - ring_size: int = 8, - prover_idx: int = 3, - ) -> dict[str, Any]: - """Generate a single Ring VRF test vector.""" - # Only Bandersnatch supports Ring VRF - if self.curve_name != "bandersnatch": - raise ValueError("Ring VRF only supports Bandersnatch curve") - - sk = self._secret_from_seed(seed) - curve = self.curve - pk = RingVRF[curve].get_public_key(sk) # type: ignore - - # Generate ring keys deterministically - ring_pks = [] - for i in range(ring_size): - if i == prover_idx: - ring_pks.append(pk) - else: - # Use different seeds for other ring members - other_sk = self._secret_from_seed(0x11 + i * 0x10 + seed) - other_pk = RingVRF[curve].get_public_key(other_sk) # type: ignore - ring_pks.append(other_pk) - - # Generate proof - ring_proof = RingVRF[curve].prove(alpha, ad, sk, pk, ring_pks) # type: ignore - - # Get input point (h) - h = RingVRF[curve].cv.point.encode_to_curve(alpha, salt) # type: ignore - - # Get output hash (beta) - beta = RingVRF[curve].proof_to_hash(ring_proof.pedersen_proof.output_point) # type: ignore - - scalar_len = self._get_scalar_len() - - # Get blinding factor - sk_scalar = Helpers.str_to_int(sk, self.curve.curve.ENDIAN) % self.curve.curve.ORDER - sk_bytes = sk_scalar.to_bytes(scalar_len, self.curve.curve.ENDIAN) - blinding = PedersenVRF.__class_getitem__(curve).blinding(sk_bytes, h.point_to_string(), ad) - blinding_bytes = Helpers.int_to_str(blinding, self.curve.curve.ENDIAN, scalar_len) - - s_bytes = Helpers.int_to_str(ring_proof.pedersen_proof.s, self.curve.curve.ENDIAN, scalar_len) - sb_bytes = Helpers.int_to_str(ring_proof.pedersen_proof.sb, self.curve.curve.ENDIAN, scalar_len) - - # Construct ring root - # Construct ring root - # Use getattr to bypass mypy valid-type error for dynamic class subscription - vrf_cls = RingVRF.__class_getitem__(curve) - ring_root = vrf_cls.construct_ring_root(ring_pks) - - # Serialize ring public keys - ring_pks_concat = b"".join(ring_pks) - - return { - "comment": comment, - "sk": sk.hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "salt": salt.hex(), - "ad": ad.hex(), - "h": h.point_to_string().hex(), - "gamma": ring_proof.pedersen_proof.output_point.point_to_string().hex(), - "beta": beta.hex(), - "blinding": blinding_bytes.hex(), - "proof_pk_com": ring_proof.pedersen_proof.blinded_pk.point_to_string().hex(), - "proof_r": ring_proof.pedersen_proof.result_point.point_to_string().hex(), - "proof_ok": ring_proof.pedersen_proof.ok.point_to_string().hex(), - "proof_s": s_bytes.hex(), - "proof_sb": sb_bytes.hex(), - "ring_pks": ring_pks_concat.hex(), - "ring_pks_com": ring_root.to_bytes().hex(), - "ring_proof": ring_proof.to_bytes().hex(), - "prover_idx": prover_idx, - } - - -def generate_ietf_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate IETF VRF test vectors with various edge cases.""" - vectors = [] - suite = generator.suite_prefix + "_ietf" - - # Standard test cases (matching ark-vrf format) - test_cases = [ - # (seed, alpha, ad, description) - (1, b"", b"", "empty input"), - (2, bytes.fromhex("0a"), b"", "single byte input"), - (3, b"", bytes.fromhex("0b8c"), "empty input with ad"), - (4, bytes.fromhex("73616d706c65"), b"", "sample input"), # "sample" - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - b"", - "Bandersnatch vector", - ), - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - "same key different ad", - ), - ( - 6, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - "different key same input", - ), - ] - - for i, (seed, alpha, ad, _desc) in enumerate(test_cases, 1): - comment = f"{suite} - vector-{i}" - vector = generator.generate_ietf_vector(comment, seed, alpha, b"", ad) - vectors.append(vector) - - return vectors - - -def generate_ietf_edge_case_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate additional edge case test vectors for IETF VRF.""" - vectors = [] - suite = generator.suite_prefix + "_ietf_edge_cases" - - edge_cases = [ - # Edge case scalar values - (0x01, b"min_scalar_seed", b"", "minimum non-zero seed"), - (0xFE, b"high_seed", b"", "high seed value"), - # Long inputs - (10, b"a" * 256, b"", "256 byte input"), - (11, b"test" * 100, b"", "400 byte repeating input"), - # Long additional data - (12, b"input", b"ad" * 128, "256 byte additional data"), - # Special characters in input - (13, b"\x00\x00\x00\x00", b"", "null bytes input"), - (14, b"\xff\xff\xff\xff", b"", "max bytes input"), - (15, bytes(range(256)), b"", "all byte values 0-255"), - # Unicode-like patterns - (16, "Hello, 世界!".encode(), b"", "UTF-8 encoded string"), - # Repeated proofs (same key, same input - should be deterministic) - (20, b"deterministic_test", b"", "determinism check 1"), - (20, b"deterministic_test", b"", "determinism check 2 (should match)"), - # Very short and very long ad - (21, b"test", b"x", "single byte ad"), - (22, b"test", b"y" * 1024, "1KB additional data"), - ] - - for i, (seed, alpha, ad, desc) in enumerate(edge_cases, 1): - comment = f"{suite} - edge-{i} - {desc}" - try: - vector = generator.generate_ietf_vector(comment, seed, alpha, b"", ad) - vectors.append(vector) - except Exception as e: - print(f"Warning: Failed to generate edge case {i} ({desc}): {e}") - - return vectors - - -def generate_pedersen_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate Pedersen VRF test vectors.""" - vectors = [] - suite = generator.suite_prefix + "_pedersen" - - # Same test cases as IETF for compatibility checking - test_cases = [ - (1, b"", b"", "empty input"), - (2, bytes.fromhex("0a"), b"", "single byte input"), - (3, b"", bytes.fromhex("0b8c"), "empty input with ad"), - (4, bytes.fromhex("73616d706c65"), b"", "sample input"), - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - b"", - "Bandersnatch vector", - ), - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - "same key different ad", - ), - ( - 6, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - "different key same input", - ), - ] - - for i, (seed, alpha, ad, _desc) in enumerate(test_cases, 1): - comment = f"{suite} - vector-{i}" - vector = generator.generate_pedersen_vector(comment, seed, alpha, b"", ad) - vectors.append(vector) - - return vectors - - -def generate_pedersen_edge_case_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate additional edge case test vectors for Pedersen VRF.""" - vectors = [] - suite = generator.suite_prefix + "_pedersen_edge_cases" - - edge_cases = [ - # Blinding factor edge cases - same input different keys - (100, b"blinding_test", b"", "blinding test key 1"), - (101, b"blinding_test", b"", "blinding test key 2 (different blinding)"), - (102, b"blinding_test", b"", "blinding test key 3 (different blinding)"), - # Same key, different inputs - unlinkability test - (200, b"unlinkable_1", b"", "unlinkability input 1"), - (200, b"unlinkable_2", b"", "unlinkability input 2 (same key)"), - (200, b"unlinkable_3", b"", "unlinkability input 3 (same key)"), - # Additional data affects blinding - (300, b"ad_test", b"", "no additional data"), - (300, b"ad_test", b"ad1", "with ad1"), - (300, b"ad_test", b"ad2", "with ad2 (different blinding)"), - ] - - for i, (seed, alpha, ad, desc) in enumerate(edge_cases, 1): - comment = f"{suite} - edge-{i} - {desc}" - try: - vector = generator.generate_pedersen_vector(comment, seed, alpha, b"", ad) - vectors.append(vector) - except Exception as e: - print(f"Warning: Failed to generate Pedersen edge case {i} ({desc}): {e}") - - return vectors - - -def generate_ring_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate Ring VRF test vectors.""" - if generator.curve_name != "bandersnatch": - return [] - - vectors = [] - suite = generator.suite_prefix + "_ring" - - # Standard test cases - test_cases = [ - (1, b"", b"", 3, "empty input, idx=3"), - (2, bytes.fromhex("0a"), b"", 3, "single byte input"), - (3, b"", bytes.fromhex("0b8c"), 3, "empty input with ad"), - (4, bytes.fromhex("73616d706c65"), b"", 3, "sample input"), - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - b"", - 3, - "Bandersnatch vector", - ), - ( - 5, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - 3, - "same key different ad", - ), - ( - 6, - bytes.fromhex("42616e646572736e6174636820766563746f72"), - bytes.fromhex("1f42"), - 3, - "different key same input", - ), - ] - - for i, (seed, alpha, ad, idx, _desc) in enumerate(test_cases, 1): - comment = f"{suite} - vector-{i}" - try: - vector = generator.generate_ring_vector(comment, seed, alpha, b"", ad, prover_idx=idx) - vectors.append(vector) - except Exception as e: - print(f"Warning: Failed to generate ring vector {i}: {e}") - - return vectors - - -def generate_ring_edge_case_vectors(generator: TestVectorGenerator) -> list[dict]: - """Generate additional edge case test vectors for Ring VRF.""" - if generator.curve_name != "bandersnatch": - return [] - - vectors = [] - suite = generator.suite_prefix + "_ring_edge_cases" - - edge_cases = [ - # Different positions in ring - (10, b"position_test", b"", 0, "prover at index 0 (first)"), - (10, b"position_test", b"", 4, "prover at index 4 (middle)"), - (10, b"position_test", b"", 7, "prover at index 7 (last)"), - # Ring anonymity - same ring, different provers - (20, b"anonymity_test", b"", 2, "anonymity prover A"), - (21, b"anonymity_test", b"", 5, "anonymity prover B"), - ] - - for i, (seed, alpha, ad, idx, desc) in enumerate(edge_cases, 1): - comment = f"{suite} - edge-{i} - {desc}" - try: - vector = generator.generate_ring_vector(comment, seed, alpha, b"", ad, prover_idx=idx) - vectors.append(vector) - except Exception as e: - print(f"Warning: Failed to generate ring edge case {i} ({desc}): {e}") - - return vectors - - -def generate_negative_test_vectors() -> dict[str, list[dict]]: - """ - Generate negative test vectors - cases that SHOULD fail verification. - - These test that implementations properly reject invalid proofs. - """ - negative_vectors: dict[str, list[dict[str, Any]]] = { - "invalid_proofs": [], - "wrong_inputs": [], - "tampered_proofs": [], +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.specs.baby_jubjub import BabyJubJub +from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_SHAKE128 +from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW +from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.jubjub import JubJub +from dot_ring.curve.specs.p256 import P256_NU +from dot_ring.keygen import secret_from_seed +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.vrf.ietf import ThinVRF, TinyVRF +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.ring import Ring, RingRoot, RingVRF +from dot_ring.vrf.transcript import scalar_encode + +DEFAULT_OUT_DIR = Path(__file__).resolve().parent.parent / "tests" / "vectors" / "dot-ring" +RING_SIZE = 8 +PROVER_INDEX = 3 + + +@dataclass(frozen=True) +class Suite: + prefix: str + curve: CurveVariant + + +SUITES = [ + Suite("bandersnatch_sha-512_ell2", Bandersnatch), + Suite("bandersnatch_sw_sha-512_tai", Bandersnatch_SW), + Suite("bandersnatch_shake128_ell2", Bandersnatch_SHAKE128), + Suite("ed25519_sha-512_tai", Ed25519_NU), + Suite("secp256r1_sha-256_tai", P256_NU), + Suite("jubjub_sha-512_tai", JubJub), + Suite("baby-jubjub_sha-512_tai", BabyJubJub), +] + +RING_SUITES = [ + Suite("bandersnatch_sha-512_ell2", Bandersnatch), + Suite("bandersnatch_sw_sha-512_tai", Bandersnatch_SW), + Suite("bandersnatch_shake128_ell2", Bandersnatch_SHAKE128), + Suite("jubjub_sha-512_tai", JubJub), + Suite("baby-jubjub_sha-512_tai", BabyJubJub), +] + +VECTOR_CASES = [ + (1, b"", b""), + (2, bytes.fromhex("0a"), b""), + (3, b"", bytes.fromhex("0b8c")), + (4, bytes.fromhex("73616D706C65"), b""), + (5, bytes.fromhex("42616E646572736E6174636820766563746F72"), b""), + (5, bytes.fromhex("42616E646572736E6174636820766563746F72"), bytes.fromhex("1F42")), + (6, bytes.fromhex("42616E646572736E6174636820766563746F72"), bytes.fromhex("1F42")), +] + + +def seed_bytes(seed: int) -> bytes: + seed_value = bytearray(32) + seed_value[0] = seed + return bytes(seed_value) + + +def point_len(curve: CurveVariant) -> int: + size = curve.curve.POINT_LEN + if curve.curve.UNCOMPRESSED: + size *= 2 + return int(size) + + +def pedersen_len(curve: CurveVariant) -> int: + scalar_len = (curve.curve.ORDER.bit_length() + 7) // 8 + return 4 * point_len(curve) + 2 * scalar_len + + +def base_fields(suite: Suite, scheme: str, index: int, seed: int, alpha: bytes, ad: bytes) -> tuple[bytes, dict[str, str]]: + pk, sk = secret_from_seed(seed_bytes(seed), suite.curve) + input_point = suite.curve.point.encode_to_curve(alpha) + gamma = input_point * int.from_bytes(sk, "little") + beta = TinyVRF[suite.curve].proof_to_hash(gamma) + return sk, { + "comment": f"{suite.prefix}_{scheme} - vector-{index}", + "sk": sk.hex(), + "pk": pk.hex(), + "alpha": alpha.hex(), + "ad": ad.hex(), + "h": input_point.point_to_string().hex(), + "gamma": gamma.point_to_string().hex(), + "beta": beta.hex(), } - generator = TestVectorGenerator("bandersnatch") - # Generate a valid proof first - sk = generator._secret_from_seed(42) - pk = IETF_VRF[Bandersnatch].get_public_key(sk) # type: ignore[misc, valid-type] - alpha = b"test_input" - ad = b"test_ad" - - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) # type: ignore[misc, valid-type] - proof_bytes = proof.to_bytes() - - # Wrong public key verification - wrong_sk = generator._secret_from_seed(43) - wrong_pk = IETF_VRF[Bandersnatch].get_public_key(wrong_sk) # type: ignore[misc, valid-type] - - negative_vectors["wrong_inputs"].append( +def tiny_vector(suite: Suite, index: int, seed: int, alpha: bytes, ad: bytes) -> dict[str, str]: + sk, vector = base_fields(suite, "tiny", index, seed, alpha, ad) + proof = TinyVRF[suite.curve].prove(alpha, sk, ad) + vector.update( { - "comment": "wrong public key - should fail", - "proof": proof_bytes.hex(), - "correct_pk": pk.hex(), - "wrong_pk": wrong_pk.hex(), - "alpha": alpha.hex(), - "ad": ad.hex(), - "expected_result": "FAIL", + "proof_c": proof.c.to_bytes(16, "little").hex(), + "proof_s": scalar_encode(suite.curve, proof.s).hex(), } ) + return vector - # Wrong input verification - negative_vectors["wrong_inputs"].append( - { - "comment": "wrong alpha input - should fail", - "proof": proof_bytes.hex(), - "pk": pk.hex(), - "correct_alpha": alpha.hex(), - "wrong_alpha": b"wrong_input".hex(), - "ad": ad.hex(), - "expected_result": "FAIL", - } - ) - # Wrong additional data - negative_vectors["wrong_inputs"].append( +def thin_vector(suite: Suite, index: int, seed: int, alpha: bytes, ad: bytes) -> dict[str, str]: + sk, vector = base_fields(suite, "thin", index, seed, alpha, ad) + proof = ThinVRF[suite.curve].prove(alpha, sk, ad) + vector.update( { - "comment": "wrong additional data - should fail", - "proof": proof_bytes.hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "correct_ad": ad.hex(), - "wrong_ad": b"wrong_ad".hex(), - "expected_result": "FAIL", + "proof_r": proof.r.point_to_string().hex(), + "proof_s": scalar_encode(suite.curve, proof.s).hex(), } ) + return vector + - # Tampered proof - flip a bit in the challenge - tampered_bytes = bytearray(proof_bytes) - tampered_bytes[32] ^= 0x01 # Flip a bit in challenge - negative_vectors["tampered_proofs"].append( +def pedersen_vector(suite: Suite, index: int, seed: int, alpha: bytes, ad: bytes) -> dict[str, str]: + sk, vector = base_fields(suite, "pedersen", index, seed, alpha, ad) + proof = PedersenVRF[suite.curve].prove(alpha, sk, ad) + vector.update( { - "comment": "tampered challenge - should fail", - "original_proof": proof_bytes.hex(), - "tampered_proof": bytes(tampered_bytes).hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "ad": ad.hex(), - "expected_result": "FAIL", + "blinding": scalar_encode(suite.curve, proof._blinding_factor).hex(), + "proof_pk_com": proof.blinded_pk.point_to_string().hex(), + "proof_r": proof.result_point.point_to_string().hex(), + "proof_ok": proof.ok.point_to_string().hex(), + "proof_s": scalar_encode(suite.curve, proof.s).hex(), + "proof_sb": scalar_encode(suite.curve, proof.sb).hex(), } ) + return vector + + +def ring_keys( + suite: Suite, + seed: int, + prover_pk: bytes, + ring_size: int = RING_SIZE, + prover_index: int = PROVER_INDEX, +) -> list[bytes]: + keys = [] + for idx in range(ring_size): + if idx == prover_index: + keys.append(prover_pk) + else: + other_pk, _ = secret_from_seed(seed_bytes(0x11 + idx * 0x10 + seed), suite.curve) + keys.append(other_pk) + return keys + + +def ring_vector(suite: Suite, index: int, seed: int, alpha: bytes, ad: bytes) -> dict[str, Any]: + sk, vector = base_fields(suite, "ring", index, seed, alpha, ad) + producer_pk = bytes.fromhex(vector["pk"]) + keys = ring_keys(suite, seed, producer_pk) + params = RingProofParams(test_vectors=True, cv=suite.curve) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + proof = RingVRF[suite.curve].prove(alpha, ad, sk, producer_pk, ring, ring_root) + pedersen = proof.pedersen_proof + if pedersen is None: + raise RuntimeError("ring proof did not include a Pedersen proof") - # Tampered proof - flip a bit in the response - tampered_bytes = bytearray(proof_bytes) - tampered_bytes[-1] ^= 0x01 # Flip a bit in response - negative_vectors["tampered_proofs"].append( + proof_bytes = proof.to_bytes() + vector.update( { - "comment": "tampered response - should fail", - "original_proof": proof_bytes.hex(), - "tampered_proof": bytes(tampered_bytes).hex(), - "pk": pk.hex(), - "alpha": alpha.hex(), - "ad": ad.hex(), - "expected_result": "FAIL", + "blinding": scalar_encode(suite.curve, pedersen._blinding_factor).hex(), + "proof_pk_com": pedersen.blinded_pk.point_to_string().hex(), + "proof_r": pedersen.result_point.point_to_string().hex(), + "proof_ok": pedersen.ok.point_to_string().hex(), + "proof_s": scalar_encode(suite.curve, pedersen.s).hex(), + "proof_sb": scalar_encode(suite.curve, pedersen.sb).hex(), + "ring_pks": b"".join(keys).hex(), + "ring_pks_com": ring_root.to_bytes().hex(), + "ring_proof": proof_bytes[pedersen_len(suite.curve) :].hex(), + "ring_size": RING_SIZE, + "prover_idx": PROVER_INDEX, } ) + return vector - return negative_vectors - - -def save_vectors(vectors: list[dict], filename: str) -> None: - """Save vectors to JSON file.""" - VECTORS_DIR.mkdir(parents=True, exist_ok=True) - filepath = VECTORS_DIR / filename - - with open(filepath, "w") as f: - json.dump(vectors, f, indent=2) - - print(f"Saved {len(vectors)} vectors to {filepath}") - - -def run_tests() -> None: - """Generate all test vectors.""" - print("=" * 60) - print("Generating dot-ring test vectors") - print("=" * 60) - # Bandersnatch vectors (supports all VRF types) - print("\n[Bandersnatch]") - bandersnatch_gen = TestVectorGenerator("bandersnatch") - - # IETF vectors - ietf_vectors = generate_ietf_vectors(bandersnatch_gen) - save_vectors(ietf_vectors, "bandersnatch_sha-512_ell2_ietf.json") - - ietf_edge = generate_ietf_edge_case_vectors(bandersnatch_gen) - save_vectors(ietf_edge, "bandersnatch_sha-512_ell2_ietf_edge_cases.json") - - # Pedersen vectors - pedersen_vectors = generate_pedersen_vectors(bandersnatch_gen) - save_vectors(pedersen_vectors, "bandersnatch_sha-512_ell2_pedersen.json") - - pedersen_edge = generate_pedersen_edge_case_vectors(bandersnatch_gen) - save_vectors(pedersen_edge, "bandersnatch_sha-512_ell2_pedersen_edge_cases.json") - - # Ring vectors - ring_vectors = generate_ring_vectors(bandersnatch_gen) - save_vectors(ring_vectors, "bandersnatch_sha-512_ell2_ring.json") - - ring_edge = generate_ring_edge_case_vectors(bandersnatch_gen) - save_vectors(ring_edge, "bandersnatch_sha-512_ell2_ring_edge_cases.json") - - # Ed25519 vectors (IETF and Pedersen only) - print("\n[Ed25519]") - try: - ed25519_gen = TestVectorGenerator("ed25519") - - ietf_vectors = generate_ietf_vectors(ed25519_gen) - save_vectors(ietf_vectors, "ed25519_sha-512_tai_ietf.json") +def scheme_vectors(suite: Suite, scheme: str) -> list[dict[str, Any]]: + builders = { + "tiny": tiny_vector, + "thin": thin_vector, + "pedersen": pedersen_vector, + "ring": ring_vector, + } + return [ + builders[scheme](suite, index, seed, alpha, ad) + for index, (seed, alpha, ad) in enumerate(VECTOR_CASES, start=1) + ] - pedersen_vectors = generate_pedersen_vectors(ed25519_gen) - save_vectors(pedersen_vectors, "ed25519_sha-512_tai_pedersen.json") - except Exception as e: - print(f"Warning: Ed25519 vector generation failed: {e}") - # secp256r1 vectors (IETF and Pedersen only) - print("\n[secp256r1]") - try: - secp256r1_gen = TestVectorGenerator("secp256r1") +def write_vectors(out_dir: Path, suite: Suite, scheme: str) -> None: + vectors = scheme_vectors(suite, scheme) + out_dir.mkdir(parents=True, exist_ok=True) + path = out_dir / f"{suite.prefix}_{scheme}.json" + path.write_text(json.dumps(vectors, indent=2)) + print(f"wrote {path}") - ietf_vectors = generate_ietf_vectors(secp256r1_gen) - save_vectors(ietf_vectors, "secp256r1_sha-256_tai_ietf.json") - pedersen_vectors = generate_pedersen_vectors(secp256r1_gen) - save_vectors(pedersen_vectors, "secp256r1_sha-256_tai_pedersen.json") - except Exception as e: - print(f"Warning: secp256r1 vector generation failed: {e}") +def main() -> None: + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--out-dir", type=Path, default=DEFAULT_OUT_DIR) + args = parser.parse_args() - # Negative test vectors - print("\n[Negative Tests]") - negative_vectors = generate_negative_test_vectors() - save_vectors(negative_vectors["wrong_inputs"], "negative_wrong_inputs.json") - save_vectors(negative_vectors["tampered_proofs"], "negative_tampered_proofs.json") + for suite in SUITES: + for scheme in ("tiny", "thin", "pedersen"): + write_vectors(args.out_dir, suite, scheme) - print("\n" + "=" * 60) - print("Test vector generation complete!") - print("=" * 60) + for suite in RING_SUITES: + write_vectors(args.out_dir, suite, "ring") if __name__ == "__main__": - run_tests() + main() diff --git a/tests/test_dot_ring_vectors.py b/tests/test_dot_ring_vectors.py new file mode 100644 index 0000000..d760313 --- /dev/null +++ b/tests/test_dot_ring_vectors.py @@ -0,0 +1,93 @@ +import json +from pathlib import Path +from typing import Any + +import pytest + +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.vrf.ietf import ThinVRF, TinyVRF +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.ring import Ring, RingRoot, RingVRF +from scripts.generate_test_vectors import RING_SUITES, SUITES, Suite + +VECTORS = Path(__file__).parent / "vectors" / "dot-ring" + + +def load(suite: Suite, scheme: str) -> list[dict[str, Any]]: + return json.loads((VECTORS / f"{suite.prefix}_{scheme}.json").read_text()) + + +def ring_proof_bytes(vector: dict[str, Any]) -> bytes: + return bytes.fromhex( + vector["gamma"] + + vector["proof_pk_com"] + + vector["proof_r"] + + vector["proof_ok"] + + vector["proof_s"] + + vector["proof_sb"] + + vector["ring_proof"] + ) + + +@pytest.mark.parametrize("suite", SUITES) +def test_dot_ring_tiny_vectors(suite: Suite) -> None: + for vector in load(suite, "tiny"): + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof_bytes = bytes.fromhex(vector["gamma"] + vector["proof_c"] + vector["proof_s"]) + proof = TinyVRF[suite.curve].from_bytes(proof_bytes) + + assert TinyVRF[suite.curve].get_public_key(bytes.fromhex(vector["sk"])).hex() == vector["pk"] + assert suite.curve.point.encode_to_curve(alpha).point_to_string().hex() == vector["h"] + assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) + assert TinyVRF[suite.curve].proof_to_hash(proof.output_point).hex() == vector["beta"] + + +@pytest.mark.parametrize("suite", SUITES) +def test_dot_ring_thin_vectors(suite: Suite) -> None: + for vector in load(suite, "thin"): + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof_bytes = bytes.fromhex(vector["gamma"] + vector["proof_r"] + vector["proof_s"]) + proof = ThinVRF[suite.curve].from_bytes(proof_bytes) + + assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) + assert ThinVRF[suite.curve].proof_to_hash(proof.output_point).hex() == vector["beta"] + + +@pytest.mark.parametrize("suite", SUITES) +def test_dot_ring_pedersen_vectors(suite: Suite) -> None: + for vector in load(suite, "pedersen"): + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + proof_bytes = bytes.fromhex( + vector["gamma"] + + vector["proof_pk_com"] + + vector["proof_r"] + + vector["proof_ok"] + + vector["proof_s"] + + vector["proof_sb"] + ) + proof = PedersenVRF[suite.curve].from_bytes(proof_bytes) + + assert proof.verify(alpha, ad) + assert PedersenVRF[suite.curve].proof_to_hash(proof.output_point).hex() == vector["beta"] + + +@pytest.mark.parametrize("suite", RING_SUITES) +def test_dot_ring_ring_vectors(suite: Suite) -> None: + for vector in load(suite, "ring"): + alpha = bytes.fromhex(vector["alpha"]) + ad = bytes.fromhex(vector["ad"]) + keys = RingVRF[suite.curve].parse_keys(bytes.fromhex(vector["ring_pks"])) + params = RingProofParams(test_vectors=True, cv=suite.curve) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + proof = RingVRF[suite.curve].from_bytes(ring_proof_bytes(vector)) + assert proof.pedersen_proof is not None + + assert len(keys) == vector["ring_size"] + assert keys[vector["prover_idx"]] == bytes.fromhex(vector["pk"]) + assert ring_root.to_bytes().hex() == vector["ring_pks_com"] + assert proof.verify(alpha, ad, ring, ring_root) + assert RingVRF[suite.curve].proof_to_hash(proof.pedersen_proof.output_point).hex() == vector["beta"] diff --git a/tests/vectors/dot-ring/README.md b/tests/vectors/dot-ring/README.md new file mode 100644 index 0000000..06ca1c4 --- /dev/null +++ b/tests/vectors/dot-ring/README.md @@ -0,0 +1,22 @@ +# dot-ring Test Vectors + +These fixtures are generated by `scripts/generate_test_vectors.py` and use canonical compressed bytes encoded as lowercase hex. Each file contains seven deterministic vectors covering empty input, non-empty input, additional data, repeated input with different additional data, and repeated input with a different key. + +Suites: + +- `bandersnatch_sha-512_ell2` +- `bandersnatch_sw_sha-512_tai` +- `bandersnatch_shake128_ell2` +- `ed25519_sha-512_tai` +- `secp256r1_sha-256_tai` +- `jubjub_sha-512_tai` +- `baby-jubjub_sha-512_tai` + +Schemes: + +- Tiny: `gamma || proof_c || proof_s` +- Thin: `gamma || proof_r || proof_s` +- Pedersen: `gamma || proof_pk_com || proof_r || proof_ok || proof_s || proof_sb` +- Ring: Pedersen fields plus raw `ring_proof` + +Ring vectors are generated for Bandersnatch, Bandersnatch-SW, Bandersnatch-SHAKE128, JubJub, and Baby-JubJub. They include the concatenated ring public keys in `ring_pks`, the verifier key/root commitment in `ring_pks_com`, the fixed `ring_size`, and the deterministic `prover_idx`. diff --git a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_pedersen.json b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_pedersen.json new file mode 100644 index 0000000..f72feb8 --- /dev/null +++ b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", + "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", + "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", + "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", + "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", + "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", + "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", + "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", + "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", + "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", + "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", + "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", + "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", + "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", + "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", + "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", + "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", + "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", + "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", + "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", + "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", + "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", + "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", + "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", + "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", + "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", + "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", + "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", + "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", + "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", + "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001" + }, + { + "comment": "baby-jubjub_sha-512_tai_pedersen - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", + "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", + "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", + "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", + "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", + "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json new file mode 100644 index 0000000..7a08152 --- /dev/null +++ b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json @@ -0,0 +1,149 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", + "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", + "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", + "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", + "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", + "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01", + "ring_pks": "7c72597deed7e9f1454139d631276dc3c06d576448519c0eb7667e78e0400895eed9a3e41726dde77f93828e8442e09b118b2952543f64354c08782b70e09f26b2d2a8d0cbf02ce3d8f3eda1b9ccd2e5325ddb4e99d1364b5c2ea63e6b1eec8f6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200271442f492fa743e52e8c9c78e85d8ab601d7bd52d50a8bf117056f9e1af71108123a888708dc105d0d530f8ecebd3f75f4a836717cc6045a9843c44c7a35e16e7d8fbf066ff21f40fd2290d57c34f36dcbf9d693ae89a5856f0ea37547d262f0d9e01aa57cfff424b3395aa19f56ab5f971a1cdc5dd915f4246cb3eed180e91", + "ring_pks_com": "dc2ba94ad6f31ba7b1ad02433a3f0d42f50a652218a1115418106bfa2d1777a34187202f7757dc82ff6576b18b685109873a774ad4d6b3d3ecfc7b7b0c7a1312da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "f9a870a23b1fdf1f483ca4835de8cd6c7ffb6a4a6685370eb8eb92c647ba11a287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16557befdaf649a92c4333257b279c463e299f1932ac92322bd4d925d4fc4d7826b48f42b6cff2c2103890bc09a9d02da15aa08e85aa641e286fb7295622717804653d3423f5dfa7d123951377c1962209cee0bd486e3cc7f3b2ad49196d31a700ed58626378b929f5536808eaaf37b0e8f36334e2db51cf6ce2f25e4239428703fd8791ee01b1e7d622a11f2975cff50bd86f62c0ab3c3289456d5ef1fc0a1d0270e298c606cb4ab1dfd9c3c1381ce37bc40455af7b40b5333c5d41efb66684148d6214b211e95dc71573553dda6b4fd3b27895109aee850dd29ad77f6138d8253df5b4a70197fde024019449c983f0d344aec3c3cc6b4209f00bcfc9cabf6705f7f628d778e8d9746f1031de057d00c6e6d17ea3753691bd7e5d62dc2e064c19ab53c0961b6c992468c280fe32322e0ad8d3bb14cf990c9ed72e117326f365032f4c7c583f42bf195b8b9966bca141d7a2c100a02996414b76fb475968d6cd07f9e070800c4e17b4c7b32f4afb18ccd6ee388c1d0cd6c70009da3fb014a1ef8a8c59c981345bc243542359b7142a0676bdf9322ca7b5d00689e1dc281e34841e", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", + "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", + "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", + "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", + "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", + "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403", + "ring_pks": "412e2b2c036b79c6ecd7a63db946276d4be039f3b9273e3a6b302f9f8eeda9880f0c9bc45affd54b2611c72259c3cea3c9fab7077ebb08a2fbcc9ca65c428d06a6b10742e0af9e7f1973c62312527eac96f7f2a91c47554ad761743b896ea8a3e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6aef8985c46a7a2414f11331008bd0080f888f8a2bc2734eb3fe2bba9b9ae8d5893727a027568f0074c515de472f670fb5d32a242ebcb9337ed8d30fe0dc6f7b816eb06269af0bee3e8f2fd1194711b8e06b3d06f46e02c648e2e7586a627ef4800ab9ef88055b85b9a62dbc3705b91b079b47d8019cd2a11a470316256215d742e", + "ring_pks_com": "49879e1ab940c170b98e3d4566bb0760af86e076db2b7ef09a8fbf9c50ead29e4316a81ec77d2e7e2533084dc2b67fc9db4f91c3c2594d2de6024ff48cf342a3da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "dd90ec2b3cd494c96d94bac1646292dd1fc07d6850ed567f50cee86b35a285a787b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161e5138357243a1b8c6576780706bf64e7c9532bb70e802a5429ed7ecceabdf981d803a7a414775707c35e688d011731819ee0c7df456479d352d01c568ebe2a8aa084b01e89ba741f97aecd97b18cbbc39159e9ab949d1d7259354cb6fe55a2854aa23be55d5d599ad56e2004a77a71343a1850d785d788993daf8a796e1111cb3841ccd8b6898c8527da88de055c6190418532bd616952094663563dfa4f52bb823374d5c3f67db8e2c1362309baeb93e0618794efa8289be33da2fdc40c5080d8f74778f290107d991a03f01cb2e48f0f09a025025f860c0ea609c65acc322badda836ab2bcf7dcf62f087fbb9dbe2897c3d89a256226555715fbc216bdd1d7ac2332cdf700e6d6d8dc683ef193986f46303e94428ebad41547214e637ed0f42142fe1c9a0cf93607613fd8d41278bd1e208f9f560ea77f2266441d69c9f2f2a8b8aa3e1343a071679ef87d0b998f1edaaa578184f23d144a1234656f87a27b376be5d2aadd3af2a2c8cc38c415d8efde19ee4868ff223c5b41a70aeb9d30db9e739c6d3ff41e4f02063c48e9f337fccfb3babfe5c428ebe0d6022e8dda98c", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", + "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", + "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", + "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", + "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", + "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105", + "ring_pks": "8d18615a037b0c2a33780e63e94d922703b3c02aac995df2c23c4e31310bec96aac5683720e7087e06f506f4e3c5e5df9cbf9637df1d2402cb692ad8f2df19020943a9e1dc819fc7c64cd8c832fbb7bba65e19f55282b16ab95020143378c32d494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae6d52a510ff0e896ace2ad063219e29f19446bc355124bb8ab4c1656c78d90aa20cb4ad24a1a98aaf988bde038ebe3f26891544e666cf7d9c61419c076c592a8f3803eb3bad25a4d50d248f8bdff0fa6e56d451d9572a62afe0304f34382cb79320d0840ca7076800a9902420dcaac2a98ab84c3a87130406c0d280d33b6a9d1c", + "ring_pks_com": "770b13dac04e3757ccd1ea19488a114dc154132904952e2b4bcd0f963ac6aa86bd803f91d73e58dbb3e3772900e693204a0a06075101eacad6941d0466ac1f8bda317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "b8766db2369fe13a4d3b6b4d525c054c51a36b6f91cfc87592e1cb228eb9e00287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16e11765f73130ac9cfac4ec7351eac9d008ba59fd23aa1a321f7c17878eb9db108b3a9836ce1365d49b50c5cec15ad87c9a94d510c2a08d0c07281f89273a742a0ee015d076833de0a643414f8b9856a2f3d64c5bc4dc108b2e223c428c402c27fd9b5d047b6729bc410356c2c51831ad33aab82b16a076e1de9e1c2b1046af29166b5865fa1719b7ad9a5a3f3f48bb716dfba9ba1cb67a0169d609c417670d1de3e0f65287167f58bf92d9fe843cda96c290d8a7df2485c330dab9b55e0208073fc8847e5c87febb0eae5edecbc01aab47e27302e0847708ae006a5ffbee800807cd1dd757b490bd6576c84c4d4513b6e761d696970268fccfcc735998f67609b733b2bd97494094198743ad7dc3d2916dcb37b77bea49c53ad925a7a0fbe82e2979189fb4d3a78e8307bc588a936cd00ef14f4a9d2dbb508eedbe86fe29588eb5709e18c95c7573b6898e7b33b0c27f56faed8592b1efc48e6b91f59b93481f558f561c308e2b26eec034269179c916efa2e3d89799f486108f6c2e45195f1011a513307ef71c7f322be1a4ee53c7870bd18e9dc3750188e94a1c13b6e6868d", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", + "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", + "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", + "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", + "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", + "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105", + "ring_pks": "8d97f89798358e2f025ce77a1f8f5d1e15aae1e7adc3117b52da812b5f7fe5a711c730fe2bf1a8595d76ffc25bdd7ed894ba0fccc0d88a3e51104256d7f617ab11ed85a426db24dfec1d1c19f6665293443a9287cbf24d26502e336c44ad6fa7311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92b3e10628f090e0751fd4cc6f5e4e3c5e9f5144fd5cbbf79592a1f9d320c7c6224e76d5a2203c9127dfba15ea9c52b4d0ea27bbe9e65d4ac5b935daf778f2cf11159d38e015ce2059914b951f0554b783add482161391d3710cefe9438f00380eb1f767ba11cd83edd1b0586ba97ec89ec90395295a27dadea3c19fefd28baf26", + "ring_pks_com": "fd9f605aa94a5eae1ec661fdcfa6c79ab858b84461df3d42ce4d18f64dad1822ddd6ac79532b37c5d9d5f0198daa02a2e776be3cf1998f8daeb3eb6db61f4d05da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6deb42de7ef3b5f2b77fe4897d5101a6af66ecbe984ce49806952c048734b69a87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161f151ef843b2205d4f220617cea8bd935b3a981298feaeaf86dbe50eae9b181fd73005b022d755de9fa36cfc53c9c1594e8cd77693191679caa05a8713f1fb856bd0d60103fd2bbcff876078381de77ab5d17fae63a3f1c14f58a1bf6b8e4d252107da03e0e554f5e255c719f5701440ba6e89a8f8b84eaafe9401bad0ac661d1a86d7828c6686d79b6a2f1543066497e664c132866a44114af78218c627791496e29bf5d1e270a2e02d84a0f95b463fd481e3b5abd19c5af884bf0c87abce2ca5d3daee9d9c54c5e7102709e6acafb745ee3b038cda1f1f82b10d5b1e6ae622651eda850112e3d3f6d9475e9e96b886ea11224b583af99ca44b5d069a8bb71489cf28e7db305ccef945f59d67f699b3fee67f202eb666682e0c00f25f32aa1a5f60a663147fc6987469753a5cb1648d53cfe96a9960e98d960660533d9281885edb905316b81f115fdb54e56bb11ac1b57bb614f5d3a8d392ac66a104b0e321bc9ded5e04badf41ab9c41781416aeb8d0b442a6a595a691df96e59e92139da56d158c50bcb1377c4bb37301314a13ea962c0eddf8d56054b00be45e696be204", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", + "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", + "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", + "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", + "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", + "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603", + "ring_pks": "f69aa870343224fd26eb055cec05f58dac0a47e2c5903e89a3971e05b1e9d1ac14d0796a59e525da94b474bdfb67d8b705f74ecbf4922b281f6e1647c80dec238636a6752c40e1795e1be4faa253ceaaa4c4839236c3da8d3a02846b83ced90f5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b18592505d33231d1d2a13cc6503ebc1e97c9f226cc0f2812d58743c65e9159a978b0f3918512eceb4f42c1002ade53a8f86e0d07be415accd456c0e59924edaed2b366d196d6d879977fd362d5fafa00ce8ec8627d209c7df1e7368f4ae5309aaaa7b65b1ad2626d843ec49ccf0e7c688ced9271d6fb0678faa6cc5033b2bf90e05", + "ring_pks_com": "522876664bf0045a971a34508432934fd96fa4cddc68eb63b4628c605053f38284bc283ea4bb77af7b16b667d804d95bdb8c20d3fce1b78babe691f5da5525a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "51bd9b960c09879d634914f14dc10badd69a52fd4066208aadcef0b4f55bf7ac87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16731ec27f7f83d53b72ad03d3dc58c3577a643ee06fc8a1c8dfec21d01131a5910380ed20e9dd96482988303151e20af5187eebbcb7c754bafeecf7e1a947472c5accb6ba30e4892ab691a9dd6ecd37103f9afc6673eebfbcce0e0c2b3536a80f45efb38ca0891df51884f85532be97fcf75c6ee6799067678224ce7f0798a61bc245931215931b61f4985ec0290f031418558630507e1a98870346e0c204862988ab1dd59ce40d4f0c85c3703e9dca445fb06a6bc21ec7061b57d5ce14d2a114cef5a124803044df8de930b6bf6ede68f52334f8b17fc03041b780f53c93e202b7a7da1818a5cfa12186416212161660c7412d5c923d8edbd0094b48ee213b19c6cde3d48c37897bdf3fe0da86ffa2b7eb3b054ef028a480af803c4c005c3202daed8cc7f357415989f4bcf3b472e4972c353182b37fdd890d53cd616e3ed294c3f84c9fcd3ea573b97b970eb0d2a5bde0b4aff8bd58542c86fa592b07a755281b279dd53d08c83357766ac0dd848f2ba1d4eb021ff611f71397248bf29a71277a392b9cc4d15477158483963d9d035c4396ba41ca93c11149b9d0ebd1dee317", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", + "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", + "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", + "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", + "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", + "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001", + "ring_pks": "f69aa870343224fd26eb055cec05f58dac0a47e2c5903e89a3971e05b1e9d1ac14d0796a59e525da94b474bdfb67d8b705f74ecbf4922b281f6e1647c80dec238636a6752c40e1795e1be4faa253ceaaa4c4839236c3da8d3a02846b83ced90f5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b18592505d33231d1d2a13cc6503ebc1e97c9f226cc0f2812d58743c65e9159a978b0f3918512eceb4f42c1002ade53a8f86e0d07be415accd456c0e59924edaed2b366d196d6d879977fd362d5fafa00ce8ec8627d209c7df1e7368f4ae5309aaaa7b65b1ad2626d843ec49ccf0e7c688ced9271d6fb0678faa6cc5033b2bf90e05", + "ring_pks_com": "522876664bf0045a971a34508432934fd96fa4cddc68eb63b4628c605053f38284bc283ea4bb77af7b16b667d804d95bdb8c20d3fce1b78babe691f5da5525a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6ce421727f29371d9a332d1180d3c04be61a1370f8cfc34a06f0a5272aa2c29287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16c9deb39b90acda84aac8c1888991f458e86093ddb6173c9453e8aa3e456ed5a3e3506e65a63192ac6dda1cd836b4eac1d3ee7ebb1276dd7744fa7f37a3c1bb14e9db9641cbb486ecdcfb349c444bef5ccb181eb4ee57fb1a07bfc36e523755221e2b9abc0c04ea2030ea612b80cf531ad172ab6ab0bc8a84fe965f576d5f291d90f8ad87183f5c7dcae02385373e3697ad6e615edc434ca2bba7b158092b7c186bfe3f572e423d44890484c6abf7ab6d5bf25e204adbd952cac857bf2642861fa76a682f8f1adc6454e1b50b8ccdf01b13aefb7bfab338aa20b98f4c8da46f1655cdf484eaf88c69de3bb96c4d23cc97ac67eb814de38c7476a8ef41db6f4220da8c5bba7086db6aebd69934ddeada73b65f7f44559621f4232d94807d1b6509326bac4a6107ef1731147e67fd12f16795830dbe4b8421063f42d51b6b6dd1a0e26148668628b6458f2d415bab4792f7f884fc82d2f97351c975c48ddd87f90ab64a7ac3e24a9c0182dbb50ccd6a254db316628a76180edcd1348fa85b23731b9951c601cad91ca36555fa8f947e42d3990062218142436c0b972ec965ecb2a3", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "baby-jubjub_sha-512_tai_ring - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", + "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", + "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", + "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", + "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", + "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401", + "ring_pks": "a677a8194c4cdd1e9d85114ccfe6a350f014a1cd818055f0566f7060627c38951e35e40bfeecb6015ecbc77ff455cd7045bcb8ac7fcd099f6028f64a85137f1b4623b8c7b65ec47a9521fdbd00b6f5cba174b36b0a2ddea04753c1424cdecc0e3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a06bea01a9166a00137636f80e6550714aa48ff5f87c86f0d4b315abd62d3b2811081ec41b15c045d9dba6e9fab7465709cfcfe8ef94cdda74cec86f97a8ab2086b297d8721f2c8b8f8b5310a19ac4dc880e9a391a08bb17c28aec7c3ff732a287daa09270ea40b719fcfea71ef6ce2bfda3277b62d1bea7ba903cd67f7ec112ac", + "ring_pks_com": "7cd2ab3e50186bf385177f1ba281b33580fb01411c0752126c4a0228a87e9e09706c790573fc90d8ce365f37cd728d64d252951dbef1a64e1d909468fcf17a19da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", + "ring_proof": "6b037a603dca77d7a2afa8228479b7c67770b9aa8b30a5051c43f5cd0268bbad87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16cb9d594fbe506e4b3fb33be87b4db9ea4cf8f55f791c3b202462c78b9c9fef9429c9f901e9e340da5ba29fbdc38ca3558b37deeba102b4b8df79b72abf9aac9bef73d482835c93e1bd238169705322e14ec123964e181cda1f58025b759e871151a1da33a3d7769a19c37444fdb743eb689377cadc9364da078273fe918c1d183496b9145c565563e595bd430686bbc78bdc1de5be99091ca32c2510d0c953182e5ced6d83b10cc4377b60ee5b5fe318216db1d827caadd202b0d72ebc601521b028c9befb5880727203492a2be816b7303ae18b01d4f9b12d75184368f59d2aad5622f0695a4d86e95a277305854c1071ce12acdcaaf710d44001cd0e4c1e1f8d2072d30a479c37e6800b0f190618ef76e78b25953e19af5adc41f706698718a9194eaa69239722bdf7b53e25bb59e3efb09bb9ad238d66fa0e5b1abc8e3f16dc6ff738c8f29ad254fc6f40cf1036d40087e8f1fc0dbaeaf8aa669a9bf7771f3de49dd86d7a3c7a722f866c41a8c9fa1529e9eaca6bee71e256b6125543c605a691d00a402e6aa09847506f5566fa0a628c93b9437a692dc2d93371369d2ba0", + "ring_size": 8, + "prover_idx": 3 + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_thin.json b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_thin.json new file mode 100644 index 0000000..5727c6f --- /dev/null +++ b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "proof_r": "5c51d2d4206550cc271b92e84559ac0e77c86868570ce2bef19b542a3e224198", + "proof_s": "27aaf6c9b2f199330fc94cc8563a0e7a985532738c685eb897727f38a0dc2a03" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "proof_r": "949b3f36307170f4a1ebc91049b02b3aca4296a0bf3ed6d63b5223b8dcf3d1a4", + "proof_s": "6af733946286c0e523617d33dc69a2ebadec880a5216ade18f24a0a7bb767d00" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "proof_r": "0312558c1881cf60d8981607dcc28be481f0f99e4c638991982d0c572a0bd9a1", + "proof_s": "4d2c1990f552d8b894051dcbcbedde4607fc5bfca896b49f7eaa35548e1a1802" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "proof_r": "ad9bda7ee584b308db23133776bf39363bb9d4ce57e23ed66c6369b20dc0fa10", + "proof_s": "d2dc4037da4b24e6a9ee98cea2cc3796382705a6ca3eaaa27aa267656a0ec100" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_r": "c52877bfa7b282f40255ab8f84c95e30ee41e2ef648a742e23a94d37d6d45f90", + "proof_s": "e1e6a697dd866e137269d9e46cd9b88a5d65bac03fecf7a6f3ffe8c9af9c5f05" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_r": "68c4d71df0b535907fc12d6ee1684b6e4dc9edfba86cd2f2e4362c53da6dae05", + "proof_s": "97ae93e34fac032734f6b8ab5c74229077a5d2b44d09859fe042fdc3b0e6dd00" + }, + { + "comment": "baby-jubjub_sha-512_tai_thin - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "proof_r": "f976c046262cd6e7a5d92861f6a50537a0c13960c6eabeef321c4157fe7b1ba7", + "proof_s": "b8c61b40793e6cebe5d79c1e2ed57c524be638fe729a606a73ab38a3f4a36b02" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_tiny.json b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_tiny.json new file mode 100644 index 0000000..514ba45 --- /dev/null +++ b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-1", + "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", + "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", + "alpha": "", + "ad": "", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", + "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", + "proof_c": "561493e9f5218706de62898426047a57", + "proof_s": "d7da066400d586e7171abbccdb0605f525101e9619d95c60a06a6e575a087f03" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-2", + "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", + "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", + "alpha": "0a", + "ad": "", + "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", + "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", + "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", + "proof_c": "43dbd59fd726dbd0050a18db789aaf88", + "proof_s": "75bfef482126840f5133ee5686e4c127ff8e133eb916a74d0f9a4830bf90e001" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-3", + "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", + "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", + "alpha": "", + "ad": "0b8c", + "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", + "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", + "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", + "proof_c": "882c0b9414feda55ff1bd7b706d47734", + "proof_s": "59f85ea9059570ee772c00362eb8c9ab986336bac7680008e0d968e641b36c05" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-4", + "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", + "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", + "alpha": "73616d706c65", + "ad": "", + "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", + "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", + "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", + "proof_c": "8c3b8a0bf4f0ca3836f18253051fa307", + "proof_s": "1e826e624d7200b13846747780ffc1c10adb34ff2c36884126be1d87a153d200" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-5", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "a6e767f8e99c05057658975c5e642865", + "proof_s": "6270b0c5ceb52ba5b037ed5e8bb6e0e1102d21f35af868ef9b79fee61eb66704" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-6", + "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", + "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", + "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", + "proof_c": "7c9c59ea80b457ad4103dff28bfd5213", + "proof_s": "831ef85c1a67b89dc681b01323e82903caba2fbcf427479aba9fbe984706b000" + }, + { + "comment": "baby-jubjub_sha-512_tai_tiny - vector-7", + "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", + "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", + "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", + "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", + "proof_c": "fcefee1fd633471d137e21430fbdb472", + "proof_s": "34d32470eee6076281c340aa27324804131873e36a4ced979fc53113ec13ba05" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf.json deleted file mode 100644 index a6b9d8b..0000000 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-1", - "sk": "bb6031cbe6f6dc0083781e0125880dd59be51d69096a6e91e5aa0cf1a7e2d917", - "pk": "bd32288892578bb68df50f75a4c8d890e01306a2a33a4572433f5fa199e0099d", - "alpha": "", - "salt": "", - "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "64c23462a26edd657d03dec918a4452de4e25740e664f6c0077353a8ccd9cf32", - "beta": "d8d5518c5c45328e2eba993939d0dd4f300a4856ee4dc312c077b2ab7a4d1b33cfcd1f20a21992d178310c60bcb0c4193e542a21f566a8c4ad425152c90c656b", - "proof_c": "b29a056a0735a5bd001940804902e74b184a45f252c72857236c5177084c7b0c", - "proof_s": "a523cedd9639ffd7d91214543ebb6a62265e8f404208be485b54b6daf8737f0f" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-2", - "sk": "7690825d266f4ff1b66406e2f953560d648773e6e9b8c4f643d49630316a6314", - "pk": "aba649f1ec853f0ec18eed327a21fada721657e4162ae57b42687d4087284066", - "alpha": "0a", - "salt": "", - "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "72c5e143dfb28e0ff4fd30c64f78526fc4fcfe05fc132e0e9fba88b3455d4d10", - "beta": "7cc98f269b6205ed35970d3789d1b9249b1689df26ca7781edcffa07aa0edc35f02b4c588e3b5623dc96a0e1dcb8a3beb34275e833763b0c62970e392bc9b89a", - "proof_c": "e3177ab81e73682337b91e3dcfc2712c7351a1de95a90f33c10aa81fed0bbd1a", - "proof_s": "1f468b6daec3c0484dd242839e39cae4419e1ffb27bdbe82474fd1075dfeb307" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-3", - "sk": "b0eb0b5750285dd6d2d0e5b7ecdeec7521d1cd5a92823c2679aa597995acb508", - "pk": "cefd465e65a72e31346219f3b20c53f7a801a9897e3479adc53fe34400437fa9", - "alpha": "", - "salt": "", - "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "6cf61877cf74482db2ce24b8d3cea0b97ce1fbbe19f0222d29e9a82d7f207314", - "beta": "102db0061521f1b0984e1034f44184ebbec482f56406f19fb290edcddc06d6feb6bc50bc01e394f33b8b6cea418229d38379f79ee8a9b4e4e61f03a6aa403a46", - "proof_c": "0c0b714882ff792a909390e2cdade8885fa5d7e13c6ff39430748d7b9c412316", - "proof_s": "f5fb4a8d103bdf3c6d124bad9b8eb8081903e19b98640bdfa7a0c227714ec51a" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-4", - "sk": "89a818ea311553b92eb4eb56b85b9ca29c92c0e903c969df20066aeddf415f05", - "pk": "c0e72cb09d87bbe6b21c8ff552293ace36174ad74ce38666afabc42aee6a0b57", - "alpha": "73616d706c65", - "salt": "", - "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "3d4c4744fcaf64343585e59511ec721683b2dfe2673da10f79ec1380e720339c", - "beta": "eb4af7e8573f118716271f46ae7265176590c777137a09337e34e4ace85622ff96eb3429be3a744dff73fba4000e51a06b908be371116c56406b3fd843a5db66", - "proof_c": "150b4905456067248bcee6b647945d043f5fd87e6c59c91529568914e17b4b09", - "proof_s": "05a61cc608f56e8e8baa8258c5b5f4d7029868e6f2fd48f295350a0f6d1fbb14" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-5", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "proof_c": "778688ea224eddb484210518c0f72cbac84297eaa38b5c2ea369fd3798ee9b02", - "proof_s": "b708ee674378576fa3a88a95dd2f0d3fe113f1b0df4044ac0d3637e8007d3c13" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-6", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "proof_c": "5a7d2b1efba148834cec4756721f687b8b2a95edf0fa2875292134d43413a805", - "proof_s": "f0b7d23052fcca3c2921aa839910b4c2b9a2846b1ce448bf97fbad2d036d6717" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf - vector-7", - "sk": "938834d6c6369917101c182c58e7834aa8763fee2d35f7e06559075288c62310", - "pk": "d33123beff50fdb78b69ca1d6139e78cf1aa891e5795ccff942af0d20d8dd9db", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "44b48217381ac12326614d32a14c3d07299ce4e04df80810ae0b7dc59f6f0980", - "beta": "e07d922afaec4804ac28aca6db75b6f36376669628014bb299e32259eaa46b871257b2483a387ba518a0b0e3ea3f1061e23d140fb387b83650850c937c897d04", - "proof_c": "e829e7d44ff14fc5f94726eb5d950ded78bb361cfac63c1070e7f866dfb88906", - "proof_s": "c52ffa893d988bb057734c55e291a32d8277f7358934072bad00ee0788a3d412" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf_edge_cases.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf_edge_cases.json deleted file mode 100644 index 16bf78a..0000000 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ietf_edge_cases.json +++ /dev/null @@ -1,171 +0,0 @@ -[ - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-1 - minimum non-zero seed", - "sk": "bb6031cbe6f6dc0083781e0125880dd59be51d69096a6e91e5aa0cf1a7e2d917", - "pk": "bd32288892578bb68df50f75a4c8d890e01306a2a33a4572433f5fa199e0099d", - "alpha": "6d696e5f7363616c61725f73656564", - "salt": "", - "ad": "", - "h": "9df5e496d3ed088949e2cd72b3083d0e367e4ef88fe5ff95b1f7d5fccfa46e1c", - "gamma": "d41f91055ce74f42f311661b5b897a1ac4720ff986cc8eef1e53eace2da95782", - "beta": "419c1e666a1559c46f92e085a0916af7f880c7aa1fa8021588275016edf4bc9e3206a7830b3ae4827ae5744abd3667a85c2e921ba41a96eb52296e87457d8761", - "proof_c": "8c3b4e46797755968530fb236f30f3f3ef6f21d1ae29c6e2b5a9fedb1ba39f0b", - "proof_s": "86e13dfe1035e2e6e903a8f14e6d59f86c9934bf4b91ddaa7cdb30cabf11fb19" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-2 - high seed value", - "sk": "0bbef8f29f1616e9f979e470190902ca4dacc6e41e4899e6f8581779584af80b", - "pk": "ca91c65bdb71b38e698b22b0d765424ab96a16b92012c2c0bdb206789cfb23c4", - "alpha": "686967685f73656564", - "salt": "", - "ad": "", - "h": "d93beaf0789df353b7dcbf4bfdb401f7d6ac5fd9d2b12118b6c2105c117b2ed7", - "gamma": "53b6f3716d933e6b8ec7ecba40e7a4de477846a6ca899dba1de372058efbcbbf", - "beta": "083bcf875b1db5fb829de2f8c80df17a6c43008d441852349e8c61c7330d7cb7377705fc8b6c9821a5e25e1978398d6baf1bcf2dadea35b9bc2ec5b1722507b2", - "proof_c": "6a26d13058f490adee2754e223034a43ed0774f869d0f91d3e634d1b30ea5201", - "proof_s": "e3552d15cf131d8d50d823aa27581cd150f2a3f7617aa96c4d4a2546b3de7100" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-3 - 256 byte input", - "sk": "3689d2b6a0eca50dd73dcc6b7c67c2be3260f57690f4542e2ab37bb515726711", - "pk": "1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab3", - "alpha": "61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", - "salt": "", - "ad": "", - "h": "c7b4b456a89529bc6fc7ef3257ead51d80f9347f01319253f2f44fc02e83513f", - "gamma": "8d2b3780de0bd4d0b8d9940c674d80d02c723901bb3a71b5db3a304c02382eda", - "beta": "816ff428a2b8c5f41dda5c48947b114efd224b2fa748819fe3a0d02049922e0e1355192b09e73518b8d3bb4bcab7067ac7fa4a32f7b156279b8700ffc14cbc14", - "proof_c": "253a77812811428fc0f167581ddd29b32bae063809e9d9348855895ae5d0ff07", - "proof_s": "5a272739dafb4bb348ddc064fe6cb7bf1f1d6243c8ecbc9c693e9e5bc09fdd08" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-4 - 400 byte repeating input", - "sk": "399d4a0b43d5c52e56eb1cb2f1216b6b0c76c7bbae269e833fafabd1ab724703", - "pk": "ab8567344ce4bf46c172ac21bb27e3c3db3ca2a552da722a8936218866e3ac8c", - "alpha": "74657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374746573747465737474657374", - "salt": "", - "ad": "", - "h": "39ab657766bef57917d71ff095b7d0fcc5f322e2776d1cfb673c89b6a7a84e8e", - "gamma": "4364a538966de4f7970782ba84ace202e5cd9adbd46d22dda106b192bd8d7c3d", - "beta": "4bb814899ec96765a72e2e7bcb52d73c57bc7f94d34e025db81692c2665f976d763b335e30f42ba4a06f3e93d74d982b21534c3f474d36f4f2e1a66d26990b12", - "proof_c": "0f654f21275beca3807e2fa23fe147b647f0a681a8f1e84f42a2ed4492c85c18", - "proof_s": "817ea1735c2da89d97bcd1f3a4dfc9d6f34d1a464209b9fc9b38fc2d1c570813" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-5 - 256 byte additional data", - "sk": "2c50785a40875293a2bbace907621c10bbc81775e43e53c485cd4100543c2815", - "pk": "9e90b59f3af8db3c54c6b238d3775ca575d046bc0535adc6c6221bb63c2163c1", - "alpha": "696e707574", - "salt": "", - "ad": "61646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164616461646164", - "h": "ac48cabe9331b6e95e532268a686d188a185ac76dd88e5d8f151fe6e5847be60", - "gamma": "c3273763e18e31505d5653f40880128cf894dba152730cffa34607dba3d42fbc", - "beta": "1792a4c08ad26da0e4c3f419baea837e26ae210f9c36b8f9b4d74407ad455df17401bdf903e3412244621090f98fb74479c374eeae45ce365852172901d9536d", - "proof_c": "44251bc7422c1e7ac1358c235836276a4295ec0c827c5fc1468b71f6e81f151b", - "proof_s": "6f0e17f774f43c9c00ac67eec9bcb46d3ea2192667e5b81299bdc14e8e921d18" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-6 - null bytes input", - "sk": "9b992384f7194cb721e3deb70813ca531757c558377aa83502b31b8e535ec803", - "pk": "8525166f6b13a3886ec5491252a1bc245479ed83ddcaf26c103393d4809cfec8", - "alpha": "00000000", - "salt": "", - "ad": "", - "h": "8bdc2de613af315fb27ef26842260e0774b7942d4e04e4aa111460132baf9ea6", - "gamma": "606587b67028dfb5567f6b9fc09f714c79dd8c80facf3c881eaf36ccb3d1be5d", - "beta": "0c2c9a79aee79bb8cb9a7b6422d2260fb1ed52a11c2c5a751c8f23857fd4684827dcf15b54e42d7b0f03cb431f01706bdaf6b4ea2da2df3228566b4a8072c514", - "proof_c": "cd167b2b47d1d1c6aac843ed483ddba21d3b70c7c546ae2d8e385506b9660111", - "proof_s": "3520e7fb2e943a2ee4b325a3f99d85eb118a115be403becae579c5b321eab609" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-7 - max bytes input", - "sk": "1b296e605ae1c777caddfec828915af153aa0ffc104c62b6215ce4352ca4ae0b", - "pk": "63ebb0b2fa6ea9620b6dbbbbfaa2a758f23fa53f5125af45f2cca6cec661b54b", - "alpha": "ffffffff", - "salt": "", - "ad": "", - "h": "9f191d27c7ccb016ffb36c95da40c3fec0f4813fbc180e2229bf17381f8154e6", - "gamma": "5251b98bb94041c1b736eb5d67bb1c488f7e8bef971695e17cb727929ac39dbb", - "beta": "829067996e8a609d18e0d3a4f0f31e79e2ef5c60c1a982f040be6e5252dffab58ec00adc196f07ce995e995d8d88152deb491d4480dc03163fe4b878f36b0098", - "proof_c": "64c8abf832446a20594b43633c1fb187ed97b928ac02e6560d79159dc4f2c617", - "proof_s": "00dfd6e9263d08580cb198cfb87167190941cf2dbbd62475f65f18ba4c579a0d" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-8 - all byte values 0-255", - "sk": "7c61acdff3e9379caf53eaa919fe6d7fe55ad3a907eeea85e5a7ba1cfafacf03", - "pk": "085b4bdafaf4e655d4edf09ba5cbc2c46cfca02eba834a9e1875891a50723e15", - "alpha": "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff", - "salt": "", - "ad": "", - "h": "0283e9438a8cc08649943d47acb47a81a3f0c37fae4f5a0f1177364fc91bba90", - "gamma": "bc976ca0b06edcd81eecbd739f660f6ea1ae232c1128642d9d8cb4b5c75628c9", - "beta": "8fbeea9a3847b60430f30c57bc3ab56b8e5ffc708a897b90e0217568bb2dfa9160bf01d1a642b7bdb0b6a7832ea75b6e4df9504f4a357079acecf59c6834c238", - "proof_c": "ec99cd74855151c0013469b09d92be2eebf663dd665c4a0a6d5fb5735077e813", - "proof_s": "c52c81a647109bc1a9f20f7de60a1e37248120b1ffb6d732cb75487a4ed83e02" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-9 - UTF-8 encoded string", - "sk": "2fa6e5797b46b331ce3458f2ec649ac65db01b185e5d3507d06f13049f315710", - "pk": "3e3e8fb4e9c6c6609bb74ba84a1e5b5495b4a5e2467bb7bd3e08c5690e5c1aac", - "alpha": "48656c6c6f2c20e4b896e7958c21", - "salt": "", - "ad": "", - "h": "f7f5414835cb2e3ac3fc053cccec9cc6fa4f4724c192008e8457d92721f85380", - "gamma": "a4d4e514c0572d539b8b2617c30a7a9d0fc4481eeb92b7430403836cda67ea6c", - "beta": "cb5f1a5512ce946661a49d706560cfcb1e1044198e58610c9a4c86f81d01c34273463a9e20e7b31bd131cc4f1b54aa3676696ab21395e9d618de82f1d8995f14", - "proof_c": "3cb3dab603cf68945ebd9f94ec576f69b6c430b0e22bf4f4305d8a7322aae01b", - "proof_s": "d64d697fec5fdd3075e7e73dc3ff7cca0a9ad4c7468b6499c8b5e52fb4e05e0a" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-10 - determinism check 1", - "sk": "c267a2466cc6ed33de1ebdbedb1066ea44ded5ab70213d97b94e9de2181a050d", - "pk": "7bd626abcca9a16fb3e1a42f5cbb982557746ab9807098eed822eac6a21900db", - "alpha": "64657465726d696e69737469635f74657374", - "salt": "", - "ad": "", - "h": "cd5a271f4a95da1d08d496afe837d8816753cb808a562822b03bb334c3cbfa07", - "gamma": "114223e73f4c8b0d24a4a6f14337af793a2d7eb3adfdc515402f6e6f1aa668a2", - "beta": "5799fb831a360f37aea7c21f258d91ff414e838bb04e167912a2c769e26e9a4562e682c527dbbae7a81f20e53d4f86923c5353362ee17c149ba226e21858231c", - "proof_c": "e7b173169b17dc7794e57094fd86d4981a8687debc5ec038afce3c75191d7418", - "proof_s": "832629617b3ec71a8bdc7bcf39a822cc4fd884246bbadbea581541ae9dd41f12" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-11 - determinism check 2 (should match)", - "sk": "c267a2466cc6ed33de1ebdbedb1066ea44ded5ab70213d97b94e9de2181a050d", - "pk": "7bd626abcca9a16fb3e1a42f5cbb982557746ab9807098eed822eac6a21900db", - "alpha": "64657465726d696e69737469635f74657374", - "salt": "", - "ad": "", - "h": "cd5a271f4a95da1d08d496afe837d8816753cb808a562822b03bb334c3cbfa07", - "gamma": "114223e73f4c8b0d24a4a6f14337af793a2d7eb3adfdc515402f6e6f1aa668a2", - "beta": "5799fb831a360f37aea7c21f258d91ff414e838bb04e167912a2c769e26e9a4562e682c527dbbae7a81f20e53d4f86923c5353362ee17c149ba226e21858231c", - "proof_c": "e7b173169b17dc7794e57094fd86d4981a8687debc5ec038afce3c75191d7418", - "proof_s": "832629617b3ec71a8bdc7bcf39a822cc4fd884246bbadbea581541ae9dd41f12" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-12 - single byte ad", - "sk": "5d7694907fd4768589b8fac74ee6f819519820268073c4216b924764bfe1c30d", - "pk": "9492eabde3262a1c22a8e59d1c9edbb859ae4c942447a7bd6d32e6c2110f871e", - "alpha": "74657374", - "salt": "", - "ad": "78", - "h": "742ee92f44171f25c3e3188261e0261a548f6d10b9e1ba73c06b19732919b18f", - "gamma": "8b01a83ed60cce810410107bd4bd9a34d1eb7f99eb72e5b645129795b49fd9dc", - "beta": "bc430f55d0bc464891175d3026e387ee5dcb35c5c55e6558b4f681b2a052cbe3f685391190080fb919091a9bbd5eb5646f468ea5f07c6244af59ab4bfa1565ed", - "proof_c": "79ff58980d88cea397adbe5d7ecd12b473d19496edad2bd6b233253f01899212", - "proof_s": "6961c574bbec3f0649bcb4d9a2f7cb80c6022ddee62721ac98494822d111d301" - }, - { - "comment": "bandersnatch_sha-512_ell2_ietf_edge_cases - edge-13 - 1KB additional data", - "sk": "47a70fad690909e66e06dad1cf2d595822a2085cb36658b117b80b2d6774350c", - "pk": "7fd08c04d4ee71e0bd76737775c2008d413bd015e004ff6d62d4eb5d87203acf", - "alpha": "74657374", - "salt": "", - "ad": "79797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979797979", - "h": "742ee92f44171f25c3e3188261e0261a548f6d10b9e1ba73c06b19732919b18f", - "gamma": "77a3352ad17f4a607d49a57896380a1029a05822bbb923c2069ccf7cdb2c6f14", - "beta": "156c8e7a63ed89ab14d63a9fc82e6f18fcf592b8cf7657e88d797c032123f1da381619a907007b5914afda07cb0a583ca8c0d629e8b93280bd3c62dd2e1f9063", - "proof_c": "8f882087c5ea4588bfff3be80bd0a14b59a25a3d917fd2df3c6d88cb7919e509", - "proof_s": "7a334bc54d2cc594fd3c451893b844fcc9ca6eadd29878bc4ae18aae7e5e0f0b" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen.json index 37a43bb..3d20675 100644 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen.json +++ b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-1", - "sk": "bb6031cbe6f6dc0083781e0125880dd59be51d69096a6e91e5aa0cf1a7e2d917", - "pk": "bd32288892578bb68df50f75a4c8d890e01306a2a33a4572433f5fa199e0099d", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", "alpha": "", - "salt": "", "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "64c23462a26edd657d03dec918a4452de4e25740e664f6c0077353a8ccd9cf32", - "beta": "d8d5518c5c45328e2eba993939d0dd4f300a4856ee4dc312c077b2ab7a4d1b33cfcd1f20a21992d178310c60bcb0c4193e542a21f566a8c4ad425152c90c656b", - "blinding": "5e30d19c3b229dfc35de0b4b1bbb106d0b74c5dd292fb60dfeb419860f61d502", - "proof_pk_com": "6d41cec663d2b97a3134b31c874ada38813b47bf5770772a0a0607c2797e4c12", - "proof_r": "882cb6852ae5947097f8556a4e53d4bc29830f050e6c0d04a8116e62170de36f", - "proof_ok": "d59a0e474249e1b3eae619f3f3960696db8ae0e037a2881e066ef46102b76634", - "proof_s": "754f4205dae608eeef9b23698089bed6c4120dc15bd0bcf30c71785b98f53019", - "proof_sb": "a4f940b59a8ac9982efd4874e52d3bb2ebc4c77d7c2242719c35bb58841f310a" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-2", - "sk": "7690825d266f4ff1b66406e2f953560d648773e6e9b8c4f643d49630316a6314", - "pk": "aba649f1ec853f0ec18eed327a21fada721657e4162ae57b42687d4087284066", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", "alpha": "0a", - "salt": "", "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "72c5e143dfb28e0ff4fd30c64f78526fc4fcfe05fc132e0e9fba88b3455d4d10", - "beta": "7cc98f269b6205ed35970d3789d1b9249b1689df26ca7781edcffa07aa0edc35f02b4c588e3b5623dc96a0e1dcb8a3beb34275e833763b0c62970e392bc9b89a", - "blinding": "d852ce5c0bf0efcda29cf135cdfe2157efb812f26ad272e82c37344de703e408", - "proof_pk_com": "2ebb822c258c0abe8a9931746b060f9e66051512768b2a1fe09c248701da0837", - "proof_r": "b7a2e59652ad97036f262f08a10788a3692b2f36cd5213b49beb9e90aff7bc2a", - "proof_ok": "d68e5e07068507b222383dedf0165b8c24db8aac6a09780bc481d77278fc5397", - "proof_s": "160ee2c8087d742f08a7aaacb0f190e737c97be621501b4fea48f6652b512b17", - "proof_sb": "02f5c86bffb1c113a0ac18b627a772706d30c82ff0fccc55816a5f3f26bbd604" + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-3", - "sk": "b0eb0b5750285dd6d2d0e5b7ecdeec7521d1cd5a92823c2679aa597995acb508", - "pk": "cefd465e65a72e31346219f3b20c53f7a801a9897e3479adc53fe34400437fa9", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "6cf61877cf74482db2ce24b8d3cea0b97ce1fbbe19f0222d29e9a82d7f207314", - "beta": "102db0061521f1b0984e1034f44184ebbec482f56406f19fb290edcddc06d6feb6bc50bc01e394f33b8b6cea418229d38379f79ee8a9b4e4e61f03a6aa403a46", - "blinding": "acc9d0bb5a7bd514d3f1981c0e5e59d27e92e2d8e97777273822e844345c1a16", - "proof_pk_com": "bb27788abe9afc988156892a6302c076337c4a88f8f9b121f609375afadab2a4", - "proof_r": "a3632a336bb06a6ebd8c5d494dca9e5ab79bd817de08f9469b62d429b590509a", - "proof_ok": "1ced00d66c71b0cc29fbc033022890708bbcd720ca58e74314701992f0cbdac1", - "proof_s": "2f730d011b348613af40b60372f3a073cf4d53d09e1a3575526d573b6f654905", - "proof_sb": "69273896e2a1d3bd04bbe512dc8c91e6f0663cabdf41c1b1fce8e0a47695d001" + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-4", - "sk": "89a818ea311553b92eb4eb56b85b9ca29c92c0e903c969df20066aeddf415f05", - "pk": "c0e72cb09d87bbe6b21c8ff552293ace36174ad74ce38666afabc42aee6a0b57", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "3d4c4744fcaf64343585e59511ec721683b2dfe2673da10f79ec1380e720339c", - "beta": "eb4af7e8573f118716271f46ae7265176590c777137a09337e34e4ace85622ff96eb3429be3a744dff73fba4000e51a06b908be371116c56406b3fd843a5db66", - "blinding": "28fe9a3cebc9104b5ba5362bd7c954b696bae7412052e46b7803b68ad05d6c18", - "proof_pk_com": "6ec4e9570496b201c91c151d812d814d6f93fe73222913c99425c9bd5325a066", - "proof_r": "147410cd2c6e5cf8dfa982070cb9300a5e86b34635bd8f716f0ab5988db6b850", - "proof_ok": "aa37fcb902da9ffde0f752b703c957dfa532467e5bab066f2535a8084f34515a", - "proof_s": "889f3b8f14caac047f9c89f165d64694fb0bcafbe8546fd02910b3b369008205", - "proof_sb": "b9210aaea0cb361eda9ab107b83863f43caebf81165b4842434d95b045198402" + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-5", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "blinding": "dcaf61e654f336a0ce35e46a91d34908890b96c9357180ef1680abb56a8f2e0b", - "proof_pk_com": "31bfbd0e43a027a144c15d60ae5d4b9afaea4dac3a1a2998c08cd253964265c9", - "proof_r": "26ee756c2ffafd661b2b198792850a7bea7497aea963a24c4eeb5af9f255c7b8", - "proof_ok": "8abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5a", - "proof_s": "2ca0c36cfcb0329280278589dc918ab80a09ad577315e4bbddf062517f833410", - "proof_sb": "27d2f61456d6eb688c4b81883b4d0ee785c5fa285fd1d57a0ec78cf98cdb1b05" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-6", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "blinding": "698c84bdaf0b0a13c9771a1729b7490d7a81471bc419eb2f95dcffcde7ccc90b", - "proof_pk_com": "e90d77a101903bcbd0f378f11b0a72a2eb62f476171f44696bece4560219ee72", - "proof_r": "5427fbe8c832959d98b69575ba1b37bcb6427386fd511f7aa6b8aefd963879c6", - "proof_ok": "8abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5a", - "proof_s": "e00131ed77b0477d2546908566e5fab31badeaf1bee4e6c71ffcd367cb624a0f", - "proof_sb": "a8d4afb9913a7d4c934bbc06693295a0a471ce7eb462d636c56ff96a5f895f14" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902" }, { "comment": "bandersnatch_sha-512_ell2_pedersen - vector-7", - "sk": "938834d6c6369917101c182c58e7834aa8763fee2d35f7e06559075288c62310", - "pk": "d33123beff50fdb78b69ca1d6139e78cf1aa891e5795ccff942af0d20d8dd9db", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "44b48217381ac12326614d32a14c3d07299ce4e04df80810ae0b7dc59f6f0980", - "beta": "e07d922afaec4804ac28aca6db75b6f36376669628014bb299e32259eaa46b871257b2483a387ba518a0b0e3ea3f1061e23d140fb387b83650850c937c897d04", - "blinding": "4da183b72197aa0295ac8d3f55b135037adbf9aee661e4aa641c8e3acb3a7904", - "proof_pk_com": "c4daea0b04317f39f699e1fb7c7fa7168e985d9684aef3edb68d79a38437bf35", - "proof_r": "b3146e78e7069713a70783e39c46e4cb154c7c1d678b049213eeea8c0e11ffdb", - "proof_ok": "72d7562979ed8403242f1894704d528f517755cc49e858ba1bcf8550239f47ef", - "proof_s": "e614f230cf145fc634630ed89f5bf2c5764e5a8ceca115ccb63e486bb258c605", - "proof_sb": "9aa927b7389b173841f9d4b4db87b92dc5b6d48d5a669bb524fc212baeff2f02" + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514" } ] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen_edge_cases.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen_edge_cases.json deleted file mode 100644 index 4110a6b..0000000 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_pedersen_edge_cases.json +++ /dev/null @@ -1,155 +0,0 @@ -[ - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-1 - blinding test key 1", - "sk": "d9a4d2b6b7848390fc0ed8c6970667ec4eb41804e5cac63740df721488ae6103", - "pk": "d427935d99ad778aa52c591680988505747db5c7f53b989578da9317aba59c28", - "alpha": "626c696e64696e675f74657374", - "salt": "", - "ad": "", - "h": "e883d15d2c00496114f9b759a7ba7b32bab711425be2904754918d0135ceb752", - "gamma": "62e1730b8dd78426d3ca66496db651c4f568387f34ac792122de572f923ea087", - "beta": "601cc80efb18f1f610a547c8b061c64a0f776c53a1dbc0809fa65a91300c7c9fcac870442e706ff9afb77b3430432d296177fb012b4493be1e99f159bc952a10", - "blinding": "61d6c26465408b238e0fa0fa60c595a7ac3517868227dbd4c7a610c0be020509", - "proof_pk_com": "073ef02a8f3b387d4c2960861ebd8646186da1746ba7939cf32b1a353d40dc64", - "proof_r": "d3214ff151273e129914781f792392217832f1879c2537e3215107f9a70fbd41", - "proof_ok": "27a298de9cbe8a962d9c40cb827e7ea14dfedd291abf238a0386b06729ba3000", - "proof_s": "ae82e493f8e645f27539cde288ca7377923279579caa7fc29b85fc2dad531c12", - "proof_sb": "415ac87e85fcace4b38591d066b7eb4d3cd75d3876b680a4ce27379aac60df0b" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-2 - blinding test key 2 (different blinding)", - "sk": "18a245119b10328d2c604f2dbab47d0c1471e5e76b1b104c914ae012e6bc0c15", - "pk": "7894aa7d87cca787ef8fea97f091a826dd8bcefb24befa5cb8841a9049387f60", - "alpha": "626c696e64696e675f74657374", - "salt": "", - "ad": "", - "h": "e883d15d2c00496114f9b759a7ba7b32bab711425be2904754918d0135ceb752", - "gamma": "f4ed0a6182daf721ade3bdb09c8c9e5966e5fb7bc41b4be7e277edff589e1b3b", - "beta": "765427eb13ecc9a0e310d2c1d24ee214146e1fa03b578f72dcb08b280bac8041b9ad3b3e8f8cb9b8d16df5d190405c5ff91dcc688cbd9c8ad3810ccf3eed2611", - "blinding": "f44d562f7a36b67bcb80b169c70d020afc93735ddb60831816c0188eb1256d02", - "proof_pk_com": "0657bd6858c60af01b9f44be9dc490eab0b231a43ad2478816da8c31bd7ba738", - "proof_r": "b670b55c4cb536f6b19284c2e77c9b2df52e2b9e82c3a8c9f299d2d4b453780c", - "proof_ok": "afeb1b709987b593fba0498ebdc641a6776e2d2653a5c5d34b4154530bc6d252", - "proof_s": "a36684f9ec7cd96da243b7f22ac60a1cc091bfeb41eb9e50baac2bf62511f01a", - "proof_sb": "04c574c52ad96220b2a164c8e44b9a8e2655671b7c28614a65ef0ec3dc234217" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-3 - blinding test key 3 (different blinding)", - "sk": "34c7bc88dfaa779ee3945e14a6c825c5aa29b3d44d0535e3b85e613e77132e17", - "pk": "562432ee161e2256a0957602c79c82f67fa622208e9697738920fc4c308df81e", - "alpha": "626c696e64696e675f74657374", - "salt": "", - "ad": "", - "h": "e883d15d2c00496114f9b759a7ba7b32bab711425be2904754918d0135ceb752", - "gamma": "af815049f5480309aaabc9a6e85fd8f06cb5ea7c70d95c26c0af44760fbe1d5e", - "beta": "27626debbb205ff88d6019ea8020244794cdad7183a4ecaf6b2ebaca2ee3d16ea738b1a3853b102aa6555b9ad399668a76dc41b0727670317fcef4cbdafbdd98", - "blinding": "766eda691c4cdb4e3b035ffcbd7f35fea47f4b788d329dff7e1c1740cd46b21b", - "proof_pk_com": "355158477548a77bd6fea0d447d3b39b6bb41309df48b6842a52a88257e1df1c", - "proof_r": "af21a25a39d06836153053b3e47f9c56ea4f7dd7e3b2999c5eafb29dcdd3c336", - "proof_ok": "6296b2c0c0e9c69cb6605f8876cda9bba929bf70fff4c8669bed518514e30110", - "proof_s": "ff9a50552185457444e9b7d63fba74e6121afebe92fc251cbe86c62164cc6405", - "proof_sb": "43521123f43d90a62192625b229e642bd3099cb8172839b2e3557b1eeb83d508" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-4 - unlinkability input 1", - "sk": "aef0965eafbf7869af8a916b288ff81a3f46f3b65c0fe1055227bc3675e75710", - "pk": "b594ab516c9fc66a2950e57e8ce6c5b6b7d939387438a847c70aac993e910fd0", - "alpha": "756e6c696e6b61626c655f31", - "salt": "", - "ad": "", - "h": "e23efcacdae54ebe60a603d9b14438b4d1f6cb7135e3f69d3f8a22753f607d1a", - "gamma": "21b41652475e98e9a36b6e898a4d40529028bfe6646c50d6b2ba317f121aeaad", - "beta": "be14db7b18f5c45f58418967deff883e18abed1786f3b70c907d2c1697b3097ab9c5056419514b5266229dc862a322abca64bd2d4c75326d7d853fa2ddb0e0b0", - "blinding": "806e95273c9656a1398108036a4d98c3cedf66f27e4bb3964b0dcfd41b7dc810", - "proof_pk_com": "3c4eef7b4085dcf51a92a9c6e20bb930c24731206bfa60a1dd8930108c979bca", - "proof_r": "861df14bb7f18e9597237d7272ac3d7796d07e9bc5a86e09f6f2bbc129b70d27", - "proof_ok": "502f8c29006939b6be00829d7433c9cd6de5ca240a541e6d6f67937f1c004b09", - "proof_s": "19ede1b3d03111022caa606de2ec90e0242821e4f2ccc283c51f98479e58b201", - "proof_sb": "ce91eabc09afea60549905c99fe886c546a388466bd06779003669d10f6d0e03" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-5 - unlinkability input 2 (same key)", - "sk": "aef0965eafbf7869af8a916b288ff81a3f46f3b65c0fe1055227bc3675e75710", - "pk": "b594ab516c9fc66a2950e57e8ce6c5b6b7d939387438a847c70aac993e910fd0", - "alpha": "756e6c696e6b61626c655f32", - "salt": "", - "ad": "", - "h": "ba80e339745a24561b7190b01a37415f23a1d6c70e4467843ca3df8abfc3071f", - "gamma": "785bfc17e6bd87e00d1d8f264e0f6b79f11571b4054dd809fe27c0ccc3824945", - "beta": "8538ef7e96f2504c5498620129bc320b87a9ef61d1889a05c2a871b64239eb84a6cfcb25579479c3e2d6d388954c3251f49f72fe00f1d7341d30120ec7d805fd", - "blinding": "73fb8b7350baa50b1accdfdf033c8097a660c16d9c022626d6fe578aca006708", - "proof_pk_com": "2b58125047d9ae266c2fe979a4d1fe14cfce201031b283e9e23cdb7fb9f73f43", - "proof_r": "537b905571967c6e7981c46a725374e8f929e53aca450929b52bf8c70c0c3a2d", - "proof_ok": "b1d05bea1182fb8d835093fef0e3120fe21c685de94e51be2175b511a94a4fd3", - "proof_s": "b378c71d3b04a51e78ef1fad2b82f72aad5bf420beb8d0b7bf77318d6c185b18", - "proof_sb": "6d20fd424bb2089fcfaa9e09cff4684201be01bfab53244449ec0aea75467e08" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-6 - unlinkability input 3 (same key)", - "sk": "aef0965eafbf7869af8a916b288ff81a3f46f3b65c0fe1055227bc3675e75710", - "pk": "b594ab516c9fc66a2950e57e8ce6c5b6b7d939387438a847c70aac993e910fd0", - "alpha": "756e6c696e6b61626c655f33", - "salt": "", - "ad": "", - "h": "91da61d040ac021ed7f5522b25f60f2915c51d0ba56e1c8927c7d865b3d810aa", - "gamma": "f2e19a0bbc717916189f8f1304610d616e06a412a7115e1db308b2e775d7c284", - "beta": "66dd6df1a2a98bcbb2d711821a47c336981020d144b422d5bbe9c1ea11265b228ba269d3beccebd5a2fffe66e2b04a79ee8c0f73d4a569b050b8889ce35368e3", - "blinding": "532e81b23a6d696debb01af33269743a7e7fb2faa1998138d5521be4f8b6c30a", - "proof_pk_com": "6e7886ac27b1e8298a37ef353231d1c30e03bcafe3cfff2e9fd17916c4c29d08", - "proof_r": "8640957bcc785b2163baf78cf62b5b725cb2b185a40ab275e7542eae011d94ac", - "proof_ok": "afba1fad4f53ef34ec1d906051bf8b85c8c20cae2420ffe2b6f2ec698b36d2c0", - "proof_s": "a49fc5d4c8cddcdf96dfafcb033f80010f52c2efda414b866790ca13f189ba02", - "proof_sb": "951b1c768c0a1f67716c55be7d9e88a7e3c5dc621b3810ec26b54f43291d5b05" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-7 - no additional data", - "sk": "1fbe90204c967efd44f6c5f7f135764dfe3fe029b7fe4a1134f0023992cab209", - "pk": "76ca1462b9369721a59641bd875501f66139cdb24680187e67e563a1a49d04da", - "alpha": "61645f74657374", - "salt": "", - "ad": "", - "h": "339157b9737178af7fe9a2a46574b8056a84cec85b139e3cf35eb09be2efb581", - "gamma": "84a89744d5cd5ea7c2185a02b35a4e170255f248a0011d2a0304ed3bd15af047", - "beta": "7d7407d347b3cd17d0292512169ca4481fdd759c7f83bdef7c89493ed573af7a9ce1b839dc2e71409ec69506c9c46307abce0adbcdba159d57170d6e3b69279e", - "blinding": "d4d93f85165341b02a593c8c9437d6269bcd2936b70b1e5ff82cbbb309ab5100", - "proof_pk_com": "d1509dd97cc2a8da458f6dc7df38cfa2c58db9aa6dbf8b3da82f958c89a6d99b", - "proof_r": "f8aa7c2b4e4c8fff09c01016fc2a2b793816aae3c786150e660ae10a03bdb96f", - "proof_ok": "464dd90eadcc843f46ba87e78cea18aadef896396d2094715a883f324ec90a19", - "proof_s": "08f95c55bf9612b5b4d7a6b3c972cab97ad367a4ed03607b5a1099d9beda240c", - "proof_sb": "c9c87099e7e9eb534bf9b9cd59a8ac761787d6479feb3c9ecf043d3fe33b730c" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-8 - with ad1", - "sk": "1fbe90204c967efd44f6c5f7f135764dfe3fe029b7fe4a1134f0023992cab209", - "pk": "76ca1462b9369721a59641bd875501f66139cdb24680187e67e563a1a49d04da", - "alpha": "61645f74657374", - "salt": "", - "ad": "616431", - "h": "339157b9737178af7fe9a2a46574b8056a84cec85b139e3cf35eb09be2efb581", - "gamma": "84a89744d5cd5ea7c2185a02b35a4e170255f248a0011d2a0304ed3bd15af047", - "beta": "7d7407d347b3cd17d0292512169ca4481fdd759c7f83bdef7c89493ed573af7a9ce1b839dc2e71409ec69506c9c46307abce0adbcdba159d57170d6e3b69279e", - "blinding": "10c1ff42ff146efb38cdf3bbcb3344cac844ec67a30a0eae6d91cc2397472a02", - "proof_pk_com": "e2c4a40dbf0ebdb5f11b9086e4581cb87d7e323862f665725fd5e7160fd672f2", - "proof_r": "63e551f959f09dccf52b53556b923cadad70cafdd347861b15acdb9dd2bde2c4", - "proof_ok": "464dd90eadcc843f46ba87e78cea18aadef896396d2094715a883f324ec90a19", - "proof_s": "1a54c494aa8095d8298755b52f8a8f59e461091a720f5379e7434e8a4824a005", - "proof_sb": "c67017271bfb064918d9c89f1095416ede6c8ae8099f6eb34b14b9f68340ab0c" - }, - { - "comment": "bandersnatch_sha-512_ell2_pedersen_edge_cases - edge-9 - with ad2 (different blinding)", - "sk": "1fbe90204c967efd44f6c5f7f135764dfe3fe029b7fe4a1134f0023992cab209", - "pk": "76ca1462b9369721a59641bd875501f66139cdb24680187e67e563a1a49d04da", - "alpha": "61645f74657374", - "salt": "", - "ad": "616432", - "h": "339157b9737178af7fe9a2a46574b8056a84cec85b139e3cf35eb09be2efb581", - "gamma": "84a89744d5cd5ea7c2185a02b35a4e170255f248a0011d2a0304ed3bd15af047", - "beta": "7d7407d347b3cd17d0292512169ca4481fdd759c7f83bdef7c89493ed573af7a9ce1b839dc2e71409ec69506c9c46307abce0adbcdba159d57170d6e3b69279e", - "blinding": "5e4dbe80ac1c9abc93948dd839dea4dab0e634f5c51374b965d4d25f7ed27302", - "proof_pk_com": "2a55cefd2ece4805a985d77e49e93839a20eff1feace9109d1ab4bf0cf03ce81", - "proof_r": "d5451fd3f363984ed9ce89e9247116d0f371d546e44c0e3d095307cacbefcac4", - "proof_ok": "464dd90eadcc843f46ba87e78cea18aadef896396d2094715a883f324ec90a19", - "proof_s": "2ca47c4197caab0a74dd751a20656e445c8994944592c69b365a149cbfe0c906", - "proof_sb": "55dcc85127190c4d76f1549c14d5a7adb54847b7b4ea3ef42cb671921e7e4b16" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring.json index 56536ea..8268023 100644 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring.json +++ b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring.json @@ -1,149 +1,149 @@ [ { "comment": "bandersnatch_sha-512_ell2_ring - vector-1", - "sk": "bb6031cbe6f6dc0083781e0125880dd59be51d69096a6e91e5aa0cf1a7e2d917", - "pk": "bd32288892578bb68df50f75a4c8d890e01306a2a33a4572433f5fa199e0099d", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", "alpha": "", - "salt": "", "ad": "", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "64c23462a26edd657d03dec918a4452de4e25740e664f6c0077353a8ccd9cf32", - "beta": "d8d5518c5c45328e2eba993939d0dd4f300a4856ee4dc312c077b2ab7a4d1b33cfcd1f20a21992d178310c60bcb0c4193e542a21f566a8c4ad425152c90c656b", - "blinding": "5e30d19c3b229dfc35de0b4b1bbb106d0b74c5dd292fb60dfeb419860f61d502", - "proof_pk_com": "6d41cec663d2b97a3134b31c874ada38813b47bf5770772a0a0607c2797e4c12", - "proof_r": "882cb6852ae5947097f8556a4e53d4bc29830f050e6c0d04a8116e62170de36f", - "proof_ok": "d59a0e474249e1b3eae619f3f3960696db8ae0e037a2881e066ef46102b76634", - "proof_s": "754f4205dae608eeef9b23698089bed6c4120dc15bd0bcf30c71785b98f53019", - "proof_sb": "a4f940b59a8ac9982efd4874e52d3bb2ebc4c77d7c2242719c35bb58841f310a", - "ring_pks": "896c56a5cadd2f4fcb1ef0bcdddb1bacf4a26d487e5e8763f27b9f371d9237ee23f2e5a7a93e4f9d86f33d058e2df3fdccbf2c06f4ade30a770284de8d1392b9052e085ef0dd9471b00931165841b8b29b7ed29672630fdbf3f7956b12b20995bd32288892578bb68df50f75a4c8d890e01306a2a33a4572433f5fa199e0099d81d31825e465e1ffb12b770b6839db892a7dd12dc094ef78dc56adaf044d31d6f37d66df1e8080f8316e9d16ba0825aab06862176a4790eaeb9f2bfb4be3872f544f881f769c760f7afa49b497f42313b76059745e1ac6cc298cf05c6af3ded16b800ffd791801547d231588f9497f9683604dead0e83232e5b06a96da3a07b2", - "ring_pks_com": "a59d9aa5032f11de7c81dc507c925b0c7d90f4a5ca33fd10b5520d8ed8ef6d3b2d018877358271985d95731543abab83ae428556ce295c14f0fa5580a41020e35be829fea389cb5a4fdd6ec025550b4b837609e00eedf040222648f36c889dd192e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "64c23462a26edd657d03dec918a4452de4e25740e664f6c0077353a8ccd9cf326d41cec663d2b97a3134b31c874ada38813b47bf5770772a0a0607c2797e4c12882cb6852ae5947097f8556a4e53d4bc29830f050e6c0d04a8116e62170de36fd59a0e474249e1b3eae619f3f3960696db8ae0e037a2881e066ef46102b76634754f4205dae608eeef9b23698089bed6c4120dc15bd0bcf30c71785b98f53019a4f940b59a8ac9982efd4874e52d3bb2ebc4c77d7c2242719c35bb58841f310a9322f73159104407ce82b67cdfadf68f8df0eb26b372ca28958adcb045c2caef61dc547d64db6c04874102c251a7e0e49107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda92ce4b259a2b83db5ad9da63a578899315a86a545ba0581e186a26cc362bcdfc2226666eba071edc776e9ff2ae1f43bad2c642c5324917395ed099ca618065d33e3ee427bc8edeae69639ce617bddc8e60c10e79b8d46912e11430f802c9fb96cf25449190bda313699449a733a65a80667d882f8e074d0ad886c4d06697e5ac96e39588b39a602fd7fd007ae7ddde5ba434bf03fc9f0fcb621e1f8d5945730c8537c3bcfc86ba22449804f888ad0a350c1d30d899cd9068ae48b7727fd802cbe3b68f2e2431abec88bbbeadc383189e37357d984a29480f104219a7a1f786940f82361bd2f4db861b8a3f51cee87040044a8be4c0a2ea753ae97e4d83b6d3d47af0c3aaf2dd86821070c17cca664b0de365fdbdef74dd768f8e6295e0e6d1c7e96ce58cc62cf73b9a8f08131c634f01ec8577a3172a0c70ca6e0b0749b573692de584563af563e4000e0b9cb9c10325f698d45f5a3ca78e7c2e157bd70fb9ef094da72c06518688a72ba6e48c7e3ee3422b846e342faa9c4f88b020bd10c4fa411217314882a3b129e0444fc20a1388c4a6d6df5ee6766b1fd8a2acfef28e11ebaf442d60077e2ecb902a4fe475606c165f438b8996a15514e6f3891b9ba94ace688d5eecb8b5a150b9965e5e6f519eaa945b7c99fbcf66068fe7893d368946df1df7145561cc8b85d04b7dfe0adee", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "blinding": "afa1000852c300cbf235a6955ec4105b01919fa67ec6cc92ec92100f19c25809", + "proof_pk_com": "680539c070ebcaca50c4bf201869c2c355f1467e1b8ece6e0bcafc466872c5cf", + "proof_r": "375a8ec2e95f58a41ea09090f9560e580e7a3cc67998129fa98dac1da5d0850e", + "proof_ok": "5071efbef9428b2a26eb09440891c6829bc0c57b4254ca811ce453a0d5938dea", + "proof_s": "7dc8aa043b28383d334ef500fd7d770d35ab1c5ebc28df0807215911f4ab2d10", + "proof_sb": "045343af67f88d35679beab125c38b80029099b746ea47ef3eb488b1d38d130d", + "ring_pks": "c72fab1942324e73e0ce3e3871bf5f86aedc9396810e5d7f3bb679dc6492880c927c978f89a96d5e921891a62f0f87f94c53bee510fbf830eca790f65fd5895c6a48a1f585379021d6819b56452a5f08821cc48b26e78ef72e374bd8d60478715a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc7185e4dd60351aface9f606702d358e68326c6a85252a0ee8e572c8453b18c6e9b62d578397150766b324972681fd67b85d6eeaaed6cf1f089cf115d82a318f14c12d7aa8ad453c5c291a9d721ea653d99128de0b5f816e5db4ef61f83443300d7ffe543b541ede9fc6652811a54c7e07580bd807b8ae79d02acf270c114deb367", + "ring_pks_com": "8ecabb54431336e2f8140bf06ca36efe71ca9321606bc36b6f8b477120479785905c7192e63d5828d6620841499dcc14add88d2cdad60f39dbfda3bc8226b50820a12fbe55c4e335419f067fa67e39f40f5cc5e78d31b20ef1aa09f4db2c5f2b92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "a88f3e5041ce58a9b730391fdf31004a4eceec666a600ccf443b0ba82c478c88fea31f106453618afb82ae847d905cad9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd9544919af606752d54cabaa6a28c30762a42fb27d7347aa3a7d95e6451d735c7db7395793dc4ee1e2a63ab02c0d112baa66816f64eafad9351b9e11386a79abd3652805e5244bd13cf00e5d949c252d2977610d9721e05d699bc73b14bf2b5d439e5b1ae67f0b47118ff027cba90f821327e515e280b963688f881775765db5f6fde7b3c7a72e11d705f378b1fe2c6191befe8fdee47c26c72c7fad1590b355cf8eb8cec373254797612391f88cbe751e6260e764c01ee3908f5480e3ef0c42184bcbf64e9de8c35ec54b9531841fa7dfc01b70a65437dc63b97a0ad57615f0f8d245cbf9774ee50bb51834e44b620b943e3a56f55df9ae7c303a7e8783f2a53a27780de9e132bb2166861dead102926de871be4f5e7904fed91e96e02892071e7e08fb6babac40291dce9a90405865042220e3f4d7935cdce6b1b4bd21c4c3ca1913991c368d86d3d029e25f2265f8d7a99fe4e6b451ea2e6181abb4a4a7accc26feb4b3e698d5e015981eb177ca31e67c1e4ba8500e9c153d99868c04b29441ea7fb8b5aef1c9cf5bc8caa12a5282c8648d229e370a2ea03306311f1450c8366de7a8523ce78cbbe921f743f2716df8b08d83fd4c976624771f4862ecee8e4b952b5740b72a614a9ad3ccab7935b77f1e16cf9a0352604437a69a3f1bf1bea4e74db0a584892fc1ea87fa1f92c21a9", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-2", - "sk": "7690825d266f4ff1b66406e2f953560d648773e6e9b8c4f643d49630316a6314", - "pk": "aba649f1ec853f0ec18eed327a21fada721657e4162ae57b42687d4087284066", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", "alpha": "0a", - "salt": "", "ad": "", - "h": "8c1d1425374f01d86b23bfeab770c60b58d2eeb9afc5900c8b8a918d09a6086b", - "gamma": "72c5e143dfb28e0ff4fd30c64f78526fc4fcfe05fc132e0e9fba88b3455d4d10", - "beta": "7cc98f269b6205ed35970d3789d1b9249b1689df26ca7781edcffa07aa0edc35f02b4c588e3b5623dc96a0e1dcb8a3beb34275e833763b0c62970e392bc9b89a", - "blinding": "d852ce5c0bf0efcda29cf135cdfe2157efb812f26ad272e82c37344de703e408", - "proof_pk_com": "2ebb822c258c0abe8a9931746b060f9e66051512768b2a1fe09c248701da0837", - "proof_r": "b7a2e59652ad97036f262f08a10788a3692b2f36cd5213b49beb9e90aff7bc2a", - "proof_ok": "d68e5e07068507b222383dedf0165b8c24db8aac6a09780bc481d77278fc5397", - "proof_s": "160ee2c8087d742f08a7aaacb0f190e737c97be621501b4fea48f6652b512b17", - "proof_sb": "02f5c86bffb1c113a0ac18b627a772706d30c82ff0fccc55816a5f3f26bbd604", - "ring_pks": "f87047629bb53341ce4adf8ffaa491fe46cee9d818c8677aed5591e81146fb27805404f26bdbe04130d073a8d9006dfeb99bd288c33b1b97020c3b967a54ad6706c9edcfadcb62e5b076b95544faeef6c9e52c2f18464647c6e0285149dfc8b9aba649f1ec853f0ec18eed327a21fada721657e4162ae57b42687d40872840661ca837c328d997f3f3949cce5c339475ecd96eae770f66edaefe4fb18d9606062aa5fd48cbcae1d6e8ca0a2e768563d0964887f562a97fe5f0632ed015b1c0b13aac177edaa7e7a642b11530e035049542aff82d6b164312e1f4e118c6cabb465558491286ec9d38d4ad698c414bf33d48c096dbcfcc06f1e3709266861796d3", - "ring_pks_com": "904420fafddbae907a5a35b4e62beb447fa2e54f0c6b66324b37a76e1b5b25d14522103ef8c5cd9c2fa974322df495fda94155f6ca27e9719379d7a289a3a777ac6ee12efb865eb0175dfe57fa0b5fce7c803da7e20b4887994d1c2253f440da92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "72c5e143dfb28e0ff4fd30c64f78526fc4fcfe05fc132e0e9fba88b3455d4d102ebb822c258c0abe8a9931746b060f9e66051512768b2a1fe09c248701da0837b7a2e59652ad97036f262f08a10788a3692b2f36cd5213b49beb9e90aff7bc2ad68e5e07068507b222383dedf0165b8c24db8aac6a09780bc481d77278fc5397160ee2c8087d742f08a7aaacb0f190e737c97be621501b4fea48f6652b512b1702f5c86bffb1c113a0ac18b627a772706d30c82ff0fccc55816a5f3f26bbd604aa3fe441472341d521fb9f2e888161c99483313fcf535b2e63028ad22834c85f6e07646c8863e9110cb3b121ab8a34369107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaeb5940a0db5a15864b0b7ac99fbf5923d26a6d52dfbbb06bd652850b834c268117e5b13e68ae4064590705918ffca6cb114bee30458318340b052f5c7c3558d8ded8dbc0ed72804c67c0990e7ed7f6acedc8002138a8b8e8c69d84e7403800410b2033409f6914cea867a7ca2e402ec4ae727c3ff8ffad19bf04d80f538e94691fe01e9a29b76544f0978a5a21a230352e008ae74e040a1e68925d5cbaf8d60deac1e2565e314aef2426cf8895d8496ae316cd3566759c1caa732096073015c44cce458dce7369ec7404d4d7c9a527930ddee61d4b8bb0e276a257357392b13ff1b65a13e2c0a34b9e51e92c49c6e60584718ae8dcfb989c7b3871519553c33e02253614e41598cbc64499396314945cdafadcbd4fa0b714433b7531da4cc485fe630a0777daedbe8ccac582306858a96824031e1a0d85196fa84683da9d45e848967cc16200468151c9fa0ce1fad4c2d704ff7e5025500d6fe51af0f19bd8b6f9b925a8bc3c6cadbd52b90197ec03aee0bf0652bf4d2b359c7ba56b2620abfd0af207ca54a2506d8bad799caeb166f80d536a65d68bedc93ec21ec711dc18fabf0588f7214c739d515e68600ad83d4679facb5015bd5f24876659adda3c8abab82609ed5054b21650cb47ecfe1d5c5ac8f8cb181a77bde6bf8c151c739b067586ca6bec66bc7c417b047e10491b19d", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "blinding": "da3304d52c2a64ea75504ac44776e5e4900a38eb9a61f9478285aeccba6ec41c", + "proof_pk_com": "a356a2fafe785bc79860ef65534df7c1a2ee7d45ff6db649537dac1687ae4785", + "proof_r": "ec546c1932f72e146a0e45d7107c97e99aacc9c72fd6ae6fd0b0417124aa26ca", + "proof_ok": "1d661c6349b33182ec77b58457f6d45dd9a618017ea021b986cf1eff18c4ef95", + "proof_s": "1e8eb9a8cf017c6a50d0f947f9bf02575b60c714c81602a8f06965ec7e67c300", + "proof_sb": "49c44ad19ea4aaadfc98c2586660dbf3d8a434bb062fa948d82fd4a717ac5f12", + "ring_pks": "1ad0d7c5871212216dbf6e45917a4a69b6ee1900ab2dd5a4d8ef5c31f1b3b119317089fcbc32d033a054038193d7b22d8c0769d349bf83bac97608f43a3ecd185535ed8ad5106bff6bb3a730be0140be4c38ae1b3e8e0a145c389e627d91d8abff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0debe9f354a7c6ea76068c32fbe2413eaff2bcafda0ef6fdd781c5edc3414f75e030b90a5da6096908b5916e48331ca1700a0902a5af89dc23c5e2828bea50fedea1bb9d61e325f7a561c720162dc26c6f18468d2dd57d8752f9dc1e818a2da84ecc930f5ff48ef8a5de768cdcd82c2f6e0643aada0c4a828cfeaf932799bc8b99", + "ring_pks_com": "8dadfce9efbd9578b929e23619407a35571d38913e7a321756317140e2eb736fdb1f0fa291d6bfa03dd1bc69242327a098082c00c77d2afbab7bbb9f129452b2383a977c97f2c7c523aaf90a0ed07d306abcd0166afc76f6b04ef56d5dd2dde392e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b1db26d63b3147361513641ced7124d32642eb1246aefd0c25495387ec3e8a1dfd1ffe4870e37ce5e3a5ac300a507bc39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda9af4c62f6d00e23d85b14c83e8d8ec6948a30871c2171ec2c98b78bd3c542d1ad95025cf9e2fdfda6d6926e42e6a2478b80aeedf10a587fe26c1feb19481f7a043eab9d3bafdda49ee2b32079524f9c5277df8ff2e976341b009ba470fe04a16f49a3e4c8b6b80791805a59a683107fb0e208e58cc6141ebf7a2af27da1836497d23f759cd4affe6aa72b952c4519e2ce31196435f9e898afe47f35802ed7526d0a2c13e5e9b70f2010f28e43228dd25305f9cd22ad2de88e67f7a007fb8d5b5760bd4e640a97f3e2d57cabf7f1291d08680b5cb393c6b0278450fc3c82f6092c6668133f7a4cdb47f6ddb4b38ec547d94c30e0d20abeb6124137a900a8c81df54d3c401c56c557c0a2fe5aec42a43778e91b213ee4b5e2186b9a0f20a09b26579f2c8d60d0e4b13785b29124e9d738c6eb9cddf67e76cc7a20850589d8066795c4dbf0b31bb99bbb86ccc39006b2a111bbef96b0addbbc1f65d5507728a28ac663e2966abd3800caa1a1e0468900c41d04c64de5c5e15b8214c3db9ebcff2fcbc06feb34cdd9c209ddb59d9d44405d83ceac5903efcaaab0ca56f11e3c21b093a01493e7d5a675a11f7ba867a77e21e50c168b4de9606076752e05e8d2ec3eb5cee22a9be270f1bd6241ffdf88ca5d3625ffafe26eec01d8a06c7145f66ec9f0a19f992e968399d6cef04555d32148", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-3", - "sk": "b0eb0b5750285dd6d2d0e5b7ecdeec7521d1cd5a92823c2679aa597995acb508", - "pk": "cefd465e65a72e31346219f3b20c53f7a801a9897e3479adc53fe34400437fa9", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "c5eaf38334836d4b10e05d2c1021959a917e08eaf4eb46a8c4c8d1bec04e2c00", - "gamma": "6cf61877cf74482db2ce24b8d3cea0b97ce1fbbe19f0222d29e9a82d7f207314", - "beta": "102db0061521f1b0984e1034f44184ebbec482f56406f19fb290edcddc06d6feb6bc50bc01e394f33b8b6cea418229d38379f79ee8a9b4e4e61f03a6aa403a46", - "blinding": "acc9d0bb5a7bd514d3f1981c0e5e59d27e92e2d8e97777273822e844345c1a16", - "proof_pk_com": "bb27788abe9afc988156892a6302c076337c4a88f8f9b121f609375afadab2a4", - "proof_r": "a3632a336bb06a6ebd8c5d494dca9e5ab79bd817de08f9469b62d429b590509a", - "proof_ok": "1ced00d66c71b0cc29fbc033022890708bbcd720ca58e74314701992f0cbdac1", - "proof_s": "2f730d011b348613af40b60372f3a073cf4d53d09e1a3575526d573b6f654905", - "proof_sb": "69273896e2a1d3bd04bbe512dc8c91e6f0663cabdf41c1b1fce8e0a47695d001", - "ring_pks": "7bd626abcca9a16fb3e1a42f5cbb982557746ab9807098eed822eac6a21900db1531052266acde4e73a83bd2b4bac948d9565979c77e3ce7cf03e5ed08cd02cb82d2a5fb7d7701cdb40c10221e7b527017b9a836a728b13755f70683247d89c5cefd465e65a72e31346219f3b20c53f7a801a9897e3479adc53fe34400437fa94dec0f9d4f74fbd7074c85b593750487cc2f1d6d75756c266914d22da89ccd04d427935d99ad778aa52c591680988505747db5c7f53b989578da9317aba59c2872454e9815f1f8fac65c8ef4aaeafc4e241fce3c29b3906ebd9a87d23c2816656681708f5b173d0469ca22064a5e3ef6cef5cbf4aedf57e2eb9a39ee8604a113", - "ring_pks_com": "a2c08ffe67e5543631dc23b5bd3958ee2b32c9d4ad7adf8c745ddadeb06130018e939bf865208c07b95692578367c2c88de47391f5bb868fa54046b40d6d3b1834deb32892377f0d7120847043c070e3bb7120bb320e9cb0c4b55213dabdf8cc92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "6cf61877cf74482db2ce24b8d3cea0b97ce1fbbe19f0222d29e9a82d7f207314bb27788abe9afc988156892a6302c076337c4a88f8f9b121f609375afadab2a4a3632a336bb06a6ebd8c5d494dca9e5ab79bd817de08f9469b62d429b590509a1ced00d66c71b0cc29fbc033022890708bbcd720ca58e74314701992f0cbdac12f730d011b348613af40b60372f3a073cf4d53d09e1a3575526d573b6f65490569273896e2a1d3bd04bbe512dc8c91e6f0663cabdf41c1b1fce8e0a47695d001a57ddb51b38e1c8b2c2fa1bf746a3564a9af19d4233ef110269bfab89fcc5a5960dbb3cae19729dd449a71b2ae4b9bf89107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8f85f9c06e24e4b82ad8fcbb743a5780c54d8deca774fae99f5920b12856b076dd07bfede6e82a4bd8743f8be8091f3793df0e32c56fd4a7a9a296aac2852a3b7b0b70738dcf9dd2767661b3ae86ef325affcb2c6b8a21d46b1f4b0fc1b94d3a4cad53b5e744945d1be7afc0fd9648127705ebf45e70f639989b88e4f79f4a130172b6609d552f3163cb8b571319dfce8157e28059138834835bb4b98c09b40bff784747f7c3f8d2aa00e8698914706e7de12396a8e88de938a0e0f0e979d444d4334eca6debb347a0c92898c2471a11282a408b9eebe0b7f41b97deb4ee4f5b9c41319960a38567c4b0e8cd5217477226408a454a510a811b06b967122a3272c6aba3153bc65b035f4719d8ba3f6b224a777bba776cdd375edfa31d1a63e752206347c30880c6196b1fec57fc2848fea3950d7788d9f5c235095b46737c46288f011ae3c0817440b1eded2d82d3e5d4358df762e95db61305e810b056f84986bc4bb63aa584d71d3b07a32206fb822acd72fde7b4597bd92f3d96bf14a353024067554a9cbaf5976392147fde9b8c5da14daebbc68fc59cd3c6c662e8e45643919c7ceb7bd8bfc9fa8c82c4bda04e706d801871a323e07d653d5884fc933af487be266c818ea8e27ae52eb16580d9ac0d976d7787a19624efcdbfac5a4929d00d818efaceca1f3daa524da5e33a045b", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "blinding": "3dd321bf1fbbafd76d48c000a28768152e919789bd57cd50d5e17c88a1baec0a", + "proof_pk_com": "6d16e2ff7b28a8880499e34cfe6ffbb1d12efc27675af3fef635e8761c893dee", + "proof_r": "5311bd0755a2bbc6db01b930526e85120af5189e2fb259fc941f3acb1611d6eb", + "proof_ok": "3c4ea965693b470b0da4f9c9508da97dfa70e5e85ee78fbd3af377a2298b4864", + "proof_s": "a48a2f80da4190ec19e293a50bbc1bc047ebc38a14f4f19f97f5daec48ab4205", + "proof_sb": "eb91c85fb3b211f1ec63612950d04c78fc19a760294d01bd82f9218e944b640d", + "ring_pks": "cf0af8f7bd584961f953973a484da220fb31840a8e0f34194a4a41fc8c61ba302a36d5d29bcc893665bfc68520d76d23b1e046da70eabb094c87bce5e02636f23a22f0544a12c91cff829d6cc6ab3ad49ea99ef474607c3ffb9d46f5f4b2d058fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a49fb4d7dcd1b4507656ffb47989117066412395041314342fd8f0542994f1baaf84c569f6371c182164b6ca1b94097274c7071d3a005050df39c14275f60b01cff14a00d0c5da6624b22da5717f35c99c2a735b97ad9312ef333fda6e68456fc507944e0dcde69553d21c3f3e7b94f8f9e3ad061ddbb564a05ed3b19eb3430e48", + "ring_pks_com": "9187bc0fc0dd593bb43e03376f9f62ac4759a10fa9b282cf194ffeb97b1edb648ac32de47c202f8ffe0c4103e2c8623ba928d26889baffd69e572dd130b2869a07bca78d419741a6d9c3d9b8c4203a45ac99624c984b84a5251eb8a24a441f9992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b5aad40d12b343366083f9a84c186d2dfbd6ebd7573f1ef7ff14e49c9e1baaf64b62f751e821188ed13b329dfd4e4d799107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8e0ac886ab6a40f4e54281fc4698174ddfd2621259950c659c102629b7dae6d05e47219066b2725d95df23e8dee5b1d683e8d9403bdafa74979c83014c7eaad03c96a5eb7966e44f2a46f291fcde1b8074f3a781bdebcf0a16da471c90846f5e7623615d8908f30695fd2cda154c1e7b98cc1eef66b39a37fdc51ba8fbe03337fbe9acf247c7e74030947ae505c1806fb023a33859ece22652cacc4a4dcbe62e31ac376983e3d8b4b2ace5e88ca9e18fd97c5688c9f5097c0fcbf77bf6c674274f06d8d0df718597c27fdbdf48c192dacf3e2cbd92260583d5897b3e39719e1132e01a4fff521fbece828df70db9eb502e5874b7f6e54ade2a9e4b45b1d75d376a5e600f81a71bc2e0e5997be6c20c9b072c4afb2db101cdd1d7dab15096693654ee4d325adafe8de70f46715083ed93781e3cd88b6c71ec88611ad38b56f52393e5ad91a55d0e1d2ae7a5781101a37e25244e385d937203ab80d5497ff35214f0da81619e7357c177ff0725a5b81e89f678d5d4128a7a4d5e4ca10fc2d588f17502ff39bd6e55c584323df94aa6a85aa77e0acc93ca0ca2e34157758066e4716c1a6f6f96e4fd9a9036eb6df96d290b884806b30c0215edef803fe8f71810fa81fc6d2c2347ae8e523ab64aba48d1a6ef92d5c14684be99ead9fc3637e587708867cb78fe08e6ad8309a8dce425c97a", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-4", - "sk": "89a818ea311553b92eb4eb56b85b9ca29c92c0e903c969df20066aeddf415f05", - "pk": "c0e72cb09d87bbe6b21c8ff552293ace36174ad74ce38666afabc42aee6a0b57", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "672e8c7a8e6d3eca67df38f11d50f3d7dbb26fa8e27565a5424e6f8ac4555dcc", - "gamma": "3d4c4744fcaf64343585e59511ec721683b2dfe2673da10f79ec1380e720339c", - "beta": "eb4af7e8573f118716271f46ae7265176590c777137a09337e34e4ace85622ff96eb3429be3a744dff73fba4000e51a06b908be371116c56406b3fd843a5db66", - "blinding": "28fe9a3cebc9104b5ba5362bd7c954b696bae7412052e46b7803b68ad05d6c18", - "proof_pk_com": "6ec4e9570496b201c91c151d812d814d6f93fe73222913c99425c9bd5325a066", - "proof_r": "147410cd2c6e5cf8dfa982070cb9300a5e86b34635bd8f716f0ab5988db6b850", - "proof_ok": "aa37fcb902da9ffde0f752b703c957dfa532467e5bab066f2535a8084f34515a", - "proof_s": "889f3b8f14caac047f9c89f165d64694fb0bcafbe8546fd02910b3b369008205", - "proof_sb": "b9210aaea0cb361eda9ab107b83863f43caebf81165b4842434d95b045198402", - "ring_pks": "9492eabde3262a1c22a8e59d1c9edbb859ae4c942447a7bd6d32e6c2110f871e08c163149e07803ad289966ef03c406fe3411e0b7078416c6db9a4e53e777298d576a396cc8f5c76d38ca156ce763c12cdbc2d996be7fcf2f5985a13a76b5866c0e72cb09d87bbe6b21c8ff552293ace36174ad74ce38666afabc42aee6a0b5722d6a93757cf6c88c74d972b6de32fc38ff1ac5a44e21fb8f250277c661994ac7894aa7d87cca787ef8fea97f091a826dd8bcefb24befa5cb8841a9049387f602f4e8a659252b6eb45280ff6b92566b33e39be204d51af12ef2d4e2fa94ed1b34226b4f1e9a503061f4e39fea6a3dba1a3c499a025235ad246c41ca1e06dcf3c", - "ring_pks_com": "8e8fce9faef2b265d2f6b6c03941ca3b14a932348e52f20f3c3fc83dba3b402c11aea5283fb9267f4f16192fb89acfd18404b7ff3e520dd83907d5fbfcd72107d64610be13fb83b4bd911d535589e64e007813fa49e257606e26dcaf6251d0d092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "3d4c4744fcaf64343585e59511ec721683b2dfe2673da10f79ec1380e720339c6ec4e9570496b201c91c151d812d814d6f93fe73222913c99425c9bd5325a066147410cd2c6e5cf8dfa982070cb9300a5e86b34635bd8f716f0ab5988db6b850aa37fcb902da9ffde0f752b703c957dfa532467e5bab066f2535a8084f34515a889f3b8f14caac047f9c89f165d64694fb0bcafbe8546fd02910b3b369008205b9210aaea0cb361eda9ab107b83863f43caebf81165b4842434d95b0451984028bacefd9ff4deb9cf1a9f37f50ac1c2367d6f5e94381f9d01f30d0adfb4edf59441321acb706f682bcead8e69c47bc519107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb8124a28d003ce8fbcde1031b518686f683e9aaabcf074f518b228c2944cedf32605256ed305d53eab73ee5cb7df1be6853c36be1cd46e2868b7f1b661a436255995eb2c1e148184be51468c8440163437bb92f73f963425c29c0e30d1b9d44fc21f32836324fbef90f5547787047e5edc3c722af555e93753bd254be26e911794117d8cc732b64b5d6efe292178cc5efb6b933f0beac6b815099ad9c1d31a1548629a933801219856ec5ba007860d36811875ec61010a1beb70190b1530702bfd4e2ac7a2e415b514b2f91c670d99dafe298c7e76e36f0b5a0da3f40eb6c5488e49e25826d0a38312d31ba3f74001232ef61f81adf308b7292d64ac2e763f206e1c0399d32ee6b35ab3d2cb770b8df88e88a4bc9ba42cd5e39f1d0e213aae26d733b6a5d45764aa684edd143b2c2da25117b63dbe6a34c6d3ba19634f3c1b53875bc6c0d33abdb107bdde5b8c018c2a8c95fb904f7960ab4ff278fb3785a1025ca981f6a5b4445a511c147719617d56b75ab199cd9daf91f58a814a107388e20b4c9ae8f2b08d71b39357bb16500f1fb407da00a0a490f824b0f67435d6a0ca8723e4ba57d1834a9e2f5798729f511d4748035dbf9d96a481322a358259fb15944f39bcd13759df102b67dee8c8a3ca66142f2da5032645b39a9b7fadfa3c45753dff616d163c6c9883d541f14473a2", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "blinding": "b3f08962012e4f4aa9e6482d7d682275ac98e57f099b991947fad5a53451d51b", + "proof_pk_com": "1c48e5ce0ae9b3e7a09626bc1333544f1dbe5033d5efd591a059b04a41830eb6", + "proof_r": "17b630103915e544c1b4b1a3e8cbad9cf57e75bf7f02a18ae965db26f0d2189d", + "proof_ok": "a16224226f643245a4c42ddac28d614fbe46d02ea1f8d1dcab91a26eea0c19bb", + "proof_s": "bd55995a3f6bffe519366ea7b891e1d006da5472b8de95c5a8c7db52adf2f91a", + "proof_sb": "c946c76414f1c4b5f5825e4cb5fcda7a5aacde9fa0ce1c478caec1f3d812c606", + "ring_pks": "1709e23c0ade83b79ae0cc29762e97f671d1c5186911a747cf3f0afb16af930a903c1ff5d08d663d388d0770150e28bcba4d8c3601070689be83eaf6dd410b94bd84e8af4657eac0649c82ffc4c53e12261b112bc81e7e93a22fde1237a631bff8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a2a61c40d26e36a0fb1177bddb07e3c07793faed7720644ecdacf3a12d78c6438ea60d98f871e5b01299e5889147145960101fa6a2824c3c695ac1f7a6e34c36b0e84ef1808fae6ebd94eb07fac7eceb7e2e8b4e3c8b0ed7ab1a9411d7245d9c3153a85041f5a60f2d636759dadc8c47fefeaaeca2cc3b5f7bfed254af0db4b23", + "ring_pks_com": "87d4f857a199dd1c00b2c7cf5083f5a4f6716f507783b29f1d46e2e3ae9ee902bc227845f8cc2cf1b0287a2fc1b938ba8e00a726e50913ef5da35ed85e5c2160af82305d5e83e3c1bf88a57d0d8b2a1951383cea476b69eaeb4f0c21c46dbb7d92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "98e9ae043bf407a800c73f16585b9fa74acd7c09038d04af53da2583d62c3b2a1ad6ccbe263f6f62547067f35c7bae909107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb41e837b06a596792ebe4847f044a1c9992b00d425cefd37e7ae8cbb99b8fe8d25ef49e0dca6b4b354921967a6f9e03c956a9a5558912ecde5539854c9958692566557a9f9a81546baf91617fd41e3c7960848e584e2d1d35a8be5ac5911086d95af8ba081cfdc869dad9b5c92c6b57edc19a27a67b5bd28211f5b44b02ebb1f8a8e8e4af1c8c5335a0bf404cfa6b07e4eb69a6c8c922fd202c625f057646f2d2bf93c2b26db97602f7617c657f1e07fc8c699dcbe9cc26729bb6a338ba0f01cc1d8d8d12ffabd4265ef3d610c85f17c27b20abd27e62353f21ab87eeb6ec2030db0d0e94b8d822eed8301549f755ee8ce15834ba53e4bf959d4c55805d5e621d10459ac87f1d21508f42e452d4e6fc4761be0276234173b877e44b0fcc2932f2b7cf77015f86c414fcabe8ddc513396a61f5db640005da00ff1a8269008173988c15fd5975d05f9b280ddb58a2d51b6e3d5f857e9293e0ee1266edea2cc13c48db9bc275c76e6680cba4e4f1d593c3e79f970b82e4626276783da03b7a69a6022c6751d51008bfd6f75ca5fd0685567a27d66d39d5d6752f8750951300aca4631b0681ec5343e3dbd22deaa88da2ade9d23f36759bf59627b681e594fa53d168a8efdc2e8e268a19d8ad2f47d06b96d32532e0896510a7e96df794e28958524452c3c83156d66e22a302365e9425915", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-5", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "blinding": "dcaf61e654f336a0ce35e46a91d34908890b96c9357180ef1680abb56a8f2e0b", - "proof_pk_com": "31bfbd0e43a027a144c15d60ae5d4b9afaea4dac3a1a2998c08cd253964265c9", - "proof_r": "26ee756c2ffafd661b2b198792850a7bea7497aea963a24c4eeb5af9f255c7b8", - "proof_ok": "8abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5a", - "proof_s": "2ca0c36cfcb0329280278589dc918ab80a09ad577315e4bbddf062517f833410", - "proof_sb": "27d2f61456d6eb688c4b81883b4d0ee785c5fa285fd1d57a0ec78cf98cdb1b05", - "ring_pks": "7fd08c04d4ee71e0bd76737775c2008d413bd015e004ff6d62d4eb5d87203acf8dcc2facd30f2fd8642022af03cab1585789270b63780c6c7d0f585fb5eda62b9337969423e8850b596e92ccc668ecd6571b7be6050f82b95b6121ca71145f2ddbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f11b23413e5dcd8df01cf9335be9e748d0742f560e99d0ceda4ee797c734a3cbe562432ee161e2256a0957602c79c82f67fa622208e9697738920fc4c308df81eac9d2caf4fe45e1dd14a6f6515f9473b8e3c231543a5818a1e0294cbe7ba2f91840c872e2f2156cdfa5d9c26b0b71d6b69623493abc91ff09abf6d13847ecdd3", - "ring_pks_com": "899eb65dfff391d17e08b140a2760366c7a5f8e2733784e18a1f05009373932142fc2040e07a0ce91260fb76cdd22c09ada7f5910981fddc8be550406080f0caf028e803c2d70b5d237703c25c903fb14e322c858b882fd41d720fcfd4147dd892e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f31bfbd0e43a027a144c15d60ae5d4b9afaea4dac3a1a2998c08cd253964265c926ee756c2ffafd661b2b198792850a7bea7497aea963a24c4eeb5af9f255c7b88abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5a2ca0c36cfcb0329280278589dc918ab80a09ad577315e4bbddf062517f83341027d2f61456d6eb688c4b81883b4d0ee785c5fa285fd1d57a0ec78cf98cdb1b05882a4f8c0f8e13e47453de899a18fd14ebdb0a845c6c9b648a0948dd6cf5c96e5bf88b3c50e6ed9f50e25addf39f3ff49107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd988914da5b5bcc63d156aad362cddf65bd6c95dcc886222fcf01a406ac48828415981504d7a17481a723ab010f0e3f1983b69929fb0ca43a67b91083fa20a640d9a37e4542a1e0afdf96214b5ed5ec11df6992cc9a08e3aeaba46a716f2f80a6b37d76207758b62c6b4c4941fceaf1d96fd676e6905e4dd350bf42fdc7bb5a145a737ec0b66904e550d990b5a8cd597302a74d172df93d111cd655ac6f41ed2a42e8f98ac26b30c314a15d73db62804e3a09b3e95228400cd97eacf4fbc26211b555dffb8d907311b8eec8cb3e3a0b7b0d3a60a1fde869fbf29889c95b04f746926dd70d033bd594474ae2450b5e023a6faff5f8cf87d4ad1b8100485aa61507e1f4e65982375abefc676a653c09278597310f50c7814a0f23ac5540ad40162b7422bda98a7c8a488ca0f1cea8f9187395c70dd7c827ff96dd62af57a22f802c992d1e50b6bb9bf89828feb41924625a49ed6c7be421c3910e54c7a7015a0d83135cb33ec026501d6bb0e4d4ce07bfe3401fa95b10dc53c22e2a134a4df562b44743e6aff9e14fd2415fe4e746d0bd0eae83e53adddec215f43673ffe2d316dd68d07226b1f663afafaee7b905cc803b543ff669dbefe79218e654505c29dd3b83759a30f6b3e7d84b424d32bb333fde532cef2ed8d6a9cc0c6ad83092f4aad4930d3e74a8e540e39314aa7748890b8c", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "08fbea5cbe5af93a1529c222d6b3fcc672599e59261d9ed485ef45146befeb03", + "proof_pk_com": "cdf7907e1902ab6989732bd5824171912905725296e86d2d864ed0eea5322d54", + "proof_r": "02667d1f512990e80b127eed9139ae5590853fde2469e177fee5fa0908de1fc2", + "proof_ok": "c2d48dada6adf017c2ed658fd91789e4b63f2d6b894ce98fbfef97c56ed4d5d5", + "proof_s": "48c13fd4d2cbe731e7ecbf9cef6b2fa5c1a55f27bc0f34753cc211d7cbff7103", + "proof_sb": "78c5b906fbbca6466120cd079bf7fcdecedc1db7916ed74dbf3ddc2f5063b81a", + "ring_pks": "4e7b7f63cb30222400eddbfba85d3e068722b13c8984321d7dd5f5c2a263745418229ba45fba4bd688e4871252866b7feb0ae39c9a4b1b5858874daead40fcbf62e599684351388c330d4bc205a35c9f44bb415875033e098f6f97685541c9326be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4eafd9bbdf329e7ff67ca7ce9ae74f7958253491593ac91faf1d8cb67caee401fa826b095ae854b5e2e2a4cb7b24b6577af084663bfdf074bb7922e7cb0eb6e863a75b3f4a0e8a9c8024b88a569fd7095330cb4e9897cc9627af0aa0203bd7281de61e39a4bccd2f847a899417dcd6dbf2f3f3799cd0fc552a638e2db997f115f", + "ring_pks_com": "88018293f7c3e956e36f885ad26b0846765a3f0b36574f254eef33e5087fdc74f74bade53ff7511f4b722e396d92fda0908b6aec6f87609627a06964c59f21fc59d2d1912308d1c98a81479e23d5cf555d22202ab04b769f90f7ba763b8c213492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "87b659658d78c2d7154006f0592e159386828c8ee3955ad4795ed2751cd061d1238b1bb57c402937481854ab9a0e52a09107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8d75886422670dd4bf46279c1914efae398dc1cd5724b1bfc8780470ed67db63932f28ea195e4d836ee4cd58d054ab88a2d5bf2660bb37b0756c9fc8a8bd5d81699b208bb25689c9d90142c774f4310404ef03af8a41301c5a7be003edf060f947cc6099a8a9f515008497fee70f7624af7cd720079bb348e5a670872f86936e7f8bfd1ce0384e50e002bafa56d84e7223a4b46ea1b6e6c0c1f84e5b00d1ff71861e3b7b31eae75ddb13152b772a47abd3e555c19f038af3e6b0291ed3086936a25cf56cf28e4fdc83cd91b93786311b44926d32f9bdcf3b56cff542fbdd2f1555d1635bc5bb1a246f9d2bf2d2231ec94e7270f8ab2335d3f63ed4883bdc2f63083a8c49696436ef80801fcc31d6be53d8d77c1f0379902f6b85b8b62308561f16fcdc4803e2183a077e64d42e786d4e1a8259f33f6d97a05634cc4ae64c960da0b0757affe564bfc4441b746d63eb0bac6fd134ec0542c9042a940f9ed1d586e1a44de22fbed657e7e878a0ae5edb5480af3b7b01f6981cc154ed83c0aabb08433b2856353f402afd59a89f9698681b8a75819464fb4ce5a70c0663f3da9374e8c2818c7e3bc615f4fb23366a48019c0ff59d5f2dd3b4ca2d459f4c9d2ded5c97ab89d9aad21d446460ccfd2aa58b8cf881374c71f46dff6e976893c6d31f68c70cc0edd59b9e01f110d66f1ffef7d4", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-6", - "sk": "a2a8aaf3d50567777bb5bad73758a717ae6a58ab5c1f6c43d55ec84b61945705", - "pk": "dbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430f", - "beta": "e70e7188f4886f19e52abc30744a0c01c30cb415f8c40412f0e782301d0fc171dab886fdebc399389fa07f6c77b9a36d65c96fb3418847d4dade84f5a42d91f1", - "blinding": "698c84bdaf0b0a13c9771a1729b7490d7a81471bc419eb2f95dcffcde7ccc90b", - "proof_pk_com": "e90d77a101903bcbd0f378f11b0a72a2eb62f476171f44696bece4560219ee72", - "proof_r": "5427fbe8c832959d98b69575ba1b37bcb6427386fd511f7aa6b8aefd963879c6", - "proof_ok": "8abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5a", - "proof_s": "e00131ed77b0477d2546908566e5fab31badeaf1bee4e6c71ffcd367cb624a0f", - "proof_sb": "a8d4afb9913a7d4c934bbc06693295a0a471ce7eb462d636c56ff96a5f895f14", - "ring_pks": "7fd08c04d4ee71e0bd76737775c2008d413bd015e004ff6d62d4eb5d87203acf8dcc2facd30f2fd8642022af03cab1585789270b63780c6c7d0f585fb5eda62b9337969423e8850b596e92ccc668ecd6571b7be6050f82b95b6121ca71145f2ddbce9d95312f9aedf9b07fe5b7dca9de042f2d25f6b9b2f3358bda2e4e53d70f11b23413e5dcd8df01cf9335be9e748d0742f560e99d0ceda4ee797c734a3cbe562432ee161e2256a0957602c79c82f67fa622208e9697738920fc4c308df81eac9d2caf4fe45e1dd14a6f6515f9473b8e3c231543a5818a1e0294cbe7ba2f91840c872e2f2156cdfa5d9c26b0b71d6b69623493abc91ff09abf6d13847ecdd3", - "ring_pks_com": "899eb65dfff391d17e08b140a2760366c7a5f8e2733784e18a1f05009373932142fc2040e07a0ce91260fb76cdd22c09ada7f5910981fddc8be550406080f0caf028e803c2d70b5d237703c25c903fb14e322c858b882fd41d720fcfd4147dd892e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "d30880c1f4cbfcd180604285053331de1893a2e4e6d11624f65732f4ecb0430fe90d77a101903bcbd0f378f11b0a72a2eb62f476171f44696bece4560219ee725427fbe8c832959d98b69575ba1b37bcb6427386fd511f7aa6b8aefd963879c68abb883423e1425da3b04c295ba74d4f70d74d7c9e3e4a549cbfc5737c8c9e5ae00131ed77b0477d2546908566e5fab31badeaf1bee4e6c71ffcd367cb624a0fa8d4afb9913a7d4c934bbc06693295a0a471ce7eb462d636c56ff96a5f895f148165aa88db5f74d4b9b018caf6d9d4a8bb7537c93a7817d83d126914fd555bd24174e1ac1c9ccb7a1984f90906875ed59107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb623b762c41563a7a7124ed9fb401b1ce44f36641572409910746143376d6d9ea78ee29f9af6b783ab6911cbe2ab53368741a947a9c7c84ac210d9bd25111726718c8832a069a3f6f8b524578742fad479caccb78a734ad957008b474da8b853797586f805b1098ab7f38ee09306cbd4b880ff4c92d5c30ede36db33baec1e6b2ade2b7c73bab4aea06da78d08a333a2faeac5ba4caf52afd8623511e3f4d208dc784735a723bd944b5e155d3d5902c3476abdb4ec107be831496bcf56087e4f8e7c48245c1c51952eb294c73e9d7a8e7cb0507f206811c4817e7f81edbf471ea4809b1e706c9660ca73958d6b05a27cdf9baa16e1a1de04d0de1cfa6779d1504a467f93cd30646bca3b80c260bde8ca6ffb5ec5a0fb093c5464b48a53a1a0343dcbda173cdd64a0078c6d999fe07433a83966f38185b1dc9ad6f3d5c5b1d6528adc6f0b76f2c7436371ef38174ce356762d18f1ddd654f6c898291425692516c48a9bcee5ea1bd1cc4124d2ac57cb49a2984172e5378eb4ce336963a8721f6e46ff844b9fcb9711dfea3fb815447220ae5cc3538adee685194384b47cefec29df99e1eb4a20bcbdbb89a40ded0e1186a41cfa616948b77946770351e8e6555eb277595a959c53c7f06d2cccea950a408e7e09a69712d920627295a2233496cce2145aee69e25e707ad5c2a3e70fc6eb", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "blinding": "c8b1ce7a665f75506e6a64ef52aa7d8cbfecde0ad83946eb980ba4d06580f819", + "proof_pk_com": "635ccf83a424afe87946a36a532f2f2dd4e72b280f9a8d04e4abe3b7f3cbd89c", + "proof_r": "3b15fefece933682d3a92dc57fdaa4da6e76f80c41cb926b00376576bced3668", + "proof_ok": "4b24cd4a4c25aa29aae4e969cf5be562cb9ea176593f8c06bb7343c8da71eac0", + "proof_s": "3278c30b9d2fd950fa1a6e82e98375c8fd651f237806f8eaab44c0817b2cad10", + "proof_sb": "d59f47aa43f471c63ff9cebbab2aaea14feeeb914a3e1a9589350864f3d7b902", + "ring_pks": "4e7b7f63cb30222400eddbfba85d3e068722b13c8984321d7dd5f5c2a263745418229ba45fba4bd688e4871252866b7feb0ae39c9a4b1b5858874daead40fcbf62e599684351388c330d4bc205a35c9f44bb415875033e098f6f97685541c9326be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4eafd9bbdf329e7ff67ca7ce9ae74f7958253491593ac91faf1d8cb67caee401fa826b095ae854b5e2e2a4cb7b24b6577af084663bfdf074bb7922e7cb0eb6e863a75b3f4a0e8a9c8024b88a569fd7095330cb4e9897cc9627af0aa0203bd7281de61e39a4bccd2f847a899417dcd6dbf2f3f3799cd0fc552a638e2db997f115f", + "ring_pks_com": "88018293f7c3e956e36f885ad26b0846765a3f0b36574f254eef33e5087fdc74f74bade53ff7511f4b722e396d92fda0908b6aec6f87609627a06964c59f21fc59d2d1912308d1c98a81479e23d5cf555d22202ab04b769f90f7ba763b8c213492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "875e6616f4d39dcb1bd8dbac51fd6fbf54e2fc24ee641cadd283138cff4dd31ba336f7ecc1d29923a6330679a822cc4c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84e134c22a168085546b5d1e770cb1c578a253a6344209aa346a242ff62459fdab5a3024f74ba6842ab2aedbdd25f47d951f863a44c3712e65ee38496cbcacf173560adec3b143ceac2e42cd0ed1beebecbc59ee1bfea5346144b2f9ecc9acd23e7e9a200128dbfdabb09648c9280567f21e2bb1c7805f4e1c40dbd23df797670806eeb0f8318b2414ac1287bd58a789db5a0d683f1f6e0a00460ea6d3cc8b59ca01cd011a3d0fd230fbd510b1beae5be730626751a505d820f10c6b81695b357259b3ee526fa84a91a6512faa8f0cda007ee89553eb9eb836a52e4f687e7e261f58b411d202de8110df8a997b2b13af24d0820d559e4e05c5e70b099e89ab53d613b347adb1bb734db16c287333429e7ef42fae09873d7f7dd2ba790075b503e96a760403e6e7c5adfc76c6b55af126ff0a9cc70d5da20d91441ca4b7ca346599ca90becb182351c7239db43fa3f8c073ed9e3a4481d07cfcec2c0e04b0ff0539e9a9165553963c610b502b1aacc96128fad1f9008b13b2e5167e97cba2efb469307b07449b25000508e6d641e3d1208516a45545b90587255c6d18b0c40110afd0a5b9f1eefa33c55a7f3151d46ff0efcc32b29d651eaa60f2ffd6e07102b3ad571425e2d57ffb3b8a5ca654f2c8d2b4947ee09f964a8ba54ae05a8bf9e876896f765424743c5937b6be26e2dbb2da", + "ring_size": 8, "prover_idx": 3 }, { "comment": "bandersnatch_sha-512_ell2_ring - vector-7", - "sk": "938834d6c6369917101c182c58e7834aa8763fee2d35f7e06559075288c62310", - "pk": "d33123beff50fdb78b69ca1d6139e78cf1aa891e5795ccff942af0d20d8dd9db", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "4315192d2ce9e52ceb449a6b4da7f7e6636e53592c7f5e236763e21e9bac24c7", - "gamma": "44b48217381ac12326614d32a14c3d07299ce4e04df80810ae0b7dc59f6f0980", - "beta": "e07d922afaec4804ac28aca6db75b6f36376669628014bb299e32259eaa46b871257b2483a387ba518a0b0e3ea3f1061e23d140fb387b83650850c937c897d04", - "blinding": "4da183b72197aa0295ac8d3f55b135037adbf9aee661e4aa641c8e3acb3a7904", - "proof_pk_com": "c4daea0b04317f39f699e1fb7c7fa7168e985d9684aef3edb68d79a38437bf35", - "proof_r": "b3146e78e7069713a70783e39c46e4cb154c7c1d678b049213eeea8c0e11ffdb", - "proof_ok": "72d7562979ed8403242f1894704d528f517755cc49e858ba1bcf8550239f47ef", - "proof_s": "e614f230cf145fc634630ed89f5bf2c5764e5a8ceca115ccb63e486bb258c605", - "proof_sb": "9aa927b7389b173841f9d4b4db87b92dc5b6d48d5a669bb524fc212baeff2f02", - "ring_pks": "3fe1070f2de2fe98a5faed49eca2d34a253f5d96d922eeb6eb48a0348fcbf34daef9ed4d2adb294ba7d2124582d0581b017003b80079e2c149194af709e08a7025cce4f025f232ee58d473c57edcb0baaa21887771d4ad14675ed2708f28afb9d33123beff50fdb78b69ca1d6139e78cf1aa891e5795ccff942af0d20d8dd9db6826bb72cb4cc61a9b2dcb1937b7b2b46a353c801eb157ff1bad477d72800254d396e5a6d8cbc8439b5c82134be94bdd1e3d236df6a1cdf0bb064d58a516f0f29b1b6130b0495e35627fceaeed2a38035f6a0479cf342e62cf9856f19dc003d36a2c71f201aeaa3fcee391c90e0ef232a1dcb264d86a3a2cdfbf881105916141", - "ring_pks_com": "b55dcc443cf1cc3876a0fbc45f4e6b164a8ffc01ca6172134f7a561b34ad0db5b14bca6fb3366715e92f044753a77f39b353c852aa03821bb575f8a61d5b4fe4ce9f119b08930033140ea74596245d56386f44ecfe6e923643dd4c18660161b792e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "44b48217381ac12326614d32a14c3d07299ce4e04df80810ae0b7dc59f6f0980c4daea0b04317f39f699e1fb7c7fa7168e985d9684aef3edb68d79a38437bf35b3146e78e7069713a70783e39c46e4cb154c7c1d678b049213eeea8c0e11ffdb72d7562979ed8403242f1894704d528f517755cc49e858ba1bcf8550239f47efe614f230cf145fc634630ed89f5bf2c5764e5a8ceca115ccb63e486bb258c6059aa927b7389b173841f9d4b4db87b92dc5b6d48d5a669bb524fc212baeff2f02a2c6304a47878d0fd10d7740a22024b1128f43ecb9925051dd0f281380e4ff3a485aa1e62661271f3b27da04bac9b7a39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd95bef2b1fdf778bae43f978a5c11e373253ddbe41739edbccd5380e0cafcd10c1bdbd78e5d07390a614bbfc1a19890acafcdd6b9aa117185d4d7ad3245a6152bda0b1a579387a531917e80803b02d66f3526a3cb5bc0c347ea75edff4df21545e599ce58eb403e3051cc742ec32f467ed2bd2b4f6e367ef73c31467c61e481656911439ae0df80e79fd76f00be0fd9939f2c8a39274f31963d604922c0ed861f414f7f690ae64c94a5600e1040bd68ed720993b2818792767789251610fbf52d9779492577dd1996edccec53ed6f97d3e9527555b11d8721d5b3e15d90727a55814127254fd2f3eb4ad03ac810e862bc2856efacd732ef3649ad842e30b4263b76b3124e7de6298c400f2676ee36099e157721b3d01391b85633ba9fc0b1572350840f601d45dce6c39efd163f6d3f45ac72e99b07f229dd03245e135e61bf5a8da9e06780a3f9fe82013a719062f04d1702de66968dce2145c041fe9bd60fd70c30a0494471da6b0bc580a014dfa19c44094cb5bc7e2385ab9a18e9248615f75824ec046f381a940cd7555ee2ef866eb7663f7914c34bbe0c570397d426985ab3672deb3934ffcacc236fd12f742901147c130ee985eb568eb75dee972aa2139327f35c59254103476ffce52e9a7b364ad55ca7017b8946ed763c19bfa329d9307c42cb1cf2ec4d94cfecff846646d8", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "blinding": "3a19e858c8f2cc41037737c43d23dbf3d124ea7059d30c6ab88aa184e14fe20c", + "proof_pk_com": "0a9e31ccc09e5464e53afa2057098da6208478f9334199c60bd4bc7443a3d5c5", + "proof_r": "1ce7b8f6bcf3723106588842207b1199b8feaaf3bcc13874c0fa2e82a53b47b3", + "proof_ok": "7372c8e03008802e1bfdf4ac6b8832aded5b48ddb688af168cb67a882628ba22", + "proof_s": "4b61c4d7d6179d8e1fe407a20e1ab22acec0a4834eb36fe03f254480d9d87e1b", + "proof_sb": "40d1a91ac8b287a2b754e9ba05c7c2091e140f0fb40753ebbd2573b6e9d1f514", + "ring_pks": "1f7d660a0b615ba9d72c4f3a3264ecb6c72663bd4a9e4e5b5355b945666dbec043d7d7259fc2118a35f2038c6633e3e5665375b191b1844e19da4509f3749152af482621a9bc693e683933b691a9c6cb69c441b330af3fd820bd54fa3fdf6730704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336be212493c30d883c89aaf2e9438c0a3d3e319aacc5d2e230c4daa9f4f74e01a3dea2fde245cb1469768265dc0c3563b5cd00bba0b2efa5c7117eb9608922e61e324f8824cf277be237a85159a207011c32e1e5ab308cec54f8f6ece20339f361c27f62dcbef9d8c61ee39a9f0b6a7e7bacaa7ae1820d61b527a99c6206029212b", + "ring_pks_com": "913dd7eb526b1e4b93d730f41f1f3c06cdb3518568c27f465023e59abf121eb8c2e27cb99e5dcf8eaca97a4ec02b1f7eb1367c62f12be18509c93436b46fb49be368c37b16ffd91c00860065fd3745a8c0c2ace97e1a2c78a354521371cf7a1192e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8048dbacf65522f4ad29d64c1eaa2e914a308bd0a69de81499436e9b516fd8ccec4512abea7dcf27175f52b9be39f03e9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84989a9448a7393c6bf91ee89971748708c643a365b42421005e76c9eefaf0245b0d762f85a9fccd4900ed88d4a4ff85b7272746c0f19acc2c729f4b6ac50df5f6f3dc464fd39b4c4b8311b87600d9073bcba43577b41970105c75634edd27170f66488d405c554ee463c924efd28c9577e81f460348baa6348c547b219f114c6856071bba61838e3d3ab36a39b35d02be85bbaf775152d2545205486afffd307dd10345623760229974045eb49121835589d19c69e021a3915d09cedfbc96082c514536b64de336d27dee63d80ba735f39cae63755a433508dc76278517bc33629d83469ee0b329af5ade34e8449ee729e653899e8321e4c088f53b0a653357ebb8b8212bd2109150aa05e6da5d4821213c14f85a4695fd0461daa5a5facd1dabf69e466c0ec582b2ea6dd044459947a416e74ffb161cd5c34d9b151cfb545283bee909b4d6e2a697a07988546cc592eb7509c9e1941bb2c6b65e3d2e8f5f103822a12f9a4685f715780f6237d6ac99ada79f26d72c13626799e7784429da46e15c43da706a0b835e8336216f82e404927375bd6f4e4302886c7cafbfb4211d7cb133bb3c00aff124ec13aef4487eb2b9a1eb07d0796950d471587e433f4c19a83f14f614989ebe50baf6c78c9358a684d668410274da98e436848c83c84643326e775fe8e23efe685ed46843999939", + "ring_size": 8, "prover_idx": 3 } ] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring_edge_cases.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring_edge_cases.json deleted file mode 100644 index 5bbd05b..0000000 --- a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_ring_edge_cases.json +++ /dev/null @@ -1,107 +0,0 @@ -[ - { - "comment": "bandersnatch_sha-512_ell2_ring_edge_cases - edge-1 - prover at index 0 (first)", - "sk": "3689d2b6a0eca50dd73dcc6b7c67c2be3260f57690f4542e2ab37bb515726711", - "pk": "1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab3", - "alpha": "706f736974696f6e5f74657374", - "salt": "", - "ad": "", - "h": "3afa8b095e0c4775fa9bea6f0dbcf91d0b91975bfbdc8243f2f8d8793f2af05d", - "gamma": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c4", - "beta": "15443f49bbab62c5f4fcfb47b72a630cddb32e25b730b64f31529fc02668855d3acc0a06bdf05576b417825446a9c8e716e9c0cc720fccc03578f0f4352faeb2", - "blinding": "af451c9b051b7706286495e36f37ab0f6ec41a8210f3b0b69dc45605cbb0bd05", - "proof_pk_com": "63adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e803", - "proof_r": "7a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0", - "proof_ok": "f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80", - "proof_s": "fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07", - "proof_sb": "435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c17", - "ring_pks": "1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab365ad058d7b69d446cbe4b648633ae0cd61d6b660bd857bbe47594cb8362b8caf085f53725d3f53d56c5985cbdadbaaf92de22e83c55438666295b93cc9f42ae575f20acb05b426560c510d27bb1a791d69c4c93158898d5de334abab4da51aae88fddb2656c9b6edba7aae1d52e6445df19b6ee646d380a9a3c879855b76c55a82f93d0bdab68d44d1c9077d28001b943782a9ae07dac8b2a583858d3ccc898f5415dbd95a973b16eb7cb30f7b5e05bd6f47ed3941813dc4a343f06df09de14c65ebc6db5833114c55cf9b08756f8524511ffb1642a3a4739b5c59206713b221", - "ring_pks_com": "b6770e12c8d466f5947b10024bcb744abfbf2c3fb1549012564f299705d8efc78e85a9a8ec3627f165ea27e531f1c6728a3f4dd659d4a1a9c845d8e28831b8547216e221ee9eff6eefbc6c1f1d4aa0184a0e26316294424e6111da362248a5a592e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c463adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e8037a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c178ce6d80463049fc58dabab519aab02183952e6100e8af7ac701df148487bc4d0fab4963d67f041c4e5ce81df2213c9478d6db123d2815dbfa6cbe74866ed9855eecbbab8e2011f84f71a2e360caeac3bcd64c4b46b11ca167e238cf5f0ccfe7fb753d065c49cdf791c59057eec70071ad6cd2dc3c157d8b9c88b179f56885c231c591f36d4da1b655e69d00b95dcb44db3128250541b3fc43be9114d80578745691e3a2bc27d45378a91e15bd995f9ceab6984c789c51f51f7282dd45795b541bceefc1f095d695f787837bc12321e84917e5600a81a717e908fe8bff19d61695b5fc6b95374e2d2d22cad608bbab7e2ed1527cd16411c93d1b6ef02cb533360776657ce112988a0784a3aa7f4df82f3772fd46bf6b864092e1d4893a86d0f114735dea4a23002eb1d584c13355cfc4bf03705d0835e3fcd91696a68435b1c4f48d6bd4739b49c977ab3b57838c5a0b590f33a5ced746db299323a4c5250b354a0215a7428d0ffa78b0bfcf8a25f914675b3f16beb72e2be65f91e300342fc183b94cd50164a34f28f40c7ec24472fdbf8d152f02049bb37a2692553e61e9936a66b1b0cd0d869f60c9a0a132d076a22791de424f2e3bd9623c5d2e0ef566bf4e4a8fc62efc75a82e3628e7f8d4d8fb404b6da57a03c1f415a3962acaac187312ec859ae72d3199bb1474bf1b65f453cb97a1129433594a8107de19b39fab6fdce142970e60c505ce37c8a44b5e306af46f7815cb08a43e9869489287ae51bc2a20df55093e300cd8bada4c617ab5dfe8168e08809f93769ca372d28736650e33f46e0ae14c7b87e12bf70a653211f1d", - "prover_idx": 0 - }, - { - "comment": "bandersnatch_sha-512_ell2_ring_edge_cases - edge-2 - prover at index 4 (middle)", - "sk": "3689d2b6a0eca50dd73dcc6b7c67c2be3260f57690f4542e2ab37bb515726711", - "pk": "1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab3", - "alpha": "706f736974696f6e5f74657374", - "salt": "", - "ad": "", - "h": "3afa8b095e0c4775fa9bea6f0dbcf91d0b91975bfbdc8243f2f8d8793f2af05d", - "gamma": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c4", - "beta": "15443f49bbab62c5f4fcfb47b72a630cddb32e25b730b64f31529fc02668855d3acc0a06bdf05576b417825446a9c8e716e9c0cc720fccc03578f0f4352faeb2", - "blinding": "af451c9b051b7706286495e36f37ab0f6ec41a8210f3b0b69dc45605cbb0bd05", - "proof_pk_com": "63adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e803", - "proof_r": "7a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0", - "proof_ok": "f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80", - "proof_s": "fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07", - "proof_sb": "435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c17", - "ring_pks": "2c5051f06c7d9b492877a813bd598118087a796fa1f004e7efd806e84557fb0265ad058d7b69d446cbe4b648633ae0cd61d6b660bd857bbe47594cb8362b8caf085f53725d3f53d56c5985cbdadbaaf92de22e83c55438666295b93cc9f42ae575f20acb05b426560c510d27bb1a791d69c4c93158898d5de334abab4da51aae1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab382f93d0bdab68d44d1c9077d28001b943782a9ae07dac8b2a583858d3ccc898f5415dbd95a973b16eb7cb30f7b5e05bd6f47ed3941813dc4a343f06df09de14c65ebc6db5833114c55cf9b08756f8524511ffb1642a3a4739b5c59206713b221", - "ring_pks_com": "8298879e3b3547579f01a98afcdb196e749550d721bc8318154fad2c48b9f82ab46e73ef2c06fe178112b79e1ce93d388cf5273515f137889622e3e570f19bc3310cc07db1b48ef070b2141c8c617ee2a9cfd831297271ddc058d8bf842afc9c92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c463adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e8037a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c1782f2423ade1d318b3424ce9118c291e1c5f6083b4d5c571211da739fe20e3f4855536e87586027193f3f1d527dd8610d96c99f91a977f75acdcbc856354f68720ae41ba921b9d4e3f58513e9f5b22bb4e1cb6d27b5b3911c1fdbacf977e92dd7b28c7e242cac3b1f8458c2f435597bc56255935c8a93ca33d0da0f805e863f288fbf9abd47e50eddafcf800b53cb56fa822e25ac3327343af38a217ad614f3f33fa6a99e136daebbe515f28cb3d7b08450e5736c0e18faf9c0836c89ce82bca0e93bfba214edd65cbb3fae8670ae2e83dd5cd779be6bf4a9e8a10fe3a164bc41c3c328336fca4049d88afa33dd148ea3d9a8d15127fd374d9085d2241a3bd52ee602110307b1d34cf0bbc8e35cd5a1eb4500facce9fa04da9dcbf6060f94701f3a4edc3ed2e84ed36adc7ba6ba9ff92761533f40111a9bf70c0eb81d98478b56cd7014644c53afcf9f4685f5a388418a9412e0fc0abaab28028a89c5c7c1d358bc2bcfe149a385c400001d2f3e9443637fc0720dcc8ebbef578e3f35abe4e50c93e9734400e121eef6d89653769195edb94ec2a57c33a90dd257a55a67020d53b38a511c44025431f2d89b651739fbbf8db1cc7ff1c989ea27d7c4da7f1608a16364c667ca9e8439a2b999e84848f1596ccab51dee0f61b7077d5e6d7c19d746ab3362c536544623b201f33338bf3712b2fd9db01866c150c1811e5cf467ef778336c82b0257949644dee0f3afececfd62aa70a54417812429c29bb76897979aa33ec74de467741ddd94f9c7a159e17d77531233cf87a3701ad3c300cd2fe33e664ae44e4608e4835b86eb90ea1b6d06", - "prover_idx": 4 - }, - { - "comment": "bandersnatch_sha-512_ell2_ring_edge_cases - edge-3 - prover at index 7 (last)", - "sk": "3689d2b6a0eca50dd73dcc6b7c67c2be3260f57690f4542e2ab37bb515726711", - "pk": "1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab3", - "alpha": "706f736974696f6e5f74657374", - "salt": "", - "ad": "", - "h": "3afa8b095e0c4775fa9bea6f0dbcf91d0b91975bfbdc8243f2f8d8793f2af05d", - "gamma": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c4", - "beta": "15443f49bbab62c5f4fcfb47b72a630cddb32e25b730b64f31529fc02668855d3acc0a06bdf05576b417825446a9c8e716e9c0cc720fccc03578f0f4352faeb2", - "blinding": "af451c9b051b7706286495e36f37ab0f6ec41a8210f3b0b69dc45605cbb0bd05", - "proof_pk_com": "63adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e803", - "proof_r": "7a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0", - "proof_ok": "f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80", - "proof_s": "fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07", - "proof_sb": "435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c17", - "ring_pks": "2c5051f06c7d9b492877a813bd598118087a796fa1f004e7efd806e84557fb0265ad058d7b69d446cbe4b648633ae0cd61d6b660bd857bbe47594cb8362b8caf085f53725d3f53d56c5985cbdadbaaf92de22e83c55438666295b93cc9f42ae575f20acb05b426560c510d27bb1a791d69c4c93158898d5de334abab4da51aae88fddb2656c9b6edba7aae1d52e6445df19b6ee646d380a9a3c879855b76c55a82f93d0bdab68d44d1c9077d28001b943782a9ae07dac8b2a583858d3ccc898f5415dbd95a973b16eb7cb30f7b5e05bd6f47ed3941813dc4a343f06df09de14c1b322cfff092ad7be2877c0f344fb207bb6ddac62705280d63b038c3b253bab3", - "ring_pks_com": "8631d56f95514103f5300b33db0ce953845e25196975db54a7e98efb8839c1c9012d09cf96ad9ead46d17fb7bf460bec9566057d7d2b7fcfedd4bcdd5f295d7e5780effd5958434a65b9ed2371a3801d4fbee847bd21d2bca68eb5516a29ee4092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "10a45018d52b68045fd4d5e8fac92ee46cf56e196db1f8ef5f146354393de0c463adc529adc9ba84f3bf35d3a6207017ee8693b13fd095f6d62bcbc3c548e8037a48bbf1de0d17636480a3e725db4b863c53cbe5ae2f7a42c484e586b34adcb0f5dc5ae44b2c5e1f30644bc27b4364a4f9c964c7dc95551114c741eb8cdd8e80fbeb2f1ecee00a1f533efbd685232eb8211a8306e2ceb1a8626cff83ee30bf07435472893372102587c6b844e07d7c8dddb9acf317f4b332ab223645417d1c17892660655033aed54d0d33c0664390636e5a818d00024ca27749837ffcb0f1aab791d7120749a6bb9764d2471505900da7db6085920a17a3f16e351e33bad6a706aa6b6c9ca320fe9a6b49cbecc290e88c798a2467ca33f12f647b61de882ed78167cc1178ebc1f62fa4b074eccc535daacb860ebd5180a70888dbb06abe4360ecf51aef2815d3536801f779c6adb9db803226ffe5dc1ab1887ad6056ca462b253272f88003cb5eb770d12ee9e30dd365f05738856ae6afb4d3ce96904b499f2120c112300e5c8b45519ca69b37ccaa4737b3baf965a67dea83cbeb71ae7a168512bc0a753167190ce48e1e9115465ec9a000a912c3bd19791d238dd5b19c5687efdf59c55f56d5a3778647c57275486e3222b47031b9b8014802d5dd116ea318a97bc3267a9b80ef2fa05fff616c89911a4836f663dc26b8ac38d57d0f61e0eb81764d435f68d3484ddf2231f120e5dd1eef5a3039d2b613013f21cc8efc600e12c7341375963063035c9524208faf2a680df7525650d9e82d625224e5a33548b2749372c615d672732367628311b689d3acd5dbca610b34789df0ead77bb58afd94e785f023f041aa2982c7fcf25fca929b6cb762ffc52e683f2c20e62e8f398a8083dd784c20ed3a9af7a94b70e2f8b0de6e934c6adb668f36e498170494e461096f0b23c7dec22c2d1a9af51042ba2ad456433c90c6b9049921e2f451a9579dbe5d5493b74cfe1f25222e96f97ad0cb72228db821245705ee59dbb47dfe1a9e98ae8194ae2bd4b7aa15848f7f1d3776ea14fa53295277e23b758cfb5a1f04c02d80176166ee1cb9cfcf10881238e", - "prover_idx": 7 - }, - { - "comment": "bandersnatch_sha-512_ell2_ring_edge_cases - edge-4 - anonymity prover A", - "sk": "c267a2466cc6ed33de1ebdbedb1066ea44ded5ab70213d97b94e9de2181a050d", - "pk": "7bd626abcca9a16fb3e1a42f5cbb982557746ab9807098eed822eac6a21900db", - "alpha": "616e6f6e796d6974795f74657374", - "salt": "", - "ad": "", - "h": "19b248dca7ce95a02bb21b4d87ee894123e65f9e6c66c6ef5c6a757f19b1f0ea", - "gamma": "b51ff78e7255cf85d9232113e2d6b7462cabe81f87161974a84d6b6c46882c50", - "beta": "4e081e1f9d88d8fd714fec34be32b8110ebf69d5bb777caa1ff7fe1c9215c28f75f01f4dccffe30138d2dc447c35fbe74e1d5eff90571d65ef5fb3d3cc980e3e", - "blinding": "883568670f3ff66c71cbe7d64863b4c4037053fbc5aba288fe44738a02046518", - "proof_pk_com": "0709055ea885e5fada63485cc3781339e7de37de1deef0ac663eabb8fb033451", - "proof_r": "910c51a68373172c7916bc1469b7f2a11803179f7717de46279caa06d878c3a4", - "proof_ok": "d99d89f7fc171d223d9923556529943032db7a57a42c945b976bb93ab41212cd", - "proof_s": "9625644e7c79092dc09d7e0c946c2c24e4466f7b6cc8c843d253bfadeebe631b", - "proof_sb": "1df2d94395ab5ded2498992c8bbd25667eae79c5f29e429e8b2b85745b061c07", - "ring_pks": "08c163149e07803ad289966ef03c406fe3411e0b7078416c6db9a4e53e777298d576a396cc8f5c76d38ca156ce763c12cdbc2d996be7fcf2f5985a13a76b58667bd626abcca9a16fb3e1a42f5cbb982557746ab9807098eed822eac6a21900db22d6a93757cf6c88c74d972b6de32fc38ff1ac5a44e21fb8f250277c661994ac7894aa7d87cca787ef8fea97f091a826dd8bcefb24befa5cb8841a9049387f602f4e8a659252b6eb45280ff6b92566b33e39be204d51af12ef2d4e2fa94ed1b34226b4f1e9a503061f4e39fea6a3dba1a3c499a025235ad246c41ca1e06dcf3ceb4976fac6d0fbffe5a442f20673a387d0fb9a776358c9857ddad3b11f32ff3f", - "ring_pks_com": "847c372e52f3c3989daa769c16288be9014e04962f740de2bc59f3585ad5deee78a28ef0ab3ff803b50e2936b87654b996c32c333df41f72134bb43700c35014e3934579db4329d2b74216f649735182dfb842a54f6eadb8bbd969d225db3e2592e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "b51ff78e7255cf85d9232113e2d6b7462cabe81f87161974a84d6b6c46882c500709055ea885e5fada63485cc3781339e7de37de1deef0ac663eabb8fb033451910c51a68373172c7916bc1469b7f2a11803179f7717de46279caa06d878c3a4d99d89f7fc171d223d9923556529943032db7a57a42c945b976bb93ab41212cd9625644e7c79092dc09d7e0c946c2c24e4466f7b6cc8c843d253bfadeebe631b1df2d94395ab5ded2498992c8bbd25667eae79c5f29e429e8b2b85745b061c078cfb297beb6fb62c5da935339c795d37c5d365af77be4568f703d4778b3c0fe18496613321f656a1021f856a74cc9ff08b7520b215677afa1394464057709837dfb22b04b53c69011da7926c341cf6e77a2ed27912c40267c826cca53f876dad8074532bc06b9709eacea938ca54ce8c93117412d78249cdffd7193f217ca95da7f394602c269995cd7529434a2a8990a20095de4db0276cfe3d70084968722922e5a23455d4de13a128ae0b1f2c13a0edd91e5ab04be636671f496772d25835f4489760a4df688d3e203e7e12ddc78ace8f59edc1c5cc68c7af848e3988853f26473229188965ec0d9d608ac9f80242d84996e3937403cea11327369f0a7b242345e43e6a8fc0b17e327e581c47d46167a5fd4d095d74fdcd829b97a9d1a10a544249c0a6105eb1aa6b16df5803620df7598e4531947338945db4b5478d40712281065c295f4e4ebfb89dd1560d0ce968a40f68ec07edb18049ef262aa9435083a88289b017507e590fdfc62c05244c83e65fe6f5ed661a70d775383d400a67c529840f822dd35fe6a60506248b8b6d4aea95133a0d89f2dac9a8a9db029d2ca8bf412973fbdfe34ac3af4504e646610fbd5dbfd9978644746456e155253f147703cbfb21ed7b8d4ae3b7c3f2db59e2f7b2380d6b8a8906eece0c1f4b39e4b9a32739ad62c740f75022c242c9c1ff3d8b4fe254d4bf977d23f7b1d1415fa324cae473e8e6226eb91d4333e7170359820a5d597cdd0ce0df2d0f9d1bd363a95db241b2c478022103c237997f90ba6a1901d1e0c56e86849d4df435916e05b8c459374344d7f5a915be7e26a9cc69f1bb", - "prover_idx": 2 - }, - { - "comment": "bandersnatch_sha-512_ell2_ring_edge_cases - edge-5 - anonymity prover B", - "sk": "5d7694907fd4768589b8fac74ee6f819519820268073c4216b924764bfe1c30d", - "pk": "9492eabde3262a1c22a8e59d1c9edbb859ae4c942447a7bd6d32e6c2110f871e", - "alpha": "616e6f6e796d6974795f74657374", - "salt": "", - "ad": "", - "h": "19b248dca7ce95a02bb21b4d87ee894123e65f9e6c66c6ef5c6a757f19b1f0ea", - "gamma": "f3537c2a2937d4a20be9efb88eafa58968d0cf4df3255a87099099c48f8927ae", - "beta": "f90dba04984f0d74f8214d3c9039a7e02f90035e9fa97ba82e2b706c81a20ae3154d0f83743918aaeeb577801c4e619b83085d347c569c2e6fa55bc54853009c", - "blinding": "8927f8b0df229a08efe1faceeb0155a5e9129bc857f1df4cf72d245db71ade08", - "proof_pk_com": "1d331ff215f09607b1b0f9bf805390840f573ce988ec12e631285737fbcb406f", - "proof_r": "a3644d382f1bc5d4411fd8c85c4d895f109b764c4e04a35f66e30677ddb4fc72", - "proof_ok": "29d3efe572148b6e955fd6527d0bcb53613548d7cc57eb2f02772a4721e28ad4", - "proof_s": "e29568a49727160da5a7a2c7019de9a664945cb24d6d83abf9a8e10d6af0c10f", - "proof_sb": "0df90fb9d07cccf3f8b9429497721187b1a15713aaac1d42f4e9387d58270e0f", - "ring_pks": "8dcc2facd30f2fd8642022af03cab1585789270b63780c6c7d0f585fb5eda62b9337969423e8850b596e92ccc668ecd6571b7be6050f82b95b6121ca71145f2d4550dc51a4dc08b0d1f1ee46aa6a351462de825413d3ce3f049f6d5b5400edbb11b23413e5dcd8df01cf9335be9e748d0742f560e99d0ceda4ee797c734a3cbe562432ee161e2256a0957602c79c82f67fa622208e9697738920fc4c308df81e9492eabde3262a1c22a8e59d1c9edbb859ae4c942447a7bd6d32e6c2110f871e840c872e2f2156cdfa5d9c26b0b71d6b69623493abc91ff09abf6d13847ecdd3a8db63529a85bf4f39a8de1db15764025229116de1fbdae6a934ae2e30fa920d", - "ring_pks_com": "9563efb30e1f71a28d660c5625bf9920bf72a24f9484aa90a7a8e91c78b142e4a5e736c422ec40167e814708e15550498d33d27753d0ad2f272e0cc004b1b60dc351fa3cef6fad73ba2fef3c17ce9f78550dba9a948b43bdf7cdca443b5bd20e92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", - "ring_proof": "f3537c2a2937d4a20be9efb88eafa58968d0cf4df3255a87099099c48f8927ae1d331ff215f09607b1b0f9bf805390840f573ce988ec12e631285737fbcb406fa3644d382f1bc5d4411fd8c85c4d895f109b764c4e04a35f66e30677ddb4fc7229d3efe572148b6e955fd6527d0bcb53613548d7cc57eb2f02772a4721e28ad4e29568a49727160da5a7a2c7019de9a664945cb24d6d83abf9a8e10d6af0c10f0df90fb9d07cccf3f8b9429497721187b1a15713aaac1d42f4e9387d58270e0fb18ab45bc1b55b0322144b4fb650579ac345a955f665d7a635fc08162328dd6b4c53ec40bfc4979b873c178f40a541598768d09b0ff33df66439e917263a1dd49b158f005e6c9e0742120b1dde0a625d690e2067e9c4ab3aab62dac1ff9532d19213324c0571f1fd296abe7aef3ac83989a71db5bcbc971e2334a77b0278789f19ea5d7471a28e8ce52d17aacbb5f18ca393e53879cb4035b5883f82a90a7be08093ef27cf22c44020383a79e9d90bd0aaf986c9e79bcd6520ff67ccf20857d3863d85805ca282c4efa55ee9e0d5abd1e78a68fd5b622f977bc7ade71cf2b260432dbfd38c9fc11b37c8ceff609a1c096095a58da5db703831b60e1570fd7e1f8773d76889bf5727cda696acb2383a766365d26d83ed45cc8557b03ad258836ca28cc4b3f38f3e4876552f7460cccf5665d8769ba707460ac62703c592366b2a29c78e7f0391984ba6409842d92a14f4f4dcba9efafe3d6a12273d74ec0cd136170092d61e3049ab7ca76e396c0b3224ac6c0849369e5e44f9ad5a454c2c76612ef4e63054b139c269128017849c834d2add7a2dae513e5e71a232eef40f99158cf3ea7692505b074be05aa279ac352b782379dcf9d8bab8e8bceba1e8ee3f04f28d2896262bf99fd3868e0a9789c4a301c77ffc3e5c7755e3ca7e3b721636a4109b7290a687d8bcf95446bc3cc7111181971f87b0c4a5a9a93ce50dd9d215ca1ed3593112a0c62fa6a967635309ecb5ff4774d6fdb1137165a14476cf451036adc85f9bc20226f43d845be147974a4772519d3e9ea94805ddcac82259e4f69104c74fc7613029a3fcccfcb515546cea", - "prover_idx": 5 - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_thin.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_thin.json new file mode 100644 index 0000000..3fe9e81 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "proof_r": "d969c67f8fd97fcc8c0f8b129810ef5427786e319e304a056038caf32286d15e", + "proof_s": "374e93cc0df1aaa3cbe7a66397213275e852717462f8a9b775ed8bcb68f45b00" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "proof_r": "22be463a480dfadb3beb93ba75890a612fb65e206fecdd9fa9ec130ed16db268", + "proof_s": "2eb15dc87c37022290606c34db23fdd7a80648e6aa0dce076ca3694000a72c01" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "proof_r": "2d726da5bc791c511e7b5629bde266151d66f9f36d276dbe16dc1c900f4913dd", + "proof_s": "743aaadff73eaaccc8e6cc333542faa331a132012e9cb2112a1e681546152d01" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "proof_r": "9a638cee379161ecf1a9269801631303359a531b035076369617c42ef46afcb1", + "proof_s": "223446d4fd1450c1fef095c3fd3d5a52ae9f3a8b07a88d7e7ddaed089b235504" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_r": "fdfae23d8d8c9a5225a7bd49b65cc276abf7ce0f48a3730ca74eb57fe0b27467", + "proof_s": "d55860b9c11a56cbe92b5d9c992fd95756fe1845be0be7eaa2a291b524cd111a" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_r": "d415fcd6eff582440fab9c62819b3bb82dd7158a2faf0465512aa095e9e885c2", + "proof_s": "f2f59942f122f9566576ff9cfffbc803f1229881044aa69cca96193106b08c0c" + }, + { + "comment": "bandersnatch_sha-512_ell2_thin - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "proof_r": "9f9f6670d06734e319a01c6afb72e0e3647085dae2216c707a22643a3ef1a5c2", + "proof_s": "74544f931a955eb7f38dab224988f37425b1275b78ed4340f6d5b7c4bc91a60a" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_tiny.json b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_tiny.json new file mode 100644 index 0000000..f8f6543 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sha-512_ell2_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-1", + "sk": "c9922b7a9849b9928e15c655dd2f22ceef737cc355024f43d4b04bf4398c270d", + "pk": "5a538209ff1fc7b1c9c8e1da05b3e169acf10a8b1591b3af029fe4eede0bbc71", + "alpha": "", + "ad": "", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "54421f7ffc399872f1cb868efbb7eef4034178f1e369cebfb964ca61e4f3f256", + "beta": "7b89f2aba6af7474694f24f75adf48336e00dcc8f3ac889ef4daa53c859497a6", + "proof_c": "5685489f948058d1ac34ffc87d6adc09", + "proof_s": "be881519a8790c307e3997b9b7905061b82009bc9d73ee5c5363319b1c884915" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-2", + "sk": "0b4259ca1b10c9ed462532639113e1caf26b3a1a2d9e91ecef2fc5c2d23aed0a", + "pk": "ff341f0c9da793b2d8fef91bcbfd5b55c2185352e4289edc1dd6c64e3fe09b0d", + "alpha": "0a", + "ad": "", + "h": "8ec04d55a790d47cd32c5062cb44517f164515dac88a8ea6d972db1a7da08abf", + "gamma": "e20e48a14d1ab23cad04e8bc39705d194e87587072fe2e7114d1f85fd7fb6105", + "beta": "f7f5da876f037509854307d89273b14ef5db4f0971df0879963e1bf0e775636f", + "proof_c": "8d56976c451b512a88a28704cd62546e", + "proof_s": "68a6a31d6ce21ac9ea4e6eb688e19e08fc30a50a0dcd7c459dc0be49daaad415" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-3", + "sk": "dd60163595ff312a49aa5849917ba19020038ccd42f8a1d468da0973079bdb15", + "pk": "fbba8feb488e767b9864726fffdc8595896757430eba9162a1a5d9a03381d5a4", + "alpha": "", + "ad": "0b8c", + "h": "f508a4e84812ee3dce73ef72bb9064308128384b4801f81ef8616a7dffc486bc", + "gamma": "719ace6342e2c5b3615e33d6e081c6ffbf0813f3f42ccbdf92973869fab0bbe5", + "beta": "cc87c37551a222fe1207655f20225cb79a3dc2ebcefd7111550911e36423ad62", + "proof_c": "7d7a08eed618b81bef1ca85695b93d6d", + "proof_s": "3a1107ff4361fd10bf5b891d5554cc76e56502d9f034e921eb2cac0aa73d9206" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-4", + "sk": "dfc32f03fe9487f123f2afeeb9487cb6b1eb23efac24a60ae540f5aa632ddd18", + "pk": "f8487052801a89161424ee745189b5f7fe568819b9f13f44a8b3d173b57e928a", + "alpha": "73616d706c65", + "ad": "", + "h": "fa6a45172b622ddaccbcf7a3b2d91a36ba504598a83d5a4ed23b416063e4e039", + "gamma": "8f3ee5f3e386775c13093f84678e01594419d747a7037619fa7ea20790044218", + "beta": "67c6fbdf10f3b5abec5c5ea368662455aa1cac36a6a17621ddb2b526c52fd8dc", + "proof_c": "e44e5064895730cb04907d46262e63fa", + "proof_s": "393f12e07c2ce66c08855bcf64a9c3b1dc29e455af7e235500f04076edb0c70e" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-5", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "c43dfc9ad3e72ab427810f5879683725", + "proof_s": "08484f31d398c8f27b8ff363354bb14a5b5c23caf1436b321f5ea30b7f555e13" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-6", + "sk": "04d3da92994eb327893f747b5aee14f82353ca88f909585b492ac6124c52da07", + "pk": "6be56f1a0c32af7ff857e278dc9a0dfeaf724b5961ab0a0147663f77d3e0dfb4", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "a99080c7bb9f8f4ccf68aa2cc8a946b03dbf07735564a5102009f2c8e5013bd2", + "beta": "d52ea29fddc6c87f38e5d56ef2f5af434f90f28e8e122dd99284076b57d29997", + "proof_c": "53613c4e23191e4bc2632927f7fd4316", + "proof_s": "84e4072747518ad201335c841fac19222f8ee895f288da6304cd89b3ab4c7708" + }, + { + "comment": "bandersnatch_sha-512_ell2_tiny - vector-7", + "sk": "9504efbeadf81b20a9cb64c1331915eb3718a574227458230d1d80dfa94e8b13", + "pk": "704fd3784947de4db4fdcf0b477530d094bf5a656707b7d6cb43edbc4db7336b", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "832414b806b4dd6f98117818f1518667c8bb2bd625dfe353b1f2d22d4f32ead6", + "gamma": "1617b4fca3d7354fd43ae783bd6d0cfc526a80e257fcccfdf8e4d87e74dd7c6d", + "beta": "6b279b85a1c55f55db80167e6bee997a5bf25c1767ffc2a34ae696baf606f5a0", + "proof_c": "365b235ddb44510f484f14d5d19a1cd6", + "proof_s": "9f5f9515507c84924f87b5cf549da8b177879cd2f835f94b91e36543e0cfbb0c" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_shake128_ell2_pedersen.json b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_pedersen.json new file mode 100644 index 0000000..0db5ed1 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "blinding": "9196197366ec51cd0a1d3a24937b21beac30be36fb433c3923a228ca2797d808", + "proof_pk_com": "fde8df7a70ebd32b39895c64088dcfa22fd436d6d3e742acd4256f043061b8bb", + "proof_r": "4d980be01c90bed8b4853f828a636dc696a8d50b9477497cb1e2094d57ff5f56", + "proof_ok": "e98b9316f516f484a95f321b64891439baefbc65a5753d0a7688d34882867db6", + "proof_s": "7d98653a4f43df951f102df7df22861d17b46b9712e47d8b29c01499a4832d04", + "proof_sb": "372ba4e0f91941a40164dec5ee9842bc2f593dc7197e1590219b7b61f4772610" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "blinding": "e4f55d97d3e32f2519977249fef33131dfc46c69161aec05c20533df88919e00", + "proof_pk_com": "f5b453f86340b9e0fbd4ee1e741e4ea5d0ad89e19d3c170033c9e2ff29172bdb", + "proof_r": "cd5aca4564db7107e7e8f3d7b2b83f0f6003c98cbc68631b3e620c3d28c0b941", + "proof_ok": "d514b61e5d476e25bc0df7001b1e1e7af4ccc08f65c7cc7dbfb160bb37f5391e", + "proof_s": "5426e46493e59a1e561ba0c32631bdd7a45208ad99e54f15502b058cc1ca080e", + "proof_sb": "58dc6f40ab4766fc3b565d8d3b7206405763bca13be2b6f1b156cefa24da3714" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "blinding": "af9e73d2cffa7afdad01f6a984862698d33539394a696a8b1ef6007058c13d1b", + "proof_pk_com": "bd5587e13bc14792ed79b486eaf03db299930e2c3f84d1afc903259b43c9d0ef", + "proof_r": "920fed35b9cfa5a511cac008cc4abafe0fa887b78a8f7276d6ba88c3c8a0f122", + "proof_ok": "8c183f9e206847b3e2cfaea15da2d6283174fa459a26874fff93e31ecce84d9c", + "proof_s": "2e547d41099f8cd5350bf9786c10026723e7ea7e6c79fcd21643b0aa971cd410", + "proof_sb": "0084e899ace5292a80d7c165d4ad82c4954410b606097d4fc7e14dfa6f5e8806" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "blinding": "c146f1514188878d604101eab6b1e62657a64f256b27857da964e6761a71a103", + "proof_pk_com": "0e6e305a6a229b21fb7b009301a9f85eab3d6626c5bfb72deeabdcb4425592d3", + "proof_r": "14a340ca1d171dfbfc8cdda32c2c29c11cde9ec5c4e16c3675622be34c6204d3", + "proof_ok": "7c951ea43b3d98c6711fabaf1ff5d4fd1582e6e8a9f0dba74a7f27a4756bc849", + "proof_s": "0a091a88b4f0b8ee01304cc67c465dd0f9d1012daa94c05f7b69c87e1c2b410e", + "proof_sb": "f45765a06a6a952b0795e49f88cca9d20b40cfa23fc018d2f541667003f07819" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "91c6237feac3f60e443d2e356c88183c8d4c2764e3ba9dc485d0269c74c36e15", + "proof_pk_com": "1ed615e63fbc32b8ada345212d0ce8f67c98df6beb500df2ac1a12a7037898e5", + "proof_r": "c39bc83fee747f4198eb9e0fb00d719718fb77e50917b811da7fa6a7f490103a", + "proof_ok": "f3e5bfc0b64eb718e78b7ebabb65059a7222e142f5ac216b84be59b87fe78135", + "proof_s": "841b92b7a3d93940ef80163dadf4970816b346d9b7e6a42cd9420b4bc6fab319", + "proof_sb": "9d73dd3dfd43c2db0c938cbae59c801d0fb38a54b5afa7d128936e64f2c36614" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "2388860590f4d3dfa1024dfa44cf6f933bd4e800824183d01127ec6fc4e12e02", + "proof_pk_com": "cdc5b4d7ee3cc01f014891daaaec17e7a75790cf2410fa4da678d38f6d2ed2bb", + "proof_r": "d39d69f05353c0ede83a070c1248b684d34e035862a7d1acf7a06f7c37e6e5ac", + "proof_ok": "06d9e043a1e0fcea5c4016b86858e688b26254c5f0c9c94083a402ee89099608", + "proof_s": "99e263816fe24599c150216c79f249aef620d6c0d5c3b1185621223423d59e14", + "proof_sb": "44522fbe6cb85568f5147840f112635bf375092afca51dcfd2569b6b3017ac04" + }, + { + "comment": "bandersnatch_shake128_ell2_pedersen - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "blinding": "b15a8ad10d2ab3ba1c6edeab5d6667f2bd08a49bbfc8158aceab8d0f8698ef12", + "proof_pk_com": "bcd5b20e9ef57095cca6f259bee3bdc538464a915475a59e035c2181a0dd10eb", + "proof_r": "9252fec49607ae15f98dc2a05c0675f99c4d9b2943c4f2ac863d90432bddf2d5", + "proof_ok": "e7a8f89c36b295bad4dc1d267af453de8c4b9f3aa7136e29052de58aa24acc06", + "proof_s": "4250fda4b0aff93409d37f7ab499d645c7cf9ba56639ff6947857e10ba615506", + "proof_sb": "401f45f75557470cfa4267099f5cbf6a89c01753a628ddaf41fa16cc7abe7907" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_shake128_ell2_ring.json b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_ring.json new file mode 100644 index 0000000..6b6dce3 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_ring.json @@ -0,0 +1,149 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_ring - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "blinding": "9196197366ec51cd0a1d3a24937b21beac30be36fb433c3923a228ca2797d808", + "proof_pk_com": "fde8df7a70ebd32b39895c64088dcfa22fd436d6d3e742acd4256f043061b8bb", + "proof_r": "4d980be01c90bed8b4853f828a636dc696a8d50b9477497cb1e2094d57ff5f56", + "proof_ok": "e98b9316f516f484a95f321b64891439baefbc65a5753d0a7688d34882867db6", + "proof_s": "7d98653a4f43df951f102df7df22861d17b46b9712e47d8b29c01499a4832d04", + "proof_sb": "372ba4e0f91941a40164dec5ee9842bc2f593dc7197e1590219b7b61f4772610", + "ring_pks": "25c564876e52fbfc772f9a6de20315bc1cf00559305c5faef8f32de51f8323c7a98a990f2493a034ba5131f34ecd34e6b6f4d9796f93e647db3a11a635d5b7edc4d96bfd38970977f67fd40b7574f2a837c25f6e6252c8f404bce4b5c2b73cb3765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29acea2d6776aee029358e3bb5b69dda5f96349d7200a97bdec0264595b6c59d3cff5f0e06b645a0a38573f66633f36c53daf0d18e142d781700396773c8d3b4b0f6ceafaf2dee6e2e3dccc0af70e7d41d12555e75db81d386e24edee87eb10990af4332b744dc0938c3af29b3edbe20579c8115270fd8025578bfada72f7ed9ccc", + "ring_pks_com": "9944fcb94d80c23912130bc272a2f1cba2525d9905ec02f3a670e8eb0a5e63cb005415fac8e809985cb6bab063be14d4b17623a7b108013e7399a3a0809c264d7765079c6c6deffa7bfde2f37991ae0023159528c25fab26fdfed3e2b3c4862092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8e8cb130a517b13438d4fd49d792a920cdc521ac827ec9d7879bc21d4c8c35fd076fde9ac604b2d3d8ec4d1167e27d529107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb1c9a472387174696ff39fb49428f962accdb0cc857ae96b214d5ab4a0e8a25e935dbef995a1be99976abfd4fe5ee3828be7df11e0abe3a021e1d589228acb72138ae8cd9293c11462f5569572a34f18fe36ec66d81dce62c25a6c6a9c225d1d10066212863bd3f2bcfc4e643973aeb20e71348221bccc0a9a63496253478e2d2c53f832343be48733e2658dcbe84cec6c573d94971563b03cc133268c90940c5e998fc44a14d7691bd2e1c9985c075fd41f8db173afa656bd822c856d6ae13e58f4a94334efc8182a79e47181a22b590401b5e438c92e8b1060aee672372b196d239b522334b9bf3fb6fdb05e5f2c737f7b3cfdf391424b7bd8ce54493a54536cd15bc86daf38f06bc971bd6503c320bc83e61cb077d8beaa34aa7ee709d73e2176c0921043ae3da2e2dbc6a8f6370fc592e879746c2dce914e4fb9901fc503b3bdf48ccc1c72e4400fb817fdf3b2efad541a0d7915118d03bc8ccb3880115530eb414495ab7a9a473d286b75cf9f8cc69935a60984e164192020682ea1999d1e791e54e4f99a463e07611231c318408edf726b46663c8aa113df282def803254391de473592dbca3ae8ddd3d7d654344a81512a0fdadda6643b4f33bed04d79322be5dbb7a7e2d50c2792e997a32831a5352efbebfa88c3d786883ab0efcd81057f46fe17396781baff7092f90d35c", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "blinding": "e4f55d97d3e32f2519977249fef33131dfc46c69161aec05c20533df88919e00", + "proof_pk_com": "f5b453f86340b9e0fbd4ee1e741e4ea5d0ad89e19d3c170033c9e2ff29172bdb", + "proof_r": "cd5aca4564db7107e7e8f3d7b2b83f0f6003c98cbc68631b3e620c3d28c0b941", + "proof_ok": "d514b61e5d476e25bc0df7001b1e1e7af4ccc08f65c7cc7dbfb160bb37f5391e", + "proof_s": "5426e46493e59a1e561ba0c32631bdd7a45208ad99e54f15502b058cc1ca080e", + "proof_sb": "58dc6f40ab4766fc3b565d8d3b7206405763bca13be2b6f1b156cefa24da3714", + "ring_pks": "253af13bc23e13dd4c6712a3f5fc97d69e7333834ba637b65261ba53a5534d0bb083b50f633a2b1b2f1ba9910452d4814c8a9c088a5dd6cabaec6660c75acd04d47aa4e1db2a34ecaa1d46248470697ecc6a61b3ca4338a792003dbbe1c0ce20696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5e9d6d04e5e02637f9c8df3e70133c9691da3507fc4c018979a586c50b89f3a441610f3eb069a84dcf147ed4e05b491b6a626432476a1e0b7d4c488d5383c2013a50032358147b3ce1f8629ae81f9d4f1b72c979b6de0086eb53762fcebb3c0152b3238d26894de52eebe38953738e7c34461b0592c905a0e8e9d275aee36fa4e", + "ring_pks_com": "9751d5c23add386eda40959c3c125323d0ff7e163a6c81311b367a0e63df47cc904d5d6d43144d26d91e4c0ee83c3d07a97fc53bb0eeaf5497c8751eeaf57868d83f118c530af07fb4e24ec815c5644ae9bdbbd8887ddaa4c459bc4349ef74c492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "91c669d20bbd5ebd0b61cbd00eb8198b15046a310e49af96c1efe585703f313398a5d48a6c4bc392bac1042a322ec41f9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda907cead8084ea8774c822f0cd04f36fd4078e89d6022f7f53abbf14fb9337d7c940ada82a6862ae2583cca8949e22b1a12008b9d6076632261988538b29b7d7a51dd632efa0420400b51342645585dfdafd56151db22b01ad6256fa1964648ca1db8d9162805f7f6e1b5d3738a5ae04848069e06ad4dc78d988c411a05e704229a3198f03c8d0325ab09bd2375918ac1fb3ebfb06698eb506e6ceb855e7a607bcf1e35729f8bea934c26ddfa898c1d12166027b203ddac41fda33b88199fa0aad58c3a92087218b55c004d516f6d785a86d93cb2edb2ced97ad4904b478d816913b88238dd8e3379dab08a63dcec5e43e02b0a8292aba817b315e19f05aff3b801850968d09acd3f87231405ddc587c4957e301b6bb95a5c867f78ed8bcad6ae204a12ecfc86579e2d78edde28adfefc5e1c7da5fbeea5e1dc4d9cf160efe10b07be680ef431d11049844fcbef890941ac20102cc73d56d2e90ebbeafaff4af0d33083059cf1209dc9ff02910a4b92696458e1c31dd23bedad76e1b571f85ee1cf217403c3a0df26e8b8f05d6ae3a578bbc9e27010547ba2129392ef47be16bd37fc5bb66a71de8e639846f19b91d9d55e15cb3c80c1ffce99f9bf07b146a5f8feff4c2518c6a6a0609edb59897a6977951b5ec561813b357ac35a84a130ba848e1d0e7c46e96e81e67aef402fb5d58", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "blinding": "af9e73d2cffa7afdad01f6a984862698d33539394a696a8b1ef6007058c13d1b", + "proof_pk_com": "bd5587e13bc14792ed79b486eaf03db299930e2c3f84d1afc903259b43c9d0ef", + "proof_r": "920fed35b9cfa5a511cac008cc4abafe0fa887b78a8f7276d6ba88c3c8a0f122", + "proof_ok": "8c183f9e206847b3e2cfaea15da2d6283174fa459a26874fff93e31ecce84d9c", + "proof_s": "2e547d41099f8cd5350bf9786c10026723e7ea7e6c79fcd21643b0aa971cd410", + "proof_sb": "0084e899ace5292a80d7c165d4ad82c4954410b606097d4fc7e14dfa6f5e8806", + "ring_pks": "60c92c31fa10e43ab92c67f712b46e84f1e54cbee69430f733c5509a346ddca938c2b087bcf47f5063bc07700e01b9722a6c10bd2fe74c7a0c1f02c6db949cb604110a4ead79da5f1c1027038196d68630465ee57f521efb680b9e361426f1df477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f76283df314e6beea89ad5de30545611f4aeccb218ee3e478818b6c77c6cb7da99bdb75ff26d1a5ca95c7764c7e3ae7f4043bbcdf8061edf0d420786f3255c9297b8a123b590197d40e406eeb484dba480a84e0f6a644f8ce5ea3240f6701182e66846515d4182f2d28aab9f77dabdc5341d1e29bcf6b2fa24c39d9c52845c0f4866", + "ring_pks_com": "a8161ef19ae7ca2e6c5aec98247ab67ca41a56fc8afef3d62ae3ce4dbc72eeb08784a2858574af89affd065f19eb11d9b5128c1d4866f6f3f876008b1017bcefdb435003179360abdf3b01ebe296bd49e59deb02b4c35e9933e6c22bb443028992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "80fc4d2d0c04da81c40a137b0dd781e62751de3db1ebe8edc658eecb3604d6c85da34430e6ac13ae976bd85c2d8bdf0c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd98780e8d2db488c7ad61ea94a91143aaf9ad117a4a13ae82ac329b393dc214dad42319a1be26970e3dac288ae9bdc130b03e761acd8f6e9634fa6c81012340c9e1802a1e9027fb8a890409114e347ea2565228f31a06e3fb90d733627111775321d5df770b12f9e1eacb7aa81babde060f01ee182578239075b7e148a3497d40b864b55132725408bb4d69fcc7b9292b81b667cb688f144a792123f5df8dbb1ec41e2dd01e59c422394daa96702ed4f3322157cc20bc4a38f186abbf6a82db19d872be942d8b958234e908d651387a86ae772d51deb9dd2f15fd4aad43e2bd420113fe8c658f1f7873b1e99e71390571917ae5bcc706ac780ad19cf663e8b266b31fe7b1431b9dc05623a2fefd5c94a2943fa1d6c7b8f81e471f8de5c4a300537d3eb7aee035f244a2f07a9df3ea1b6e321367c9224b97d0da9bc2a2910fe05fa1de22110edc15f48cff81d0402abb4d3271e2a2f8d36ad9bbaea231cf998644fe1bb933ae2c2556d2f80dc767ae1e850d26dae8239a90067257be72094f4d69c9070b133f23bd9d928b3c079f9b8472a782053562a13b6d7c715b79f80f4c10b9543308cad3136ca835802876cc6a3562bc98a819dd710d07386288ae7cfcdfa57ef1400edc84b32e2f6ac997ccb4a526509543ddeb9a82b658a092ee2aff7836784323a2575e50637fe9b6bfb6f101", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "blinding": "c146f1514188878d604101eab6b1e62657a64f256b27857da964e6761a71a103", + "proof_pk_com": "0e6e305a6a229b21fb7b009301a9f85eab3d6626c5bfb72deeabdcb4425592d3", + "proof_r": "14a340ca1d171dfbfc8cdda32c2c29c11cde9ec5c4e16c3675622be34c6204d3", + "proof_ok": "7c951ea43b3d98c6711fabaf1ff5d4fd1582e6e8a9f0dba74a7f27a4756bc849", + "proof_s": "0a091a88b4f0b8ee01304cc67c465dd0f9d1012daa94c05f7b69c87e1c2b410e", + "proof_sb": "f45765a06a6a952b0795e49f88cca9d20b40cfa23fc018d2f541667003f07819", + "ring_pks": "b7cd2ee18200c6b04986c0b098d987107795b52e276d6a2817636d73302905d556c80017607086adb99ea84dbe240a9803577a8e9a9fd77cdb45141bfd13283dd3afdae42f0640b1f50889e94e2a683f1c882f22e9df608170df7cd2d01abd53cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0f586a2fc0e94d5766471c5c229ab591107edee3664e559a6f539f0209792a21e15adf788792cf549b5d20e2d6a04d9800f5e19c2893bad4af03d573d2437b2a6ee11de598b6880bfcc4b5653e2a4c762f8aa715053aa07cc69b6bf6f3dd07a5ae4562ef4cbdb6f2dc6ee2ba219e565f9d699823c7bcef0d5f75a4f7cd650b093", + "ring_pks_com": "a6a7c3584cbab41870351f11e7adc47e9791da12ce158fdbbd9fecabfd5aba4f86d5a0335c504850108724294a8595768c81641ca9740252145628928d312fa3c8f0923b6c604e1f0d61fa33c7c736e62bb768f3b6e476cc0ba6843ac7e87c9f92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "abd5c7fc2ee1d5feaedc536de9c0b0ae3714042a85660d5d1b588ddbd40d7e66342ad8d6f03d7ca8ecb9632ce48556569107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda844ce10ed4be35c10d2ae5bd1556625242a88e6f6130f44fd06650c69580541424c264d8932fd0c62d1301b876915288dc9dee3d05c3bd17ee58a3b3f31d8897292d16cd91beb90aebf588877205b9302bd101be8335f26b075e3d0a4ec40d64ef529a5b5a75c45347b68c33a8732824bb08633adab6d61d02c9cc6cebe3261aa069236bffd5c5302e3d846e3ab68c9670f5ebe787954d549c569938720b36fd405fa70bc4c630fab9a99e74f7c7761e3c48dc4eda9686d1ef8b7daaedbc1339b51d922c01db980113d35e086ab6ad633b8ce56abd74dfc7d8b19f66abc0e6e5e5cba053c546df3946c2f2a076539204961fd02d2347b8d963a958617d457337f18ee6849c1436bf0a6aa171595a2c9cb200c316244d1f4129ec09972fd454f11da8addcefcc0208f4c7a4f09422eb935319456a93eb30871028481760ad434b91ab02464c48d6eb8c707ae9c3d3b3e819f46e2855d2dd4b739c82b5f8c44aeffa1b89e5a19dad8f8ae681fe1bd2d486627f8d4434e1ead9022702c9edf71371712859bd9e82bf83b96a75090334b3fad8e0435baea8ba2864723c532f5c2ad6c4563e6dbf291f6337731009038d2a6b8111ff08488f59d069f5d2547129edbb45497f1e03c7c032df03397339b6b9c7536b1066171c9f022449037ddb359628914543b63f784f167974433b9942f6d", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "91c6237feac3f60e443d2e356c88183c8d4c2764e3ba9dc485d0269c74c36e15", + "proof_pk_com": "1ed615e63fbc32b8ada345212d0ce8f67c98df6beb500df2ac1a12a7037898e5", + "proof_r": "c39bc83fee747f4198eb9e0fb00d719718fb77e50917b811da7fa6a7f490103a", + "proof_ok": "f3e5bfc0b64eb718e78b7ebabb65059a7222e142f5ac216b84be59b87fe78135", + "proof_s": "841b92b7a3d93940ef80163dadf4970816b346d9b7e6a42cd9420b4bc6fab319", + "proof_sb": "9d73dd3dfd43c2db0c938cbae59c801d0fb38a54b5afa7d128936e64f2c36614", + "ring_pks": "ce48f6a447f0e9e52881d6dd89015ca1afb27a6a09409e96eabdf7db63d9ef1262d912712a48115f46a422ad69c21c567637c70553445e7ad9044f8d46792bb69f943ad0bc908da21eb7df8c67676fe8bfdf513434d779baeb2dc813be89c58acc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9b5cc4fd04551a6866996717b58d543a81d4482877edcade4708ac28e04e781c62ab59d40ac5282e93c3e32ad10867f7f48b658fa6e42e350492a642603dbb5d545a7434a2e801800872b552c860b9bd19f65f1edce17a03814cb75e3c5822eb82e9f6e0a30c02156e5fd9d212108b3cb1a10779a605b54d653041f5301a0e708", + "ring_pks_com": "b1c9c067169ec7569e9e4101859df32f53949ffa023318962b38baee11201842aa013f306d98ff46e8932adb79ed2eb887104ad0e1edc04f2f9c9b6a7fdaeb138ab8f5208719f29869d7eeeecb9f075af0504edde161af6318d3b1e8f180ed1992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "96062422d3c2209b52e3e2485b29ab1a54401053418befd026d425b669ae002364d7ce61268ba8a6d7c6d7cd91ff57389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda5836792cfeb621f9b50efc6fd72678bc85d2c512b6fbb77c090b427e3205c356d4c7fdc191f10f459efb7f74ed5c9f7b133dbdf0b20597af725c7e04636b5185e11eb14bee9cbca1679c971229d2a29013a9056ad764dd73dcc9727b7ecfb52c2d9b714888a65102a55a45c8ffe82ce074e39a998023e3fa837be569db6f8092d8f4bf5692e8afb3cd7fb122671b2fb186f32efab01d2c5827b2b3adfbd4b72cd79872daf7cc747bed0d0604eb6e287c2539961123f8e8880d31d7b22ca19058f90c87fe3d80007387415d4c18051e203631fcf05276b9759299ba054f36d592a4ff2f0f902b0af352208cac811e480194ff47197ef3b3d5cdd70a416031471f83a90d56e3d51a092d54ed1d120499bd745b27fc34ac083b4e4c2a20ee47907f39316af00363c8b7d182c242c91e4312f21f5fadc5488362d2de665d0ad412f87d475a63c86887dab0ef583f7e2373c18e219ac4aeb1337f1a7833a824b6d6c96321b784b0e8c45c890799fcb46d48872a8f64bf449c61a4c8bd4774491ab6dd2947b8174839f407677cf11d4f76434ac178548ff4defcfbdf8163d2b4e40d6dfa7a4adea9e6853e14b10f4eba3d31289242994b0e8f4fe1dfb781eea41f843a9a1f7e2475b8aab0883f5bb06ed682e2d5a9b36a97101f59691d75cb6c88f3402a9f51b1989bd7616c902656cd9f6c1", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "blinding": "2388860590f4d3dfa1024dfa44cf6f933bd4e800824183d01127ec6fc4e12e02", + "proof_pk_com": "cdc5b4d7ee3cc01f014891daaaec17e7a75790cf2410fa4da678d38f6d2ed2bb", + "proof_r": "d39d69f05353c0ede83a070c1248b684d34e035862a7d1acf7a06f7c37e6e5ac", + "proof_ok": "06d9e043a1e0fcea5c4016b86858e688b26254c5f0c9c94083a402ee89099608", + "proof_s": "99e263816fe24599c150216c79f249aef620d6c0d5c3b1185621223423d59e14", + "proof_sb": "44522fbe6cb85568f5147840f112635bf375092afca51dcfd2569b6b3017ac04", + "ring_pks": "ce48f6a447f0e9e52881d6dd89015ca1afb27a6a09409e96eabdf7db63d9ef1262d912712a48115f46a422ad69c21c567637c70553445e7ad9044f8d46792bb69f943ad0bc908da21eb7df8c67676fe8bfdf513434d779baeb2dc813be89c58acc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9b5cc4fd04551a6866996717b58d543a81d4482877edcade4708ac28e04e781c62ab59d40ac5282e93c3e32ad10867f7f48b658fa6e42e350492a642603dbb5d545a7434a2e801800872b552c860b9bd19f65f1edce17a03814cb75e3c5822eb82e9f6e0a30c02156e5fd9d212108b3cb1a10779a605b54d653041f5301a0e708", + "ring_pks_com": "b1c9c067169ec7569e9e4101859df32f53949ffa023318962b38baee11201842aa013f306d98ff46e8932adb79ed2eb887104ad0e1edc04f2f9c9b6a7fdaeb138ab8f5208719f29869d7eeeecb9f075af0504edde161af6318d3b1e8f180ed1992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "850d18a3e6c3de203d30e5066cb2e555e8d81d96d70fc03dfb902e476a895ce476535b515d9e8bebe06639495ae94ed39107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda400a45a21a7d21b89c865a37d5e532aa52e996add908863ae556891302da843dbd19e16ad377da566efe298219785d4950a613734db1a1b1777413b3995e07aee96bd2ac6df030191a3d7f1aedf2ccb7e57a50b97c1647c662dba723accaaf9d887cbf0d29f83281d45bf7d2f743162c5d95137ed2fd0561e0c43ea23fc1341592587209e7f6accb08292a577ea758829d021d37d7defa1aaf327ae02ee2c1dd53ae29690ab115c95758b93dc84edfa5e93fe5584f4de719c8d8964d04d3548db817859e5266a10227ed3d420717484a4bc52fb16ba58f729e1ecc0f5955720e4c47a28a2b91ba4d8531587642fb740e51eeca3e1d8adefe5f628ad389ec96bc5780a725060c305f88581a82e5b678a58a29fb7b975da5a77f09e7b6714f45645d5d628304925c999e54c6fa6d21609963678ed26f6ae7d407595083d7e4153b3d9ddd023dd7d3fd7f239cacc4e7474096bf49d573547f8f92e446b165074c8156abde6fa2828e9b0a1f15c4792c4ea52b051fc9d4a583fae3817727fe922cbcee9a4fbcbafd165cdcb4c362250c6708bc8921f73e7303a04de4f07e604d81ab4ede7ce033bc0fe3de86e3d78bb92276a875dd3af162cd53ee5dc107e684b728cbfa8fad653299e62ae0be839a79ad4bde559e4e3f779ed9f9d53a52d2b817cc865341e05b1fd94c10a875f4795f094", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_shake128_ell2_ring - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "blinding": "b15a8ad10d2ab3ba1c6edeab5d6667f2bd08a49bbfc8158aceab8d0f8698ef12", + "proof_pk_com": "bcd5b20e9ef57095cca6f259bee3bdc538464a915475a59e035c2181a0dd10eb", + "proof_r": "9252fec49607ae15f98dc2a05c0675f99c4d9b2943c4f2ac863d90432bddf2d5", + "proof_ok": "e7a8f89c36b295bad4dc1d267af453de8c4b9f3aa7136e29052de58aa24acc06", + "proof_s": "4250fda4b0aff93409d37f7ab499d645c7cf9ba56639ff6947857e10ba615506", + "proof_sb": "401f45f75557470cfa4267099f5cbf6a89c01753a628ddaf41fa16cc7abe7907", + "ring_pks": "a3d2aa7e5222f897f2727d193d7c491e45d5d3efe6389bf0f4792a9089bf559e5ff98c1e6f4838014a297f5a8c31e9716674c84e5ac20d8699aa89ff65a4bfc2020cae823e21fc1532bc8c1668b3ad1590a2397a73633fc7ffb4b6cf1817085765ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af6fc054eba7f1b13434fc1501ee575151b372a5ae5191c7fe66008cdf07bd6bab8b74f3a8b097548460c145fef75d2fdff95040d921b6f50f3d1d3ad2045cbd190bc092e8e84e749f75dc3e527c3ed06d3cf616713714f49c21dc62d952bec50ecc9964f7c221f071ba0c72ec77309502f2054233b287e2d463c6fe97cc4fc047", + "ring_pks_com": "b4330102d8405a9dad1cada2dc7a26d04771aa256e91deaefdbac78456b7f24d27a0d6d83a7967dd81cfef4c74e8e0b08ec52ebfb13080ce16d08b8bd641d7adb38849dc90d93243cc72a178fadfa673a6fb0abd6fce1402e4ca2b3bc3807d4492e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "add770e84672cf11b3c4abfa456ae36ba6eac4314d5849f5e0e8a23af1243108c02e70d1bd2d1da04f0e302854c865ec9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaf5e9cf3d3adbeea0ab804871f868afb29b8af72fcafd599aea1724f65f44b8a95240b76f21d4421400bbf76d73ac11f9580d555f27eb652448014409f7a4b47f835c075f782ade1f9a7c9a2c50af38bdf753f791684bb179f14845934b7bbf41b73c38b9a9f131a877cedc2788c21e3dd44dce25cac758b7cc06bd73758fa71135ddfa8de710c7d559b113e4f0072a44a556ef0a27429b6c98c5b05a9a81b2c790b2bde72a1f792b1d63bc59bf86ff7b0d37208e16971fcd889165edf2bd84c490aed15802dc9bc8eefca584464f772f7820c8a194ff06a8f789454ba7c812048d6b656820ded5fa840ac3ad07c0816f763f7eab51ac7ceba199549b19e38681684f674aa852ec357f24c87ec750c7a8b369aacd751145b5331d5cfc8f0c92b0298e150e54574785e48d662bf87765bd78e344f4181baad77209908b1d82f59a76dd2c7588400d24b293b0c66f98c5125e75596677eb9145ff0a3e662940e94fb050245beec65f71e3340a942be67f4f67c991fc3655a94361cbf1c73fc5f71c37bd068026851ebd270281c0182350a817b6119c980fd0fbaa5685b566d2efa8aa3e25727277f10fa7fdf1b3035642a96f665addab31e6c26a68fdcd48b542284af5510a323870ece4c101faae0d1ae0a89f02936c659ab42d24a3ddbb8aeceaf38c67f9b3ae2399659707e7d7ce88c", + "ring_size": 8, + "prover_idx": 3 + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_shake128_ell2_thin.json b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_thin.json new file mode 100644 index 0000000..c210f1f --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_thin - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "proof_r": "d3d7c7f4479e345739df433e76b32efb8e57fc08f3c0b8f27bb127949eb11964", + "proof_s": "63968b87f066889851b08e73fe19089660b49b6bbde1a10e61f25f8cebffca06" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "proof_r": "5b6eab9bfbcbe68b6f9ec329db3027e71b3d441ddd2ea0079d670dc4219d4ebe", + "proof_s": "99760e714a7a1beaebeffd885a6375f86b0391af72cb8619f9fa020ee37b490e" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "proof_r": "f9161743cd3d7438d6a16397bb5f74a90a8f2d5525b4cd9c5b7b5f698e03f40f", + "proof_s": "8be0b86c7c3f2acc0389aa47e65c902cb19e5a1fb02d080c99b4d35a9ae2bb14" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "proof_r": "cac0aad60611fa74b6c71f6635dbede529b87efd19245eff27c3876cfabe5333", + "proof_s": "7d7fcd913d28caa36fd671602d7431653cc6c1afa7fe112e7fe51f375e52fe11" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_r": "285c435d928cb3548a44ebf5f527da30f97763b12c059af365aa75d070a12542", + "proof_s": "64ab2cb7f93365d9c4de43a883d5eaa9ba721946c33b88e4c3ab7e80f7a0d216" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_r": "c03ea2f0015e8ad3786ea2c88313ab6ff93e8df2f4469e73b020e77f39c16e31", + "proof_s": "c2c9d7c57d47d7bf2d4ac101396536d7d7e37cbde6a74f6e985ad1f85a1ae20a" + }, + { + "comment": "bandersnatch_shake128_ell2_thin - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "proof_r": "f39ac083d7a350a7848bf9688c9be284ec83bd77cb6c28f4c0ed1209019bb630", + "proof_s": "e40c7763383e099af6bfceb6d5c6f2cca00444186ec661a170d1070d4654e711" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_shake128_ell2_tiny.json b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_tiny.json new file mode 100644 index 0000000..c56f335 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_shake128_ell2_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-1", + "sk": "57dedb816bc9caba3ba7cddaf8127d69a2c77ef552d3eff5a042d86715123d12", + "pk": "765bcca700018810df382459eff4421b63b7873b95843a89069b19da0a6ea29a", + "alpha": "", + "ad": "", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "06dd6e67e7889ea33ba55bf3cfba3876053e9fbb246a106c2d3a40461ce265a7", + "beta": "a2fe65565140cf8d8fcc20088ecd158585e86102e6c181d2d5c42bb79aaaafa0", + "proof_c": "bd81d36b18801d85c80c98eef57ea639", + "proof_s": "33a609847861279f528b4cad7269f1313bf39d6b745f544811885fa5e2841101" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-2", + "sk": "84f4bcd937232437e7cbc45a777607ecad7a3809e92ee4b368e3e77e5780ef04", + "pk": "696948fdcbfd3a83b021bb35bae844c3586d59a305bb5cfdd3c91208969c93b5", + "alpha": "0a", + "ad": "", + "h": "543b32e34a146eb3d43c0a4816d2726d3b97c1c3a019a89caaaea92d6b15c40e", + "gamma": "853e304b89f350f95d179cc8c7edb15b0fee4b228997b4c54280f156c2684edb", + "beta": "cd05c0c33f92ac6bd63c1675dc1cd984dbc7312795f0e1927f63aa7287864a68", + "proof_c": "64cdcb750008ee9922a0548c4e5a314e", + "proof_s": "6e5011c2cefd5eb995864ef2c04ed0923a04da14bd70d5a071e81a373f8a8b03" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-3", + "sk": "cdb2fe499ff1413c166aa7a5daccbb7d497017be32c60b1bfa5a7a9b663b8810", + "pk": "477201b34884b869fe8eff6f831eba50854d92639a61597ed12056615785f762", + "alpha": "", + "ad": "0b8c", + "h": "1af0a856733b0c0559479c650ba60b6ef865e593eb96c293ffbe2d3257634d0b", + "gamma": "7b515b64f4aec4b3f25f7e843f3fa4a7553b912707a84447bafba05b169a06a6", + "beta": "ff8f3c299cf212a4a4a30eb34c404705a05ca93276a166787b4eb7bd44705881", + "proof_c": "8b77b290283fc21c44d2d8c9b0a2392b", + "proof_s": "a7bc8fc05b2a18274b9e0b9108dcf4f1cd04f21c394a69cf9c6d058b011c7c10" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-4", + "sk": "a9522c150fa4780001d847b08fed16940eeab1d48c0d76e7b0db5ff676f5031c", + "pk": "cb0bafe0e0f04cdb708e7529166c6bb78325551ba36bddcbe95292b31d6fa9a0", + "alpha": "73616d706c65", + "ad": "", + "h": "88e229414c2413c954edda9477e7fb059c843d27f4234f7154efaa1d686145ca", + "gamma": "c6a9a7da335d6c1bc66f372271d345b9ea57e318e6850d86b6dd1276b59dfbbf", + "beta": "98882aa73cda3b1f89d592604f20f2c7255e223d1bd2edf3698a4f7749137bbb", + "proof_c": "caa2cbb33d1bc6b2c06b56e0a26f1e30", + "proof_s": "16664b84cafbbf50d49c3ea06e6005395d94b83d305b7236187786a561260f07" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-5", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_c": "6c1f153773b04d04ea270b3760d9ec98", + "proof_s": "678561187a92cb8060f816e8ce798501729aa085fb75f3011be14d5bb9b88f0f" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-6", + "sk": "9c4239a58d4e2d770c3b5f756297e908bfb7f00913059f27ece4815a5eb6d705", + "pk": "cc01286888f63264ed2e4c0f3a3059915490100f1253d04df2298bfaf5bb5ad9", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "38ef198de5934b7a36967d586807c465eb9a2834f21a11812a24f266a2d47d92", + "beta": "27f5464ad4977cc73631393943c83a6e858cccfbbe8ac00eb763066976af5431", + "proof_c": "2f1b673690396e20805066dc5de26163", + "proof_s": "20a2864ac68a085430d967870c2c1ed62efd2f7c51e1096848f1b44b3c1d7511" + }, + { + "comment": "bandersnatch_shake128_ell2_tiny - vector-7", + "sk": "6de7b5470c968c66731f3abfd7e05d16d6cdfedc434be3d5c61ce45640ff3218", + "pk": "65ccb82f869b3d508930dba60882a116a10a8eaa28a63a82539149f325b1e5af", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "6c89d9cced3b7bd9ccf4dd3e70ac0e435aebf571360fe56a16ba85706e562892", + "gamma": "486a036eacc83d800b11c53d8810fa207b4e38f668e10b0fbdd16bfca0d77b93", + "beta": "7d782d1ab24b55f7acc286d1fe737aaaf2550d5641d194e20e3ad211c4634f0c", + "proof_c": "46d06d9a752a4b6b0fa53ecaf8472f63", + "proof_s": "806b5504c2823d60c717ba153e48e70e18ca1d772ad74ed71df2fba5532ebd1a" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_pedersen.json b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_pedersen.json new file mode 100644 index 0000000..cc0fbce --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "blinding": "101ce60d21f5c6f590ddef98a4bf7f476baca00564542ac41838459e1bd3b816", + "proof_pk_com": "4b3349f30ffd34496951344d9c312dc2f9d656115c258ddcb74fdf84594c243900", + "proof_r": "802c8905555241465e7b7a9d4888373ee6e01fac23528b429cdef9bc12f3c93000", + "proof_ok": "b028f7c1966914ae3c9439c43a60d8b09278526a8e3ef5bca46217d55791fe4a80", + "proof_s": "a482ef87b37ece01ae3acd22a4074db24243db8ede369a0d926656f8f95c2b04", + "proof_sb": "d7db22d81aa9f66a9bc5554c48d972de3dc162e50f42376acbbb7ad7209f4016" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "blinding": "3a2cad30984bd4f7f87867a26925361dcd66317bd503298e3abed37774aad108", + "proof_pk_com": "a85601fa16306e57062208f6955390e2a0b046994274786d3c2bea6abde4380400", + "proof_r": "00d5981b5b24ec1bee09563d904304f111bdaf4eeda9144b0aca8ecfe0a7430380", + "proof_ok": "33cd57b070427bdadebd115fb04f2c5dd6b7985eb7fcc2744d744d28772c831100", + "proof_s": "e28ed027aa4f90b503a741b8bb3d92be9aabd38b2688566603a8cb5ff7ba4113", + "proof_sb": "a1b3aca030b593e5c5097566d584ecaeeb9f72a5dc010deae18e66f866b27204" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "blinding": "f06965d9912d89aa26b24de126d9d72519308ce4b458f58d0f27d55b59ff8c0e", + "proof_pk_com": "1b3834e85c6c1edb035b73d1c62fa8219a6e6b39fbf7619b69e261f02bc93d5080", + "proof_r": "39c2ba376b065b8fd89fc4d89bdd8b52073f5f4fc35fe8f67ee83d73d1f7a34200", + "proof_ok": "eec00f5462c3acc58cdc4210e770b12830a3a29b390fad3edbeb6ec6af3b373e80", + "proof_s": "4b7bf40c6514edfa8b15dba03bf2f96e4f8b32ea56faf69e9f10e4ff01bcac06", + "proof_sb": "79bbfda713a59183dddbf4744cc67e3f9c3e4aaf8fdb7a34c7a17d818e37cb03" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "blinding": "d197c4d1733d7fd97afb6d8e9d18d61b470e2818a52124aee01e8196a3770615", + "proof_pk_com": "fd70ec5877d0446e5cc9364dad6c2d2d4172b039226ccc74f98a208b8cc78d2800", + "proof_r": "ac535a8c53c7e33fd72116b9c64cce71158e6f9cf15bfe9c574c4a572a04ff4480", + "proof_ok": "9c1555853acb686809d7827afebf6de9c5c98312436410afc272ebb868d8aa5600", + "proof_s": "41a987008d013ebd10030ca5994d85cd8bd957f2940c9c3d015342315b3d4700", + "proof_sb": "573aeaea8072de85eef603a0c44c7f07ec212086cbb91b672e2456fe4a218809" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "0cf83dce52d08b7798eb9144d912b7b31204fe92896af3e792236be115f76919", + "proof_pk_com": "ac3b3a47430e1a5cbe997df2951e5fe13fed96d613522b9ba385b256191ada5400", + "proof_r": "e53915bcfc3aaf3ffde07b0e9d7fbf7d1d7f34bffb8ab82cb827028c596cc70700", + "proof_ok": "6876624d12db6c30c864fdbacb718ec0e6385108b11539f16bf2d3456316d94500", + "proof_s": "a1b971d14062080f7a5ead1ef8a19877823447faa9fdf34141fd9eac6c10531c", + "proof_sb": "53fd9abae2688ddd003f40a79229b4a24790dd02245e0111e676b39ea5424713" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "20537ef560d5a8d7483dec55e0b680797376e61d0844bf6971135cd5811ee203", + "proof_pk_com": "1db1d10f797d6208fa724359100b6297c74a98a3f35626ad8dedf891d362ac5780", + "proof_r": "2d6643ffbdc2d4b11024be73349dda34fb1922c7d67a617471bf05ce93f29c1e00", + "proof_ok": "db649772438941f07e1cb4d92ab387aaae22fcb0389d32668a54385a0bd8461580", + "proof_s": "505e46a525c967e840254400b59e75fb40ea21d3c132bcc5032df23bf3dde514", + "proof_sb": "f71544be7ce3ce1a4473d30070c427f84be2f80c080a008c1a4280e40754ff03" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_pedersen - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "blinding": "2cff0f18f6fa445491223aeff1e117c6180a555aa23dd710f1d785c24abf870a", + "proof_pk_com": "caa63e87aed8ca3880a2d63d97dfb9f47c8add13dbf5fc2c80cf410d7c66ed1500", + "proof_r": "ac7737523563fc44092829f1d20a02c50e02380cefc6c871fd32337bf62da15c80", + "proof_ok": "e8a1911928b58371fe29722b83ac3721bb74e85a5a85f549edcaac082293373780", + "proof_s": "0a228726dd7e3350d84d7b6d6e5f8a0b91cf6860d91157f1612df05a2a8f3c0c", + "proof_sb": "87e7717f25017e1855e30decb2bf83029e8a56fa166872ee9e408a581cb61b0a" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_ring.json b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_ring.json new file mode 100644 index 0000000..f475f22 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_ring.json @@ -0,0 +1,149 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "blinding": "101ce60d21f5c6f590ddef98a4bf7f476baca00564542ac41838459e1bd3b816", + "proof_pk_com": "4b3349f30ffd34496951344d9c312dc2f9d656115c258ddcb74fdf84594c243900", + "proof_r": "802c8905555241465e7b7a9d4888373ee6e01fac23528b429cdef9bc12f3c93000", + "proof_ok": "b028f7c1966914ae3c9439c43a60d8b09278526a8e3ef5bca46217d55791fe4a80", + "proof_s": "a482ef87b37ece01ae3acd22a4074db24243db8ede369a0d926656f8f95c2b04", + "proof_sb": "d7db22d81aa9f66a9bc5554c48d972de3dc162e50f42376acbbb7ad7209f4016", + "ring_pks": "84632747d740e4f49a1ea91b988c8c68ccc702907749898ad76efe9086fbbc350006fbd1d78bb5937cc0d46b8f5a8feb683a8b1d6326329c42877ba6e1c41e871b8014390fc91bbb66d6fc0b94663101d7d601b92d8255de190c5b580c4fe9ed596d80ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec258005036bae65fbc539a34a9eaacd2f99d194737c49a86cadc78632ba0ca335582b8092a44c8ad47a5840e863c5eb9430cd320383870a80d4b5878039b756e6edec1b80a2fab54fab6bce5c3c6c8a64e18fdc03144cf27381a3f6a1debcdc7796f16227009b6a628685c087d4f43d102650e4404e4f15281484941480fd09c56bdbd9db2080", + "ring_pks_com": "a9e3f4f6bd0f5859e4cd13beb8e3e51215887d224ead1391e2212c3a2f6f5ca77408cced1a06f339d297c5c9e130c57a98a4149cbb51be61a77942ca39f8c8225b618388c2614ab9c1b1693e4b52e5a41f8ad02407fd18770e2a3fc9dad1b7dc92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "b15de96be694ed3587bca8dc9c414e16bd26a96aa797051187d70dffd004740cc874a5ddaf0c3a80bf24a51b68a90fef9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8ba010b8fcc764fc8613b1db8d2853b9a0e69a20df78949de07ccc2ad2770f14cc314b5837fd1b5cdb5b52b8fbe0e92aa5d4c8752e24929871c003937a6c43d403ba4e9b0c9c569a0a0c1a7402130d5842ce9eb49f578a3911c899b8e16fb745e9f5ed5e003f91f9b5c56d362fb9850ee370ddc973f6ebdc209fa777438fd65ef9a858f76835e0cc7606c900129245c4901b04dc3a35dee90dc583f9ed5cc9733cd7f167abdcaa87c1c8279e778e55c0cd37ef856025d1fff5ea3c6c3f23485e55046c11b8844f44f171ed7b0784fa586ed9187197fc50b7b9cdafe53c5a4c0199f99e48682a45db1cc4ccb30b65cf462979e3b7f9863aa558796ae3c8088312a7f370133cdb9458d5bcde64a2515c637ffc758ccfc0a8d60144f7dfe68da60db686bd14248e11e5a6bd70261b1396e11498c69d4bf1daab54219e8c1a497f39968303bfc32e2a2b5cd3908e97f82aa57e05ad3715efca4ff25153ffa883d83f10354383de081309315d48fd96fcedb839e7a9a96de4e128ac9938b1ce01c64780387b1d477182b724fa40f0df327d1ab05233a2368ff21ff2035bf2f39f04c9b92b60293575b160f2cc113029dec187a1fed3686c951806efcabbf92cf0fd86809da2cfe7a56546bafe36239d2d51610a1c487d4e7f5f5f5b4ad80f4f31909b151d6cddeac61430792713930ad47d09", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "blinding": "3a2cad30984bd4f7f87867a26925361dcd66317bd503298e3abed37774aad108", + "proof_pk_com": "a85601fa16306e57062208f6955390e2a0b046994274786d3c2bea6abde4380400", + "proof_r": "00d5981b5b24ec1bee09563d904304f111bdaf4eeda9144b0aca8ecfe0a7430380", + "proof_ok": "33cd57b070427bdadebd115fb04f2c5dd6b7985eb7fcc2744d744d28772c831100", + "proof_s": "e28ed027aa4f90b503a741b8bb3d92be9aabd38b2688566603a8cb5ff7ba4113", + "proof_sb": "a1b3aca030b593e5c5097566d584ecaeeb9f72a5dc010deae18e66f866b27204", + "ring_pks": "37905ae419e7f44dce9333029263a3bc0a6815eeba2e322fb6030b243ed529138029706abf1e35af7d0b72919c326ef317b74c466a7df6f1c7dbcdf3fa24145c2d80b965a6bde5f9d3b91487c2d40ab9410f6255234b1d5c9d235954ad51e3b9f126807b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480dbfec1c6ad5a4bbc9b40c07b957f9e6fa9381a546fa168a27f43e1c6e120825700a8e8776cd4c9fd5abba95f301c2c641eeba46e4985e810f41d668dbc0ac211410080d3bc3e7feac76b489f015dbc9f6326659ee72d4d270a7ca48e421153f3e750008e2c02c8e15c0279b81e2006197c3b5abe5f68cf59b2fb80dc216c9d60dca55600", + "ring_pks_com": "a2f97a121a14d51dbc14adfec05d5c9d054f5e86d1944f0f3547722ec5076a40269415dea92ab56578903527f38a85e6aec765b2de4a8cbeb33783eddf3e57761d68377a42643ba738b730221646516625bc3883d19e0e581dc660aec1114fa392e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8a4a091855e35f3661319ec351cc6f1048bb151bd3cc3ee9fc49d063653a01cbc95b165db39695408652ec4e904657d99107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddabb53ef3435264067b5e5252ad348a2c3232a171c4a4c2730b5fac0cb66786251e953e41fd22c7c56b2e9dc69a524d008bb3d5f011a22839518ff2ed7593951b651cc0b65f4cb12903e4dbb27253d893c81fef82bce7bd130b48e2b30b32828f2d28bf27ee8d23d7d22796c76a5afc52cd7e24ca45763cf43e407befa04ccd3ca297ec38df3e6dc751876005622f8d6f5feec3c10dce65e164962170537d814b2b03e309da06a35f15252ea0ef2edc409bb3b89e9c36546164c4dcb21ecd586466465ef3ee7f1efb74e242b8ed19f391e9e4f4e1fd14462e82c766425cefc21457914d7d4b54a443f27d04c4d5228cb40b3bfb59b12ba9bcb8ab4cdb4fd06f1db4b6536bdeef6a1a6b939310921fa9a3c04a0b83565e798b18398181b95c24268f1c130ae42886c5eef862bca4b82a74c90778d026c048a2897cd1fe3ea8e53cb1e529d56062b6412db28c1d3b95f6f22c54b589ff014d8a66a16e46baf1248346f0ec9871cb01545a18f25ced5343e2640fbbb17220fea45cbb556abda7a5f3305b197c1389e7e8169db33c0d85640cb6f29b1b4a0fb4c91e1e5b877ea6fd64d0149b6b20369d52679ad4815428650fb7b81c786e5b456037887d746010b0e8a2e783fa06377e1f5efc61cc211a711bc64436da791364f29d08ccfedb100f9947536d7360348955cf69393e5b11c799", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "blinding": "f06965d9912d89aa26b24de126d9d72519308ce4b458f58d0f27d55b59ff8c0e", + "proof_pk_com": "1b3834e85c6c1edb035b73d1c62fa8219a6e6b39fbf7619b69e261f02bc93d5080", + "proof_r": "39c2ba376b065b8fd89fc4d89bdd8b52073f5f4fc35fe8f67ee83d73d1f7a34200", + "proof_ok": "eec00f5462c3acc58cdc4210e770b12830a3a29b390fad3edbeb6ec6af3b373e80", + "proof_s": "4b7bf40c6514edfa8b15dba03bf2f96e4f8b32ea56faf69e9f10e4ff01bcac06", + "proof_sb": "79bbfda713a59183dddbf4744cc67e3f9c3e4aaf8fdb7a34c7a17d818e37cb03", + "ring_pks": "5020519f31ebcc1d5eccb7dcf07cd3c74aeb984ed549e95aaea4c56786c2914b0065213d31169f9ab0da990a8bc1e25f010e253906d260098bf5dccea1580bb25580564a107119782b98508517e38f942d63b70e1818da6d14adf708a1d3ff44294a005f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00bb50785c1735184dcce0cdb58183f4463b2ac961243db1c59e979fc8ba4eef2180ffff1066bb536f2fb66f026b2dbb91c7d120a21d02e05a7a7df1326976fb886180b4495d1220c8d89532a8ebdcac21db7a52e54202aaf9d01a0ea35b9b16a6f33b80a2439a22cefaca30ee096ded6ab64185403fcfc311a5741b51a21bcc338d8e4b00", + "ring_pks_com": "97df3b07617bf182d3ffe4d3ce151acf0488bdbb747ad1699a0a5c3fd4bfae12397d7a6f09ec2ae027df39f04e90f074b04a26a126239f176b47928c7b184a9e424e275c06868199d4c59319fcd5c5729d8a4d435db264cf4f17bba845a6161a92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "af22e9bf883f46fd330815ae1b735054c4cd1b6b910bef436a66812038de2436198a52f0f8b89e66bcd6c0554d903c389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd95cd797a54156f5e2ed94d66f8785c0c7c01688337fef8b42d11695904c96dec1024df34b9e04703888101939f85f56b8e7c357f6bb524b91106f8e27db467eba6811ff8d52651c778aa545fa166ee3b9c6fdc5d147503afb3cf432a6fe81db02096c7885acc76b0caad73b81ebcf700b95c6b2f00c3911840597a2ce770274687353b2ea02e3e88b9a47c05e8a289b87883032d6f0eaeefdfecea8b315aa00caa7137578d8e69fad67731b65ffa41b8ba8f0251f51c9d9ee8eb9cd9b330da1cf66de960022d4fa5ef5bfc0705169981d13df9dd128a844efe5865ed5ee3ed3c455e8bee584de3d5365b11fe31c616d915a462b81e22b593485479b13cc48c570e633f7d4930273338ff99a23deb71c13f3f5a8c0279310a91208173d81dba217531c7c846f018aff23c524b7e2100ac1b3f95e79a69772c6c9e5c9dff2917568694170c5297e40f8605f18379edff9aef9f431c560a9874715905ea8c1edc9ab9d3db5006c92817fd62e31fb6f929e7ab044938a87835a1ed1ade55357a7a999bb594ba61c239f9aee76bfa114e8a46a055d020c20e813accff1f0bbf4b494c47eed850aa5b451cb6f0afb927add35e40cc8c5c5cb862df73cc21046b0638048b4ddd956b34306238627c932715ea301b084257cc00aad725f4860568d37f21586930c9dc9c09e9701813e5afbec2b8", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "blinding": "d197c4d1733d7fd97afb6d8e9d18d61b470e2818a52124aee01e8196a3770615", + "proof_pk_com": "fd70ec5877d0446e5cc9364dad6c2d2d4172b039226ccc74f98a208b8cc78d2800", + "proof_r": "ac535a8c53c7e33fd72116b9c64cce71158e6f9cf15bfe9c574c4a572a04ff4480", + "proof_ok": "9c1555853acb686809d7827afebf6de9c5c98312436410afc272ebb868d8aa5600", + "proof_s": "41a987008d013ebd10030ca5994d85cd8bd957f2940c9c3d015342315b3d4700", + "proof_sb": "573aeaea8072de85eef603a0c44c7f07ec212086cbb91b672e2456fe4a218809", + "ring_pks": "d107238cda2a7b7cf82f192332b7ca9f3e6d312a7b64c5c2f9d37f2e921ecb2e801488cb2f702595906361876a32ef069decb3e05bc2556d3e6366c65470ea9c2f007cb730f1a12a4e73d8923201098902d6a07d2f79e3e13cc347dcf821de6c7260805a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb87700059f34600ce0dcf0f085948d43ba7878fa9b6004c847ad6f9e12681d0b1aefa3a0011328ce6b64179b29e88d5007673c8cbf3e676cfd82492f73dea845aa7dfd86700d0521a11107fc24adce3c0562d01175c650ee091e494ea94e6df25a70213bd3c00b489f39114c2be2d3890a887415377dad3c3fbc6c9e3f4e9d19d77585053950f00", + "ring_pks_com": "97a3298066dc1b2d9b17403a4abbc93c33120e3c67af79ae615de1a1dfb327e6da25da8c6583b6d2cf4d8855403c61b6932ff852225ffadfd8fb0857e5011118736ac3c3e2d1c8cd0aeeff564b4d73f3c3f44d1563fe387e1e548ab62838ec1992e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "8e9177efdb113ef2d96a56fb65e57ddf8a520150b227a649f0b7f5bf754b0c9f63709f8f6f3b7c35a9f658c2aaa6350d9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb5efc1fc797b375945197f9e2e75afbcfa3457611f8da81900461332000fd7dde8ddf42b9bc493256926e390b2f2a8aaa9ed78d7c9ac6a5d8241b92a9f34b84fc1e315806ca43cb95066125837b89eadb18e695405442848075cd717f742e0561d0bce409d51ad3b3d15f35e789a5b6598459e6e7d5589d4adb1966a5a271104dc112a1afd62e4dec6a5e44a98db0a461d3dadfef5b812d1ad17ccb506e51a732db8685141881c40ae3819a72385b178d824c5ff9d09f2e223282c95abd6cb063de44e73367f2e65948a933b88b53e381237895f2212daeb3d718eabe6469e50f8fa7e949e691cfc76c695e3b595df1267fbff4f1058debd5c92172231cafb15b9c807687212cd6d110df0580b4f63ac2aec3acedfc35af696227b204d690e53c2384c4a2cd44ebff3091dd78bdc671fff61e45c249b8d94a85e0a9f29a49c0b8893d2e722c0de2c07768d506702872905e78cac57b8caad891f8ddee1417ee3f003533ec331bddb5d11954c4429272d339a295b45f3543b49f36d0f882c21201b0009c0b2cc00c123e8ea328ad0c0149717bf68d55f3c2c3bd89303ca49a0e04eecc48b3c4514c59794242ef0cd441725f2fac338d1595a1ad49e8e4c261c538c1de5a21b6f021984c4080f97c5e4aa34ed576b6075764d3b38e1aa676059a077101416dc1d6b23c60e8a114a81c0a2", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "0cf83dce52d08b7798eb9144d912b7b31204fe92896af3e792236be115f76919", + "proof_pk_com": "ac3b3a47430e1a5cbe997df2951e5fe13fed96d613522b9ba385b256191ada5400", + "proof_r": "e53915bcfc3aaf3ffde07b0e9d7fbf7d1d7f34bffb8ab82cb827028c596cc70700", + "proof_ok": "6876624d12db6c30c864fdbacb718ec0e6385108b11539f16bf2d3456316d94500", + "proof_s": "a1b971d14062080f7a5ead1ef8a19877823447faa9fdf34141fd9eac6c10531c", + "proof_sb": "53fd9abae2688ddd003f40a79229b4a24790dd02245e0111e676b39ea5424713", + "ring_pks": "19883e70df937bfcfba2d32906da291e47d5d05e436388ae0ae42ba16ae7450880d61d2cb5fce6d53b430cd810f59cddd491e031a9a30a89bcb60b2793a97a77710057378019a19e33eb8259997af049555932e9be2861670e2b0ac328b91961bd6900cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c76170011e948c31d8eb6b01778bff5941a70ea086f7f48a30e08ecc6e94e2b8c942d1e00dbb25acd5139a72f8ac173905670744d97768ba710c6c31f0bcc11edb051723c8075a78410bccb50de3b12f64ccb7bcbd5d5842089adc665d6024e4030d1d9af0f80b531f36d1604d5363dae87ecf6152cc357aaa23ebfa2c9540de345e85e71645e00", + "ring_pks_com": "ac28272b59ace95a3673b382952de47c65afc50e130ede0fbde9623ead9e8c7e6b5b6feb4272b91a94a3fe90e2d12e568b404131dbb727349e84fb1c8f9a6ed4f259256ae712f4774e611247b65f3d2dbf77aa701840fc6867f8edc596d0bdcc92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "98d740fbf01f88496783222e510cf94145bd8f8b9aea5fdf2b363add9d0fb6ad7a68ab6abe59a91a6f69708783f671389107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda80e66b0614769e66d0693aeae1f81e98bb96a540ece93675e3dce9d5884fd1ee76bccd0402ea6c7d0fd8684fe4f53bbb3e323d5ab16d695eb141ce885cecc2dc595c55422dac72fd4ee68f0042df087696e4b7578096cb22e4cbca6dcb22eb131244b62c4a79d6135045171de1480fe3df3af85c333006a378cbdb0e599774f1df460510bb4ff82698643c53fc5a28a2d9573349f5ba24d49f36d9a84e9a022df132d97d3a42ad7c2a9ad5ac5bdfa66f30ba59dad2dbb7c633df4f3d1b61572db43a23e0cd9196fbc3160c9d577706e7b8757f00f8379f5940a7a6c6bd7fc52f8851371880437a9e86ee571d983675c1b530ece1f44e0aed54ad5ddf15419089b2c83557632968970b15ee4ecf56d3bee68f4f7bcfef2ab65531a1bd5e7cd51d2fcd8aeae8426af285690a2ba5b8e82fcbc0012e6742b531b9c5cee24b45873b68500de721c74f7be9e4ce407a2ae0bc3e3185b1bbd3266710a4603d8dca9bdd2eb2b6bf3c8a4019f100aba63a5595552046c7ec7dda27dd8d24bb7b33811a74729a928d163f051f2992d9b5d962c248ee53bc60e89d79fae62e8c0f69a7d3d9d83a812091a584fb8c1d19a2185ebdcb3ae9c85552a4a42c33f44014ed9386fa4aa2831a23ab260a15819ad91701ea13ee4c275bb60564a7c2f2f774c8914134cf5b0f492d8edd48300f42f31fd9a46", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "blinding": "20537ef560d5a8d7483dec55e0b680797376e61d0844bf6971135cd5811ee203", + "proof_pk_com": "1db1d10f797d6208fa724359100b6297c74a98a3f35626ad8dedf891d362ac5780", + "proof_r": "2d6643ffbdc2d4b11024be73349dda34fb1922c7d67a617471bf05ce93f29c1e00", + "proof_ok": "db649772438941f07e1cb4d92ab387aaae22fcb0389d32668a54385a0bd8461580", + "proof_s": "505e46a525c967e840254400b59e75fb40ea21d3c132bcc5032df23bf3dde514", + "proof_sb": "f71544be7ce3ce1a4473d30070c427f84be2f80c080a008c1a4280e40754ff03", + "ring_pks": "19883e70df937bfcfba2d32906da291e47d5d05e436388ae0ae42ba16ae7450880d61d2cb5fce6d53b430cd810f59cddd491e031a9a30a89bcb60b2793a97a77710057378019a19e33eb8259997af049555932e9be2861670e2b0ac328b91961bd6900cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c76170011e948c31d8eb6b01778bff5941a70ea086f7f48a30e08ecc6e94e2b8c942d1e00dbb25acd5139a72f8ac173905670744d97768ba710c6c31f0bcc11edb051723c8075a78410bccb50de3b12f64ccb7bcbd5d5842089adc665d6024e4030d1d9af0f80b531f36d1604d5363dae87ecf6152cc357aaa23ebfa2c9540de345e85e71645e00", + "ring_pks_com": "ac28272b59ace95a3673b382952de47c65afc50e130ede0fbde9623ead9e8c7e6b5b6feb4272b91a94a3fe90e2d12e568b404131dbb727349e84fb1c8f9a6ed4f259256ae712f4774e611247b65f3d2dbf77aa701840fc6867f8edc596d0bdcc92e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "ae666c81112ae53e1f1c99a407ec99101fc065e1ec45bafc7e1cc158fdc0ae20fbefdeb7d9ed20753f65413eff532fa79107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdda8b466de672aae10748e9da7e61ebf22c3bf1bb99f4deb5918aa57b7c302e82a9d2604cddf28eba6f8bdecb5b56c92f7870f54b5dc6d48a661c04fd7b22ade4370b146d9f9190869487064f2c45e55c3c743d85af87d44c6a325d080116ef6d9fbca731b51dcb8953486512ef7ae45d7315b7fcccdb8f044360a79facf157e07468e6893d801a996027d43b88597efef4fc1b97d3f08669a951da599649c0f6486059b6b1995de67335b7c21afa78bec05207c55c6c6716e27ac8ae54238f038bdfe8b917808f0379d099b979441a0b82eb738286e2293e4cc538d3c3c57327054b7f45ead6fb00848834a5061482c90663eaa2f9d386d9154a62573bb9cd80de295b6e3dc8cd775e4fa4bbdc92021cb318d9228d88f361784f78530bae3b54d9c04041522b310d4fbfb4021162424c7909a2acac7c58711231d69bd56ac0728a52061e54318dbd25b32b95b478dbf3331bc501a104b47dcf33b117a0270df9ab559adf01fa04fe8df0ff5d4f14f765d7040daf3a67d6eb707e34af8b28f06acb3143dc353a9a4c908f84ab536a96b26b4ff603449642ed8d952f16981aa3e4d83133f67f4b4453af32554e3e047f0945eb4d940693b9e69da0525d1f344ec3d825b77a3443dd23ae3c531722edd680cd80871752d3f04d31975b6d45218d0e5a528a2721e8a404cf30a5c196a92da5f", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "bandersnatch_sw_sha-512_tai_ring - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "blinding": "2cff0f18f6fa445491223aeff1e117c6180a555aa23dd710f1d785c24abf870a", + "proof_pk_com": "caa63e87aed8ca3880a2d63d97dfb9f47c8add13dbf5fc2c80cf410d7c66ed1500", + "proof_r": "ac7737523563fc44092829f1d20a02c50e02380cefc6c871fd32337bf62da15c80", + "proof_ok": "e8a1911928b58371fe29722b83ac3721bb74e85a5a85f549edcaac082293373780", + "proof_s": "0a228726dd7e3350d84d7b6d6e5f8a0b91cf6860d91157f1612df05a2a8f3c0c", + "proof_sb": "87e7717f25017e1855e30decb2bf83029e8a56fa166872ee9e408a581cb61b0a", + "ring_pks": "730a10d81ce68c343ce661fbc2e967e7f0ee7214757542fc294da5e17769ce2300ba33958c71aaea5cba62057d243886ca5f00ca2ef1707605627d965c9e0bf84f804fc605c3ef299b01d76f5561684ae95ee6972bb435b15a6c26ed0917fc50c93480e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00b04e353b452753712f3d7ce66683541ebeccbf5bf346f815e40372474a1f3169805213841b553883e232db641b3e836d53d5a5d7954e9b232531c23c1253b1bb258072abe896313634f300a0c5232388dc6310d8969c0fc958386a5fd01ef617261f003ec2cb56692e8b855713125ed6a2b53943ad380ff04af107448dd9bc6cfaaf3a80", + "ring_pks_com": "92bbe9677511a1d04ca8f300f000c5e4bf9eecbac6e0af709bfa6f9fad456f6141f8f301ea69ffd72ed51d56cc9394f182856fef4061770e9e14b33d9ce7d8a7d6bcdbe507df9ebcc7c49d016441f01df5fce431cfab70659cb951c6490cecc092e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf", + "ring_proof": "a199113fa3b5e6bf315f600fe31fd8b9f8aff50d9dbd878e277b3d8f443689fdbebc216e7404e8eecb4b6b2e0487bc219107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd90490cfb5cf225366d8bbaa6520149d47dd3f036a1ebfe01c6d379c96055f4fe23e2b615973a24b7002d57885726b8aba2925f7ec404a7c3321e7cd5e9f1670f8fd3a055f314796ae34146d4db5180f3c9caa95d926203194ac81c6642a81c49d155b64921286de0a2e1c945ac53ab4dd601a3a04636d18981954da8dfd0d570358b2e5704b7f785d413c89d9f32a76c9129d4781367f6765f97861a4b36e72322dae30e78fc3e96508f5bf8ea34b0e059dc12fa65944bdff8e9f38604a8d011bd586ab17a2ea5dc4e3135d951b6bccc757e9bf4f7333c2b4f2dd69653337e00ca0ff7e0a548772e9023352b00bb7b8a18b9bc371c6e70aa58a15bec34d5d94982798be042fa9927df38e024c1e2782b654c7e978614594433bc9e7259febd0ef14182e306615d7a3c8ccdfae07efd5aa27eecfbbb274e663d7a9e09dc7bb222b3e2e22d18c7b6d5b4d0f7028b9af777443852b7610df60a3f719bcd176519cbe97e142aa1f3d223e930354fccb2529d03d4ef86598f79a8ee7fcacc2a58788957d963e77b2a991d99f3381fd98f2739a01b5b6c1989d597a6c2255c5a15dd0b96f4239910ad2fc023cec38ee28140ba4e924a86ab3304ca211b994391d08bcea124940df103dbd663019722d87470d8e4828e2014ea190f5b57e62efd836c704eacae53af187cdf878d1a42b3ce5d0f", + "ring_size": 8, + "prover_idx": 3 + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_thin.json b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_thin.json new file mode 100644 index 0000000..aed6e6f --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "proof_r": "410f2976323fbfa8f205f954be49b79f84de6914a8c11274f850c732a78aa22880", + "proof_s": "2cfa234af5add41ef307472982f1bbf5deeb70fae3852cfb8e461a56d2bbc008" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "proof_r": "430e6c38aac86988bc3e67e11472189d0206cb81dbe813410ea4af50b8afad6a80", + "proof_s": "70729a36aa86d143491c1fc5551d7f1507dc2523a3c7accd05defa525a97e018" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "proof_r": "5b738191f00557dac56032e41ce8aacfd9880b5912a64101811192bea5502b4200", + "proof_s": "b605e8b4c8821300a4bfe99ef02cc329680d5a21912f3f23558f6d3e11618606" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "proof_r": "d63b7748e9d71a5b5f5c37040a198011cde8ca1a62883116351f8b300ab2a73400", + "proof_s": "7a6274731ac669ad52d4f91276358e5f9d7445d7d10f3e00e1c8d5a2b7f40905" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_r": "1101599929d24a0273e7691fa1ed8e918136bcee8f921a6b4a254c526232836080", + "proof_s": "ec729e0af8083b5fd051282db89f0d46ea502427f1a4f2f3ea1f2789e0685102" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_r": "5cc14a294f4e6790962cb625006fa8a6272ff5337cdd17cef4579c141130721d80", + "proof_s": "03b440a7cd00f29aa1a9f029ac223b344f6fd0cd0f3f83f43ef537cbc9a4ab12" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_thin - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "proof_r": "033aba18a8cb9eff70792334874589b49589af8616d21ec3ba759d9b3af6d26600", + "proof_s": "710f4329e3516a9b777c3d15987802e783d8f2ee33f8306e0bc6d7599d8ff90a" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_tiny.json b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_tiny.json new file mode 100644 index 0000000..78a22c3 --- /dev/null +++ b/tests/vectors/dot-ring/bandersnatch_sw_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-1", + "sk": "a34b2e0e25e0c94e46558248327a46ffc6a6e9abeffafa531674da7761f1c700", + "pk": "ea19916051e8f699176441e636a03eac86501bfb13b520a3e0f24fd3d6ecec2580", + "alpha": "", + "ad": "", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "286e552e9d1026c501e44856b4f3aa96957f32ed5e34c2e03cee05426ee4486900", + "beta": "7178a83ea9979546bfbceb7bb125d07e74397e133a2dc23ba126d706f9e8c084", + "proof_c": "e9ff8e32c127d5d5c7948d7d61b6fc4b", + "proof_s": "fb2a8c0f65a696a18c2979cc04c228ffadd4b808e4a6a77ed61845eb1762bb13" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-2", + "sk": "930516bb9a8188741111f0c34f6996913ad705abc614831cb092c1a09dbbf919", + "pk": "7b7433d589da0d7c86ac0be95779aebd39b557c88253c759bb1f11491494bf1480", + "alpha": "0a", + "ad": "", + "h": "5b678275c3b8c968df733aa1d908f655f4fb283f766c579cfb6923312380eb0480", + "gamma": "229a28f9e93569ab142bbdaf4621fb05c148aba2072768a3c2aad7e4bf55550f00", + "beta": "1face71acd5f4f4a97295fee1b0eab3b66f2d27db1d5f4da5628b7666c4b09d9", + "proof_c": "83f8c1a4b92cedf08331f336da2bc72f", + "proof_s": "5dcc5b1e9cd455ff5554bde5b4662feb4c5ea605f326eb2587d210c7b38fd514" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-3", + "sk": "162578407cef66316bc4f8b37acd8ffdf8270acd9817c5373bd66779a20ecf1b", + "pk": "5f08b092890bed60b015be1e228d515d1bcf83fe254ae8f2c8621b53e33c836a00", + "alpha": "", + "ad": "0b8c", + "h": "aa1a649e5ec9bb921ef7828fc239f80183027bfd07280737edf6f04974c6750200", + "gamma": "4faf6e1431a078c14be293a344d58067b7bdf62999bddeff7c843145e038522f80", + "beta": "445e8b807d95e09c147b0761a46ff84758f145fa467913e57c0b209e7d744abf", + "proof_c": "e2e6ee29ceacf91640fb722367f2772a", + "proof_s": "3f0dccb7b75c486413ddde92c39cd5f8180843a124e7864c19f69438bdafae09" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-4", + "sk": "17a1ff6e2f640390c0daf68df221212b79f5d5b8c88a610b4ce26489af066b01", + "pk": "5a6d13e3cd5c95285ac88c1e09125426469ed180b6d5e7d63c1b1f49f8cb877000", + "alpha": "73616d706c65", + "ad": "", + "h": "f470cdec5856dc89de0714ad58c34fdaf665a9be69f40e3f3ea11f5df7f3343380", + "gamma": "aefd62fa4370d591105083e066f18ea9c1385b2108ebd8218052f821686fe14480", + "beta": "8cd4da8facab33febd81a501bfd944db3afdec1ba4c5e7015a79851a834b3a4a", + "proof_c": "86fad36b9433fd11ed17618578f29002", + "proof_s": "9b9c235f3b735e05df061b571a7341e0423e9953baa83a5841f29982df6d6908" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-5", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "48bda9ade99f2795e982ee006b9b4bf8", + "proof_s": "58e5b18ce4b907efe1dd0b4a08da77b3bf2008409316dd3cd96fb753e7e0370d" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-6", + "sk": "fd47a91def04dc1fc7492337ea29740bf5cad047be5fcddd60976e55ab062817", + "pk": "cfe9be2fa584b7387a76d4537f81ec1b8a1210a9aac7ae72c87bdfcb4c0c761700", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "570b39f4c9a058ed6c652e4d94a6020e7afe7b14c58c1e67ce28b5894bd9c24080", + "beta": "c7261d5a43ed02273093e89157198efc3d1d3906038bb43ffbb47d1848833545", + "proof_c": "9d2539f65e96ab4879563cf0187e6de5", + "proof_s": "60d99174ebe75354588fc11dbe37a9af5cfb35e8a05cf29dde47ba71e0c3df04" + }, + { + "comment": "bandersnatch_sw_sha-512_tai_tiny - vector-7", + "sk": "f00b6b8332ed0d7d1ca8aaf3a6afcd146b475d43880ff97ebe088ed7d6340601", + "pk": "e690d4b47a99a0434b40217f2228f029a49485874cff87066a822637de41360b00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "673218a4b0b407e8f5c02f3593a80c7bf226fd599e023a5135fe056b29eec01a00", + "gamma": "1db8258d742dcbcc1334322338c68d51cfe24301bdc29cd79f8d01c133cd8f1680", + "beta": "99ac81017e84d4cbc8f0b79dcfe3cc7f8899f8e1b8cf71a5194551dd79771d4b", + "proof_c": "eaec9c0a51b266729e2d095499f79666", + "proof_s": "a022d1eccb90af7f08e68893b38c6dbb54318ea2c8a0dfdff76805a68298bf0f" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/ed25519_sha-512_tai_ietf.json b/tests/vectors/dot-ring/ed25519_sha-512_tai_ietf.json deleted file mode 100644 index b5d884e..0000000 --- a/tests/vectors/dot-ring/ed25519_sha-512_tai_ietf.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "comment": "ed25519_sha-512_tai_ietf - vector-1", - "sk": "e5e07462e979ee9d1f391916b8fbf6d7a0a99077152e45ded1e678afa45dbe05", - "pk": "35421f459ae435f48f87300b8b44430a46c343119cf75363a3e7491b2ac3d7bf", - "alpha": "", - "salt": "", - "ad": "", - "h": "21dc15e10253796df23a7699c8a383ea624cce88c52431f6be220b1a56c8a609", - "gamma": "b500d8fbd29adf2ed03664f44407734c51e6fbc2f58b42318811e50ac11a103b", - "beta": "1f65e2b8d4db3d3c471f08d17309ff6601c2048a13ec3e2f4aec4e79538d35f88affb6851caf73d0386b3c751e8e354ba4692544c6bc112cb4242304885e604b", - "proof_c": "18eff3affa473e2810086d25d045b6a8", - "proof_s": "aa16335815655a5d494c59392bea1e681a0a72907d9affb2567a842ed609b300" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-2", - "sk": "90fc176f8c4c155ea592386d5eed48ce64fddbe8eb2e93039633fefa05d45e01", - "pk": "736c240d9fb667d8e045fde2217b981e41aa2a70bba28ffb95effde6c76090c7", - "alpha": "0a", - "salt": "", - "ad": "", - "h": "d03b43986dc3722f0b9712add9d07b97848c58a74b68173e0bf7d2032922650f", - "gamma": "f83d68c4b39a45f37a48a79ad6f2265b00f3c09ca9953ee6a3789a7f324a2108", - "beta": "4124735efff616f6726f254ac1217579f5c1617f1e28fd91896c81b7becb0b2d64540ee1a86c557b89dacfd940708421e6b6799ae55481da213bf682902f732f", - "proof_c": "5d3c3bb6942b4847ffb8a2d134f74029", - "proof_s": "36a1fa918b8b3a48c324373d0c2f8957f75c992478147c292d90579e2656e308" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-3", - "sk": "b72b97c5d068359b979b0fe62f72be4b2247365d94f80a33cb09c1436a16b105", - "pk": "2fd8787908f2bf080b9b997259828a56946cbd0b976719b205d57c3ce5efb840", - "alpha": "", - "salt": "", - "ad": "0b8c", - "h": "21dc15e10253796df23a7699c8a383ea624cce88c52431f6be220b1a56c8a609", - "gamma": "8abf94498280ef4960d87921cb3e09461ed4b58a7f0cfc441b923b1a6b795501", - "beta": "e0e20305c31f468b90c495fc8609abf616a9f012ef16a0da3dc9554324e18f760099bae2a467bb28de94f579b0889e6dc74c11da0e69f817600b42efcb93cc8b", - "proof_c": "1ccf893b5232d3961912674ccda2819b", - "proof_s": "ae3fa56bb8dee26186917dadc56043037ee1ab61ffb29977de2e9d3cec97bb09" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-4", - "sk": "84fc24244df9159b8ee636561d7c1e639e7e91ee07b506f9c4c438828915560f", - "pk": "41072decad34aebdbe236d361c45e3f863198ab6e9b866dfb973f539b56fc38f", - "alpha": "73616d706c65", - "salt": "", - "ad": "", - "h": "6f014378501567c2f684f197c6a97241ad08c2185bfb37d59ffdb385b22ca627", - "gamma": "5b5591981d23cb48a15509b6586473280d2341f43158793fafcf9539c449eee3", - "beta": "b836e804aa548d0d912eb9f6195bd9a61c3bb3a6b003f7f4dffa94a7f10b01fa55511960d5dad5d05efe4922e87acb9b77321032d5587b61376dc231ec003e78", - "proof_c": "7fa9c505c2668ffd17ee4503a95e663e", - "proof_s": "8d77b128daa76feee51681df64aee60e02d55a999c5abf5634f5b2b1bc325106" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-5", - "sk": "b450f0210ed04d4e4e45f88e0ee6f1efb41a9cbe6ccfdfa96559039f07e3320d", - "pk": "fdcccf6e853cbca63cfde26cb4ef51827df3fb98ff041fdfd2bf9e73958e30b5", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "2ebd33b4a1bf8bfc62a05b5de6c9a4482a5e977c8d0488364d926b72e1b7ae33", - "beta": "1ea69a712aaddf8e2178c79de22ae46a09d7540d3c74cfc9a2677d0b2870908bed005013f97585f4cd5a867e140e0bbad3a5a5a8b8d47a1c0840dbd09bd28ce5", - "proof_c": "9c5de19482849f35ec9f86ea3c1df406", - "proof_s": "a4c79c56b173b606748f6828b9f6d5b937596b9cdb5a4d1ae8ea25d8059a440a" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-6", - "sk": "b450f0210ed04d4e4e45f88e0ee6f1efb41a9cbe6ccfdfa96559039f07e3320d", - "pk": "fdcccf6e853cbca63cfde26cb4ef51827df3fb98ff041fdfd2bf9e73958e30b5", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "2ebd33b4a1bf8bfc62a05b5de6c9a4482a5e977c8d0488364d926b72e1b7ae33", - "beta": "1ea69a712aaddf8e2178c79de22ae46a09d7540d3c74cfc9a2677d0b2870908bed005013f97585f4cd5a867e140e0bbad3a5a5a8b8d47a1c0840dbd09bd28ce5", - "proof_c": "bf435fe51a18056ec8b9c5259b9d1cdb", - "proof_s": "7b35efa4296e669c77957c520356e1f7910d7529535a6cad4277c7be38bddd07" - }, - { - "comment": "ed25519_sha-512_tai_ietf - vector-7", - "sk": "a6b43e79acd386bf397f208979eda435a8763fee2d35f7e06559075288c62300", - "pk": "6c3b89faff8a4b5dfefe8f41aa28cf356ba78197020d61475feb65a62819fe12", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "928e6317ec94fac48fd75172da120cd0a6171f1775a702d0bbd80198feab8508", - "beta": "d44c245da7f6cc4867bffca9114dc255f4bd1fde47ce578f06c10db51bdc7902a2593f311aa73e9dfd1e74fcfb7e1e7184de30be48276bb114201ee305a5d621", - "proof_c": "4f0ddaf0ccdf16f93b8a56a012801fda", - "proof_s": "1df940ec753c1b49667f76a205de2a9244c87f718faa58e44f21f14a68218606" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/ed25519_sha-512_tai_pedersen.json b/tests/vectors/dot-ring/ed25519_sha-512_tai_pedersen.json index 5afaf74..a1c2a2d 100644 --- a/tests/vectors/dot-ring/ed25519_sha-512_tai_pedersen.json +++ b/tests/vectors/dot-ring/ed25519_sha-512_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "ed25519_sha-512_tai_pedersen - vector-1", - "sk": "e5e07462e979ee9d1f391916b8fbf6d7a0a99077152e45ded1e678afa45dbe05", - "pk": "35421f459ae435f48f87300b8b44430a46c343119cf75363a3e7491b2ac3d7bf", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", "alpha": "", - "salt": "", "ad": "", - "h": "21dc15e10253796df23a7699c8a383ea624cce88c52431f6be220b1a56c8a609", - "gamma": "b500d8fbd29adf2ed03664f44407734c51e6fbc2f58b42318811e50ac11a103b", - "beta": "1f65e2b8d4db3d3c471f08d17309ff6601c2048a13ec3e2f4aec4e79538d35f88affb6851caf73d0386b3c751e8e354ba4692544c6bc112cb4242304885e604b", - "blinding": "7f6a06f33f5175096a9efa38fd6f01436857f616e9ef7e07195ba85b6c060806", - "proof_pk_com": "9ab757d231c28ef94c0998ebe88615f5d2203bf0f2843635ee2849154279e9ec", - "proof_r": "b5f293b441beb4e30f5d10d41dadb01e12c3c16453e281ccb517534f91019c81", - "proof_ok": "9d654114021ac326c270c4a4886c66b2033b647894e39d6a4ba4516554635997", - "proof_s": "077ac7976f59a8f10adddb872365d30157e6643c3d8b4882ffc1423cd764680c", - "proof_sb": "d0cf694f4ec2c4dc17a6859c2a6cd272401128969fad2dba815c728547a93a0e" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "blinding": "4a22498de4c3e17d9ddf99ceafd9752bbe301a6510b5645894117cdb5d72e303", + "proof_pk_com": "22f723db530cf1acfa5f34ea78944c357f308423776c1966f13eda6379d107ca", + "proof_r": "6e6e2e0c237f721d70ed57af4ef530ab5cfe6f37520747cdfe9f4d91db5f4e76", + "proof_ok": "3ea1f7d543a114e7d72517754186da044bfa2b25c3df1b9722f29594a9a3427a", + "proof_s": "e8491718dd89be322bcb56780048d3414cde042b88ae278fae074ce6f2d8750a", + "proof_sb": "93edbc92a00957d0ccb6a209737b5b24a89cd0be74ca95476e9a5745184f8d0f" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-2", - "sk": "90fc176f8c4c155ea592386d5eed48ce64fddbe8eb2e93039633fefa05d45e01", - "pk": "736c240d9fb667d8e045fde2217b981e41aa2a70bba28ffb95effde6c76090c7", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", "alpha": "0a", - "salt": "", "ad": "", - "h": "d03b43986dc3722f0b9712add9d07b97848c58a74b68173e0bf7d2032922650f", - "gamma": "f83d68c4b39a45f37a48a79ad6f2265b00f3c09ca9953ee6a3789a7f324a2108", - "beta": "4124735efff616f6726f254ac1217579f5c1617f1e28fd91896c81b7becb0b2d64540ee1a86c557b89dacfd940708421e6b6799ae55481da213bf682902f732f", - "blinding": "9555dcb124ce38d47c9981e2aca0f7e86878893705d07452231f8161b53ba706", - "proof_pk_com": "0e276cf2a21e6f42a4395830f2655b3a62117bedf282fe571df48f21d63ddfd4", - "proof_r": "20a3a10de75f893b7ba9a065e3b7b98d1d5dea53295f45a1ac9222a159baf0e3", - "proof_ok": "c06c0b01bd51d121838a115d3bf6baa205887b2072dbec009af37b7125c990b1", - "proof_s": "cd1a1e32e55d7b19c749f33a1b6ede3c80157f3bb208eeaae5bef4d0b1bec902", - "proof_sb": "17b5f3874f2003380c99f2668b413af1453c9a2b04b1961ce5645c9a221e0f07" + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "blinding": "5d37f7af4f2a830e3da4ffd6435e369c39ac34516b3153cce697a205bed07c00", + "proof_pk_com": "c3bcd67a8d7bbaeaca28b6369df02af694a773f193c2456e10a329f31e8b855f", + "proof_r": "7ba53378c5ba70ac190ddbf40a0acfe9f05b7a22dc8fcd99c0bf40ca7c4cb47c", + "proof_ok": "f912427e0d829d8c31845aaac079b03d6e4c7a4bf46f195b70a9c1508f5a1b5b", + "proof_s": "4bb196ceb832943a136805ca6a4737581e7253f18d7cabf2c6cdee7600ebfc00", + "proof_sb": "07f7f0f937a195a993a182baa296224ad5de5aaf8951c497a89e6f5532b95505" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-3", - "sk": "b72b97c5d068359b979b0fe62f72be4b2247365d94f80a33cb09c1436a16b105", - "pk": "2fd8787908f2bf080b9b997259828a56946cbd0b976719b205d57c3ce5efb840", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "21dc15e10253796df23a7699c8a383ea624cce88c52431f6be220b1a56c8a609", - "gamma": "8abf94498280ef4960d87921cb3e09461ed4b58a7f0cfc441b923b1a6b795501", - "beta": "e0e20305c31f468b90c495fc8609abf616a9f012ef16a0da3dc9554324e18f760099bae2a467bb28de94f579b0889e6dc74c11da0e69f817600b42efcb93cc8b", - "blinding": "1ea0b01c889cd394ebbe3fb5fbed05b179522bd9df7189e96fd54f9524588804", - "proof_pk_com": "cbdaf11c80cf63c489f5febedd345d7f5da35b2b6f32e12dff4bcc658bd7af35", - "proof_r": "0bcc6e8985cdcd991c80127de2a9a6e22f0cb92b789ba8d79ddb6c1bfaa3d736", - "proof_ok": "418e7f39228c91f3cb4a71c45aa938afe33fe9ce28b1b44421bc93934ef26f85", - "proof_s": "8253d6f258ce5fb65c254be102243cb6f48fa2c948b45cefbab066aacae6190b", - "proof_sb": "14057e09f00ae5f0dc3b6692643d845a2dc420232ccefb8719b1ec39f0795203" + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "blinding": "ba0db55791e6749eb315f31da7cfa060035864b1c986b802c4a05e2fb9751c0a", + "proof_pk_com": "1b3a3614beb7112ed8de6b70b22ac56677735749178afb3ed2cbd8019e913060", + "proof_r": "12f7e7f49003ef40c4bc1d699e0d754c0984272728b95309ee57cea8507bb5de", + "proof_ok": "84a33f30613b37fb2c204d949ccecb89f903e5c4011731d037d45b1616dcb811", + "proof_s": "a5674b91eb102a1ce9c3b75525aed367df75b74350bd6258bd829a6001324306", + "proof_sb": "b0b99c1b55379dc8a30ba01d607ce7f31776d5c286384717a3cd4173ae97b00c" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-4", - "sk": "84fc24244df9159b8ee636561d7c1e639e7e91ee07b506f9c4c438828915560f", - "pk": "41072decad34aebdbe236d361c45e3f863198ab6e9b866dfb973f539b56fc38f", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "6f014378501567c2f684f197c6a97241ad08c2185bfb37d59ffdb385b22ca627", - "gamma": "5b5591981d23cb48a15509b6586473280d2341f43158793fafcf9539c449eee3", - "beta": "b836e804aa548d0d912eb9f6195bd9a61c3bb3a6b003f7f4dffa94a7f10b01fa55511960d5dad5d05efe4922e87acb9b77321032d5587b61376dc231ec003e78", - "blinding": "28128c27f15c8bcb34ffa47eee6e678044c2775d42ee3f510de81efc73a46506", - "proof_pk_com": "822d903ca3a6a3f4cd1043fa1e3ada3a131b0db788ffda728b90393b61b49c3e", - "proof_r": "a3787ba683602b8b2c89989cf476ed5fb9015d5873a9b40687b6bfc50cfc2fc4", - "proof_ok": "c8c6b221a83e8bb74c414131fc60d2b9b9af7a5c5e0afdd36fb38c4ab0e98979", - "proof_s": "22cb2b65751a019d2d7e140732bbcf85b49a0f198f2c7b3ca7ebfbb6f31b2805", - "proof_sb": "b68718816ed58d5c2ee516b1da4432739e5b231eb535532df2b9f1655e1cac02" + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "blinding": "2212ac769181892e29a5a249e0e613f739695f917d0e014dfdffb6fdf170b50c", + "proof_pk_com": "0282657fb005dd982950eef22ef5f9ed3d13fb224c983758a9501a62459cc0f2", + "proof_r": "5e49ea3612a52a5a142eb3f47fa35080dae7958ff22c8b54fe3c2de462f4f928", + "proof_ok": "d797517b29708c6c804584407fb4a857f6ffea7046df027cee8adf13dd93191c", + "proof_s": "b0b0ce660112d1b6bfa8d9c5768dc6ca3402d1c45246ccab718032a8b67eb609", + "proof_sb": "776d144386b3c0fe91e6263f433ce3f61ec9135b890b6d6fbe1300bc49634d0c" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-5", - "sk": "b450f0210ed04d4e4e45f88e0ee6f1efb41a9cbe6ccfdfa96559039f07e3320d", - "pk": "fdcccf6e853cbca63cfde26cb4ef51827df3fb98ff041fdfd2bf9e73958e30b5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "2ebd33b4a1bf8bfc62a05b5de6c9a4482a5e977c8d0488364d926b72e1b7ae33", - "beta": "1ea69a712aaddf8e2178c79de22ae46a09d7540d3c74cfc9a2677d0b2870908bed005013f97585f4cd5a867e140e0bbad3a5a5a8b8d47a1c0840dbd09bd28ce5", - "blinding": "6f4e64bff17299d3782a99728d2408a52d044cbe8b88418dbfe92c711b595808", - "proof_pk_com": "88233ddc83a6511d3abae783efb08cebbe768b2c2d55c3c60cbb67ac2911c151", - "proof_r": "908498a0e393ea85ae0b973d6471fe5ed95c163416be17f061ee5ded3f4445a6", - "proof_ok": "b157b6db04c6d98044e3bf7e9bbb5f80ba6f05e8b2fa891556a82648f2b10bb7", - "proof_s": "ddefc61dcac68f68570bde8af86f7c3d5df9c8fa40a62110160b0c8f8f40b104", - "proof_sb": "0859ad3f780d4b375603add075428b1b7c0faf5537809dd0a36573a93dba0307" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "031707f37dfe8ef4837a376320d6420e71724c66d0d8391746228d19ef6bc70b", + "proof_pk_com": "7344a2db6d51163b1d8042f11258f230af3adb0722eb228246c9e8951a5b68c2", + "proof_r": "2471ecf11e758e80a8854f9811b323bfe579cbbfba0234d1ff26839c38606d16", + "proof_ok": "80019d3fc955d254a092014358c71cfc57e60fa543ad3f0c5e1526b88c3b33d6", + "proof_s": "cde860d02df38ecc5241a308c0dcb32273a514b74cdec679f48eb6097dfe2000", + "proof_sb": "4e6a6c18bf9ec98eaafb16d958539edb29a924762773818042ab7614c75d2507" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-6", - "sk": "b450f0210ed04d4e4e45f88e0ee6f1efb41a9cbe6ccfdfa96559039f07e3320d", - "pk": "fdcccf6e853cbca63cfde26cb4ef51827df3fb98ff041fdfd2bf9e73958e30b5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "2ebd33b4a1bf8bfc62a05b5de6c9a4482a5e977c8d0488364d926b72e1b7ae33", - "beta": "1ea69a712aaddf8e2178c79de22ae46a09d7540d3c74cfc9a2677d0b2870908bed005013f97585f4cd5a867e140e0bbad3a5a5a8b8d47a1c0840dbd09bd28ce5", - "blinding": "597e8a42cdfc9401a3183e3f4760052c181c31b6670c4fc6373dd34c267c9d0c", - "proof_pk_com": "6cac8e9bb47b3e36615c0447c8f786963f183f8154104ed7ac5424986962637d", - "proof_r": "a8980a5ff70b3bac7c0877cb479964c7cd20be03319bc97d0fe846642294ddb4", - "proof_ok": "b157b6db04c6d98044e3bf7e9bbb5f80ba6f05e8b2fa891556a82648f2b10bb7", - "proof_s": "91ee8b6383610af8a13fa9669106a0b14e16d2d9f41467cdb79ce79c42260305", - "proof_sb": "24f904ab45b72795bcc1b9929fe2721c26ac6741abff88b7a01ddd21b655e00d" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "blinding": "6f4e473d9391539e5bc21696a434287db0df24f0630de99f22c238f94eb32b04", + "proof_pk_com": "5b3a2e50f9711c889e7b97cebc91c11c1d038cf1f0e59017fe2c40671203982b", + "proof_r": "a8f57c79b2bfc5f2e33739ae2d7100d299b032379b014ce5fe671e3e18c1a0a0", + "proof_ok": "a446fd73943c1138dd2ba4433596287c0dc72e09953f412d0e34a87606b34c80", + "proof_s": "a9d177a177cf3bb87411def3a5fcad728f06f98a910c8b31fd8dca45be455206", + "proof_sb": "27da39875b79eb36e38359d483cba9396f726092567de795020a5fb7c83bde01" }, { "comment": "ed25519_sha-512_tai_pedersen - vector-7", - "sk": "a6b43e79acd386bf397f208979eda435a8763fee2d35f7e06559075288c62300", - "pk": "6c3b89faff8a4b5dfefe8f41aa28cf356ba78197020d61475feb65a62819fe12", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "207c492a4fac9abda9e98875891f1ba2135b065706696b852516a5d7b55a5ca1", - "gamma": "928e6317ec94fac48fd75172da120cd0a6171f1775a702d0bbd80198feab8508", - "beta": "d44c245da7f6cc4867bffca9114dc255f4bd1fde47ce578f06c10db51bdc7902a2593f311aa73e9dfd1e74fcfb7e1e7184de30be48276bb114201ee305a5d621", - "blinding": "b0b7a867260f9bf4401479da25bf2ed5e64ec2d94af450f9e9a8072eae9c6d04", - "proof_pk_com": "746b88ff4dfc33ef34ec68d7d546fb943032a620c4fe3f47e9228a4d09b76f18", - "proof_r": "770f8bf8c8522b8567eb568d9e414a96d755367025654a7f865b7fde9a598d0b", - "proof_ok": "5ddc291a5d991412cfa143ee5dc1f83134ecc5c436dfce3e0ff6cc15abaefc6f", - "proof_s": "6d368e461e267c6e766e056e17a5b187b2828f682a9dd17731b150d45f193209", - "proof_sb": "8a04fff20f06c46fa7be1ce969f0b7794c6ba70d68bb40d992a24efbc5a98304" + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "blinding": "dbdd83ee1fff976aebcc282d42398b7d9dd7b94d1e7af4621279f6df69de180c", + "proof_pk_com": "cea64ca9f0ba93036b4f3c397a263d70d888f2c250637d9b9db492dd8a25d85d", + "proof_r": "1e9493b522e564b6eefba836da96f6c3ce17bd2c36d247776e50ee2d66c0a6a1", + "proof_ok": "075ddb875f12b6db5fe2eb75593c1e88d46b1e645e8144651d59763e4844f031", + "proof_s": "323a0d6cea3affaca38718015d313e1901744c592573732a58c4964d1eadb90f", + "proof_sb": "1174ba0758094f616888a442f1208a36f55db9bf869e44404445da277758d005" } ] \ No newline at end of file diff --git a/tests/vectors/dot-ring/ed25519_sha-512_tai_thin.json b/tests/vectors/dot-ring/ed25519_sha-512_tai_thin.json new file mode 100644 index 0000000..9b1c520 --- /dev/null +++ b/tests/vectors/dot-ring/ed25519_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "ed25519_sha-512_tai_thin - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", + "alpha": "", + "ad": "", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "proof_r": "a010ef8b17cef692e4adf9390344dcd8712e5d757d8a90a13aa98bb64940331c", + "proof_s": "0db90352e13f441815b9ad13a9039984e3033c713f74cc508aecf27a68f17400" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", + "alpha": "0a", + "ad": "", + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "proof_r": "be988d69654aba82f451e0a2e92b5952819be26dc9d760cd43aadd074bc89048", + "proof_s": "7f589707c3112a9a8eff9506c45f72a6ece2f77ccce21df3a3751c5e90ddd10f" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", + "alpha": "", + "ad": "0b8c", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "proof_r": "bf51e65697f85ca3730c71c4f35341dc26a9419ca3b958495bb110dca1cf52ae", + "proof_s": "5dd7557a170fdee82e6125c540097a8e86f461037dd13d6e3bdb9b1d3de4ee08" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", + "alpha": "73616d706c65", + "ad": "", + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "proof_r": "51a2ccefe2d4e0ec3e37b6ea914f0531c2cb5c746edd23007c3af0baeb368061", + "proof_s": "425fb6df6637738889ac1508d78d91460a100cb474c9a5b167e29474867a970f" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_r": "90262f7a47c165dfc375c40a34de2b31511f5864bfccf386da6229bf5194dc04", + "proof_s": "85d34aa12673ac6c0b42eb9420b1b2d437d589adecef8c461c055e6212b30c0c" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_r": "fdaf0be7ba509458d79e5a4ee97a91dece316038b3566a11bfdc2da017df8605", + "proof_s": "587840eb4c5685cc93bd6c50dc7ea43010015d37f76242d63acef2805c6adf08" + }, + { + "comment": "ed25519_sha-512_tai_thin - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "proof_r": "47f1e3c256b03929a9649459e17127bcdbaddaf59fd2da16cb14b681a9bfa260", + "proof_s": "8fbd55ac1464578e7d95ae21bb8baa1da484c2e4c82b6bd3fa89c49a1eacd304" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/ed25519_sha-512_tai_tiny.json b/tests/vectors/dot-ring/ed25519_sha-512_tai_tiny.json new file mode 100644 index 0000000..ed31fdc --- /dev/null +++ b/tests/vectors/dot-ring/ed25519_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "ed25519_sha-512_tai_tiny - vector-1", + "sk": "afad084b806c150bb1a82c8bbfe536bec74d5ba07a073a1f5d6f2257dabb9b02", + "pk": "5becb598cd8039adafb423320454789b976bd66710333a8c72d372ad9d6abbab", + "alpha": "", + "ad": "", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "0c2fc1052c5b58971870651884039331067a3a034ea72883e2d66e381e885211", + "beta": "67f1ee48a2e26322e9e62f4bd5bb42d8de8356d3ccc577b957829255a498995e", + "proof_c": "44444e2aaf9b5e7ad9b3c0758912f996", + "proof_s": "247b1bc06e2c5ecc8d85f5f185b6e0e6e8f5381c1a4fb59b4cfdfb5c63f97907" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-2", + "sk": "75cee60c135180ac460ecd1980e5ffc98a71e29a37f39296e77aa86b3007d909", + "pk": "cef24e430c3bd24aae46aede97881f6907aaddc780137e52a68c4c5fc26c7651", + "alpha": "0a", + "ad": "", + "h": "ec4e5ae3986b297162e32400cfe57d52982d50b1aa8cdd5ac610242017f8bae0", + "gamma": "629b06467514cec46075885532029d6f9d9fdcebf216a2ac657d746491df7410", + "beta": "54b6d4f4a00b8d573532d1172c759624d7d4ff24eb303e7fa22c4580b0408b67", + "proof_c": "a8eea0031ab89afa3840ebd28f1d4526", + "proof_s": "b67d2093dd5aabe794c0c8e5c8a8b2791a44f7714237859cd414db1ba830c202" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-3", + "sk": "5fc138201144a1045d5ad0b4ea85035d287f6af78605d32ed8579b001f97bf07", + "pk": "65dda9990be1c09262b2566cde138af148a5dccce7cc04e0577547c13000297c", + "alpha": "", + "ad": "0b8c", + "h": "bd2fc4661bec75d8f85cf5ed7c1addc943ec02a03fe3758f37d2e6a326ffaaf6", + "gamma": "6f3379ab2a63f5d5bdbc107e5cca039ef4a0e297609f968fd0bcdd68778c0100", + "beta": "9e310a151b263166e3141a6e7658fc3e8f155ea41602e417cdca43e3dac492a7", + "proof_c": "88401f18baf04278e73ca352eec5805e", + "proof_s": "13652f819865e7d994dcc8e226a92653e16fa563fbc250766b51093d5b13c602" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-4", + "sk": "c22f7ccfff46f7ad5aa12e3def91e9e170616b9a96e3c4fd1e15f8fa5294f70b", + "pk": "6dbc711c610519f7b9cc954bb555d09dd274f079cb7f8e1acdde6eb6cf066fef", + "alpha": "73616d706c65", + "ad": "", + "h": "c3716fd090a0dc610f7e8c9f974d53f4f9ab1fd3218bbb3710430dee3dbcff23", + "gamma": "6a8e9c4487fa9ffefafddd23d97e53be365c0eb062aa77bd7b249c3c542317a7", + "beta": "e49921eb9901cd54d0bc96b7017e9e3907344f22b8cc58f55ea6ac0aefbc5b55", + "proof_c": "c30ac26f9961ec5cd94661325db6d803", + "proof_s": "52eb8200c459cedc4ceb13d55df4812f456add6f02b795b6f1424a04f34edb0b" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-5", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "c260de688db7869e58a9de51347045da", + "proof_s": "0b4440c53777ba1ef38e53341bc1a9e53af067c799c589e34898cf8f2fbd2c07" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-6", + "sk": "9b70cf364ad0c6c4a3f0db7bbcf8d47ed8424bc7e4fd1ba20ee99c934a8c2204", + "pk": "f1f84e5c1ada1a1e5060f25f3f59865f658a4569732a51761b70eced68dc63bd", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "1faca6dfd86e8f054840d1e8deebd7d0cbeee9956236d6fdfdb4e990db93bdaf", + "beta": "83c6dc7f94a29c1c62a863b3deb26c39ee6c2529e6a0ef646e63dbc75ae7b848", + "proof_c": "82d8649618791877c054a7f0613aca57", + "proof_s": "b8915a96ffbf0fb91a960c7ebdc51c9ce9aad0f3fdbca6d660b49b389c389f03" + }, + { + "comment": "ed25519_sha-512_tai_tiny - vector-7", + "sk": "ddbd63616b12061ee62e6cb8421c17b3cf0b0292b7ffd2e0dfef42ddb2755905", + "pk": "7c78f01cb8d169301d94725fbbc90e8b462b1b6ebd55fa1fc00c3eb6c8984d63", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "5efa4e1723c2b407b7636d47fa439cb15a9e2d93804eb5288b472beb3419133c", + "gamma": "50e95914de34f6c771a1075a6465f0389f214ec9190470103db4dd1f389bddaf", + "beta": "884bb2b3a90bcd3190e676d08616339e13784890929940d791e49c2f6ba9453a", + "proof_c": "bd260e6063fdb6f89edb964196a89879", + "proof_s": "68633c206bbd865be1bc6ce6f5f32c3c0f6bae6d74a95f493a3f4d4485f0c70e" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/jubjub_sha-512_tai_pedersen.json b/tests/vectors/dot-ring/jubjub_sha-512_tai_pedersen.json new file mode 100644 index 0000000..546b94d --- /dev/null +++ b/tests/vectors/dot-ring/jubjub_sha-512_tai_pedersen.json @@ -0,0 +1,114 @@ +[ + { + "comment": "jubjub_sha-512_tai_pedersen - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "blinding": "b411f199a190e143365b0100b6056affb910d956cbe203fa1f9d9ca7eba93b0c", + "proof_pk_com": "aa37fb22978457ede1d5c9a1cc146ee3fb16eaecd4badcffe9340dc3f1e1e19f", + "proof_r": "e0af508af99628925dc9b548430cd25c8580ce36df323dbc1822ae3297aa069f", + "proof_ok": "e34616acb7f465489575355fc52b58e2a846c00f976c442975571d1dc9679442", + "proof_s": "2918db8cf1d38144ddfff87cc170b5bcfbeabf421fe0f29a66529fb5b0257405", + "proof_sb": "805862bf392db5499502dbaa8e92e4b1f8d65f43de4b3012290e38d5e3756208" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "blinding": "84616b1b767b43ff53b1902474b9deca4416b45aa488928fe128719555505708", + "proof_pk_com": "138f9a70acc072087cd47ef5dab86b120636bcfb6a8e3879d46d614d2736c844", + "proof_r": "17ab4dbd47f7889cfb50e82594b38f5b24b6fe90517f2a11d3164932c7ea0f53", + "proof_ok": "005df5bc38e095b575f4e80307e92bf493dd9e6931b3eac1e86b8fa66e8d8773", + "proof_s": "fd721148c4808f0a518560eae6f7f7ff757f890855ac63d5c8346240aec3490c", + "proof_sb": "3559963241ea508e2deb30ceb9dc77b07c1a599604e1b8696b6d8d6839529f0a" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "blinding": "86f342277fe5f88c92337b4e79594c6335dee5868b0a5c4542a0321dfa4fa608", + "proof_pk_com": "7536c30b71337821df6afa9887e769687ed89759aaa7f147898921ac6ac31b47", + "proof_r": "5d9cc07b9b9a98c193cdfe58e9e0e550cb26210ecf435ce1ad76c59511613234", + "proof_ok": "ec71d389fda2e9ee50c61b91acc704c5348039b47872419dadc9a25cb6ef8693", + "proof_s": "743e695720f3ceb5b52c978f5be8ecac8bd4389ff8c83e91a2a572df3af9ce0d", + "proof_sb": "a5e34e4383a074a21bbe09cef2d0424e8030f8e9543d45e02b6a3d6ed5d6e80b" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "blinding": "f2ac62314b4e1a23394c6547807a4ed6f658c056c2f13119f72562b45d324f03", + "proof_pk_com": "f0118d7b17f69f42a238d8f7ffd1b1d8c05a85cbe0c324da60ff2cb60b10c705", + "proof_r": "9bf9f2d0045fcaa887a9e31c852ccaec583df32bcb82a722dd1ce7cf106099ed", + "proof_ok": "c28d503817a895359e8d86c4dcdc5bfeeb02845936b05d6b01022d54a5104fbf", + "proof_s": "15a2b2036994955aebede90b405f0a5971807235a429390e48f2f851c1a61a0b", + "proof_sb": "bf72d5850b00d002707e2300a27801cc7f22c84fe6e1053cef66e19a4e98e103" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "24ca3f6a30adddbec78a9e468f1eb09f214f06d086db71abea944f70555e0d03", + "proof_pk_com": "1ff2f4afef54d46de90292564b60c454a48132c806168a976061aa6223d35972", + "proof_r": "aa5c6254903a5050350581c1a9a54589cbb108cea0d4043a189fe6fc8c215904", + "proof_ok": "3b7a223f757f8fd4eeedd1fe28eb72852d4f007f383733f960726c4bcfcf9792", + "proof_s": "f4437660312f5331dabf71b3003e012fa6857bfcebc3b9bd84b734fbfd68ee06", + "proof_sb": "672158bac1e24e831816e417cf692452c8305f1b42a8f23eac4a49d10051d20b" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "187e68d7c3d3664eada848c3edfc19defe50ee099eb430cea8933b67a6d7440e", + "proof_pk_com": "bfd25e18e4818ac6cdf07336c162412c6a07770cb2d9598bb9b3c4338f889e71", + "proof_r": "ee9ed1962f50ef09495cf5ed0cf0a8fb90f0fd70437d1e5a9f918ee1112f56d3", + "proof_ok": "c7473bff4b6ca801c0d4f0da828c20257dea8d7f39c7b8e2e499db5e791c3091", + "proof_s": "5dce37237fc16f96b2f850121e65711092c0ca1a78a68034876717f7e95ef704", + "proof_sb": "c77bbefd18f04f6a65ebd6a6f43e3a88416069e84a89893923d57c29d5835707" + }, + { + "comment": "jubjub_sha-512_tai_pedersen - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "blinding": "cc07c57642d40b84be0c2741dff44254f2ebe46d506f6a19e6c5499edca31300", + "proof_pk_com": "2556f967c10bf28e415e2edcf6c0df0f02373117a00c4abd4f59690906348e5d", + "proof_r": "662b1a6a1acd770ebd8e78893cbdc0ba2b22d3377a4665bb2dde06bf0f5f7dbe", + "proof_ok": "c758b260c926cc5bc12c6d0532ed451bcdbe877e094fa065c906743cb0f5bea4", + "proof_s": "e444981f96a004a00d09ddb83c284468ed23107a6d92f20467427aa3caa7470e", + "proof_sb": "99c41f609962f251be9de67860727d57bca0d758fb0bc45ef07bd8d24c7ffa05" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/jubjub_sha-512_tai_ring.json b/tests/vectors/dot-ring/jubjub_sha-512_tai_ring.json new file mode 100644 index 0000000..c68bb74 --- /dev/null +++ b/tests/vectors/dot-ring/jubjub_sha-512_tai_ring.json @@ -0,0 +1,149 @@ +[ + { + "comment": "jubjub_sha-512_tai_ring - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "blinding": "b411f199a190e143365b0100b6056affb910d956cbe203fa1f9d9ca7eba93b0c", + "proof_pk_com": "aa37fb22978457ede1d5c9a1cc146ee3fb16eaecd4badcffe9340dc3f1e1e19f", + "proof_r": "e0af508af99628925dc9b548430cd25c8580ce36df323dbc1822ae3297aa069f", + "proof_ok": "e34616acb7f465489575355fc52b58e2a846c00f976c442975571d1dc9679442", + "proof_s": "2918db8cf1d38144ddfff87cc170b5bcfbeabf421fe0f29a66529fb5b0257405", + "proof_sb": "805862bf392db5499502dbaa8e92e4b1f8d65f43de4b3012290e38d5e3756208", + "ring_pks": "947176f65ec527784f723c96428114f6368fd1e97266618f4beafcaf7e4fe6264da353cab18b51a9841e6dbca069268107f545d01de3df74dd7329efa0761b3757ecf565eac553d35cae78f5a72d6071acce2daaeaa0758e3527e3ac86abfd86a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2eaa2e18eeb8dcce9ce157ba696f8ded806770d1706606235799d674b3e5acc324dfea4259a7c8421d544ddf749079d16ca071250ba055b32ccafee69feab20d928d14cd152f893ac16e16aa2a4aab1a45866e050bf451143b2415246e72687f66901446743328d2d719ea41b6ec31facc1f318091adcfab34d234eeb2ec9d4832", + "ring_pks_com": "a16ce0b1eaacb51d7d7f1e5f426c62e10a2d8458c0a80119fdda2934ebdc13ae4a5baf6f029ea807db763720ea92844eadff3c4e17d60e24b0ae8baaa95de03d01b2a4f3480ca15b57f53a6b7c36e9aa8f72c55a00ddb8d360fa1edf6db792e7a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b8c827f20e69575051dc213a68b75f88a3d90d8b7dac9651d9b57d37c33c201ce251ded3c1bf53f0dc40f8690efc5ce89107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd870bc74f4428da688ab8bdc336b514b07f7a33d6a5d1b04fc949296e346c1c63b8eaa9baf148914752c114f9d5895281989d253d23f2331db3d341dd7182ac6fd2a557e7911bc91efab62999f9dcaa6e14a780abe866c1ef022cc850e88f2d881829047758011f6c40812cb3908624f3160ae712ca8ab2f382d190615279394d7f4de82e1fca50a3043d11a0c75c0e92f3e762473a8f7dbf2b70509a1a8ad81233749ea7503eeb00a5dcf64b28e4d57e06e314e4573358c137bc9e724f6f435cc80197d19cac0815b6c98d862b1724a234d0050574385428ca06fba472ac0c0014ef811501e6a27192c7b6f1dc44c6842a6af9ef38ec9985381c1e51e58581265adda8ee41da716abe4b1d9e2c624a5cd6bea093acfd9b57359f9f706b7b764e344239d720f939d99cba5349caee2c61f06495b486fd2c2ee656214f1edd050581beb8f5fd24fc8bef9d13059e686236008dc7660d52b7fd505105bc1fa4935404904ba7f72874bdb39df797201cc7cd78e129faacead42b8cfe9c9bf56147b9cc18629a927aef5cf58b3a2fa1f7843ba4385227aa1819c343049d01d929ec329bb44a6859edfec835894826de95f0fe586ae3c121ddb5649160860f51988bd898e09d494ac66e876944d9e7f71734ba8e1dc68a4897b80df8f36b9524a71078d5642fa506e2ba8a9c105ba432489d33", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "blinding": "84616b1b767b43ff53b1902474b9deca4416b45aa488928fe128719555505708", + "proof_pk_com": "138f9a70acc072087cd47ef5dab86b120636bcfb6a8e3879d46d614d2736c844", + "proof_r": "17ab4dbd47f7889cfb50e82594b38f5b24b6fe90517f2a11d3164932c7ea0f53", + "proof_ok": "005df5bc38e095b575f4e80307e92bf493dd9e6931b3eac1e86b8fa66e8d8773", + "proof_s": "fd721148c4808f0a518560eae6f7f7ff757f890855ac63d5c8346240aec3490c", + "proof_sb": "3559963241ea508e2deb30ceb9dc77b07c1a599604e1b8696b6d8d6839529f0a", + "ring_pks": "16be802c6f2315d43778e2f405388ef08c3a46589268a3b469ce365e23f22a98b8a71a7b5c4a83d377f4c3982854308a62f805d99fe970b77e179894b3285743d64143388a09164a0ba01bc0c10a39eef0a30b4bae8019ffa9ae894b249b8a20b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc14b03bca9c96a06e9f080983739933ef160e4ac40cbb7246b75fde7a7e2319056d35974ac19d8705f741f004f04385358ef4f7117fde349861095d8c800b9d39da5e99f3b1c9a12264366f6a851ad5d73f63c15ad3d33116e57e02bc380ade650dd1ed49fbff787ef66e6675298c29c5daf646a581c0c0a3ee0c28f6761c2426", + "ring_pks_com": "acef61bf00a2e61d9261354a0f945b344e2832efd3961fd150cac68b7c0fdee2d85a52deef6a55e54e6c46a38a634ffab534eef7ea8ddc54401153673372c3db89946638b61eae8d209b851a95b6856b8d305fbc28d55c89a5787de17ebaede3a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "a7efcea88a8011b720952e2fe243aa74a7cc4fefcc7037c35ec873639b744013182bfbe2867d422381d0fd99154971769107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddaafe8bba5d293cd32b3fdb9a20f49ff37c877f3adf75f55e5857039cd95613b08cedceda13970ff86c27175259de551d984734beccc780c397f31879f21ce773b43eec89bcdb31a445808cff7af303d2893227370ce1f0eb31e9aff7345bca14d376959de646d0b7c15ad0700bbdefc00225879fa446046372d594a462312e358bd0600db56a503c37fa09f245d98b709809a2a2538c505012eb94f020f5982db46202246dbc7cb813325bbb21eabf3cc3e45b4db31568354054223ce96f831630d2b5874fe0d1965945b9c3056a6fed2ae679a60ad22e5dc1d609a5a56a8c3a9131a96334887dfe68821d40d801a9458973b91e9a62c11cef8b3affdc298f583ffe792a16c3776be5c4ef3cdf997721dea0bd4788db53330e10c0185b48cc6da5202c727eadf22084e58da1d1794c26f68aca0bc9b4b3c5167db982a2995a33a2d4e939b21be372d407cc5ae0fdb71af0c50b525544d8e9cd5eb8189c126c562c6a68b2e805a381b6b6bd451499993e343d666a3844a5f716c2a4e73f136d9f883ad89b1a05123a5ddf6d5c3906f2529480349a4d8c23d95f4a66cd8f8d0acb4f2e891328dde672fd6eff00305757c68f84d3624bb113b1ab2d64a82be5023fa6f6d5d90a2b717cd26ff18e46a937a8817c7e74045e6107baea0f8125493277f88e6655ccd8931c01511f8fa2f52dd5", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "blinding": "86f342277fe5f88c92337b4e79594c6335dee5868b0a5c4542a0321dfa4fa608", + "proof_pk_com": "7536c30b71337821df6afa9887e769687ed89759aaa7f147898921ac6ac31b47", + "proof_r": "5d9cc07b9b9a98c193cdfe58e9e0e550cb26210ecf435ce1ad76c59511613234", + "proof_ok": "ec71d389fda2e9ee50c61b91acc704c5348039b47872419dadc9a25cb6ef8693", + "proof_s": "743e695720f3ceb5b52c978f5be8ecac8bd4389ff8c83e91a2a572df3af9ce0d", + "proof_sb": "a5e34e4383a074a21bbe09cef2d0424e8030f8e9543d45e02b6a3d6ed5d6e80b", + "ring_pks": "497e49d88f32452702b4edeb32fff72a83af53faf5ce983ec5de9359d432df4ed77457ff91c350e1e62471e0d51f62824330a43de2e1f1279a3e5acebe9602c813ce542d9b9981462b7a96a16ade16a25ac860a355162f07217ffec74b8e3989460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3bf88871df4bc748ac860f3afd0b719ab5a702ab0cbea4a198f8b1d7183d75e403ee3cc143e540ce996bb1d63887b26d793d332fe464e8520c890ff74cfca20a1eb97aacde8134940bd9e3c2dedaaf701011e2dd3de9155b765d17913400cdb3a0392227168abead686db0f13a20968e35af3a21d01e7d51c4829789f250af2c03", + "ring_pks_com": "afebb6ba2c8d9abd7a091bc3eb1cd89256c60540bd677126f6277afb1102cb581d24316d3a48f70d9846d47eb3234ee8a384beecc2d38c797f80c7e1b7da04c8fbddd63aa761f351e90b35d75f8fc8ac09f07c5546f03e83338d889978c7e7c8a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "94aae3648d69b05b136c59c8391f6a0a8d8f138e591ec254d9096da442bf7369e3101787599aedc87b0415b87b094ef59107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb4de0636b732197770d915eba2b54cbd02d48862ff87907f921b62166035a37d8e27f08afad9b3f234314f91d2a354bf84018eb9116a53e45a490b7101ac40bec41b3d353726d07bed987f8500f6c8fd1989f413b7de9a29a5386a924492dadeb8b4fd5dcbaec201ad5b2a0341e7f4a1beaed3682d54787da95cb32b311459563c1e7e1622f9f2a8e3bdb890816679ff3558d41a6ab7dffc6a7e2bfb1f6f2c327995bda3f1d7e56b0c0b4dc329ed04062f45971e46795e6a1b7f6b0e4db186408a2f5c1f6e4ee1dd38920cb646234736b092b6fc74d2bdaefebc43792e509325515b32883a7b360f08378faa96acaaaf82a8a5d44de730dc82e256d0762fbc013e3350a9c8b6604bd4c250450b71bff03375c5cee097b373ba4edc17c1a45643c0b0ca0693f008a1d52c50203ac2953f954ce29bd01ca7c09ec5e594d5072135a11116ecabfa34ae13bd3119b0948873fecd6c73efc7d8e186b13f1dcad6a5c52ff393d5dbfee75e20d27ef2ebd896ed6ced33b4c59e43b4de2a4eff43bad62d5de45ff69dbe4b372e76835a2603c36fb9449b7ee9fd78253a2fd1ba97142f85b9b29a2c5c73db794ebd20aa8b64c4153c787f5f384f52a09aa632cab498fc6ea5ae99fc80b04f2fda7c1ed9d73fcb25975dd9a22613baf2acc6a059274a5f0f057b9df7deb040bf7081f7e79d611db1", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "blinding": "f2ac62314b4e1a23394c6547807a4ed6f658c056c2f13119f72562b45d324f03", + "proof_pk_com": "f0118d7b17f69f42a238d8f7ffd1b1d8c05a85cbe0c324da60ff2cb60b10c705", + "proof_r": "9bf9f2d0045fcaa887a9e31c852ccaec583df32bcb82a722dd1ce7cf106099ed", + "proof_ok": "c28d503817a895359e8d86c4dcdc5bfeeb02845936b05d6b01022d54a5104fbf", + "proof_s": "15a2b2036994955aebede90b405f0a5971807235a429390e48f2f851c1a61a0b", + "proof_sb": "bf72d5850b00d002707e2300a27801cc7f22c84fe6e1053cef66e19a4e98e103", + "ring_pks": "1a2bd1e1ff35b6fb7b6cbe31aedcdae65393766c2396add21b0a7a637cc3732815619ba38ce2acdb436ed3349386addec8fc606d4a40ff04756c443c302a0f00858941ea2ac7d037003e68326a9b0b950f739c6c3534f15880b13429f6c406a2aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f16e6921b9c41c15c5d5d5deedea3d40155352946244fc6523783667aa1e6a7a5db4ec78183570a161f9f96e309ea0577058b0ce80cbf807fff72d4759ec42e6cf5abde2bf441788177b39911a8d9078a56269201e51edf1c77e07514db8245697e1586d2031a9b0efdd39c5cc345593d833d065f7d1aa95e0bd1aef95fb347a5", + "ring_pks_com": "a5b6b0ba00cc7931341b03afbf98dc6798d06891667b2ae33c4093bd4b5cc236cadd8d68dfc5db0e60efd0a74a8fe616b0dd2399320255e950fa1ed93484eeeadbcecbca4e4ca0117d01dc55efdfbfb9f30f9c9fc52706eb9061379169d9d127a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b14c815249e440c2408945b62a84dbc2a84d2f490edcfbf345697fbb0646a74a54c67d07c0e7e7a04637dc78fb22ce3c9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd9922a6bd9cd97ad6f4eb9f2352ca952ced064f866da1929eff978295ac897d5f42a3d9fea7f3af0a40216bcbbcc66903abfae29f22a6d8dd08a14ff7afa21c640a57b690d50d7ac73371e06a7aaee1cd9010b6a1d0de92f4357c33012231817fea2f08b660ac11951c57e9ac3132f8e1c1a559b1f7878119436ee97e39e992288fd5209938fdeeb6a3f6260fa083490b38674fc5f9f714e7800c0437be33102977cd52fb39da461a9a08c11142be467982fc3d98dc69c663cea53037af344f58aca86d91e10c5c025b306e754415f79c0d3c6dcf55ebb7252bbbdddc708f3e3c472d2e3248581853b6036c076b3c15de5f1aacfdee36cab6787bb0ffd3558f102c75b77e23f114c28e4a1d72aa20ecf5cdb24c9a9700aba2f50260b1d9abe732d2e657ded6fbe0511e7893a18bd622cf40034c4a7b11b3a43dbdf44ba7dcfc43805936559a3154d27dee15731030d8d775c1f8852b675c94b10147cb3dc36f297859edb0da6088b07ee7a00d4834bd83963f998c3ecfe795b0f372e4c2ce4ec977587908bfa559a10db8b9e06d1c643991aee19c1073f7408658dbc05c662adad0c29f74fef276897cc448ac4e8731cd78882f3d628e79230ece762e7388d67cabb4e214683f554313408e57b1164e16adf37d6232d2a439a13bf5e335f2a39841da5be23668bd3d3282812c3e2fda48", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "24ca3f6a30adddbec78a9e468f1eb09f214f06d086db71abea944f70555e0d03", + "proof_pk_com": "1ff2f4afef54d46de90292564b60c454a48132c806168a976061aa6223d35972", + "proof_r": "aa5c6254903a5050350581c1a9a54589cbb108cea0d4043a189fe6fc8c215904", + "proof_ok": "3b7a223f757f8fd4eeedd1fe28eb72852d4f007f383733f960726c4bcfcf9792", + "proof_s": "f4437660312f5331dabf71b3003e012fa6857bfcebc3b9bd84b734fbfd68ee06", + "proof_sb": "672158bac1e24e831816e417cf692452c8305f1b42a8f23eac4a49d10051d20b", + "ring_pks": "0a5eb1ba63ebcd30bd442ce43bde7a86cef7c5c2dd56dc5c8d0bb6b3fe5075aac4959d0371aeab9e057ed60ac09ba7931ab86679a8fbbfd6c5a92e7f9c0434c3c12a6c010981467af185e26ef851324bb3f95e9f68bd414082a5454dc2f32882f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d5a8e4e8bbade5e475328ca2aaa03e932636bc1282d1d30456658665725b7cc844f3a0244ee4a673ab39445b1d217eb694885f3c28d41a5d4342196d21278fbd1c63e094fd400abb815eadd41e041e01f9c3d71e334cce5d7ba90bd2ec4aeab92aa459df1c0bbb34c09ae1796e8a1cedf8a4f6d557a75507e8b61ec366b06bfe4", + "ring_pks_com": "ab69527cc09cba81977cf961e3eb3ea2d3802b12821e87d700eb7e86aea507589b2a149c366c596cdbb7f603d50435bd833a11da678d3f797f0f2c545dc79ca53bda4425fada49ff6338b24dad56eddb2aa875dc8477246c9052041e629f2be1a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "83cc16095cbac0edac32e68cfca2d43875767de6b8844f0cdc3c5a2bd22e90693f5438dce247427e5e8602309cdcba7b9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd8c2c358128b17cf6d3ce46487dea071bce3141b9273fdf37a67a652c7183fed30ab3cfaf5b3e24136f310d7700cd1fecb924fc82ee8ac156abbd83de06a5890e122501bc27a16e2ab718fb8d1bf1a02e8c64da37c018b84ebaec8386691acbc86911fea162f4b125af5461d19a3522cd58a893231c6e23e0f6197467d19a01639d0bc19bb4acfbca7ec93a435d364ffc81cab8bb5c3d72c8e51bc569a68dd64d228942ca48459080e9cdf2467b8ccd1c630d8189f927eb423e72a240b079b5673036a43850230884fa7525db1c8b924d16cfc8e9d1646a575f7d6ff542c6c60bfe65ea0d9f7564c75b0cad2f8a4f61ce9cf3d4e137f5684db4d2fbe6e2cf365b98ea487a34b3fda6df31e23ecf040a20fcaba6bd1702be528fda2921827a0648743ebdb507f67b8f1930b8196292336f7112dace57e24d604fb21747604da1018aa995ccea4c3e9383d2ac6da7361e2929f98e9752140017c2e959e62d19d73f402ed8420b1ac6529b420fa02f26b1d6761135db5c1dc2d12a54e27c3a113beede6fff5a1f3c4ba73573e371aff19720b7864e630fa8f1d27d9d068e1e3687c8863fc2628da4c2e79a1535b46e1d64d2a70e7db344b6f5423041630ea95c1517864128e7e1641f7ca8406099fb3f371c0d33f93aca8739a86ee2884761a97c4bf3f810eda39e39d84d85d9f33d4ea30d", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "blinding": "187e68d7c3d3664eada848c3edfc19defe50ee099eb430cea8933b67a6d7440e", + "proof_pk_com": "bfd25e18e4818ac6cdf07336c162412c6a07770cb2d9598bb9b3c4338f889e71", + "proof_r": "ee9ed1962f50ef09495cf5ed0cf0a8fb90f0fd70437d1e5a9f918ee1112f56d3", + "proof_ok": "c7473bff4b6ca801c0d4f0da828c20257dea8d7f39c7b8e2e499db5e791c3091", + "proof_s": "5dce37237fc16f96b2f850121e65711092c0ca1a78a68034876717f7e95ef704", + "proof_sb": "c77bbefd18f04f6a65ebd6a6f43e3a88416069e84a89893923d57c29d5835707", + "ring_pks": "0a5eb1ba63ebcd30bd442ce43bde7a86cef7c5c2dd56dc5c8d0bb6b3fe5075aac4959d0371aeab9e057ed60ac09ba7931ab86679a8fbbfd6c5a92e7f9c0434c3c12a6c010981467af185e26ef851324bb3f95e9f68bd414082a5454dc2f32882f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d5a8e4e8bbade5e475328ca2aaa03e932636bc1282d1d30456658665725b7cc844f3a0244ee4a673ab39445b1d217eb694885f3c28d41a5d4342196d21278fbd1c63e094fd400abb815eadd41e041e01f9c3d71e334cce5d7ba90bd2ec4aeab92aa459df1c0bbb34c09ae1796e8a1cedf8a4f6d557a75507e8b61ec366b06bfe4", + "ring_pks_com": "ab69527cc09cba81977cf961e3eb3ea2d3802b12821e87d700eb7e86aea507589b2a149c366c596cdbb7f603d50435bd833a11da678d3f797f0f2c545dc79ca53bda4425fada49ff6338b24dad56eddb2aa875dc8477246c9052041e629f2be1a40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "a1387f6fe03ae28c6a74a991abc7141d2ac54365f22a0657c06f6fc7f8a5071862d732da566505281652417c71dc73b19107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcdd84560e91584148d71fffb0ce57e7f9466c752841d5429fa7845c3d991fa46e1aa7ead037226ac082cc75ec9baaf86e6e97714905c461d7a716423b497819cfbb9f682568c0b4d8c292b9838f2ef8b0f56d10c8bf607ea84293412b8b949618f0bf4f4746e247e8966e9137e7eb64ff2b43c9d5430518712369c82a357efa2f30abef26d29c1f092d0acfddb084a0a7bd793c9e4023fcdd88e220fda7ecda203d205d9b14db85a86861e64df50af27ae6b89332f4b4fb0126fbcf4216176a560ae18f95bdf3b9b58b356b37c233f6b5e6067f4e710e3db25e7a82baf34460c9405047975649d0a439f18511418d89b782cbbd612166f1facd7339241f267328679f5666e6583f83196289abe27889e5b3530f5a8e7a99832e8dc645dae9e56253e91f7721173c829374cdd2ffa2596ba416e12ebbcd61915e8890a65ae4d50119b9340716271d6b3e57c9c13897681eab80343f0721436a7c94e8fe95e4eb45afc52215e4f1eef9fc86f2243c1b4f04019e3279740374a44203005a3c85327c31016a024dbe4f74d61e0a366aeb700d22b8df48f47ff28e7fa5cd57748574dce115542e7d5b982fa56a1c8af222c15cb87ffe0f6c88fe3e6853509cf656a445679139885081b18df4c297b43fc843accd65ab4737bd099210ba2e584a0eb5cc6a6576657ac6b9961ca0f66e16750a9ecf", + "ring_size": 8, + "prover_idx": 3 + }, + { + "comment": "jubjub_sha-512_tai_ring - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "blinding": "cc07c57642d40b84be0c2741dff44254f2ebe46d506f6a19e6c5499edca31300", + "proof_pk_com": "2556f967c10bf28e415e2edcf6c0df0f02373117a00c4abd4f59690906348e5d", + "proof_r": "662b1a6a1acd770ebd8e78893cbdc0ba2b22d3377a4665bb2dde06bf0f5f7dbe", + "proof_ok": "c758b260c926cc5bc12c6d0532ed451bcdbe877e094fa065c906743cb0f5bea4", + "proof_s": "e444981f96a004a00d09ddb83c284468ed23107a6d92f20467427aa3caa7470e", + "proof_sb": "99c41f609962f251be9de67860727d57bca0d758fb0bc45ef07bd8d24c7ffa05", + "ring_pks": "2cc7b037577070431d3fd173fc037a1e461c3c1b651f00eb5919f07078cbc707d499efddd85d8c44debfbbb60d8b12e8d8aecd23c57623439e1de62232baa2daded4e02f371676283cef58d9d5ac778cab4690b7131c182c5dc7375cebb0eb6e0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf7260218e8fca337623843267d382ac21aacbd3c39708e3679d5eb0d326ea413b37ee65b99370da6e1a96726a4cfdf41210b1581f1a465ff7bb3d3bcb39ea7301be66a531a77d274a9e332e2f25b20f8714e9f9ea9e3776335bcb5b7ad078e7a3aea3e46ff67516f23c0c8e83df91c661d0425874720d4e5995bda4b7fafbd424", + "ring_pks_com": "a540c38cc514731c07cb22d3e73e108d121f0f034fb9211b5349a28a997ceb12b7809193d60113327b59d0546d12168797167b920e1cf32bdcae90aa02ac7c6d0cbbb449c865e92d9a9e43445b846cb586a96d285aebf88a6f64cca76ea5347aa40eb7cbebb436f8fdffc2a4cfd038c257bdff3f2dc17b5c6bf0fc5bdbe4a521d56d64023e42d531c33e214111d637dd", + "ring_proof": "b207cb16e97433954600bf42f1c9c6160cdf0fc064eca2ac8dcf6a41fe8969147a9cc8b3f1aba6680f9bf7fabcf97aae9107bd20fe94a01157764aab5f300d7e2fcba2178cb80851890a656d89550d0bebf60cca8c23575011d2f37cdc06dcddb907b180c459eebf99fd49349ebcaa17eda4de7d4a994d21962037d83acdf3991a93b23aacc3eb6f75bbb3aab5af78388664ee178a311f2a1d242becb503cdf65140efe7c11decad4478efa136f2a6ccdd47333101d88acc67bc7ce8091c6f9c6f586096ff1ee0e8cd723f4a8edebb6873616ddae4fca53dfeec9bccc28ebf1efd769a0c6f88851b05974b5040535de4ffc17503fb62a964af59a9e0334a50451508d3a13d26f931e83da0399e5f17f870e63f426d40335fb35e246caa9464008379b6f8b794c52f06566ef5c95fbc97f3db7ba960d3475680f91e5987efbb3479a1987f789a6b298cec6a3b7ad9022256c5ac1777f23ec8e217495d62d70f198268742f4e72cf849372f28e22139fdea44511351cb4a4aa3b8f98357de5e53189d572c4a82fa9f4d6a2732ee5ce96c016f7aa691089bac73c3fe548cd25d841aa0947ba12c20f4efd39ea0dae8766fd7b149ebcfb9c36d5260cdf81252b359f4fbbec00d1ec30f389bc4be5f36413493aa7a4a831e084661de8d57a243df9025dd70a2b5a1e9f3e32b91f989ad8244ab047f28ee00b1712d5a7770acdf2c3c30fcf6c0bd52d3528916c60068adc3ee656b1b4900621832a4dfbdad39d193dd7aa05aad0d295858bcc90f44a2a8a5c26543cc6482f9cc95e215336cbb1719dc5aed822f742dae67770d18c14f5b3048f", + "ring_size": 8, + "prover_idx": 3 + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/jubjub_sha-512_tai_thin.json b/tests/vectors/dot-ring/jubjub_sha-512_tai_thin.json new file mode 100644 index 0000000..cc1cd41 --- /dev/null +++ b/tests/vectors/dot-ring/jubjub_sha-512_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "jubjub_sha-512_tai_thin - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "proof_r": "7e54a24273da1570053421a614988ffb6cb0b8e24ab4a66c169f32cd44a71692", + "proof_s": "a42f3b65b9b8c65451a0be2794eeb86c18776d62f11c55eaafbe69ced98b8b0d" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "proof_r": "ab2d2548c852aa9ec77744fc1f60234f709bbf609096410087faba7038912548", + "proof_s": "9ff42b229d20c64c7d9b13e5e2daeb4cd3b0d7c11ae3da94ab1e50784ec75108" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "proof_r": "fbcb6817e0eb5538e6950b84498af2ec5cebfa093153505c88b7b3af33a987b0", + "proof_s": "ada5918baeabac6222489accfa589972d03d6c4defafab161ba1e58523fc9e0d" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "proof_r": "4168db297f150523b64291215a5ac911855d2db50b5e9f179008ee555df208dd", + "proof_s": "56a8e685e1830e14cdcea99c87957e6df65bd0cd800aa562ceae1dfbb3e68307" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_r": "8d6d5e1d9701418504550c829bed07e271b367365c7ee47c3c15313b38fd84d8", + "proof_s": "4a0ea02c72504cf58472d0b5a716b1e1bc73528a3026056386e69ec1d267ba09" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_r": "a9e8ec9f65f47d7b8ac184c5c05417de02f172a585d31d7973fde1f2fc61f037", + "proof_s": "a4426bee50b4d06332240f6c0b5a1b46f4e47c507c0d6b0c89c3ca15d634940a" + }, + { + "comment": "jubjub_sha-512_tai_thin - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "proof_r": "e2941c813264e454707e849b635f47fd81554843b91854b67caa9210d2eea089", + "proof_s": "a1de4f8e2ed19a8effe3a7e795152a334bdd21c2fbcb7a91efb807f091b92209" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/jubjub_sha-512_tai_tiny.json b/tests/vectors/dot-ring/jubjub_sha-512_tai_tiny.json new file mode 100644 index 0000000..9d358ae --- /dev/null +++ b/tests/vectors/dot-ring/jubjub_sha-512_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "jubjub_sha-512_tai_tiny - vector-1", + "sk": "cfac020198d62848680fd08c1a97afaaf22816509526c0ff5fbef7fba1ac5003", + "pk": "a4ae336a7c0e041c1f2d8223ed46f8b710666af0abc36452e061580add44ae2e", + "alpha": "", + "ad": "", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "39e534e862e06523b9124e4f70cdba9bc9f87c0c391d861e758c8fdb21b4131d", + "beta": "70118227520fbfe0be995369eabae4a63711b28310a1e992539d9c98c8d24258", + "proof_c": "b2269d3fd834e21fe3506f3b44e61ee5", + "proof_s": "c8f85cb28e95b080b007ac0886552c9ead5687d71ba48442dd4ed2c16535fe01" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-2", + "sk": "af537f685880c38badef63e2602c43f290fb5e5ae69ff6ab0bfc584da0fa7801", + "pk": "b763e57a18f7f2c7f168fff043d0f9534a2d946e77fe451d4a96dcb6183097dc", + "alpha": "0a", + "ad": "", + "h": "0927b14a9ac6b4f45ec1f4b87ac40b9581ec82a5f8359f92914939e0f420cba1", + "gamma": "0cb410c0a8314353839d7822e448ea3cb7290515a66046a79494fd122d9f0d4a", + "beta": "fe5e1efc9ac47a77b58decf1f5f3a00c3fb5d36983451f28b9bdf49ebbeb820b", + "proof_c": "8fd0ccaa2fbbf2657014bd57ddbf75bc", + "proof_s": "955227f7a1158f01dbc5089e599e4d63ac6132ad00c130d748f5e1379e3a9905" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-3", + "sk": "b24ab8ddc630e0ad500b7359093996a2d5a36180c67a4e9acbd9cf9667123e0e", + "pk": "460c907b11d33ec25a0e3aad51582b018c11628e76c7ad1f5c4cd924d68d0d3b", + "alpha": "", + "ad": "0b8c", + "h": "ba25065ca671d1f064ce46b2b4d8d3736f81b163e7e4fad9286a16807ed2389a", + "gamma": "f33eb5c2df57e30cdc7e114f4df81fcd30dd1054ec0aa05abb3fec10c83e3fb0", + "beta": "6b4a1a9d27f205a218134ea3e175f37afbf070affe7344c6d2a5896cc20a7605", + "proof_c": "114362601668f4bfc59ea37b0c889042", + "proof_s": "6909e3b58008382d0e431be97c86d0ff07eb9a6cd6e58a4a4c338af9ef06ec01" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-4", + "sk": "b1306f1829c63c5439d05b593431d161206731e06d79274fe807c2bbaa054009", + "pk": "aac7f09b228e2c029d3e0de590dcf94f7f600f306069009dab1469767913a89f", + "alpha": "73616d706c65", + "ad": "", + "h": "980e5c622aaa831a669e1b2308590bb1f9132c2682e77f86ceec3b35b8bbe705", + "gamma": "de7cb130fb772c77f1681a2791c842dfbc7c48cd5e424e57f3a0e06e3b82cc24", + "beta": "1be8374b8e442a12d40ed2cec0767b19f7012f4bfde8d5827f7b4726e2c95125", + "proof_c": "983b3e5082b9b0f5f4d91617dee60dcd", + "proof_s": "b66b1f77cd46eaaa1b70ffc35394d57054241b6c9c2404697177e549ef7abc04" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-5", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "8837150613e8d5dca81e016bb76769b0", + "proof_s": "3f12d2589633c89100a44d260a4b1bf454e0ad009810b27827769c83cc040508" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-6", + "sk": "93778cb7c5438ffbc0dd212d64a110fa8be00568792e9db73bd786899d7f9e03", + "pk": "f456960260184033c05f8ee59fc4bb47b6f6f7159a12b1aabb3290f1882d1d0d", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "11db30661bce2c886d536553a149c6e034845e73071c2024546219f83680e458", + "beta": "23a0f4ed74f74c560ac58328988ff949165c7fbc104cb71f54b711762c24ab75", + "proof_c": "5c721d287ac42b0d3a06c6ec11204e71", + "proof_s": "42fabf57e2e146fe0ea0765e3e909c6ce9b7b801e024949f2a25838a10022405" + }, + { + "comment": "jubjub_sha-512_tai_tiny - vector-7", + "sk": "b4caa97fa4ca22e745b91618d4ae45190e139cb8b0ec54a5d23f1e792670d009", + "pk": "0a9553d1b6bd642a38d2c1aac34e853646ee75b3c007cbc32765d7e1f73bf1cf", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "350cf14c256769313bc4c98e41155fc741260df7741cec4f675bc8dfc04e2498", + "gamma": "bba8bd572bdfea3d88cd91111530bb70a1e369f0bb501c16d5dc4f0e84772b1f", + "beta": "fb89b3a69bcc791b58e55396a747dbdc27bad6353b87fec3a40b223010bdbaa9", + "proof_c": "f14e38cd954ed8440892f237a1c0ffe9", + "proof_s": "af179e80e9a7b854828a0b049cfaff27862690612fe8224dc7bf40bbdb85690c" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/negative_tampered_proofs.json b/tests/vectors/dot-ring/negative_tampered_proofs.json deleted file mode 100644 index e8f141a..0000000 --- a/tests/vectors/dot-ring/negative_tampered_proofs.json +++ /dev/null @@ -1,20 +0,0 @@ -[ - { - "comment": "tampered challenge - should fail", - "original_proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "tampered_proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865783f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "pk": "cafe3a1001a946130f813ce160fc9c9e8eb25130d352da2e60706de439552383", - "alpha": "746573745f696e707574", - "ad": "746573745f6164", - "expected_result": "FAIL" - }, - { - "comment": "tampered response - should fail", - "original_proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "tampered_proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0e", - "pk": "cafe3a1001a946130f813ce160fc9c9e8eb25130d352da2e60706de439552383", - "alpha": "746573745f696e707574", - "ad": "746573745f6164", - "expected_result": "FAIL" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/negative_wrong_inputs.json b/tests/vectors/dot-ring/negative_wrong_inputs.json deleted file mode 100644 index df169ff..0000000 --- a/tests/vectors/dot-ring/negative_wrong_inputs.json +++ /dev/null @@ -1,29 +0,0 @@ -[ - { - "comment": "wrong public key - should fail", - "proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "correct_pk": "cafe3a1001a946130f813ce160fc9c9e8eb25130d352da2e60706de439552383", - "wrong_pk": "65ad058d7b69d446cbe4b648633ae0cd61d6b660bd857bbe47594cb8362b8caf", - "alpha": "746573745f696e707574", - "ad": "746573745f6164", - "expected_result": "FAIL" - }, - { - "comment": "wrong alpha input - should fail", - "proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "pk": "cafe3a1001a946130f813ce160fc9c9e8eb25130d352da2e60706de439552383", - "correct_alpha": "746573745f696e707574", - "wrong_alpha": "77726f6e675f696e707574", - "ad": "746573745f6164", - "expected_result": "FAIL" - }, - { - "comment": "wrong additional data - should fail", - "proof": "0c00ccefb9512cc4879aff8435211325aeaa475b820f2249b84b3813b6b43c865683f8f5215b4a24a0bd3456be26aeae66e6eec0dc53dca49f5fcc4bf96d6f1c79cbe349e38c20a8733b47ec8ae8a230576fb893c000a6515e92dbc4f6f43f0f", - "pk": "cafe3a1001a946130f813ce160fc9c9e8eb25130d352da2e60706de439552383", - "alpha": "746573745f696e707574", - "correct_ad": "746573745f6164", - "wrong_ad": "77726f6e675f6164", - "expected_result": "FAIL" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/secp256r1_sha-256_tai_ietf.json b/tests/vectors/dot-ring/secp256r1_sha-256_tai_ietf.json deleted file mode 100644 index b7bc05c..0000000 --- a/tests/vectors/dot-ring/secp256r1_sha-256_tai_ietf.json +++ /dev/null @@ -1,93 +0,0 @@ -[ - { - "comment": "secp256r1_sha-256_tai_ietf - vector-1", - "sk": "c5be5da4af78e6d1de452e157790a9a1d26ab227b9b4932bbecb1f25bdfad001", - "pk": "02c181a35af479d9a162b73c90608c3e361f092775c7ad7c129fd9bc91593a0a89", - "alpha": "", - "salt": "", - "ad": "", - "h": "032c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - "gamma": "03d3426c4c27814afee7839e01e381b50dde13844bcd4395e92b9db7df4dbb73a8", - "beta": "6146cf9d87d3a1ec079a275c8bfdae962581ad17ceb7f31248e9fe4ed8c8d625", - "proof_c": "3d3706bec8f5ce3055b59f4be09f561a", - "proof_s": "0c03053b88bcb9ab387b46bd3fe128f5d01db464b4ccad5add89a235919ef042" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-2", - "sk": "315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857", - "pk": "033344327b43edf67a800ffe0b748c0b7c8a98c10eb11ca8039e51e366682d1ccd", - "alpha": "0a", - "salt": "", - "ad": "", - "h": "02fd749a4b45933f12b05a8a5802c93be40571127d0508fa793163ebbaac390ba6", - "gamma": "02036a6ec9fd7b6e5e2b0ed87c962037269b235c394d7d84ec1bf7f2ca579bf5df", - "beta": "d641198739b5d21eca24b1ee152dc6a2ff508166a67e149a17207b3bb87de423", - "proof_c": "88ef3f93544843771bd0147d52b01568", - "proof_s": "532397487fde5d96b49b06603726309f536524b15bc1064f2da5fb32835a0ec8" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-3", - "sk": "25b1166a43c109cb330af8945d364722757c65ed2bfed5444b5a2f057f82d391", - "pk": "03fae5ae6ce5f3092273e5ebcb1fdf38ae602fb8aad993fc96e25789697542dd77", - "alpha": "", - "salt": "", - "ad": "0b8c", - "h": "032c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - "gamma": "02a10251021d08b322b70f6f4128a8859e99851617cc2b32f24d9a592902d3e6c9", - "beta": "05924c242644ba82a7be497cd4cf93e4e7e0e7b34d4eeb5971816e316fb36650", - "proof_c": "909a601908378cf44a4182aac80ddb46", - "proof_s": "bad0217e239120ca19437d840be8930a16f28289c5535d29014b6266c79078e6" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-4", - "sk": "3f5615898238c4c4f906b507ee917e9ea1bb69b93f1dbd11a34d229c3b06784b", - "pk": "02136d9fecf7f787aeccaec018c43551596e0d9b12c54de331ba35fdedc803aec9", - "alpha": "73616d706c65", - "salt": "", - "ad": "", - "h": "024d2f866b0fa33bbd70d75149911e3e14e079641474dfe8c17713076a135731c5", - "gamma": "03e316a994028396bd5fd8a1f1622631a2a9126793e78e675519053612df7c7d8f", - "beta": "81d0f037c444da4f5bc9b23ea2c023b592bad5c5f3d1764625325aedc2fbd541", - "proof_c": "774f2c93c97a491ce187fd59c40bc2f2", - "proof_s": "19fc252edaae0c87da24d03df8a303e08e0a624aa8e112898bcc37aa257c7c78" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-5", - "sk": "ed32e3079f035965a9dfcf6cbe9c1ab61423903b7882d9071f4f3b7f3761e7aa", - "pk": "03caade3d839dc06e8089e3db568c6c6e46c3897507979ea06880e1d655f3c7684", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "0270658b189a1dd3e1a09c94e7f63cd2f16cff7335e8550376d3dcee11107a06dc", - "beta": "fc61f727df976b3c47eac962bc5dd19b21c3e1de20116efab561ece693e63ff4", - "proof_c": "38615ad3f364db158d1ad9cfdf20467d", - "proof_s": "adb37699ce34b43d4bdf510fe4f50664d5997bfbeb7b2be69e2d2daba318f68f" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-6", - "sk": "ed32e3079f035965a9dfcf6cbe9c1ab61423903b7882d9071f4f3b7f3761e7aa", - "pk": "03caade3d839dc06e8089e3db568c6c6e46c3897507979ea06880e1d655f3c7684", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "0270658b189a1dd3e1a09c94e7f63cd2f16cff7335e8550376d3dcee11107a06dc", - "beta": "fc61f727df976b3c47eac962bc5dd19b21c3e1de20116efab561ece693e63ff4", - "proof_c": "bd50633d13492eb543d5bbd903d56b71", - "proof_s": "d68ab7a1fd93812e8f379c7512e35c4c1ab8393abeece429bea2aac4609bd51f" - }, - { - "comment": "secp256r1_sha-256_tai_ietf - vector-7", - "sk": "1023c68852075965e0f7352dee3f76a84a83e7582c181c10179936c6d6348893", - "pk": "037520c46ae420a9971682b4cacafba8c1b995377aab705b32424020e0c060ac49", - "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", - "ad": "1f42", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "03e33a1497b4e97158c14f483b48a67fa8b269ec6742a983afca475caf8b573be8", - "beta": "e62495c6bb2b121a016cd2c5f1caded76c47c20f403763113b127410431e446d", - "proof_c": "c539d7106efeb93e2c5a61363bb57091", - "proof_s": "0dd9bbc2caa91ec93762ac04cd1db042facb26aacbc811f013e7349e74481a6e" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/secp256r1_sha-256_tai_pedersen.json b/tests/vectors/dot-ring/secp256r1_sha-256_tai_pedersen.json index 5812e52..99aef28 100644 --- a/tests/vectors/dot-ring/secp256r1_sha-256_tai_pedersen.json +++ b/tests/vectors/dot-ring/secp256r1_sha-256_tai_pedersen.json @@ -1,121 +1,114 @@ [ { "comment": "secp256r1_sha-256_tai_pedersen - vector-1", - "sk": "c5be5da4af78e6d1de452e157790a9a1d26ab227b9b4932bbecb1f25bdfad001", - "pk": "02c181a35af479d9a162b73c90608c3e361f092775c7ad7c129fd9bc91593a0a89", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", "alpha": "", - "salt": "", "ad": "", - "h": "032c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - "gamma": "03d3426c4c27814afee7839e01e381b50dde13844bcd4395e92b9db7df4dbb73a8", - "beta": "6146cf9d87d3a1ec079a275c8bfdae962581ad17ceb7f31248e9fe4ed8c8d625", - "blinding": "1bba7177112fc18872d63a6780b7e675e6ace2d5fa98793306acfc3ee17772b1", - "proof_pk_com": "02442967fe11460a7679a934ebff4732266de13b568e0ce156364b0061d0d83b38", - "proof_r": "027d3249b16be618eea08a007fbf3fdbfed39a1b6f233a156bbc2d7459424e5f2a", - "proof_ok": "020004aea67d93b67807cc0d4086c8635195bbbc4d6e8300364302ccffa899e56e", - "proof_s": "4f46bbd67b5014c42ad9b203ada81b74d52fde932c508a03ff1824a57cc3fd31", - "proof_sb": "642098ef0c4f7d23e8b7bad3159d35ea006f9c3f58b186a64d487050e02bc865" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "blinding": "a0fe3ca2941f67f07b21864eddf7eb7b3725a13e02b9ad292693ca7fe5965646", + "proof_pk_com": "76880ba4627b1052ce9c62341d3504c96cf57f10fc6a86fd7fb19cede470ef3700", + "proof_r": "f4fd80e46c233708aa57ca6722ca44f05564bfb201136b6de709a4de9d4c080700", + "proof_ok": "2de01f0087982dd9ff13b6c9c12159826027be607239712b47fcbbd9801177d100", + "proof_s": "81736cfa96966fffde32f1b39e7c7c6916fa4a242d0771b9a75551226f0df7e8", + "proof_sb": "9b6eeed6228a15cfb502c282ad0df78437f53c63b4c96a6e9063e7af9aaee8dc" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-2", - "sk": "315ed405fafe339603932eebe8dbfd650ce5dafa561f6928664c75db85f97857", - "pk": "033344327b43edf67a800ffe0b748c0b7c8a98c10eb11ca8039e51e366682d1ccd", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", "alpha": "0a", - "salt": "", "ad": "", - "h": "02fd749a4b45933f12b05a8a5802c93be40571127d0508fa793163ebbaac390ba6", - "gamma": "02036a6ec9fd7b6e5e2b0ed87c962037269b235c394d7d84ec1bf7f2ca579bf5df", - "beta": "d641198739b5d21eca24b1ee152dc6a2ff508166a67e149a17207b3bb87de423", - "blinding": "97a9758bc8a271819fc203fcc8fc1065fc8e9af8c4650c1f0b3e2b25161b73ae", - "proof_pk_com": "03d80e5c1a795579b9dedeb2968c895882588a240c7a29219ce745ad0194e47c20", - "proof_r": "0307a2cca54e27997d6630f9f8af75535c0a3832793477a961e67f6862cf6449cd", - "proof_ok": "0262dccba997fc1ca5f9903d6a074b8c0373e7678c758a934d48875c97e00f82fe", - "proof_s": "b026c9ea5c9d858be721bea328ee3c26fe8646b0bb6d46aa3a3777527d370998", - "proof_sb": "f42269f496e688747e4fb238fe6ffb61a00fb53e8772fdd7e4f2db183560e511" + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "blinding": "7e6df180e6fe8c763f3e18488b6610c09fba9835b1991ff025283d2fe78639a9", + "proof_pk_com": "c5fbf7d0f6c011c82ea60f2c492cd2ea780cc17a0849333b1b65660bd0b51b6680", + "proof_r": "cd188ce4805b8664b15524809b72ee50d1b73c4c42bf45fc7dda5ec1dfc8789180", + "proof_ok": "a3a22d5ac446998e1b99b6e4dea8defbba4cdab7377fa690c155589a974036e600", + "proof_s": "66579415dd689f22d59e6ac47511f5556e69374b909ab7bc98b1871247cbb0e7", + "proof_sb": "7849433f58a18b9526c57f0e632bf281fdb97bfc372a835b0e32e2a3a79d45cb" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-3", - "sk": "25b1166a43c109cb330af8945d364722757c65ed2bfed5444b5a2f057f82d391", - "pk": "03fae5ae6ce5f3092273e5ebcb1fdf38ae602fb8aad993fc96e25789697542dd77", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", "alpha": "", - "salt": "", "ad": "0b8c", - "h": "032c15230b26dbc6fc9a37051158c95b79656e17a1a920b11394ca91c44247d3e4", - "gamma": "02a10251021d08b322b70f6f4128a8859e99851617cc2b32f24d9a592902d3e6c9", - "beta": "05924c242644ba82a7be497cd4cf93e4e7e0e7b34d4eeb5971816e316fb36650", - "blinding": "dbf17134fcdb89206f4392aebd65317f9255e8e0a4c510539f75a3cb7133c346", - "proof_pk_com": "02a8b6d971fc80a9f5cf409955455afa7a042f5c0df3c20e66c5c5dd41f3b01844", - "proof_r": "033eaaf391e629a01294a80d44bd70ab427e44f2dcffff635ab63dcde600fda1ad", - "proof_ok": "0245d7f655fe0a707c6802d52bffdb50dba82659744526d30fb3b3f1ce36e71172", - "proof_s": "57fa54126ae27f5e4da597de49d61b0d2f33bfdbc051f68c8c79d4b2762d84ef", - "proof_sb": "0b58d6598fa5ad5e3cd59f1f20b6b52d3385f7aa975b6ef6dfe5b8dca7530921" + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "blinding": "988699ef8ea68f263c0e4a04d0acb91c7cadf7d9b2d14609fe63c939d5f9a05a", + "proof_pk_com": "55f477728e6a70c8163ffb5e10f58f9dea174360202172772ff3043381b8372a80", + "proof_r": "cc7aeb499bde19f5f2033977ba059e7aad32d409ed0d5caada91b9ae91dafddb00", + "proof_ok": "a5ac3d436f6f034e9fdb718b1239cf43dba8940a0ff135f675fd262450f1e41b80", + "proof_s": "194b1c88b2c2191a7f242ae76d2aa3bf28fc9c66e42df1ebd8c7dff7ad0a91e7", + "proof_sb": "4b866b363824d4afa6ab752a8170797daf87ee8c15dcdf2173585c0a062506ad" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-4", - "sk": "3f5615898238c4c4f906b507ee917e9ea1bb69b93f1dbd11a34d229c3b06784b", - "pk": "02136d9fecf7f787aeccaec018c43551596e0d9b12c54de331ba35fdedc803aec9", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", "alpha": "73616d706c65", - "salt": "", "ad": "", - "h": "024d2f866b0fa33bbd70d75149911e3e14e079641474dfe8c17713076a135731c5", - "gamma": "03e316a994028396bd5fd8a1f1622631a2a9126793e78e675519053612df7c7d8f", - "beta": "81d0f037c444da4f5bc9b23ea2c023b592bad5c5f3d1764625325aedc2fbd541", - "blinding": "ebe494f8bcb710582872b81cf42eed30d6ca62a6de6b3a98ba4652ea0964a83e", - "proof_pk_com": "03d2a0cd0d75aec43fed62cfa3936a6244b067edae4cd96ff059b0ecf27a4c01ce", - "proof_r": "026e75f12acb3ff4f2ac51a6ba0133f3662c0444956e708cc2949fd9de873d93ef", - "proof_ok": "026f193318a1cfc4527b673c0e6fce731920f889378286be67d12b2069284b88fb", - "proof_s": "f1ce2e8ad0b1fd80aae27f6fde67d20d57892e22bd8ced4851e904a53e808817", - "proof_sb": "1322d1148d5acf65641be88cf4353f6b71f2a47cc72eb83f6b4e812ec4b2ee03" + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "blinding": "04f4b3fa241b685771301b59eb5f58ef34b16212aba9a6e749c854e091c37edb", + "proof_pk_com": "be9c2b7a3a9ce7b66ba3e2484d486d33880ab01a28b9ce4de91a2a06d440938f80", + "proof_r": "00f03b28429fdb737bbcbd9b99abbef7c890a78debaab05e01ed5ae4a911bccb00", + "proof_ok": "7d2e4efb526d5031e1d60d51d5accc44d748fc21b14d40d2b4dff47796fbef9100", + "proof_s": "58e9d92f02bd0493d9ef7dee882d3f504bf045d90dcfd89f1b0ab73fd352de95", + "proof_sb": "c3f5974dc3b9a0b95e006897c43ec5ce749a2f37ca2140d340522057d84c3f1c" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-5", - "sk": "ed32e3079f035965a9dfcf6cbe9c1ab61423903b7882d9071f4f3b7f3761e7aa", - "pk": "03caade3d839dc06e8089e3db568c6c6e46c3897507979ea06880e1d655f3c7684", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "0270658b189a1dd3e1a09c94e7f63cd2f16cff7335e8550376d3dcee11107a06dc", - "beta": "fc61f727df976b3c47eac962bc5dd19b21c3e1de20116efab561ece693e63ff4", - "blinding": "c519c1521953653e24b08794e3ce41d1c0887cf8549052ab909719c7d26a5cf4", - "proof_pk_com": "035e61007718227c13a1203763887d94e277a3904aec108a8afa7d55197d2189be", - "proof_r": "03f6382ff9216429572af39149dbd29fbf8e07fe9be88864af6486f4356c3364fe", - "proof_ok": "03c03d5975fa771da14778ae3692023700beac65406c5a10c0387ccbe1cb5a55cc", - "proof_s": "6056facd8af68d193129cefdcabbac0fccb36eab06752b97346f5a4475363798", - "proof_sb": "75034458d5ea847571ec3cd627f8933c2f6fc4cfd73c47348cfa3e0c9496a2c2" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "9fbd401437473a215fd30bd97a23438c46b58991b4a987967168a957ac290be8", + "proof_pk_com": "b9cca35eec17e7c8633cd4e0bb72a9cc49e2a567b1ebefa840f80ad4fc6c0e2200", + "proof_r": "b4c0e181852ff3960fd7ae9ee95890625974fcd9f468de96908732587f504b8c80", + "proof_ok": "b98d9ee96bbd7a32e949edc7dd5e2bf74432cbb79569f06fc74dac4cee9fd62d80", + "proof_s": "e801a9455307447780ed6925886c944f27b815cb3a50259eb6097e67c9fb77af", + "proof_sb": "42e291c5bd51c7e1e5cf7ca7e1778918aabea74453579f7e879a609353de44c6" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-6", - "sk": "ed32e3079f035965a9dfcf6cbe9c1ab61423903b7882d9071f4f3b7f3761e7aa", - "pk": "03caade3d839dc06e8089e3db568c6c6e46c3897507979ea06880e1d655f3c7684", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "0270658b189a1dd3e1a09c94e7f63cd2f16cff7335e8550376d3dcee11107a06dc", - "beta": "fc61f727df976b3c47eac962bc5dd19b21c3e1de20116efab561ece693e63ff4", - "blinding": "65e9ceb1fdfb5005edf10d071ab9564c8cecd82d43aa5bd9ac4749cfba7f5832", - "proof_pk_com": "029cfc417a6d364f5abac9e5bd0dd8ea561b1f3d7afecd8b4cf48420c2efe8dc53", - "proof_r": "03f23c93d39c21c1d7cf27f83c127e50261bc0a413936c2d02168a1bfd3808b09a", - "proof_ok": "03c03d5975fa771da14778ae3692023700beac65406c5a10c0387ccbe1cb5a55cc", - "proof_s": "2b4f0bf326eabdccc30a8a5280db9fccc3f0a9f698423d503341671e120dd2d4", - "proof_sb": "4327b4cfeee5c9e2e7bd644b0c27a4028e60d761dae9eec635d895e6d73a95a6" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "blinding": "281e228d0bb4305724a2ea3a1a6c97c6b91b8b63ccc2716bf5ff541c8c43e2a9", + "proof_pk_com": "e0f2271ec3d66defa1441918018055ddfc34083d0f95fc8f3013663db5f2322e80", + "proof_r": "d33f6c7a2dc4cc87644d24f4e13389cc585b14934f6d1c897fe9f0c0c6be6b5c80", + "proof_ok": "1cfe9a872ac7c88eea71dfe2e15d8dd921718994a6088ccbefd994932edcc31a00", + "proof_s": "3c8938aa49fa3047bedb3a66c33f71dcd75e5745c2d93878578ebc9fb4e6cab6", + "proof_sb": "466e9c2e02e9594e87c9002228838f356e326640229f05d40f857614b3ad26f8" }, { "comment": "secp256r1_sha-256_tai_pedersen - vector-7", - "sk": "1023c68852075965e0f7352dee3f76a84a83e7582c181c10179936c6d6348893", - "pk": "037520c46ae420a9971682b4cacafba8c1b995377aab705b32424020e0c060ac49", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", "alpha": "42616e646572736e6174636820766563746f72", - "salt": "", "ad": "1f42", - "h": "0330c348b35054c3a750e221f52b8a971f29c4877db2c0714ca96c3823d3f40cef", - "gamma": "03e33a1497b4e97158c14f483b48a67fa8b269ec6742a983afca475caf8b573be8", - "beta": "e62495c6bb2b121a016cd2c5f1caded76c47c20f403763113b127410431e446d", - "blinding": "56dd3e229942c9ce83a633eada569156ba48299138d86cbe5be6ab17c948b973", - "proof_pk_com": "033d5ae9f84f66cb8442238092fe82c6e9800b6e71838f814ea0b44d7a64a1d33b", - "proof_r": "03802435879ed6ed7b6303de9b6b559f7e2c0bbdf84fca108e2c9513fa6b2162d9", - "proof_ok": "031ac62b8614f8782336434c3a516baa18493128c4fc5da6c8bbd019165a60f7ed", - "proof_s": "3bc27b598a5adf93559af8bf395e9ca3c85640e0fa4066bcd616e705b40f1bc4", - "proof_sb": "d718d69a76c2bb2b966790e614cee4549645aa1269325e8ce24695201acd1c26" + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "blinding": "756feac9778ae51176097de2a902ba8dd6b3ed32f9fb18ca05d6731fe0380111", + "proof_pk_com": "f8b560dcf1ecab375c77acbcd8bb3171b616a63cb852b8eaa7ea6b8ce3aaff8900", + "proof_r": "e91fe573671cab6cdc2b407c7a6b62e418a5943534e4d4a7b99290fe053ff55480", + "proof_ok": "eaf9046feb444cc58b5ad18a4a6eabee567d541ab7a38bad8ed19f4c53b66d1280", + "proof_s": "ca71aa0fc67e4841741b12061ba1bbeab1c68583e4e723ae10505cdca468c4f6", + "proof_sb": "144072c4c69a00d1daa83af385c9f426e16a4760ed7d9b6e834fec323350b267" } ] \ No newline at end of file diff --git a/tests/vectors/dot-ring/secp256r1_sha-256_tai_thin.json b/tests/vectors/dot-ring/secp256r1_sha-256_tai_thin.json new file mode 100644 index 0000000..6cadf75 --- /dev/null +++ b/tests/vectors/dot-ring/secp256r1_sha-256_tai_thin.json @@ -0,0 +1,86 @@ +[ + { + "comment": "secp256r1_sha-256_tai_thin - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", + "ad": "", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_r": "424e91586f512ae0b5f06692340436bbea6e59fb52dbcfb0f41bd8721b7c48b600", + "proof_s": "c6df155f8b9dd28d7539e10c194c442d32e401c563710f99c11d3a2fb0a7d431" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", + "ad": "", + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_r": "0660e1f457f31eda17d1bf61cacc9623ed5d41ec1c97b7ba37a00ae16e1936b500", + "proof_s": "1cfae0cc88e4239bc758702375a580e7ba81c048cf8cfc46f1564d6b397ae41f" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_r": "02e7f057abd05d9894c8b17fa414e0010ad3ff130e5cd7a4e6f1162a2dcec35180", + "proof_s": "a3f3eb3f2ff76aabaf390f5483f37cf7169b881f6e2fc10e2da005bccdbc477a" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_r": "08fc37baaad0a2aa712faeb8a03bf669d6094b338df0c83ad1282f132e34072d00", + "proof_s": "69e3bc059f57ffa2b1383ce7f99ace9eaa2d8a33165bcedc9d204b60f94c0fab" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_r": "794c512aa0e0fc54fb9c432588506d4bf4fb057b1bd8854d60c000fabcfe09ea00", + "proof_s": "c87538f6265457c6a452b242267443fbd4d228abcaf3951e9bb1fe5f28960a40" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_r": "2b464138e9ae2c8646fb5bc071f16fbc79980b51edd5b799ba5339b42ada6eef80", + "proof_s": "0c65f1810be63a68b7b17d0deefc624eabb3082f8953ec41f93c2dc9abe187d6" + }, + { + "comment": "secp256r1_sha-256_tai_thin - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_r": "3ee948cd1b3c5c22345f221377803935676872536e19f0b6289e7feab37bd09900", + "proof_s": "68bb0eb250e0428194e4a861414e278e0e4e9e7e5c213637ff72de846defda9d" + } +] \ No newline at end of file diff --git a/tests/vectors/dot-ring/secp256r1_sha-256_tai_tiny.json b/tests/vectors/dot-ring/secp256r1_sha-256_tai_tiny.json new file mode 100644 index 0000000..3557e08 --- /dev/null +++ b/tests/vectors/dot-ring/secp256r1_sha-256_tai_tiny.json @@ -0,0 +1,86 @@ +[ + { + "comment": "secp256r1_sha-256_tai_tiny - vector-1", + "sk": "a6f929bc795713269110dce57b25f9f34ee972df7f894f19bf75945c77899976", + "pk": "7145060a27d8ba67eacde55240aa51fac0ce249265c6917cb14674cc17bda26000", + "alpha": "", + "ad": "", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "198e23ed3c4374e87040892f3e27fa24fc6c43ce76c6a01b783449d4ba5c22d600", + "beta": "7191e30cd6eb8fa95bcaca98eb50d795d9b58dcf9bc436ddc150ab471d584319", + "proof_c": "c1f5f3ea1b0ca19250084a3e9dc5bd72", + "proof_s": "87273dc88eafbfe092126d6e624165db553932cd7a78779b1e06184a265e3c22" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-2", + "sk": "6f2849b8387e78c7b08c3ce4354823e78042be078673f302cefa719e96115889", + "pk": "772858bd2dffde7d435ebeefce923d59f416badbb301579b9e14ea8f8c80ea8e80", + "alpha": "0a", + "ad": "", + "h": "47a7793ba232b4e3ad0af74ebae888b0c08ba374af1b83e003219d0d332803f080", + "gamma": "dd8408b6f0e1f757294df25677dd77fa7707576fc262cf390616bc1b853b547200", + "beta": "529823e598c282691761ac89453dc0d46535e44618bfb6dc781456779a9b6fcf", + "proof_c": "34e572fc2a1655dcd547695555331de1", + "proof_s": "2ff7d8e383b96f8038848f2f971af825ee88521b720a03babae0ab8cdabae95a" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-3", + "sk": "25698697f5156d9e6eadddd873457ab9851a8e0d3d8e93be71e12bdefe75eccf", + "pk": "e38c2e70c3826cd7ce18d706b2611b9e072fd3aad3a91e803bd2e503c01f70aa80", + "alpha": "", + "ad": "0b8c", + "h": "1e9cb002087eec030a7bf3f62fd8aee8771329c5dd793a4ead9a3f5147d53ce280", + "gamma": "07fc53bf7c9231e5db2c5fdf02d533dfc9cc3dc5511ed7d298b47abe2364873700", + "beta": "a19e7aff31d07f9378d4b635b88497e07310618667df395de70d759111cf0069", + "proof_c": "860017b8a4fde275edc469c3a61415f0", + "proof_s": "8b64f17113614156f21c8a858135d04e3af8b1d5d8a7f32b38720745e0f864b5" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-4", + "sk": "f27af419b377cd83f7fb2ae1ab4fdb4cdc20335a5fe354e3be9be135ee447035", + "pk": "5a41d457656bc56f187f4afdfdbafb76ebfced448c1707a59473fba1e974575b80", + "alpha": "73616d706c65", + "ad": "", + "h": "af188575c08706439e88ade99fb58c8e204d4ccad4f7a8d6a9f63afcea83bc2080", + "gamma": "8472b3563c4579a3f88c8e886d9d396a14bc47a5110cb3d3354b6df28e75af9e00", + "beta": "a1b7480b45a9d47d8a34dcbb80da6061543babf5aacd396e0db39fb384eefa34", + "proof_c": "a7b7b6afd0cbdfb8420978d345bce0d3", + "proof_s": "e0a78bd2da5fb823043f5cee9bb42dffe8c74e1615fe798c93a9a3125fb51b69" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-5", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "dae5ea449f898c7047393098c2f03b83", + "proof_s": "251511bab28d16f21ffd1fae3a68484d54492498d70bc2f3fbadcc0668571696" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-6", + "sk": "8eb21e5ff2760c2bd1e1c09db7534e53ab73b238e03fb27953f34fdf9f682823", + "pk": "5fd86814de271359db1b0423b42c9d1b6f0e15f335bb5cff05c20599addb57f900", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "a7d39f19c5e6ebadf814d43d4793d2f126dfc80f476f69e0c88b635bb0099dda00", + "beta": "792afedfec266002e2ce84b4c937f9da14b4c0eabc719d55eca852727b5d3fba", + "proof_c": "38eba01c864b7b6b35d1e747a640ba57", + "proof_s": "4ce01d01e8e0f3576182f086be61089f14dd2e38484ebfd758e3f6a353f6fa27" + }, + { + "comment": "secp256r1_sha-256_tai_tiny - vector-7", + "sk": "3b92df69cc7ecd397e9a92d799166154ca50c6da71018e9ba0a519ea4d91f501", + "pk": "e742ab2e259f8ae9cbf1bcf285bc593b7a9dbf3b3754598a5812a98258bf990c00", + "alpha": "42616e646572736e6174636820766563746f72", + "ad": "1f42", + "h": "a5ffeb96a9ffad18b624235aa63addcda6a5bebea433a584b0660879a9c50f1080", + "gamma": "8fb567ff0aefc94bb807d4d31904d9a0692a80de7e5d96a00e473eb65d252cbf80", + "beta": "33e50c66fe660b43dbf29992461ee2a1a48a8322ac7829ae8a82a67a556ff88d", + "proof_c": "a801990255dce4885c7ec1899a20ed5a", + "proof_s": "5bf22e88a3dcbe68272dc5bb703f4b06167eff9aad8816b4dfc6fd2ab9e67f49" + } +] \ No newline at end of file From f296350ecf6892c5539a0a0bf111a8f87b299805 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 20:47:26 +0530 Subject: [PATCH 08/33] docs: update benchmarks and VRF naming --- README.md | 28 +++++++------ docs/BENCHMARK.md | 44 +++++++++++---------- docs/chainscore.png | Bin 68983 -> 0 bytes docs/chainscore.svg | 23 +++++++++++ docs/cover.svg | 52 ++++--------------------- tests/benchmark/bench_ietf.py | 10 ++--- tests/benchmark/bench_pedersen.py | 10 ++--- tests/benchmark/bench_ring_large.py | 2 +- tests/benchmark/bench_ring_proof.py | 19 ++++++--- tests/benchmark/test_bench_ietf.py | 13 +++---- tests/benchmark/test_bench_pedersen.py | 13 +++---- tests/benchmark/test_bench_ring.py | 38 +++++++++--------- 12 files changed, 124 insertions(+), 128 deletions(-) delete mode 100644 docs/chainscore.png create mode 100644 docs/chainscore.svg diff --git a/README.md b/README.md index 996228b..48a936d 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ alpha = b"input data" ad = b"additional data" ``` -Deterministic key generation from a seed (matching ark-vrf): +Deterministic key generation from a seed: ```python from dot_ring import Bandersnatch, secret_from_seed @@ -63,21 +63,21 @@ seed = (0).to_bytes(32, "little") public_key, secret_scalar = secret_from_seed(seed, Bandersnatch) ``` -### IETF VRF +### Tiny VRF / IETF ```python -from dot_ring import Bandersnatch, IETF_VRF +from dot_ring import Bandersnatch, TinyVRF # Generate proof -proof = IETF_VRF[Bandersnatch].prove(alpha, secret_key, ad) +proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, ad) # Verify -public_key = IETF_VRF[Bandersnatch].get_public_key(secret_key) +public_key = TinyVRF[Bandersnatch].get_public_key(secret_key) is_valid = proof.verify(public_key, alpha, ad) # Serialize proof_bytes = proof.to_bytes() -proof = IETF_VRF[Bandersnatch].from_bytes(proof_bytes) +proof = TinyVRF[Bandersnatch].from_bytes(proof_bytes) ``` ### Pedersen VRF @@ -140,14 +140,18 @@ docker run -it dot-ring pytest tests/ --- -## Contact +## Benchmarks -**Prasad // Chainscore Labs** +See the benchmarks [here](docs/BENCHMARK.md) for performance results. -![alt text](https://raw.githubusercontent.com/Chainscore/dot-ring/refs/heads/main/docs/chainscore.png) +--- -[Email](mailto:prasad@chainscore.finance) • [Website](https://chainscore.finance) +## Contact + +Chainscore Labs is a full-stack engineering and research studio with a proven track record of delivering secure, high-performance systems for leading blockchain ecosystems like Polkadot and Telos. Founded in 2021, our team of engineers with expertise in blockchain infrastructure and modern apps has successfully shipped 20+ projects, demonstrating our ability to execute complex projects from protocol-level engineering to production-grade dApps. + +![Chainscore Labs Cover](https://raw.githubusercontent.com/Chainscore/dot-ring/refs/heads/main/docs/chainscore.svg) -## Benchmarks -See the `docs/BENCHMARK.md` for performance results. +[Website](https://chainscorelabs.com)
+[Email](mailto:prasad@chainscorelabs.com) diff --git a/docs/BENCHMARK.md b/docs/BENCHMARK.md index eb47ca8..c62b5da 100644 --- a/docs/BENCHMARK.md +++ b/docs/BENCHMARK.md @@ -4,18 +4,20 @@ Benchmark results for `dot-ring` VRF implementations on Bandersnatch curve. - **Runtime**: Python 3.13 - **Device**: MacBook Pro, M1 Max - ARM, 64 GB RAM -- **Vectors**: `tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_*.json` +- **Suite**: `Bandersnatch-SHA512-ELL2-v1` +- **Vectors**: `tests/vectors/ark-vrf/bandersnatch_*_*.json` +- **Baseline**: ark-vrf `benches/SUMMARY.md`, quick mode, AMD Ryzen Threadripper 3970X --- -## IETF VRF +## IETF / Tiny VRF -Standard IETF-compliant VRF (RFC 9381). +IETF VRF-AD proof. -| Operation | Min | Mean | Stddev | -|-----------|-----|------|--------| -| Proof Generation | 1.67 ms | 1.78 ms | 0.09 ms | -| Verification | 1.58 ms | 1.66 ms | 0.06 ms | +| Operation | Min | Mean | Stddev | ark-vrf | x | +|-----------|--------------|---------------|-----------------|---------|--------------------| +| Proof Generation | 1.67 ms | 1.78 ms | 0.09 ms | 185.4 us | 9.6x | +| Verification | 1.58 ms | 1.66 ms | 0.06 ms | 194.5 us | 8.5x | **Proof size**: 96 bytes @@ -25,10 +27,10 @@ Standard IETF-compliant VRF (RFC 9381). VRF with Pedersen commitment for public key blinding. -| Operation | Min | Mean | Stddev | -|-----------|-----|------|--------| -| Proof Generation | 2.30 ms | 2.38 ms | 0.07 ms | -| Verification | 1.88 ms | 1.97 ms | 0.06 ms | +| Operation | Min | Mean | Stddev | ark-vrf | x | +|-----------|--------------|---------------|-----------------|---------|--------------------| +| Proof Generation | 2.30 ms | 2.38 ms | 0.07 ms | 374.6 us | 6.4x | +| Verification | 1.88 ms | 1.97 ms | 0.06 ms | 215.4 us | 9.1x | **Proof size**: 192 bytes @@ -37,31 +39,33 @@ VRF with Pedersen commitment for public key blinding. ## Ring VRF Ring VRF with SNARK-based ring membership proof. + **Proof size**: 784 bytes (constant across all ring sizes) ### 8-member ring (domain size: 512) | Operation | Min | Mean | Stddev | -|-----------|-----|------|--------| +|-----------|--------------|---------------|-----------------| | Ring Root Construction | 28.07 ms | 28.28 ms | 0.14 ms | | Proof Generation | 153.35 ms | 155.18 ms | 1.42 ms | | Verification | 4.05 ms | 4.35 ms | 0.19 ms | -### 1023-member ring (domain size: 2048) +ark-vrf's published summary does not include an 8-member ring row. -| Operation | Min | Mean | Stddev | -|-----------|-----|------|--------| -| Ring Root Construction | 330.76 ms | 334.71 ms | 5.07 ms | -| Proof Generation | 525.28 ms | 543.04 ms | 29.13 ms | -| Verification | 4.09 ms | 4.22 ms | 0.14 ms | +### 1023-member ring (domain size: 2048) +| Operation | Min | Mean | Stddev | ark-vrf | x | +|-----------|--------------|---------------|-----------------|---------|--------------------| +| Ring Root Construction | 330.76 ms | 334.71 ms | 5.07 ms | 138.5 ms | 2.4x | +| Proof Generation | 525.28 ms | 543.04 ms | 29.13 ms | 482.2 ms | 1.1x | +| Verification | 4.09 ms | 4.22 ms | 0.14 ms | 3.37 ms | 1.3x | --- ## Running Benchmarks ```bash -# IETF VRF +# IETF / Tiny VRF uv run python tests/benchmark/bench_ietf.py # Pedersen VRF @@ -72,4 +76,4 @@ uv run python tests/benchmark/bench_ring_proof.py # Ring VRF (1023-member ring, domain size 2048) uv run python tests/benchmark/bench_ring_large.py -``` \ No newline at end of file +``` diff --git a/docs/chainscore.png b/docs/chainscore.png deleted file mode 100644 index b879f46ac3da983b8a47989a46f1f0efd4829dba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 68983 zcmeFYhdb5(|37|IN@PWd>@u>mIT6`Jl;a?KBztd%>~%;YnPo-in8(WAvNI0Z**VAF z+xMwgz208GKjC+Mp4a6%7aq^YxIgaWcE8>4C+w-JJRv?cJ_rONR8)}B06vUCpi9ko zSAoBXU~>k5kL&gd+D;%4#l7<%97T=0>%bpzoHXR6LHRv*mVjTbyp(z(1p*aC6C4}i zf^Z0LD9T7ZcgISOP55(?qj+Z?^JfGS*lC6Fh zsqgb#vsPO(*BcWCY~cT%|C7M~N#Oq^@P88cKMDN*R02?AZ4j>#HiWr7ZR>0%@XYOn zLt`ENL;efL_2VOJzGM+EPn#$PJJ10WKAUe_c+#ZKfDqZ;aO*v;V>*=>$GtGV>80}qjTBi4zQiD<9{~H1FyT=3!@t=L7#qN$4J@$?4 zRriA6zubB3L~&shfhq|>f;_vaOA+;&9Bt(&p1Bmh$>Nx3;O5__i%^Qw5Ry5Buoaw)5OXi3L(Q@&lsdT|Ocb4IH4psMBpry6_wk~IXZY5+ zF3)CkYRA}C!N7!Ow{ZTQ0|eq%0nU(JO-(ODTa9UUzO^A>vD4;GU44IMZsCi^TYo6c zhCo7GYgL;>YBp@}6S9i&-G$4kzj3qq5=$PL3szG&o9vW5oe1jV=Kj^f2w_A^{L00+ z5+~0&Z5y#MGTnAzO%L*}kJua`GmQUqz@&q*exFwI> zHNq{&@tzL;mxt2HXqH#>xxJX-#QL8xcJ|h-_20g79FDgeP`mE27uhwi5?0 zzGP?`bxDjpnLq3&8Zy{`04J0UzD2{uZj{r^)}5{0IR#%FoIsOeIUc$p#7t{f-4USe z22K#{SuqyIJkqDTIE*(y<46-vP&}iXwdousktH^Z z7q1=km|7l$)feQZgV|9*gyp8X%5Giy&z5d5M2o$9@C7t(@Ni#O}H0SvHCoNjQ4hmrFF=EhnT!=`*jIg_30#hsl%j%OZ&fzD02s8**04= z-rtixx`kRa9_+UM?|>VexP|$evmqjSy!U)gx-G4*bd^<8kMSiw8Y(6EcIn?kgFsM= zj2nhKM0NCCvDMUCUy5ix>t3!3nYwmih##&KpB$wGX}vM2vx$IJI?aXufx2o`1y@^^ z8d;XGRX7p3G@pr0C!#BDE?6qaPc&8sQ8u%UgH)||{MEX2S^HJ@{6(qOzo>Wtr&A&RbiMqOdCcw^6){8!&J66trz#e;qTJn$>VEwSeX` zfvJrqGx5bS*%-$`sV_#Cp*1-HFY4Pp`MepF$L?GhQU7iZT|wDYzCC^ehm-2LK754= zHZgI11$to&h|DRG0!cnOiuY4sGv~2plTe@DfA-nR5?abbG+@<8m6p-w~-ayqXt_@a=hZJO6@#8N=!igZl{ zi~liR_xw)27Xhj~e%eD3(?h$Z7xo4ENr?p{6A&V>DnIcNx2qN$Y}HmtHd=IHjJYaB zeP$K|npY}97hQ3)3$fGW@Z!|}t87!<2c8ZLC^twHuv7wo(1h7@N4vJ+i#JAnO5 zkv1I0tGt;Lpb7X))1A?x=&dG?UIN1uId!;vZ{!*gPlU;P+4B*)>zh<61=PbAW&#;1 zW-|0Z1Uqorx~XhJLw2L2Arm=C-}+D9`wLX@?VpI`6?yH8+sV@Y`s(hUk`Pt$#HQ`% z-YmS3{=x}B@qoKI;)AZwgydX)q#{SRvyyU&$IZC55z-WO6f#^&c}XXB2;NX$Bp`WF zvHWn+VPek(6@Jd>inhR|HRX5O|Tgagl2g6HGWV&G=`ew$Q(PWA*Qcfo@qU3gy8k>Nus z2wgx((X#l361PQxuAx;uKrM7Y8rF}*~(@9=r0ZrG2d}l=Fs~Ln4g-g**p^=Ez=70 zzLaWByz*yj`&26co24f7^m?jes=q@dn?NXePc`59-0#zCYc3wk9D;xC+Sqmp6mE?WfT@ut>W=qZWJf+tsAJrnHH$13X1Z#G_B7i0%ASHTD0xcTM>QA2F3^A`tZmVqB7G=L_Iwx=qU zx2TI)eFuFn#HnzWboP(U6yi6!X!eHOS@qu;8bbBg!LgQgN>h?S+T6js;|%>aakePu zG5C?hD7^9>2XX3W%EpVns6Kflyn))cvH>N=H@SG zS@V8(2o;^FQO~M zB0Om)B6hK_STQGkUHKPqpq~drC~o&vLZhtjO<5$3O69uv26|zAL5@+4T4}OA;%Z#b zw`;a}YbQ25Jg7LZINMfe-0+vP8h!J5&r*}_&Tm7g2PbD6Y$)WnbZ>Gq@O_l~yEt@R zXTX^lwA&T3+bPdpAvphisD;Xh4UyB@ozvtd%UKQ%yy)Oqlt>6vg)Hz8^e%W-ADW0V zhzzsF9t0`mw*@w7$r~X;(eUM;3_@iM+ZB@Y9mnomc zF3Hpp_8uEmN17+Ek;Fti@}EL0X|X8i>LbYL)is$3nW13b3n-zU9nZKSVxA8=tAZco zY~leI^Fv2h9$1L8kla!HglIcEanI<|_+>jkfhp$ikwd&fxSmFLgr;D`U@~we*8-O6^$)ek@S*wyX&A-xlnjc6IAws3iFakak|%%->sxyc?>O2Ct~2r zP`_HJ@$-I=<*FQQGj$S*__UDJY)Cu!yQXeilQvrN$i%isNeWrgT}`<%JQ6|9_y&z= zEsb{)G`XNNH>5}iPWAE0vRII1@Q1@0L{gkdz`Tci3%dxdy5r1e<$7joTH>{EM}a)O z&W{EY=DB>>ExxO$O7)P?O_3BP%uWSuXYPMB--J07w~i$cttu=>mb@hIL$hnryg8A# zQ$tVh_H0&@VqUfZ5wj%!E=?Y?OJBQlltPuKIyg3T%|T3-(~bf0|Cx+26j$xKkswxv zcJo*By2h_rh@fq2=w*i79gLybd<~LQ+@W$R#x)#clxdLvF70gIjeY{&*PfxTyVkaJ z$ys(#DuXFrndd*+A_t^5sg=**5nPMhx_BJXVn6!e)9`zKagADLWt%VxGc7E7`#CX7 zbjP<<2)%dFE-{B&gi}&RMf6tA@Z7wJ-qEP*pjBDud|$+_5fV1|ABUj|SVL)^1^ok^ zG7<>B(~Gd_$EwW4hSv?c1FXH8pJ-3yl(&F`W6Nqv2=$}?nPJ7!`W)y!){XW)`Rso1 zewJLaCGFp=jUt?ZVo)kbzw>J+c_T)K_Gzby2s5GKe3dR%@@$1ckl6)Q+!f%V{+w9w z1PR_>K5~;-ZCi4gb$*Rx!qlrv#y{p64Jqo^si6keNd7hNo`Iq-ijh6Ay*)3tQz;x& zuu17F`}J9OZWP>996jt^bkj;)`fqgOO}kPAb(Mzt@}z*B2mJ*jV{i*K_ASqR*W8F8 zOt^(&4b>b>o%scM$n6MW;Rulw+hp!HrL;SV+{PO_={{5(-Fn$WydaRDT<#L44O#=G zRq#3cC?*jD&rA^yLO~7@q-kg5Q`>(5Ljr+6l#`zkSvQ;q0H)Qz^cX;m ztx)OH?dy$B7?=mF~^X+LnqdF($$dwQr$?z8mQ3 zebul2s@-hANhR?{v)Qr4Br6-y$+eL6bUw&(`3eYB{}vkIn-`^gCo_9pRYfaC_Unn$ z@#4;(B|+wZrgXAIiv`A&CnSv;Ru)xgtYcJfxH+nmx_}V$kCkwcX=kqX9$B0P@0x<@ zq0o_C_RD}mCZ%msjk|Kf#D`o<2oSf_LyFFo4{C^wDLA9zKx4lxD|N*i3l?{$%_PD8 zF2*lK5@GgM{)hx3i2*gH1B%&5EdYwsakOn=*J->`Xt6f<8mT|Hq84ELQWlsSGD_*H z)5Y<{K>||>AhbezBHhA%zPDiW=M-jY;{RUHRp5HsPb{rV6119Ut`ci7+!Mql+#>1) z5JF)%l|P80NnDcwFxfZgY#fZXFv3BQ;+akWdnDRC?MHMAQ8s|$lr6bnUSA`T@4MR^ z!i@!oxR1$w@Bc-23Dm#>EpQ7;iTmk9cgTy}H!Y^{TwnYnAd-4xXVqKZFmklgT_(J; zKUnWZ+))BlGyeOB{gqPAV*Rf7Ovdy5`o|uuf5hOIOVVt7J9AyOprx9LLfGCx6flk+x)!zacZVF=v+CmCGO~kOp^W`Dy9UHS)uevwKDC3rOX2uh?jz= z1v`e``b=zmeYvGvKjISaEhoCh!xl?ZN?5qv;ok_MGN@EP(~&`>TN0C~Pr(%!r!@<^ zLD#6n1!aFX6pd4JIM14bGVt-_>2OrWi`d{OSYBD1Qf{cDQjx^i91j^9?3!|A3C6V< zD<0J%xE3eFTB=U}QR1n|P+UBtdq?V%+7Z4Je=(3@dNqM)#Qh+@(W@*PO;30zI$Lrc zTLc7t3a2W@Qc>lPnj!A9Aeq5WM%d}O%Us;F8oVS zu_13Tvecs65cKRbDFLW;QRR47he8{eF9Qt~KYMnZ`|pZO0>(u-8VL+`d_00P+r~wi z*i;>>tJjUP9<|qVK@V1k4mpTz4ZN!B(xmND{Fd5L0ToWWh%sGuDv|o(oeZK|K0@`w znWb?<(4_DE;3e6rF~*wNLpY!v31n6qW1R$4jY>m%CTsdTZ=oEbf0RZglz}3GEr>b4 zB%yr3ak5&0bt2O|Z4zVZAa`?F|$ zpeatoJ;Bm(JHCR@O5D$(EpOy$@kXeAT50?M9C&puKoIwSvEryIpT|OE6xwy0a?U`BnJ-493 z1hRw4)6>;+nbZ8V3b#y-@PwC~it@R;TgBh%tG#E}N8JGUcw9#oSdu>RLeUi>CPco~ z>dx&yhm8CHNOn%HMEvW5C8uhDvZV^uUC>+qTT6!qVXll3tQrJE2m_cWt9Yn*)^}Vd z7LLxzkphW{?z+j2Yx~6|a{W55s#G^~#`d{ejTMf*VYwFO`y@bs9rE(=$fv%H*)jG& z7PMf_@Avo9ssvbO%;a>%6A)AB_;_TC{a5O-J_#yKnF)Wv?>mx~4fjz&*AohpgI~2N zS%SdP&kfm;6j#XxK+?g+Ne-1K~G*Ys=DFmR7_Ph*Q*A z3sRPcqZWKTK|>T-HYA4k^}~nDJ1aMwr&s>5d7)fFD@n-M9S$9t6O6;I^xsuYK9NRv zyr;HUEltppnYdbtXWI;1)u2Uez899{Tz>r{M;kwDNhk;~isD0O$C%i%F*nj3YU;aC zbcAOfw|^O9&*0UevQL1UH?)o~A;c=kbNqGikVnV=c>!3+)1?L$g9aq@Rc1jLb7-a+V}w5 zeHJ@f_$9)YvnYU#=Pw_|OW_agLfMvx7qOK77>6Ik>zJ>~ACZM=cs;RP|2pCb%jE3< z?z~2euH=k5!Pm^q&i8C`bmTGcHClG23#l)nJqeTY(=Iw@a zSBes}bo;#1eMw!Q#;JtVK$vB?S#a9+`F$rR0w(w28fOub)9YnjWlh|gZRvHDTj~X+ zDHbo)8=*<+!mBBF_1f})?IslRZ^*G$^EH!QQ?LEYtki$IZp;_WEnnp(TuXQP%3JfN zbOAvC{C|YU5%fgg!`=%4cJJ4}3|4pDgN@gv`PKly;)P7j!m6D3E3T>N0}1xRoK`f& zmYBM*1z_naDirfAKNUWE9*C~lUHuzz`Q=F>JYuynhK(n8rxUc6WIl-ZNd)d_559hr zot?<4@y&Ft?Ziu4+x4jrO$Z-Dsvk4Cs>mK zluG6KuKs#NDU?j*4{Y=!C2KaGw3c=9`xNR5=R)1rtCLDB)Y#H0pC1=F`bkXVb#yJj z+ZQV5;Y#8Loa;!Z^C0(R$+Pq8tCcK4kzOODzCig}7PkBrAStq#(KMwtv8eW$N4$S; zK~4oYgX34~6pAek@h1rLoZA8_c!*&~+vF3>lw_#mJWS@pweM`d-HGj#g=h_BCt0h~ z(U0zP*@T>v_N^F|WYR`Yxsg?oa~yvD@Pec`7i8&HF3GcBDI(jlvSvh9jw~9A`{BfC z^}u$PYP{VLPb|KiPleH%g+zGdYH|Xn_v7mj@Rd~i%i7l~0C&iv!TjVI^iwE~zaqZ{ zN9$0a%aNSr?sY?GN)7$q+TytlE$kW>@p6e?emx$rIqat83oAYzT)OZCwWY&SLbeiOBOgFnPbz$QFrX{E z)M||YhB^M!9cEo)K@W({Pw1#p`yF-B=!;(Glf*(AwYM`_1WzKSpEBnOktovaR!A}{yw!cUn_Ov{Vh zZby@B*lfn^8N;Jxtus2}sZ)bAL6v1%{jyhqmD+GghzaY0vLwc5b3WLE-P%>+&w)jp~pABccjOfZRFbziR3x!1{S?7@x_)Tyr(YGpt71&O~lIh z(5k)7ANXo7%}`%ZOOPEr(%Zj$1q>(y+lVj%NF*v9Ia#nyotq#l|{pMCaM1Qt7JE$<2 zo4@GgubnY;KQivp2lkv2((W=7i9HvaM)NVgv|&O+>nlYdLwBnU&oSW|=zTqRRnppMv4mUwwfXcQFu;z0nsl?<`lPDK zITn2*CD;CHBJcTuhce0bl5~tU7(P*M-77T&6te)25gDd1jVO-nEoNJTo%OVx{0#d; z9?*RmA-x$zWlSXsT?*{k}9q)Y$E7e=Xv(D`B>qox@4`BW}(~U z+YLj`-=aor9Ja){+WDhoI#O!(d$<0$8c<~9`I1$A-i@c`f@LPK5<;fCS_lW!D~;N! z(%QXQ7{09qhb6mmp17IE$sexIlkyj1^?4do+*Mihwubc`dNiXHjoxZWN|J4==0IC) z<*%Fr4v*1b@xD6(9LA^ll=V8?hHn zY*NbQGZSCvEVZC)tfb?ThPKb1O{(P?Nka*N5-TKrco>E{?47DR=?fb4yB8=}?R@m` z*jEB0;eFKP6l0d89kC!}>;s?^eak|tg!e!n4qOzMk*EWFqj~f%07O1FLA6x7&o``CNke(! zFG01n^+hb}@LN}>b?c@SxwMZEh{YKO3B+m;lY6T7n%&W!PFEHA%C3`5(yqCzrJ9xH zu?}$_z^>P`$Hlzz6MIg}${tww=eUO3K+-r6uVqpW1OgY6=cHTAdY`LXsv%qk|A3@` zua@;6K3aSEy38LMOpSB;=N`Tb(1C#*o>F~_UL2}?N8e>y8CpnMdK;FI)JFZW0>oy;dY5BfOu zOMtUma%q`|IwFc(&oAbmCo&ZuB_ z8v>EUPhQ`F=o78>=EBHjY;ROd`5dvmiEE`t6glm0sCI>B1S7u{cV@XUOPXgLt*@Nz zXg6h+u@YsM)x)|%gz}(@i0nsxvOpUP=(8KkpLpY{l2lf(lCyUL zQkEwn0Lhtf>8lld7Awm9a&1Hzuu!*uEmV}Xj*rWOQ=bNsWT=g=oE%MI!vg?lALevx z>u#M(f7yj>hfC`CO)rqGUK=GlmULMw`SdQl9D1tv5;TQPSEkrdtmR9^9KQ`lq})Tp z?|wn+i3QQoPKIr3BIT2Rg&5n79rT%Sea5B4RD3)Ie zXw8DWaN=XAVx_9=K&BSmcx~A-BN6}&fR}g%HEU!<0u1>}F(Yhams1*p>`e5|Yes_lK&BmbVMQwZTz5jr1#4z4}}2C=dmkdl*>y&#)}9bDi8AoXIa4LZOYgQ^z<&YmLKNUMs5hZVAtV^{U3HSzu8KN|&}rt}ImmomwPX2mxuJ)& zO?H|ltSmnbzSzKA!paXi{&DE-pPEYYvhB_3Sh4aoX&xII(f^Rp0wvyFzRQ4ASo6c7|swdL13`3eRuR=Zte%aK8W^q5c3R2!-t7@wi2w}k)dL|P7hnX zH%A|on`l~{HSVo>kJ}~04=D_YfBkHOeR1qblcY(BZ~zphl3NzUc^wV~@p`_TfvZxO zJ!4*bfykfg>D@?M{MUZO0sB$(7~~RX#Wf1gQL9e7W+QDTE7%{#%NoqoBeCBk_l{%F zr2S5rp-na#?8TNE8ZRd8%?eMi66Ykxx|002?1AN}TJP+ioQqRL@YC@H_KLU$8n9t;4G$hHse8SiC1xG0_i z@gb~~VD#UR@F4lP^==k#7d%*Wt3MbWR!N9x56T$})MuNT(3t|ri_@EBpb5|YU3vHu zqsJYH7}Y{d>#tktQ=fgo)#G6!j^tgK=d(3UzR^08m`4!F*16??u?e;Tp9%r&Oy8&- zr1#dMP*NgNj4$1%H`2oLU^ME`0ml$12;$W5BQ>K@MQ@@}9E*E1y(m}fzj-jlkW&4e zfZ#YeA3Y<|6w#8b+sA}gv^U!TH^VnFBaJRDuI9}qfmjYH8r2OJ55W5r8WL7=niv*| zxru~_H3I17t};2UNg-NP-EBVAXTMv%KI=1H@Ea~>Ro zG#@Jt-@`pG4Sv%Q!CFSL$hh)h#=t?aO{$`E2L(RIHQFQh-Ah1_NgL53%I+~^I<8$Q z{*P1Z2j?{ms<4?p{W~w9?|2~;BRe(w3+DgCGa$u^!&Q;Px0+-1t_>Bt`v6EN$D(%WSTc&l>b5NSoxgAh=IzXEzlLvnWew$!>tNa(zhw|T(~Ii6 zlIzJgx{zl8B<`^s=*L5FsC6`5?Ma_xdGc6TW!!S}8l}s6e(1>VgscK}jgrEEOf)kb63co!~~3^2a-{*|{m z5L2OMlbOS6N&makhrpdS&QmxoOwYXsCthY*3QWNM_UU62p~W-=+z8uf$9vp9KoU?x zl|s&~*sZ|6J`gs2XF`qLJMNOg$w&H~-4yC~8X3BzP3?#xO}IE;Vtk$f*c-bieuUtl zq!5iP-7By0_FGzlS>p4#nC!beV1F_^U1H+~2&9*d?&Qvt{(WXEa4Aqq7B^6B z82bwiRNxF0v+-K9#!bm9R1({KiU8_U?AC8lB-;ew8QWrd)!X@vaO}{DZDd8vQh}W|EO^Aq#lo%NOIN^R|)$8Vg*g><_Vj|#Kv2TJA$(0g5Wyy2@gm!_Nbhh5t;R;~(LG{MVC1SzH|`r$eRD?Lax*9WQa1nZwn2uXD;qrKafAq zQ9&vSmTiZkwNKh^W>bCZI5ab*{4!cIbC0!c3v52gotzefTBU0O{#j|dD%lEt_&93+w3Ai(F7ratx2dic-?PwqY z6wE(m+3nSo#D;_Zl|n72A-F8vwpLUTznh$|YSY4Y73e%pCui*nLm5ou+eQN_@ao+Y zXl~KV?<|OIrvf=zd}Gf|s{Ez<6H{6yGWsW*vHBm(mO>aqv_(tZmuvfX*WNadAtb~|S z+f@A84vp&e86VvmIJ}Xj{g3GClSRpR=~9Aw?b@tE3f}@CKlgf$$g9Tl3ITgi;b;YE zK>1FlU#q!AeIl+b*yb+HUA(x}E!Dx-NldEjf&diZUf_$2)5Nl?vgQR0hP(8;NLsdF zTDDzuxBrQr9yM$l=2+GKr~Z&m<6(n)-kJMuug1S0X2m z2-A3s%K*@xB!tBFkvDqUS5l|L?DXSFLp=mn+kmnOY!F9;5jmnML%aP|qJDTz zJ)~U7_=*NUWDxG+Wa4Xh2c#Nv^U@s0HMu>f0RLNs-tW`0br0g%+1(q}iNW+YwSA3u8 zQCzs~o57-RqvGE=iG>YRyE%gta6#BU21cN}1O(wp5FP`nbGG7`kKc3iR`T?}aJw3} z(jo=QpoHm9I3UHh0&p4S8A}}|toRD(yN&){7O;$8*iGPM+``_q^r$KGL1)@<3yY=> zpc02FF4DAGy@PCCAeV-+%f$MFPYm^Mp+zY9p8ohHC#X8Xrjf7+y zeHZkgW)E~JYN;++jriXppaedDXr#u#^<8$VLRwTF`?k6aLI_z*88JU}4!R=-ZVRl( zToRv1K(F6u!e}z%MvqPwygJQof)Y<1o~V8>v+-Aqb>%p*<)dR483BsYdEG~SwOO`L z52p9l$`W*CeA51caA4K0J5ro-OO8#8YGc^~wi6aaOyidO-pecW$zlQzOu_*N;CxS& zHM#+iBJThJh2|(FP~OUuud(zd`MkZJM_i67_8?SyTC|&l){h)EFCnOzR}aZjtr|*m z*;qdkD!oVdmE^+V_<>@lx+3eyBB7H+#K;i&wt`VGS(9llAxHXh>kQkjmX;b#dQ-%l`n7ASZ>6CCMxe+eM7#(?`1&}Z zLZ^Xk^KDW%J!80s>ttRd3h!e-uXW)6Dz$-lek!d&ldFt|vA6e^rP96-n;)f?siPjg zmXM}p!U2GL{QT0I?yaVSJtBG5QRTWc&|l{IeEET5zrG53)-+_D9=tn2kKOq@K=E*^ z;zec(ApuP1MQTPzz`*ejdBGF>UdO)QHKshli@Y&WM>{h3_%g&CaUGm;~#}1}FNS0YU3+%5PBnwvsgBlRJ>yEY3Ku zKA$y?@Q{Yy1^8E!TYS*iAp)Rn z(ox|&$eAzKZn(TU7(ZKy#A>(t&RKjx>*-;yL!m>(s=n8x4szHMS@&~{yp0FN`o2y- zK(aOMA*H!(W?OCzv~!?x|EMj;;33O=@6ln&3cUo~J=sIGL$}-7>UyVIx6Rb^A{s|Q z3CXiZyR*F>{hvDyMO9(eo~QnH&ju;g47@UZymlmO5w%)H6dXarv?|J;`t%zjQ@)!l zmXGab71a%rkEzF;s1}~+F!M+d?Z^P{!Wzr(VeN4%Kh6ZtRzDRaedo5qt1!w}k-IlIA@MTNnltL~Y9PL1D-^I+c1|nKNj_00&UOboFBfv&$uWx4_7xUM+#0*+x zczPe29J=2day^fpIQ8{WYhwuCS_-w@m8FB;xwNg!lS3t!YP1ub9DB;$I(n}Z%OlTn z&(pW6#p9vuHLX#HYfQ2y)K$kO_=&&U_Oj>pv6DHM?>^IGmT&t+6i}sqjh{b?PB}`o zKW)-#+MpmdW4M`poO_*MKu=IZJ-C&(|FPu6(H9{JF{RTWjgCP=odMHw+e6pPq=*@0pa%^_{xXl`>)40%7$xJy_;8Cf})D zrFm}2HXCQoAJ*q?aJ2jR&k6JRD1L(j`~Wy8cAP&!^|x?YiEMQva9K06u1 zyq4|;JJ0yHvG~Py_tw^UZw8Z~*LDvin_*Lm`z0IL#ckBd`26h?PcNQ265f}|`ccAS zZIj2J$($VTbU692us@7H<^q_I-Q7v>!+7w+BFQ7^LfY1sKS-QPCCULB?B&66LXiFO z6RLi`(^psXuexk|FR~@i9{{RW?|rj*rUdvJh6?K|>)6C})j^}c*vtUaG1e#0cS1(C)5qdv@~@zwERk!ki+!b?0lwVx$X`pPLn&77fvbai3A6ACLmOWj1oUyfcj zq+;);t^rEXbz6#46`>1{P5jv3zI%2D#c{GaK_+1!^O)v$GYUHj$=)SM9DagE6Z{hn z=K3VSP7g7>SF05cXSGICNAEm(NN-{R@hUE3u1YX-a4tM)DMmusGko8)P+FZ8I+lx+ zi>gUwfH6BFJ!EG`>IFxz`b&r@_>qd+q?$RsH0jRa>5OoZ?{HNCks68BHqjJLwN9*= zTi?)}Ivb*rtNij+`{gU$wwztpyL>;>tNy$euOMc$&Hy_#Ouf9w`YXieuwSQac%-dq zUf*C~F6UOh4&{>RTtp|J6iK|iIk?wzi8JU^A2)1=!R_h{T?LAd>}O zH-7~O7~=#pe`xYhiscLN^j4a%H!QLLb>f7`fnT@lVD}TAZJ^g=k{$J-CThiOWZ|3& zikcR*A&?I&wBWE&a|1dr(}_&rJ0Me-FNd%9f&=XMEXBh+J7w2772+fzAtNX4l(yV9_K-NqRKO`>Ucu%=4Gq=e8rOyCfid98U z%f=sU+()EE!*-p#y@Xq>x%Ah}W9Ex~ei_^DI}5Xu8&Uz=^0d&@K=J2$?v`z-i;&ql zL*h*hww=KFLmlp^WIRi?K3;;wz18CT##>@Xxr!3eMiNl^UNo^0S!&Tw#vP;WCb19N zQTJ4a)GI!V5&3$&kn)v6PIGP0(4~IE0*Qj($lcJES0^N_^v4R<63R@3TxREl3g()# zQ;yH+AzxqNg%_pFzkJVjf$ST`N(uws1Xs?^J4 zOEsIP>9^A--S7u;==eY$vy0f=l-hBh zZw_MZ)hvkKYDSg1KP)|THQu;|h8@4?3d6wIO|Ewhgz2;2(R!$F5OMG6ic=5VGtG%y zOfq!2V{>(^6O-`RRhj>n@kaH$jI8@sP?pPa;D-Gjj7(z2`y`XHydA{G2jRX%T0j7; zccWU_ibX^zkim!=VNvS1IrpM*+)t(XH1NuT?2TWf+vmkf1CQMpSW&GAW- zC8xI>&bt=TI48DUUjajL`eP{S!V!Y(#+UMBIWxCcE#B*PRe6fM?^C)~B^;$Efe7{V z{lJ}PN0IoZEth;cvq;V#+LSxI5F7hdydfr?dN{?qEEyBq3*nKgkzBgci+UKcJo6xL{LqinO1{f+$xtSlT*#B!OJ9Y=iuV#U zj~o$Wqhr@8^|<4y9Q8NH%|Yv%&4~b279e`#M|Rn|?K+bzo@w{3UR}9#ys;DMFHwXq z24R94CnWkWMAV{3=$^Or6=&Dc_9=}05)h5&r9-cb=5hC9d%!FzBv{i_Y~%~lOTSd0 zrO`H9<`Z=qHW`%G89-NNVw~S}&G7mGkFhI|-!7$Xx1)${-4-7PYO<@|2C~xEb3mha zb~=-x2)m&Rd*WDpzaov>CBrNI^jikuQ~F11&?ld;+wQ~H<(O&$&S3BM`lSw=B!e3t z^&h^7n3_}=Q->x!*=*V=+i^VBrQ^(;jn+0ZeaOmBoLNQhRkgH>lJnEv!VcdB%FmmY z)>*?;yNjsqt@xOZS^KKJQO{G_JUIWxA+ecHWEEMY5-Tz;Xk)LjVr<{dI8zFX2}1!4~i>0=qMb!twT(sCKKBqK+mj>{l}z zmFILr9peCjF|P9=^q8SIDcWSMcJWzP(l#}wP)`M973`kR@9_IOxMYB@C`^=w=6=nl zN3lmQOr16Ve0sVAq1!s;eyF_+(3?Ql|0E$_sL3j?}GjD_+ra(|f8+GU-l8BD;qO zksvCn;8xzRXM1;l>etEjte_Iu+1Cx$Skq~`uJpQ?M?C^qt$HdT=|(;iRC+RUjWEq_ zqpILV)Yo{Mvz_j4rr=ols(8d%T1}mv?X1`^Yzl$nZ!YVkm0;)9G7?wDrX_0C!>KH9 z_zRDd_I16dd1^s=RQ({S!l9UnorXNa0UjRx>p5Dlp6A+$m_en~j&fejp=UbH%4-f8 zI1z6ZoiqACa(nw{Ur5?56&M>}v?ee1eAKk%IX_xbFoC;0)U>oM2teFs(?l!*)#O%{ zBn#mZJynY`0o`KK-#1%^!DX_K5v zrAd(jPJKt0*wwxzqxoNWl`M<9uTslT3YBq!yOSn+b>+vH`)GS!7X8YsPzN6kNh4p` z$IMhEhCHhB5^EcGYu!nh?f#Uh}4AW#mu6z97bN>imdI}$_^YNB$vYb8`rVT{qO z3B&HoXPs`$c-!zc@dP4mi;m`(*=s_-w5~6AM=ybOp4Gvcx~+S|>Xwd|Uyzx9zIei#XA;#hq- z#6c5sxCE=^0A`ytt65I{CYHXntjiIL#cz;BCQaC7Q#<9a2TowCANukyLYN1f_R-hC zH0NYVy(6CSr%2nLUAy@LanZ4~`vzKqWv0SNCNj0%HP%azHT~CnKhk}!!f;ere!-p} z#pS$(YQ=uUi#tr@Nm3amyZMFZ56_Y@cw^EyHqHvKRE|VNLIlUP`4ILE4x`lq%D}z6Vy9IQ!34YaVM zdPpFKGJ!TpehxXejJnFyjrYXT#VH`|y9Ek;p9&q4>u6ZZqRhv!=eqGo^h!}78;weP z(nlD#qzggyXGuE~>e5XyOAnhcTWC?Z3&~e@!y4-%*SA}#`IeLIM2|%6D2ttlEU3TS z6pjX({#EwmlDWF(_H~3S6$h`j;raD*2F}Ao7r<*lP-?PP9e*EJ=2A7h=;50L+*p_dbCw0`<<-UnTJkdiEJ%;+^cBEA2T{@4AuVXG*T z`1g2;$lDWlEkjJM)HiaLyfw!)`$@c4EdmoF{APqhAyU+1n?9?SXGXxCNAjraNBd24 z+*_REts>7e`e>~bQBe+&BSJbzMK{iTE@)~Be{sYDxzboQCBBiJ({f-|oRPCZd zg%CMH;w1|xI)8WAOCR5R|5z$$T}51O9a+ubwOY!jE7lt1Z$%Kl*mGNrien7CFz(Nv z;)HqxATbb#fmn)@69eev)HgLD1z`zsvx3RhA> z1YL0jpG9oXU6s}0%LH$&f@D8%6lWm}ev~Tbh)0ovoZy3=OceR(MujCZ3ba8K31qhA zigI`oH;LrJ2O*0h0a)2XkNhTC*IR4mddnWGgtt>P3v-s-Z=3u-roJ*PsxE4KNP$s8 zN@-BK47x)=KvH7p?vm~l6iJa#x_f{bnxT^P_0^-MqRKszVzsJE=hO4W*^u3s44WC zae!-EJ^Y+v_Z2Smdr0{CGfpCbxF}I57cIHxx5b8`V5;taZbfHFkk7{K$qMOcYPJ<_ z%h*X4g^zM?-y|lb0DDt>x#cHVtQbj@P0auNfXatQz~pxVRu9DU&3jh%IC%>;?{V+Q zHz8yF&Sn~7{hL&QuSj>G5tdLK@(r8JzAW06mz-hERqaMVCItdr0i8e|7Pq5OIY{Z9 zD=~A&Ld5+W)s9>$)3qp=GnN7Ww&bU8VIQR*o)0grwgOvn;!$0K7>{!=rdHvZp8MB{ z=mCTrs#*Ep8My_g8+SzAqpEOAv@*yzj~)2{>*RdC?p%}eJpAW8Yn(USv-?YRm@nxI z(eW|Hg>Srge(xBvb)V&$*u9m5&;qvc!827fIKuu@Y)J#WuGm4^cNei&)@Hsdz5byq z{q|WQ)fqIdF7%ajm5$`wZ694Be?~0;H@+qi2-DqQu#Gjd#G>B!Mp}9>mg#*}S=}@M zif!WjpA;@YpnnOVOQiF2+Z5@J1Y72~Q-%oEL8ZZ7uX^8sZ0SdJeKvrbRNcp0418+7 zXVGrK^Pc{%*4K|3@XcJq5(rR3a&i0 z^zZ+|X|N@~d{X>oI3GmS4VtM_S#5+7#ro0q6chk#A3<4=Nh4FEO?W+ofXo8Q$&H_a z7?-014^n@WAZ|$I9h0JZwpvs-d=C-8WbPPn6CD)~gPXR_S&TjKC-_g#-PKB9X+h>b zcD8-=)->$*l0WUn;q69%;k0ZihohvfsLA95@Tso#s&&M@GETM=ExCzU^Ds*P!RNYk zcXXpYE-``@EBUjl(QDN;HVf$I!)s3b$cJnjn)y+W4l>9%#V9lXwdF?EE2ky&=izqSz#Nm_inS83F0wSaU=m07K#OXL?z zSPyo%LvQPYv#@OPY`;wmJOqrNS)||n{YkvOIhQMp6bKnlQ&p6hrIftt=Qb{A-?rvU zX^c~`fg#3p+DfAR&RSHl7Z!?+xdgPbKjl;UF8&*I^8y#Hsk34&yKY)~qyfDG8HCmXSJe^=fqEeDR+6bbu!O1r(v!XEwlFBiT#kSe zaGMSnpgM1;ewV+yJ_-PZ49Z)2Vg?7#vZxXDy%&3h(ia(00Gcm&*PpMMr1%kD+w(-? z*wUY8i{vfUO=3jLz!{;ZYZ=kD+ue!He-a&4unqw8um&pYV0_ypuJbl=-(gm?)b&f} z%?(K~upc$2s8KLYl0=V2U)}KyS^^?+IS7CdGEs^uiE)0K!0wkwM3RVct zsB7n!-V8>0;#j{I*sDk@=osGR3hk(&{13E#$OF&c)O8e3%{}Ts-?YVR(FB*!#7wy_z;ssDUbEnDYcl$n z?g5up+JFdYgtJ^UQ7@!i`BAL8RM(Mqv8-1#2TP^L*eMtk_F+$*nqO!t(Gf*yLa6!U zdkN}xDfkNF=r#Y4b}qU=CK3j7Z2WeMV^X}P@m;Oe!XmzTuA7EVxC)2k&J(|wqoY`S zN^AUcMx@U^cP`J-&v>j~oYZvQ%A0L^c{ZNoAxEyW4ou)(xPKCK<*nrioF4ij#mK^n z1>5Cckr@cC{w2o(h4rfx-&5n+GCzOL10BHjQ4$NBO0lx=VPxDKXU7_;@YZ5NNL!io z$1qG76#qr^EWWFGfBJY%$_uCy19fyD#b7zANPBuz_e3wNu3h?^Us`-o?YPtTut=p- zOVngD)HV?p85_lWJfFw)PRT&nK1fom;ASSH; zHa61C6^B>jLb4=Pn;bh=fAr_~220GcqsvTMu^9}MUIX@2F-#d0r!PZ-G>m=cZ1w;0 zor^&VQDPs*?oNrh7I02qt;l<@NK+DiHgRkggl?|6QZ(dLWEtJT!@|Y=Zv%3WM9-Du zbdX;-A+xiln~}bO9|4a;k-)s>#_TV;hj1AJP#CC4x#`51{+}mhVf0_AN`1!kvtQzsrTj93kMwO4nNBEK$?7gboy7mP6 zKVgb6!hB~YhCfld232N)Ze9Gd%mo*a6nv1(Jk1nktR16H0beYSFKSLi8}lkmdfla= zotCyZnJ7r!E>#hRiG)B}cH7l?%#YjJ#{a|a3esLVvJvmJ#W)c=%yNzB&5g8U%QsiL zhLIZ{AT&Ql5Mp%%X4IkZZ(FfpK84O8DXO*99diJjaPx|G0;Z`4AfLY}H~>?aYi0q_ zbSZQL@T!L34j^OPRoP>+5t*3g6eM-&O`Hm%KQHiyJ|KIz?KluJLyZ1`m~7^ zxDrqBrc1+J>3|*k9W!^F!cUHWgQb}NMy-`Sl;!y~1ZI1B^fs$bGQH_}{L~`ei{+wQ zGadDn^Tn^Z7UJewt6R>8L|uy$%D95CkzOfS3Ys!_c`#2r)dmaHCE6;8E>Wd6=NZL@ z=nGuhmr;K#RcXss%KJ{@Xzp9oog@^uZGx2fI>yyCe!#Udis;8OJzAIgPcBsfTp|83 zUcE?2u}YBixgl>v{U7>c zmpc@o0p@BR_1=LG=h#Bd4;0Oe+h;D7Q{&BFy5-~G8jyFAZ`}N=ARZ96l8r9%2ToIK zEdA4Ck1+T$X|NgziAHEc_3$&sl!s#fe<#Md}s+)=n zD@&SUQ-i)x2FkvCDO~N9834S{-p1fjklH95ob6L}Wu*Jm$KxSYeSx!5e`kk@OD$jr zIHJBRcPH#Z=xznnfC)tYA?bCJNuhThXp8?|WpNPk$hDx;*{-(jaAq#SPR78W9xg>0 zmYVMK@Dm<#;Jbbdnr3uH!Gq+lWl_508NVL;rP(6uO4Dibl*H>)21M1cq-E!l+NN)TD9Z;Z)nTHFDo*a> z8)zpf>US;5p#2y{#s{bCny39D6ni`?v$Mkk`M`Fdr?L_aJ4kc}*-n+-5agFEyI8EV9UqdMtV2MvL;P`k(wh8SMp6wKLjUITzK~kBO66T;VkR!EPE&$d+@(&}!dHN3Pb7rG>nX zBV+PH#HX6vij!mlgGKL`)4e#D9&NB=@5Orx-)+}LAj*~S&o6m(^U1SuJX`h6G>6`3 z1my0(*m_92^#qIky)TeJ&;K{;U`~M)T!!@0W-QB`)|o8irjddMvcBuW0S*r0#<=p!1uiVAlKGWtyt579fx8w%CiGA7>jbL8h@BN>$De(0_6 z4sScMOcIslV{J=X-;=GA>;1pg>nP#H1$u}2{V~y2^kIKe!Mw|*z`odd~7xfae+fQ=PbpEA^BRNjm;i;efMjt z?anf;pmlw)NqU=nD2*R&m-hE}B#8lrw<1W$!9DB@8{eXEJoj@v0mi68s;|v+bLi6` zQ?d^R?h(S&&Y4o;#sTGxd9#U&h?Or0v?-?UQi9eQ$!GE(RT2sG?SH%-aD}iP?&pXn z{$wlkbDp(eTB^*iXSq)}Ysj+}(w2$zwRZ*tD6 zVblnGs;F3RN)K~VP?6Tp_QtG|+)kJGAV)MFwR-JkI9XfW#}#BA`-Md3iPv{*5OQ$5 z!e6y&+y7ibZ#1yXLZb4QOKjiobIzHWuRqa`H%vmg1D4udV82CLq$gsP{r29>Jl@%a zV>U&=SPdko!^1&&^aUFnl!m#VFr&Hry-@x4H#+V`vvUz-TDA8y6hbs1&`xg>cvTWE zYi0hy6%reLi4N^>b=u#(SevP+ZkgUj-<$|$+u5&rj4$STqT1>He`m}od{D)MPjM{j z8s>87r5s=XkX7P|OmnUmVSYJ|-nDhwSKe7P}viWZTKXX`APzGk^B4?GaKf1!GA zr^nNmW9C~LJ3#mfyQ{aTa8wvAd>snVvK1^UvCk#WR904n9wd7iJkuLSCp1=f{;el3 z!=&HLUcaHFXILd|y0!LmP-bMyb31`@W& z5*_6q@jJS>-c(l>C8J&70bBt(S+Z|8qwX8b6Y4x2W|PEPl|&RwS|n2!MlplUOeX*M zrAm)Dz(!0-(j`ocm9mZAJ0C3s>P*$>tS2OQc-zCPy^KJIQB2= ztxWVmq>(!O-@J01>Sf#*BDT`o!wK*_f65 zE03GUOCbqAINP}QV*m&OP)p_-2rtQjh@Mt(^-=LgXIT~yKWX3!yj>Axq6#aXeBe~| z1$@Ip%c!ZkFvy zei;2syvkVNV)e}zFH>LYKl(qC06JtbR|FC>BOJ8 z94dx6KF*fM&X~{DQEYor6~}>0rDw$01^m@v7{)gEJI(9TGBlkM6(+6!)$@UL>-tK$ zm{aJxlF>TXj~Oem5Sb~HeKc&1RAv<7+YS8Ng(gErj-B-0&SH3NAcL4lo6}qmLFQgh zWD|>TE(-}n*hE;*!?VEx+SJE2OZnj(Z$neniq26mhnUIKQMirKF_UL5OZ_ z>B>Ici|qMT#3tJ@TF9$l`(KRW`JUlpvX@%7F&~<)Nv2p!<##94a?a8vuG3N6m;WyJ zo&Pp|E#otq7mp)iRtnPDgL5usfxe?CTg0ni3@+NTk3{yPatuqL^ZIm#JlYaBO>Jd zs8$Rq?_KCa<3I@F>+cfC0GRnO79r-MFm^&J>QfZ%KEQeDi7tbjtL;hviF0qUlQjp#h$O&@91KI z#7&pm2VHc{1ElNR#+8{r!d@ifWpeb2VP2hIJ^C2n@4TVuT5u4Z6F^?ny4|CVbC3UnJBMtK6bJy97OkV-9UKt;$7w8_G}ZgVvfUcQF0z1_;QlK z1aB%N#0tnBA6jPag-{+JKpxjwo8d_pR#mhDY-) zeTdoe=O#>8NQ1XjsS6R$dm!mLmpVC*9l7=1T^R?t$bNfh^2>24-K%fMUi@Q}z@v|b zElLX_$k>lZ6ypfb8KLD2yjn%!=JO1va~zjbT;!wt<4=oJj@(WGX7CzNv5NGa!U}}M z1==Cs2R?QDm%msoI`#o?Q|!EJsQ-q*q(3qxnU`Fy`Ap3++dRgc+}%y34sQ(H z4uAiV^SH{H;IJpf7$T1Mx7*lpHQu9O2~&ygh!CXSPX4j&7#UCh)~1|9Q)0@>!~dM# zl?MC?bM%Tjf5UdlNQ}HMRDKw;>+N;Wl42-`Qq%Km;o=Lfw)15#hvZhe?@b8}nM}JatnUlw_~6#6E6^XDp4Rq!!)*YZV*<= z$mW41#im0U%VcX^r31&8rrtkTEj9WISJD>g{+#@~p6c$C zeE=?dd@eDg+sq&cw^x4bIYEYQ%L-Wlxz?e;!je%N|@RTW4z zpS?ofKi$;pc@wa%sO5~l78uS6UJl*oM|Vu2j1)_2XajEVhXSQ0`ZDrdhagF9iq`An zrn7-^?Q@L1&cnIQRC45Usi6*A(kkqgFs#VErh0(>QvyDLNfk=JG+)OHVf-l^nDt_&5-L! z%6LbUg~!x&;S-Hw=N}yT#QB>)O)G_~Wp&s+8mSwTM{oaaG5_7>$PBf@;{ekQ2#Yyp zH@8O+JAm{mipNIZwmbj6hGxG-d9H`31{BCNq~HS`?V;DZlL=CP8~2rs3a^{;`2DNUu*nv8!9%rEA zUs0|jPXCC#rOKi(UJ8EB4)(5FU6K+oh`S+fGCKfDnN7PrV<$}uJ?AQi8WH!W7wNjw zO&|7%+U;z8U-sLy!;05OUm9!T{OP6)4!VBhFo)U**{fEbJGgo_1x-;6K!<{3+3nWO zZ_ezGRAwe~uI!!g6GUM&W8S42VRb?&~4rX zPEU-YZ7?TJ!Zh1kd%S)dXFqpZFt=Cz@iWqHmvxqJ=Wo_SKDa`kTp|yipyrY>g(K&w zS(0oaZqn&-`SbqcXUPfGi0daR%fTJuhYG=CfgnCPpeWEfJv90x_A?fUcE%D5qv8Bz zkV;o5IM4QFw)_g8%Ene{`tNPp9;Q;YEZ53w|1z+pVvMDKZ}!Ogq;&z89~!xcSU?W1 z^|6-X_y#|T!!u7^{$)aXF40KZ>}}DM`z%fp{He`-?lK|b7y1;Rz2gJ?_X47@J@5>p z`TdX0#O8KYiSNJk^muuIT~OPBISKGB-G4*H7PXXqB(dKSqOfuO_F6QWxo?z?-_%N0 zdgUddad6?#`qJz%H_cO}}Q4h0e@gW*|1`(#-4cMpW8x z*IYXqjWIlfcTCdXw3E;$b2*sXrgwS;zeTP~s?=ATo>OCf)aTK+ix!jq4w%2bfNYFw zE1(#Gs!>aDz-fs^RW&B`H{y022mHcthYf7IyKiQXv@`oNaB;nPs=#h->XtN8&i$PlaJ=ho)#d$toyqCDoEH=87%U;(Y*$ttL7_PL zQqpJOhwV_Tl@EpUV3Z(^QHp0TFbY{(eGn6_^9F!R^7{LsiK z2gT&$zyK$KbZ$N*+&-RdT#c%2*Fd<)WV+f>V=0E=?FkNnk!siGktki%^dDSEya&?FIxykh>uhkMMs*{RokCv zq2wWG(gn4;HRT-!l9Sp}^Qtqy^2cJFAM9QQiXYi9dpI!!T^Pfvq=#!5_h`PU9J z<%fuObnAb~_X@0Y5%Rw|5-9xyugkDfSM=FiJBG&B2SJV;Mi45~?acfSKG}qSjQ8}Z zC*Jf5@>U|RVr2A!6(UuEut<3_vh1)wU&?V9|bIp8nVXRcBl(7^}v%Iv0||stP0mzVLC4O(0Jt(R{WxpsatPyxm&u_yd~o`gd&@h^!ZTOu5iJ0s>UbATo8E zC@4ENEV>%IfBkEmE~3U39w4M8h8$VS4=rIPtfHNLQ)U7W+9Q6hDgLJ-L+kbpKXH_* znt01H6-bAv-WB4eMpYlm>ug+fVq{@UH7I5g(dJq(PSNzY*-VKr7dw zh-0V6V8QR;%q2#ARLAU9Wr8BEf2Q-)^)PmWk5rA_zrCu)MUyD@K{*$j`zPN$g&Y68 z#(Kp4po5en;}Eqb8=zUYCQAmd5haoDO0Jdz{8fh=K zqieGuz$5AU;B2G&)C!MBH@0gcg;&qFGo`BEmrLP1q=00b#PjU7D*!K9D~J6Ms#r*A_<4Z>5l*hy}~*OlL5ew4a)UaSjAWZ=~km z#kysa`8|G(tx^Z1_XNfQv&|5W9gyh=ktM_>-p|yn-JOkn;Pa^^N=#0o^d{|@-q<=C zqYs_LR(^C~2>|Rb0ksu#c?q`-J??J_5VoNO2C#iKkjTbrhea56`+ko>8=Z>&WN&P) z@3lM~vfh>9XCUf!;3T@-mYVnj=|%;$@SuGW>`%M)3e`&%MKP-=2Y+@1Z$llOBEL zEpqUAhTM^V-`49iTp*<6-L^5|V?KKyKCkl=654segEqfLHv$H3+^vB44xqO+nVA-9=^_d!q1^Lo?c);_)d&fX{!-A9p0%}InP6gN>+TMPKCwb zfYRG6!@P-uk@(+1)a!n%DA@30$49-Vt`uj%4`{5Z^d8#)D=o)^=tHh7xhhVE%M8Mp zxPt_B#LL!l4rsFu!?y0~mq%>OA)I%dm-_0lr=m;cJnz{?#7UlFYwGA@)%NkfAs$W6 zN}xP2Yn+klbpEOOl3i8HnC4GUB2b`)So$*|9WRZ)4|5y1FoLYXoh^siJ78MDgPk{I zad;42J>To5zECB-?)t*Yaj4O313n>JTI8`Bh3e` z`&)1EqU})Yi!=+(E}zeF&|*`Rj?UWi2a3r!U$GMOryf++E0?v^Y=MtD@JB5-?nUbI zQEChrGWgBwGJGW{%hY-dCwOe?sH)!y>(LKK3;T44!<`~P0z`M|-5l6*muYq^^YVdM z>4rPhx!$|SYcSMp^D=@o=Z>|b)^JO=UbZ00CQOFnr=jFLcD^0L-4X^zK(CH$+- zq@8~bHCEH3aU~RM`RIe4bAzEE1y`P^z<(EfoEgs^f4q!R6z&sboanZ}L6`Tl8RmMd#dTTEre?bcJ9EjHHE)05|jg<>z<1bJ@%D(${}l`E}VH zC{nLQfAI!H;|bh#%gMsz3|-%`>P8oAyY?JH25n|^m6D0f$OrTV5WX3@yEs2~X|es^ z`WXLaU1taybtZFx=)b5W4Vhi5=-yAtDnXoaXb&@qhV=jizIzewa|Jc1neznfpE;j` z=;Uwpc+$$W=MYYDMf{ERzBFnPu9I_1F_$%U#MWg$LGCG6)&pgWgT-a>~7M}r6V3*$=Oa{$5oW+!ME{ck%)v6_VW90_Nqvwzz zj7~3YyHmfvycW)Om9#SXX;yjNb=^qqV6|7vfBRJTS3l=jfNKa-WCZqCkKFDgOmXRH z#sUAQ>w&Cb*4d2lnMSi_&uo!7tWg=D=$B%D6OX(C+x_C}*!_iVL_W!{tNx|#x(Pdj z^KdIY45X=9Auza?W!BHc9;Xqj`6>@1MgE_q!M%qZ=+kH9qf7$v(W>`dBS#ufhSMzpr;E#=T!Rt=OdeZ_dMejSH@{`pc^-H$)Z73EgN9!YtM8lQ0(D#Y!qmOAcB+`xG}lDNY{WF zFSFihOK_7SdKaJ_H9pE)P3tJR41N65yS{j2d|l2Md6J_+$r9W-TnQrqkL5dI&`EQjzrH68C&4p z8uu;_fp9FXdQnnEW2P>5--=u0jNcWCxt`EXzqa{I8R~a)d~IDDx@@4(jSEmCUjl@3 z37bO;p@UpQMKK5Y$CXmUD+F7*36{@r)KZ(=*s(u5i9I7*F5ngZiV%AJOHe$r7T!!sL8{}2|bmf;l#5BysVvrT52^cKz!g%!6?ry)y?^t3E| z>b>n$`}1LCphWV1r>{v|2u2(5NJNqc$**=&c}PD(AoAh&+T6eU=3?1ZvjRGqW$t+8 ztLts?cPvu0=5Dh6=`r4?*3iJ;AqOPmlzs+t*ME4*)=US^R*ldYpYt!0=fRuw&5CL=7#*EeWH)2J>7g;-9!PTh7MmOPW(>XavQh~hguvqc#%|)pMuO9{@d zEF7-`(0WfB;O<-_XY@A#6p)Of?q6Mvwov#zZmC1isF*mig=+x^x7rriS$Q%>rrTkWV&OS`C_t<;#hka%|dUm2{l*FSIW^LQB4CP(oq-+~EpY?kgWYRuEVIc(q_S z8tkifH8?6uH_P1Ykwcl|HD1@ArYQW@*Cp|2E{DpJ-l~zkTNtXHit#2tv%w$^_I+<# z%w{@*)Oq4H8XM-y-d%EV-%vV`?qd_>sox#0 zl?R%DBh;A24*(jbZfp3Ky>Y<97j9TQ?;qP|+%>~A!y@GNpRRY9Rd8da#x!r4@{s9O z7bdP;DA>VYus?1jS~QhtEZH1KC0hiAs%7__?H-}yv)*jk7Wb5}iA)lh}= zL4Y<3*M#{tqEV4i7v|RcAD^uXXcXs!ZaJU3_qdh+fhZNy@DaJB(({aAOdm%oP6M3j z>1-Sho_ckb><*9)Tfy;k)bg58?g&kh%6r4+VV1F(#i>6K(7R8s9axN+R3hU?#KeJ- z=e7v6_etR*)vy>5R8>!NLvzWI?3pZ~mgYDjsSz8U@wQz@OEcF_Wt(|5tFb=V2#eN< z4%|hz>xSpoBL_jd)(=0MD321awnu)O81}D|NsFS;k^L76-I0AO$1PRP6*==Oc;QD+ zR7J<ux6_m!TIi@0H9ODL zEBz45s7gY$tyce%AiiV>OnrvOr0I@i zWTsS)vFv^_c|DQ1n^ph;@hOB+b--XM4a`vJacq>hdL3VJyp1O(PW$hcRkZ$u;MOrofwnvGU8AK3G2{Ps=J|>qC9uyPza5Hr?ITzcr2jI3Hg|9y4=D=Mh z8j}*JnnJqB()*e>A$f*?Gp~K5M@V{peJ0#gLg!BpO${y;bsx46v?4+Ftt$^3LVs?I ze*~tkJmW<{lZ!jc8N^y^H)Nk42oF2o{NE1D$OqRkTFzkYjcv>n3dR%ZT|)iQkpTRZ zuf+_|C{*}!JRrDtM~uykyiUDJ0bXs}HQur5wWV-~4@G5G+nTbz%**-rDl0`QW+0C! zy>vHKEi({TV=BQssqnXbC)p%Qm=mOqd;G%uhbL~B>MI&g;tmi>E72}pd4!lOC>m1IExX3!`xjv9L%2O5_L)7+QYSG zyzUP}2o@0LQFO1QR_RqY!MFzr0wO<`HCGbW=OytZw3H2m%VtL>R@ra z$aN!N#Ou3Caj6>cT?!uBevb_aU&2i6Qy}>E2k!6=VuEW{yFdvyD*<(5!VNRd+ddM-?TF#8J@mp-pr5z=6hlSS zMFP+KC0*b5nrhIoC!(maAW!VNZ8Eka3{Sy;8KIfwkp)$>A8)j?pcl@GxuhF^NftX8 zPA9(jQ>0r_XX5!z+d${Uj-q|`dp;`^d=Y&Nu{7pd(ZjsvuqjH5f?1}%T=;qqTAcm} z5x5AQHez*3*r54v|4tQa6$*sNj%7S?#Y$WQJtf7haaa(>yCc{H>6!zFtf;A(hVk$Z zZf3h@q|x)B0jmESnU}u~MtHRGh=?ka3AXXgDZiO_edr^4^$H#?nC?ZdiVKoai{E`U z@hVwHPwBvJx}v^;QjZxAQCpZbxO%Zj84cNKdSM~C)QG+Rbi1(emECM^4*K2^1cy#9g|0$a z{5(xv?0T~9Pdly0d++ZE ze#xci(!ur;rDiCg0SdlmW@aKd4j34bJ}rD0=to z4`+aqLivXtE`nD|Mk$n2*dYnYx-ms=6w%MSzdlgSoqn)S&jZ8th@ud`;?aZ zwpK9iu9ePV%pxi|J+f3@pv`;$FSs}TjDYA)&_wGbid>)n3WlwzEd*b*T}Iv?@YNlI zi=$QXp6!=ZB~i+9lN(E*CaJcCx;*a$Az$hw(?XA^WJcR%yag&Yuq*ceRu;a%@$yMJ z1k^g*c`Jxv#x68iMhlKjwz>Ki1af38tI#T~ImMZS3o571R@VuIdC#lhB6}Z__mq7U zNzDP&d-Xu1H}e{T7yhVFQ55};m<=}QUTf$eqoBClaiVJRzJN;%Pe5+g1y6=S?6D&;i_`G%qO@DV5Pw8N)hf2{Ay5MD$ntHIzzgy)fw zK5P99VTpLPx2orM<5KJuy*Rb$TAlr9>rJMJuE9t9~NVP+e2kr1gdrRgN}FUlP!&L zzWEAE8i7?G+TbVHqB?2u>~WvreT)YoPP^Yu$im#0+O@1)=I;*d2uQ4WNTD=4(cm+% z>>$H$GNURj@3GOh| zKc5~iDeZ&t^II4OnJ+xkYpe1}=d*Qs#y2BSP_+ZcM$Ya`1v6`3E@z)F!_U@j?U@Dh z(EkZbXv;M=ON+()6*lU9gg*m?n*-w+{Se{2oUnkHRI}iGeh^+1a!k_l86im1$RWi2 zp-x#9)=w9Z%#*AZ!k^KYXiZ?8f{W8(+7;C>M297>4Px;+GIdzY`U6Xrxlm zg0G;y=53@I5#;?B`uAf`nzh4gEucyLB#~g7>{P0r4&!hxpno-Xo>scy3$D>^bl(;> z@GV<=liRc!O64(N;C1-sHrVRX&NepkPJ#oB5!}%S#GI#xZ6E{s%8zD=X7s7_Wf(EL zKvN~=E`5IzmQTRA@u&k_B=d9aj5$gQkmgHfUT6FAG?N@BZfKt)D9lz>9pL)t+_Zr* znAMBfmk z;ruhb-u2znJE+BhbosSe7waKoGyh5dwm-C1%I!TGOjw76=P{8EAGm^bZtb0m*A?_OTBP7AttaRBE z>4xf9F#3J?>bx{NOEbx%yT|vp5Aq25S)0a5)^&A{+gu3kijg(SPIS%{(d`M6()@oQ zl`C8W==T1$#TV*T*3hUFmO0aE2a^71g`ERRIy~}?taAyfupYU+rw`#_^dLn z=mo?da+~-xs01o*Dj0WljkI#A;Wu8>wHg?I&1)*2H(%|E$>;$wiN0yHD!rXnh-Z9% z-SrwEJ0XyHa;Ihzn$P&v#-0}XaFm2;rBwO=YTwG}hUfu!lnq6|*k+&>qjiMt-x&9Zg7jV}C3@c=fsVo`OC!K@6_qS7`A^ zmIpKnwBHprO+Ymx8vbU!zUbNdG_3f4!l9Q|`kTMJ@%Ok6U*cR%jV`QEgN#{pQwTs4 z96I0n9E)?Qg|8Z^&*mEB+)|zb#8IxAeR+MTj>C{0=NbS1nWOvY*jo+_sNHhM zmcA+O3Rjd?5BpbK@tHT_BIPwMP?twNAMdy}0G)M$zzWYnpC*^I9~&gFkl;gLS*81m z0NnL(3erX7Y!m(=MpTba{^AJnsJFq-j zcWt~7!-^+*DHUn&!yPDsOFVcYQ>ovyJgFEgWI+FN*676%``@R29_fMTFc1^psm?X? z1!|)CKp-b-PLmSa*}npGk%A6R*4dFeWp5?VHU()Ny&k2Ku=N3CoO)R@EC$6-LzO?+shK|C_bRfc8^ydx%<6^v^(pIc=Z-J`^@#Ah35-=a>6x(dEVS{X08vWX3uJ7F>XROb#U~}2tY))E>{uXyVcKg^vRO9W3<8jf z)APo}VFi`LvZB|MPtdGFR*Nm={5lcA{+`ZRg1jcLr4Sv-QOd$S2f{YhBXCfj1z-+G z>pM2l!xld2Qy`uNMAF*9m3PPZ;)%Yco_E7Dv=ZEwRRU#{V?w=F36nKhhkvZ z%*czGTip{l$vyo=UVRm~n4_%+f?_&Das^L{6^LePUJWs&iJyl&1cPXRuW3;w&9M&6 zG0h5#E=gy1idH$zW&PjLfNVy$WfsSy2C+nAB1sLNVy95x3260WKLW{Q$5Zt6WViz} z5($(n{^XgQnN5eXj(mL_NJ^{Bjh*nJOVdWSJ}sRYu=9{dkLw9mC$PY}YsXg1t> zi&^E@UVVl%m|BuA(>hv1)sQ{c3T{24_uA($s|P_QR>YuUpa9AOw0R`iC+eTyX_iNK z{osPj0Sq%0NjV6BP(*;(RX7I`=p4cGPXRmxWi-W8$(3S6_l+g4^CO4&=B4gs8IN7z$vQzj z2W%6`jEf%#`rexDD2MIt8s!z;3TDus#Q$+3*%?hb995oZ+Fl^u1bPGa;WuZ$Wb#}f z?Dy5c?;2oV@P>bUA!lR|W(dzCg7_L!ZGwp~B_7iZ^hDYf0|Lq&P_|iRl1pxMvsi3M&Hs=+cLv zVg$MboF4J*3Z0kAc!f?2iUk-P04L;Tm8KK&V5&nZs%^@g$;?BlI)H6F!uUgCDoYfp zyN`RHJ+m*96oQ@4w+x;6Fg92+ACsynh}<`BKn}wG<8YP{SxX$f2qEYW zb^v&K;5IPt34H#K#!Zjss|(HPg?Yfxq+{#H^nNwnNCYEzwLF%3p>{LRjA$}^BuXrSG`JmfrImEOS@3LgJb+w=* zIp*ovL?(YtSopWoc91>K+)W`Cy@{G>ojY+w+IGfq)H6W)pl?OK)K}@K)_!gpa{aEnDYqwut zN+q6uwHCp*N(LFYjZ1qvANYg1{YAQh_a+_`MppfQy-=47OQ!kR)W?)7c!8CvVg}JB0UNjc;57CT!q8E01H^hb`vabpzkyTkOwKpmr7_2oc44u z%|F~zJ}L$c3B6YOuHR0%j3(~CPt2jx*7H-rBRdvn3;7nKG|0a3EZ3m% z>d|V*gVRK}LIi%=5WU4?+Z$( zmKeR#s#t*H-c1^m@ch=-D-3!hk%MQjDyS=N=M%zdyu6pCnBAWaQIZFj>~?Ujc<3w} z`)Qme9`t(6nUA?jIXi)pe@r@KbS^IV0u`4&-@w9vham432YFZ-7865?<>u`jU!8JA z53y7BH$foL?|sldy3>{x@6ym6_FZ{dF^+AtS4IYLnY14%B5r9lQPV$Ua1eT}&a#fU zYvs$cB?|~fMBhTlxpSqowmowSUh8e?=CRu-SZs~Q5z;K63{>X^Vh4Ka z_dWaEKEkzgB2Qsoz8a5)zw^NxD>-a@PhYA{C-RECnEs1Y?4)IpFnqPVk zwx^L_Am(cgfuIA!B(CrM8o<7*4szzK+Wn>!+c531gn&-mC+o15Vc2-6EMfw$c40pYCD z5eMt>_^F1@50{~>U0q%!0p^{n0FmhKmVfKCSX4`AoiK;x%Psq_A#uBl4lYE2-95f^ z@VwES`r6S01f2Ggl9m$HalY=WOAu6s&*h-zp)8fd)hZwU|9-uX!XLnJlwt4D%U@HX z;vloc41BX?MiBF5(_$2f?aGJ~;@b0`8#iCO0H2nYX*urb zUTa#R6Rcnzd7+(AFDEpmuYpc`5ilAy_5w^X* zscNGhR%`9TyyE@vAQ zTz;#(%~OYHG*AsWJ*MF~cdPL$apL%_F(tsZWK3auZ?eQ`Ic3M6pdHW~q z^QEiB(c$9UuvdYZHlMVZr`^97lW_@3cl}#aEPv{FJ-n2aLM<4dFE}&@;LYSJ8tF$w z&VFFul+fY8cE@gyjRB;TXl1csMcP zGxp_5FxLMwYA_ybC-d8L#Kz%Ki@+&pk2-dE>clhI#H#hEc*zQp9yMlM8*xK0lMB&!7 zgJmXP71>yR8ULMe#W)UYaB8Ys=F)(Ud%q7jCmdjnF@ZJ?jz!UEK5@lVSizluW?xol zG?l;q!n>Ex%GF1^28;|GUdj{{+Yr+L3c_MJEXMV9R>*s+!P6D7wVu6bghE->c!2K% zb)Sio71B6o!OK;db+LRIeFdGd=Za+F`|&tgx@X1ndbr)0UmmD>9CKZ>0D$s*oT*-;NWzgwoou@YX*b4U8y#CR^=*v|o zU|4e$f7vBGr|4zwq)tD+%4qk_{Hpnl7{~H>|JHpQN7&8wG&s$Xkm`MpC|9LZ*Ce6W z-KEy3d(vX0Y{mxMqNA?XV%o$Xi(6?h4|yJPc}uA z1eCk)UV~HSqh!jTYqH-QE#7g}&@^cw43E{?)g?(JE9yB~uS1^(PJc3vUuRka+W0G8fVX{&Y*WN_-qv@U0t>A3@5jjgB3b2BsO#Ke5JU z!k=Swrl#<+gG**`7P3`FjQMc?2N-Fo<-EIw!Nhv*UYj?IlYbbW`>*6Z$Enrn>$dcW zB%~=tq3NZC@(0xxDKPpl*tftZNN01Pf(j7OeKN2m@Kg(8vHb%cd+Y6wA3*j0ATjD{ z`8$KW@b25~+rO8r52~!!_Wq#Oqxj+BE0Y$#&t~zb4!=BSo%((BDo0%U;vYcN7LrMN zE^Y}=GVRePzn%KVQp8nG+x*5Bj78igr0O#pk1e-jR@2!Ljzv^3I!kUWg<*YRN&iWV zo0u{@wM?St_)f2{_Lbp=a|&Z3Y~g^ZSt?bmy76cpfvkEu^+o^bts0NTSi0Ze%1D+< zN#-JNvJJY5U~#b6U#!q=tQ06~w3sjTYNletAyXS;djAPAJ)irZeH>DOP6waV489@d z5C%3Wf7Hl2+tXMXHhw8}q)pOZc1e6yV@R2<*U&E*S{OtSxP*r1y=C@g>RcjHESEfB>xNf+_ka z9Fj^5iUr!{V}-ULOpgcUnDKQisVlEt=VmBWMPo z%_N}^0pyVu5iN&K#Z8%kHQLxC9edz*jO@Z1-`L&OPo)u@9`BHe5A~4oNXvDG`2?nY zCc3iFPEE}IC*3v|%sy#*AAjMpR`I0lW-Vzu2_56sr(Rn!PU2dwCn@sC_pW+aQA6y# z7g(MOhWsNc+9VqXeYVHj68^Ly@R<~Cxw^MS>+NqT@ET6{CQUJ?%h-aL3VobK`T6`G z4@KlRWB*sz-V?EdQy|tMu@uk!T3lnbC;z%2l&UvU!0@l`WN>U!CxH{%`a3Ab|FI!a zTqeXC! zZKnnM>6FUqrr+yx&K(RYU) z+T}I{Db2Qm?3$)`^X_^EhV%#-84W9DC-}%LnKRwH1g9Z=T?1d-G&({Qh$%I*>Lo$WwT!NPA%iF;3^jvmJ~LX;J#ixV^5U?hH%h%4%tNA;a4=rCZ*17tg5U z$&?juC8rky=lLHuYglr@Jg()1ecK~5Lf2OY5BRmto9)dmj)@F#cbmaRJ?4gg{XCoG zdJ($S+NOkdwWgmRC+R|86p3Dcr^)M>q*KgI)e})f0=w#Ns8`{fk+DxQ&t|>VBp!hZ zDV4+psYOvj&@c1Y$NRzsKAiXz!a00zib4&zZa)I$h;*DE*L+jmKy~TQ&1o;2JZ~kA zQf|$SnQc}d^sBOT+lDVSldr3dvQM8MzQmOaARcGiEGYF?lonRrp!&^nwuAk7Qq64> zWIOeFjyBBos%`DpLZDa6fBO*XB>wsmALRIvyCC%4sZ<8thQxsf^j)3tg5ruFFl(=V zxUKncFIdO%}2OUMxaGsa*v-Bno6MHO9WxRBJwi}aM zi_$dVW|TGOISld`Hu@;>Sm&gqH~nn9F1+MvNL?~SfA5HF95qP8a5>QKM(KdV#b6;w zr>$n6e!UPQATUxh$K|v-zGsq41Ac!Ofs}+0i8w7#XL%BalATTZ%;CEv9#A9iSb$f+ z&)WPFQtgzM2|azwA-Y{i)^0_2_op@mT?#_*!|644X ze*(3b<~-B|!vJl*Pinh$cBg+7z1GNO?rE0u#^vlb%|W+At&72BoN7h@yuO|@{|cXd;5RRTN@P><~28m;n&Ofuy@o{ zU*#pI$AsFEDS(H0(_VF` z9U^=2@WnMS z(6IBB@;b+Rre9XDd~U>`S$UUh3H1rXI}3J6M;_wSUW(p~LdNoa9l}}c#g!(mAK-k1 znGPc^cyo!52ekGlu{s66gg!sv^SjN`FR@|}E)}&eyy_&DX3oe<4DCR$VkB6cy1F+* zuFgwzMc-JeD3CtFem^r@hfO^UjFcg&9jvMN(xfn=iBhA)gEQ3?yzp#Ft@78zl|?H- z^$&Um!R0I@VZ`KBb+By^nDa|$^zeS3ySoH-CK^yJS$VUC5p zLx5?T+EF*tz(F0S`pl6|eM{V){934H?FlhypPsG^B) z=p~_I)`}`wRn4TUwv?e<+1fvU#W|tw5U53mhWR6V6$(kWkiN@mwsVfA2N@;@I z^VBJASk}}=1e1&>JUxhirT+C@=mp_0S7iqBhwC4HJ-6s2WP7?0_1{g`eCU#X9s!h* zQ4+3ArM4CnW;uM=-toHTT%pF+DK5-R4|cfc<>|sHH~h@%t4Lwr*@3A}+Pwt!Z~#hN zPDUS(`sC$bj(&CiGrhx8i0ZrC zG`x)prSjmORe8i!FjNB&&3{d_5%{SyA6bNZeXMnIf9)R&v(P!(DcO1bg+COI$&i!_OVn zn+g;?GB(x0B<{|gA8tgby?RY=j^T|}7%1(;x)>TuOr`tf2y|K+Bxl)tSvVIC);5yB za!dcP1~s-oU9k;beSc$-@rAkohnvvpLCDGVkjou&X&0zr$}5^C(F>0ST64dse;7I; zS3}rn!pT2kRL*a{uc*!+U5zIN10qJ%gi4tH6W{>E{*yJv3xrRI(ed2*Hol&#Bb`=! zL;f}1gwj+4J$n1yf)e$c!+No6bJAq(N2O&c5i|x*kbPQ`Awj1H&&R;97gesy-)aA7snxIRB>C7~Z=#v_ zG6I6M0AItm;6gzU$|d8HFSff%!1R#X?W}xB`d})9tmRf6tEfhRX5;(Y7RI;eOx(E> zX{Ll!hw}(pPIX-CVt?nv>k`7AjavCU#xL}hdT&)srNkfAiqKV-f5`su`UoX?gH&Ai zj>(Qi?(Bo!(W71VNJ3wqgD?1C11py3_-=m4Q`jTL@a{=XYItEneXD z7^NS}svw-M5WzO-v5~Jt)&^k)Lzle88_Q0MPHjXr*U~s~(nB~ZXsM}70D`eSzHT-z zS>;Fi46@PG&r8}X+(V7J`H|n?a_4$0Ml`A-T}^FwokLMoceaG!fwa%Oeeg#_I z+ImK6n2(!^E*=2ayBnhwN>&;(f#ly%#KmZ!TrQH5KCNU8VOXmzg7m_jR36epygyp{ zgy_hx|I^@f*0n#kUZ?q~2pvzATFK1gnxW#%FPsXrz6x)4UvE!i>B?F9s=JR?jUat& zA94JIa0%b~OVcxa$~sxZ>;8qgm(sK0zR%1lE!T`-10W+NXX)JeNM@gqIVHtzR3rH? z#3{hGKM|uO|IDhbSy3fRje54D9a%qPORq13c6l9>I9XTjCLnryx$Oq@eOM`fXhg?x>rhhBi-pgqrGjXG8?a zm!BOkr^q7FGKjf&V9_t7JrMUBI4gEMhdDpGV5_Jg-de@HrFhQk8eUv@hyD?J4 zqNqke4RbBcM(ARSbA^8UdB4z;FzP-31BDQiQHe4k5nWNnN_;L~LLBiba`&$sNL;8Q zj3=osM9KG^M`7^Umv()OW3){-^)M#XQHaI*Lk7we8LPJaQtiW~G4=SXaV7dVS)LT~ z-4Z9z9+mSSb)NpMr;~8W+Y9Hvtack_iU74dUMCPZ|NeXHnV-pt%N-WC)KBOKed6x( zQ6c*xH)ySdI=p@Lnu7t1AAb5>OvPWq2x=g0=E|OuWi|@ccI!?_6j#U0ts%y%cU%De zY1mwxjcQH4hvP%4#s%kBb%9vP&hn+Z$*(^xETnSD(Wwlx>;P zs6ZL2Wx@E~h$t<>SPHpC*F|4Vq35+}eEm*A7u-A>Wfk5H&weeu9H; z?D(cS>)AzFQsZB{-34`=_*=0Ol*7XOijmhm_+^^ZdzeOD((is@eI2*0h5O#H(Z9}# zuaFZgVyv+c;K(}?u}!>?7F@bx{@Ay$lpDog*G>c}R(514U($49@TfZLqZ@pxQ{!N5 zM!+gnr+udI7sZHDy!%P~z=GUK{Rd%ZXQ<5WtzOkDG_60pG@Zpw%q_cMMt2;SygB}Z*RJVk8X7d94h#pEr9gT{zCO)13o~nq+7QgbqH3?$2b_InCP|-F zZ)g|W2qL?Py>gax)yr!+4}qgmspdQ4>F`JoW1S(yU&8sxwz{zHvc-5VUvQpq^4c^l zh3KGaKI(g~W|4GYG#gthmDdG3*uB3IvvhilD!rVKC;vv5y#osblULlE4&k-J6 zy^nvub;qPBO8pn?n}{L$wxy(A!vs?eIz#AHf)ED-M_tC8u{-#5AUDq&8Y^rBm2Z6do(a*4`}@ymLQqt5jEI zrW(T;zCKC26!86TA-J4n$Ji<2?sUhiDd8QdG&DyI#FTe@`!Jqbre#j@5Z;cmbWf5H zGgU+~x9b**?Aum1B6J}jYZ;XK&&qQj=0_{YXNG9Vj+c{KQH14H zM_M5R+JbpwibZma(Gma{H|iuJO6{Wc@G+NZ8AD9)>4pdKp)v16PKzAMpYES;h9|J` zZ``h=tl+gyjq4z$)lZFtRV>nT(yO*ZlhB-b6@8|P`f6ToxzhbIdBGUV9OHS;s z-SM6MMPa#^Saw6*LXx}NbyFNY@#N2AoEUqAjWC7RE+&ttn4Bk5b~P_uQbA8sZIIb; zI>p2&uy@&n)BydGQ;{n>2MJ6D1hVogb&BD44oedTrAeIaG_P{XX|tJ~vZgx*NRY>Q zESDQS>~o3me@FE%?PBK@D#_ogd#93HTR8K%1h^iw!du5bxxv&>R#I)OIhIZGG8!qG zZ3f5Zc1u5XAyJ1I(x2c60D&s$;xWx3L3{i0<$v4EHhY@e$H=eh1N)U^wYLXO^G^2q z{pAkM^>lg0_+mrSKcJg3y|LU^A6*zN{*Aj;=daKRdM5@(WG+Rs{}zmPCrnm5To6wj zmOciXaOqH4+oxZS z|BTsf0Ff-J{HazcCckt;RQbYU9NLej>o$Vm`GiMMme^}*;wo(hy|e+v@3Dvm{k!UQ zSc9MAEK4GG6>lQ5+AUSsRkYMlf~s)(V0?}Xz~L9lC#~l`@YpseOt;o3)J_zgzhd)i zU#a$4Fdbw4gHJghI=#M@XxdP@z~M-R7(U31N3vawQb6uaw+-;h*~zOPjf& zU2ydqcnphEoq~kJx#z(iUlrZ%^7>aa9qtakcz`#th3C((Lxr3``@e=!>~ThV`KPf` z>*V!hQO`o+JOk;GVd=5d;`dpO%P02N#N*<-r2H5BH%VB6VFjaT$&pw(#RyhH(l8>t z93R=2%x+;$bRmx@maAy27cp@|FZx~~fMzqM^2@Z8q#)1K;n?hX+IfP6$3MO>d-)q> zzPz!dbIAxo2{q5wfwh8OM>itrK{H}pq5j4$qv>BC36X3h(g{t*fA>=spZTf!BJ5ht zq4TR<<0}S9Fd)g^m>!^kKIsW8 zz@C10(`4^~TVXAL1%bJko-{9~cJ#mo*6zqep!hUC1*jL~CzRBeh&Y9b_aIIXE)s5ogC~ z0?opCZ!?_LAFybn9Ps;RcBEyN&mMUwG)vN)wBc7oW5YH#upYm1Fm~bTs{=QnKi?Vj zi$RlTVcbIreFyZFFfq?4uE2sM%O(fcC7CR-8VcQ$(dTFpz!XMB-06lVfD zqM;=gZRV@4y4RF5m+g|3d0Ok=iY!X`Gn9XQY=F=90k3#|65pc6`{ItyEUvueah(t~ zqzE{jAQ(8IS9-O*t)`B*6BGhbN_Ue%!rezHJI54u(G0b1^o zn4GmGTQvICdluEKoVBX3@~V2b_55EcG9ZiR`w)!MHP1v48C7{Z4mV!j-YRa+j8mkN z9~38vfV@|cKFH`Ieuo6GxL#!+Uoy9xe2Pp|UQ}el)~l%f-v{q4wh7@giCQpQD5`F- zulm?6|7LPKZg23k!v^2a%4ibPoU>`50&d{Oy@Aap?`cd+7X?{A`CiB(LK2*bk+Q&O>?^ z63gPP`NF^>=6UeUKPR6(H0YNl+C;{77GKGf*UG;nda%<_lc5-1%|vp7yHzY)YOS(k zzvxAHb1WYGDdf-Vt<#G{lo}xcEb$m$sM?~h+wocfLFN0_Yc#UeSf|{zxdhuSE&&zdEId6P2BT|%`{U%BonanFiOK&8tP3{({CklZAsWt$;(*Zy$AQ6 zQ^;4}^ADCD!`B@b@IlSBE5oAJ_??Z5#>~%Kl0x1bJM5cGa3+a|WKy6PVo&KPp;w3? zxzA-ZHqUbR^UG$K(L2a5d*<41iqnKxTqj)Fny^Z`+%aMI-ut8j)0YHq2ck%}wdyA9gQ~HY z`)m3}$x(Z^#lMSdfcPO++Kd8ay$4nQLP7V{DGV*?TYdz_?_XmPw`nPxx4rR|HMZT! zFwfDC=qP~$L+>SE`!EWyoE)}u22L+ZT{QC0?#IO;Z28nR!^RmIx(CJ)VoK%VG5*fo z*OBer&CjeM`Sxr%U-qIs94odk`E3(t;si@|4yeJ)g@tU#@0_`v;z^zT)~L_kuNNL- zA}0Tt83uvmS4=S=ar1rSdcJ1bPyCD`;r55V=2=(6*$fy6p~4}OcPuH5_ce9hc%fyK zw*?b451WYHM}m@avKiQLTDJp#X__LS3*^h6{Xbh+*s~)$$)08Dq0(}=)-@?}qcnMY zHWte-J0|t}YK$?Wczc7DIJ8@a_D(07l5Mj4RmF(-1#gtaR#R-4D=a~gY${lsuPFbB zv0h<70aCJltG^qs;eOBA2!xXutyIL)`K@3auWiwR%Y;_?2_&jqAKjv$8+lFCf|RM> z?OAm8!HDxp6UcAcetEK*+nr*Oj-*!27KsIuLLa26YDNAV_O+$=L@5&xeRSJT;V#Xq z`P(lK&^wcdD1(p~htM9oH;tFUvk@){%8IWs|yGh$e>N{;4 zqoqo?so+B#7h%!>-iO6;J^F67S)CqHG&Q>doS2XIOe+E(9V3THh*3*fbk_`RDI(%a znkCu5s4-r6MOhU3^Z9KW=2TAH$Ls?F{=a85`?(bD+`&G@;u6cX2plJMO6ahrjYD~x*CeLTkdeiVDiA(-6^YV}=kJQc{42N2X0b4ruQ8%B|Ll^7S>P z-3hTVo{|2gLs4cj@gus!{@?azi?SP1PO9scUM-mHJ5B5S^0R+b__jwn+G0?dbMSL0 z4ET+vjIY;F;6`nm^Bz%YJ&yvX2=g7&cNP}x-l)jRc_in|m&uc3<^9kvVr(ds#r`W^ zk2>Ll_!CX_#CiOZKN1wCj{LUgV}gS}sd81c9%b^X%Qbe`sXKR`e!7#>)l~nMPKRq_ zH%xbzbo2VI9}KPazIK}&zXtIF$A>V`K-@J-%3S?`5mG%%Ro8eT_)NQHKaDO^cEtU! zKA*7W)7zYYmvAK8qd*NN%}eifN~GNHCO9;>W+T5jrfzo_rlXTq6$lW+L8CH7mhs zZNoGqm1B+rTm|B4arOJfLs93qU^REOa+ij#6#s5NGm8#?_I<7x$b8NhY9tB`ze*^> z+b739Hl?5@&P)t4fuNanwzd~Lpt-fmq;+)2|6T}AZhisxcIl_`mg;L8)f8i+;6=YSTvwg*PBae|h#_MTl3{~_YVe@<- zLd9R@hiPTj@wl|&p#p{j_cpg=U>VDb6j%G>UfQ!JiTcK z%;)1OKW9W9yJTW$xfICi2R*}W``o~|f@*e=6!T5e^d{@RvXj8wk3e@qe3SmaWEg9} zI&zh9W5eM!3^dF;USvu`Ul)YpTX>X>;p0Md0p|HTN}M=c=bzEr0Www!5wPaUs{{9C zC5fIN-o?dZU5H16wu8I$*wtL)3Q?Z%m!*u-Ztfd=;} z=x%-*6idT7#b(tcC49Q4G|@kLaOD=XYW!^PIe5z_W4iuE1w!7&$Xc4Pz56%P3~i$K zp|$z>YfJGt+zbaG9jgAXG+i#7>NvNQuciTVlO3@NxB|lCYPA%g;8& zNB_Aj`(k~6ogLlPxB=J&y3;hoqVb7IZMI(1WO-Eh9~erGs#>N%?OOoGdP z^bKwitoZ%8hy19APN}4^8oqbB)3Am>rOrxHQ$u%L=e~Hwyv%=ZhmX@h~TuaC$2=oE8 z=7+pm^X?SI2!gmeM54TE%*VzRP*wr5mMlMy=_)nUTfBMrw-+CA(A>xh z;Y(MEl$4nOMGZNl0AqO0Y#$S_G_)k{z?$~v(rRv2pF)A!M`86cp49g^FEr%r4w!dv zklvh68OLjjE}QlXzuUXyZ2Go-OV^1Fde%r4sX{EB^?Yn1?(~sr&;B9`nl34Q8ok*o zRL>2W0l{4koB%_MnNLK(3bKWu9dB1{&i4cE(qw5@Ud*GnV;m%g%)teLa>Dy0j}tO( z7*JR;7rovC|04Xo{(aLA`=g_d-ToP`x(2+|OU3N-*wci%l?En@@}us3+v7Fz@?X+9 zKnafre{0ppe|M-69u_)TP)oF3SYr2*@!09s=(CyXdY-%w0=HpDN=%Unt-EHFcf1u! z5J5;wFjy9s15sG^o1d$+T@ut(y=Hbd_UX~ zC=MF$-3vQH{7V#4(TZ~j3lm(wJ3=LAptm2e-m`HhC+C(5+0D^`cR!n&+g9*m=RBm1 zBH-k=-oF_&riIBrvTuH(j!C!!9q7FgYkzrtYUAT~c2RJKx$b+pA9OgBv2NDH9VfCi zu`m&HmWFF=HVByEsyF`Zs@)f@<|Fum2Ep?WBL%UgG82QgN$nGxz?h_G+w_Wh4g+t_ z>d#Q7NW^X$x8?RWFf%2Eys>u#{Qi1FB3Ad}0vtwk=fz8@r=%l3NiAOUeT$Pf3B*4g zF74giyHiTqmr=@mA)>!v$()p^K26}p&z$h8)O2bg-iCWjP#M=4e6J7=bs1FxY6Y%N zok~NZE%K*G%Vv-uJi=kCmlZvh}o z2btT)uKn5ilKy7#D3Ss*+m(OZs13)5>fU7bEhRe!Af^4eW>qnFaN+{^T5psF4b?*; z%7J?$#JRuhq7(Esv7}TkXb%|Rh0i$`Fu@D|V9BXfB^kz9j)q^V{Z_ybWP5_|3_T^E zDX~z{)(-7~Y^Iemn>Q0{vLTNGcs}>Qo*qgC1H$F^WO;0J$#|Tg<>U>5Qr2@1VbZZT zTIu)Hb1f{0Wif zrO*jt8oJf21UtpL>un@Uts4#_gERRTGvs3Bt{*iNUU?sXSy}nl;<~OlAhtIj?=Rk< z`=R5iXZ3H9^c5Z?ajv(38zM|ZgAwdN z<=@kNeuj4~nMJ6Mx896M^k6H98vRT9!vFis&X{?_70k>CD8^D-c7o9T#>eoRyJqy= z!kyJcKh}Iz^7g*%%bS4fbv;yod~&xoFnDA}N! zg1m2Ne!LE{#F`?D)kT6AWCzof0NZY_)I(FAFHe@^BO{A1$~yqG4pHRL+74@h)sI9N zQs+u&EsuJif-3m<>_@Eep{Nly=-Txc8C1|M(^60+H4|t2@FYY#=iS(p|4$ou!Fb>#IL3Z_vz&QwF(i7|N5`0p+Jn30%BeEhw>0~I zrezpXyHsDiI_`iHy{x~38V42BeH7Q(e)W!aJ8hPW|3U4;UikI_>EV2G%7PM5y~EpN zKnRJ!zuTxw?$ThPp>b*UpNzZue&H$XV~P)H!$w$Zt7+GIEdJ~-4kh8U{So2*1H3Ot zoCEK|1vr$?AYVuYR_&w4G%!yKNEY4oqQ1O{hhKB_zgq$62h*#6t1X)it6dcE+r{2d zr}S3(ol=UCGndOYIHqXA_XKidOl`Y%M^nzUBeKu;kstQ*R|tQ0EuJKwdAZmw#E=%( zi@wb06uX*(xm(FIdp^~0IzOLSIjx4-nXx?23t^%7(vjZ%KK1JxY;fb1NIFxdp?Zc! zVoQT((PA`U>CS$QLbxB{Arh1J$39mq>ARPVOj4OF3s;i8Z)ALc7gr&jUxPLK=72T7 z6Bc}ntqQEsaJiTZ`^QWTjSe-zejDlOriAaiZp04QF6boc zTf4Qp$x|+Z3M|RZ!@i4SMSq&_7UU>L<3vp#sW28@WyhQR6*} zpG1mCj2X~(`x3ipb}?Kw57>vpm|i8 z_Qilj2T8n#o?$BsYqo!(9if;c%QTE7M8v zRc%4?s$-u)lw!FCaz4P77@O=4(cEA`Mp}H1-^*S8R!qt?%csfiq%Cy!XW~kr)Nw#m;uu$9rsZ-@^UN zdR?GM1lO(9q*6vq+rM+9GmvZP_;|mr>9UzmT`#Ck6Yvr$jPHR8D>-8m>))h=$++#zZ*j3 zwz)vOnNE8L#IM9jqoZo-MU8#Li$#5xwv_QwD_1*|S^|3PZ`mL4R3by~s+z;!Dr5Fk zsfJVT*`2>phCh1yduO?Xr>H$bQ~$C0OLcT}&fia9--7hsKkYbjo~v;eN!Xlk|GeQs zo~lD?K{Ay%`w*;Wq@cuyv^UsYPX~55BkWf}7Z`YJo@Mh=$uB??@-II;KRK_jm4;Z< zclOy19y-3h+QX<{(`~X89gr8tvSfW+e~akNv$`_KBk=mjqqqeev`>f|6+Z=m<~?GK zQJVT+GucQr9nJ-}9&qf84iT~5Cp;mKNvzRcleWBj_eH^iKZN@SH!iRuINI_{&%Ko1WX8A4x2! z1=r@Uy^Un!I22FWjXuBmJ=b&o5t;}*0M>(S7&B4otzlkUkOmK7G&2eLuIcS7r)faN ze@WV>m~pV~ah-c!ylcjNGc)@rUbwm$?!lt!wF53m|B|^U21v`~DGHA8t3}m2#$FRr z3`Wuv!jm=kkOd&W9)6>cFU$1RXg9UFpAQAs%sKnMcw8!9IEv$%PHGi z12x+HMI74v0e3 zcR51-ehx|4_wkNy)Rg0810r;@oaucqj;;qIU>i;ujL$g!bdkLId>K_{1rUyHyDfE0vHKtvR=EGk(N^rc*{q52C z@M-*_DrNIWMyn%QEMU7zK^ekOMWbZ5DwgTca8j=wmfnY3ckslA2cu z0K{kk=SypM_&=64`medV$WJi94uA{LL#tFjcDIVFH^CbYhbkB(sccU{y*=y76E5Al z)l+&rT_Jp9I<6#L@)KYHWrsbZY;Q)~Y$nDTebb3pCJrvQkYi^^lYO1PBKlaIxl!fy z#9VNaMO8}e+lg=+AaO>1x`#vu-;5RG)B)x*bbtCna+IsnPiRhpl!LodAGe$xJ`3#+%ENAx?ZqG6H_cNx~brG4BpaPGda>>|ExMwG#ls_7Vk$J zGvAvxS+n~G6bv5oxyUcpsN?OoHtf&uw`{_4}%Fh8&DY_Rr7x=D1}a>%Y7>L-lJVMR{Vr1e$m^L z7LGu?FBMU71x{Z-`qKMU?EHBCYwv^eavLc#$E7sk zk4j!Eg2S~3Eb?59=R##b6b#&Y>u3KV0PFK0*xw&^)nEdXG<9kX27>f?p24Dg^dT>` zs9Rgb^P1mLcstO7d$aZUV)j)dfbmBpnw9nM@W*bM0ZA z0s0ZcLvQJaJORQ9j+AQ_E*(=?Il*uAHNj3IlOl*aZ=aOA$)$P!w%3v^xxT;Xn@46) z&AB3$k{lCzEjK)xaeX-oMFb>w7hObPnY1l`EaD(GKjaeDP?bE3EZM>u8+Zs{vHF!Ji*iJGydHB>ncJJ_a7km5( z28>l=10<3y+V+3i`|iIcvMOgA}PE1f&Uqbm@c^${XMN?k@W`ygk26KA+4zGjr~__mq2zV23@9RvFf8 zMMvDu3UUNDe@IDwz*HeDWHr}c9Zmrgwf-J5YMW8;{C-P@3&&(_owot+r=`rtX7J1b zvQVROpJy7EdLnp%l3B4Lqs&jX{;+;J32rITxIIDF!-e(hcL{6)CRUB5RY+pS(mV`r zlCtJgUPSij)xJA4Ylqbw6Y+s1?h%7Xij?QQqj1VZkrd*uIGvI3=`*#rn^7KCT-g5` z?)-IMa@Bj@@e6lNpGm8JqStYC0BPT}-{+@f(}!Q*j`Imxd!oY4foV15GgB&m?zdnngJtQ)sm?pg^GZ}En-C(%wvB74tp{|_#Jrq|}2C{tm0Z6oLHZmeR zelFwVA=PB3jpeqmUkD=JnN?q$40CtgR^e5dKn|sk_ysPAd<4~m+*iqh)=vVe3}Hi|Hka$3Eakwq#MA`Nbd<$*yS>L*=c!n|32j<^XwNGA1xyv z((UCpNuyjZoXc|U*4cf&81UP0nIH$o=Dw_kW7s~v%QsJyW2^DmcBn*sI9#n4Wi+vd z`vb15?3}iOAn_5*%gsFpuWr0Wt?Wi)89&pl=jNDK*B@xzphSr{uk<6sh0MJ(En`D)3Sw*Agbd*9MexfEaVbAgk7Kz) zTC=GVKBSkR9%R^7{PCoC2+?L}&}CbirdoXWajprCQSJ2Eo#-znb&sOINVzjSiU&(% zP+;@m;(En4msYjfVbviIiA-qN=iZ5MmElkJS9+yo;@*DQ<4krLU0B+Sdj$bymWm~% zVdNbM<#m$4iO>xr^@nUUUU@XX2d`h29_}HRp+|P#y|z$#i1OiOCC>?Dxm*@QN%dPP zs0pS|>w(>woLYr-1ul|_@RpHyBg%4IRJ{)?KG^LGT`ump`Kw%>w`blNeVK4JlAfb> z@nRyqYj3MiMq2CDt1{LvD5b{-bk~n~jvMRIp8K8l@J8krUgam_XBL0l_5ch6ofigd z=;Ei2G7XzL?v2;MSAIkod~wdhSd{KX})ikA%%d zp7xO$YO@5Ts*9=2i7-kpMZF*W0+@p@WP0=G1XF*awNv?7Pc7fN>!?8P&A^(w{ri;l zicw^GdrnU|8n4!jv+bAHo&oCicd$Pl3340bgqtbPU~tH<(?Qpg;7H2Eb@egVZkEHjc5oJR>7)Tb+B* z0c790W{5M^JQDO{bQa|5lc6hK*F&Pxgj@$wd>S0~MYiO!tlvb)QDu~|WO3+ewXKa- zPx+fX?;8zOo=UE+^jK9}7A$g9sJug}j1B-;f6#Sm&f0{a_~IwdKBF{`7X!QXd^NKw zhO*i2_62`=UH@hwJ)EeBV%cLc6=5Nbs@MjLdB23}Y) z@J*7&yaghB*__!A>-3WZ)GUfGI5?yi>}6?*D%Z3#-WU6Rks2krdAZs8&fTezB~{%4 z(Sn+g+k!eLcqBPU>}@EOp`J>LwcT+Dm5f;XV`#% zRMok(5L_SusL{RpcIA3&1oWp}^-C{4GfGU|C7N+@;u26~GPvaus8PPUfcJ`RtV%a1 zkpG5A%uY>HZ913mWRK;_7I@Mt-lZVY!1?X!<)4ewdW))6HB5jvvS$9B>9>-*Hi2QO z%xb?S#Kd84L?OIK*sYB8ua83AISgev5M0T4!*7a<=__M9Mk=JrwVnZcxTE0>JN+$3 zHGQ5aAk5fyZ~ono$YM?ZAK#+#du|v#RtADK{|A>#U>yJ82_o$8ryafkNzxlkiE>|IV(7eCjF?IVh-=mvwr6u9lew1%ZKQ%`C zQd*TX?@fqa7S=agyxeR2s&c9PE18Dv#iz7C>!4e1uT<<`$U*2{NJcQx{r-$E9L3;# zqnllpMb`Q>U3Pj$9&dHE*YK_Z6iJ}U2boI6n@ot@Z~as35qqF$&!e4zxz+3Zeb~hK zM;awgdC5#IshbuE`<%I=*J^;J9{(k{aNZ9qKCv#mVreebvwxBr)!@9 z=aoh<)+P4liscXGm{`P=d+KHJCU01E8t%P>h>Ns;gNL*~(ev8FuWt_7p|n?9@q#p$ z*pqN&B-cur4Uk?pDG%d0oLJgtlbthQ>EDe~9jCGf5 zaERe4}6s(&pNTOCOm-s9xFS~K9P>e zx$gW4Q!#$8_Q?6(f%96Ox0XCeF7I7}yIX_eh`~ckufl5=nhme8Y~U^8gf~-%rFc5B z@da!J>yiPv%p*=q!c$u5>Qxgpr>zYVZr^HL9Zy_G#eMyLSZ4r&`DSZu&zz;+;}TL& z4ho)(oM5LkDB@4qTy4N~U!_U<9-Md71DX7EwgI=;85rzMTq$H5)}Y~L60y|Sn}1aM z+47`u-EGHLPQjPEGZMo#jh+YwP9LndR{i~B4!t_a8vFwc)HkJKy-F(0h{n{g+iiG6 z+!AI+JE^WW{z&18sp)$cHXsmw ztK-?oz-jDdhYz04%Edm36nf&X?Ve-8zNMhYK%R7;-gg9EVp8bzarHtSl->?o$?17F z#-iFk3nI1+a+}!PRPl?|XIS7K?rf}g5)OVcs($xt4dPD;&}x?-QlGh6(s)}TC12-c zerS`^3W{LH4D7yb|0Le%>}Y@thhE!9b&j5ZMjmVS`rqts(YD`Pb%I>Ri=fSj8kfV$ zqt_{&Og4!Q;G7jM0(iyx@!Jcp`66ha`Wu{G57W2IbsPvE+Wn5XQ_iktG~K@C?)S6; zdUkJPbfC~XK-NnS+SHwYD^j1wCuY@hAmHwvaoHqx_2p@F?pIG(z@EcGX@Ak^px@y~ z&yt><1;v^+i74o|8jiHuls-P}6(4AM)8ye3`w>zvB;6 zY4tvx$c)Y?4FH+GBtoh}1%_uB&+11<0_D$p^@M~W;?Ohjv3qtP1%2g6S!`g{dspI| z9gfdcx*bMNR6?HB9eCd=y1Mu{l#r@kRm^k2bZ7Ht_=PD;cFUc zrLVJf7H#L69dDLxJb=Yi0zEPLHQ=?+AWnrWvbfkI_)#l)XL3$I+$(S4*;mXKK-Yi(;)FafmWM+)EaU1v8;qTz`UXB^&G9 zmLt6(J;)a)wC8V$K&W*Qa_*31Ok8+#ZZKLWQ2wO;B0cgiSW6^xj-{wNj0 zGBn{wqj=;mHg=rQHeSyUB?!E37n+E57gqc6zR(m?rSN^>v9kl%qQ@qPub4l|kMvF8 zBzmp}I{lTFd_*hkN|PV((d?QEmP1c-spTA1Y$Se-=^rUISn60?YOD2Hgg+(G#v1@z$TYeF>ixh5Z!VRBw`D75zc$GuJGvog;t zhR@F=%^ajpR8lZOh%XmF6*8tLipD4rrVH^O645znS-zi4V;&S6=sC%YLE9cVOSWmTZ8YQ@c$SrSRJFgNEYd00E}JhG*nnBJb@A zS*(?l6Og_2ky zGe2Y-Owd>)lI{}PP5Yb7Flmtn5{9`1{^$3%9nUIif1NkEH|>?h;H=0mnclO{pF#-|V=Z|q8v15O zxai&MeZoB;nbnoEsHQjoQ?>OTN`SKDdO?UxW-k0iRs|Pd%JTC^paUtB9q7mnfINfh zeo_S~f4QOP8qa?21V6TEk9_GjB@sLy_zR%eO+>Ei7A~vl&|%g>jSZ^v2ew`9>JY;knU!kw`5`j#&q$>c z`6&r2oFf1JGVR#c{6q0M%Ca=S4=~xMm z)ymr%{o<#f%#)r@dF^X;MV7dgh@zvPV|6@mCv8e$lIq@DpTQhd6zysfL+5_(^=-B> zeyuxE?#$HY^IYwtv5k>fN2CX%+gnZU1r)8s51$lgzraN4nk0lHdNAD0B0vp*=|~_47wDy=k%3X+&!GJ%jlLs|sHRV6RR)fuC-*nC%>D zL+;Gx1`=Eu+qc%MH%|Am+N%&!o#8K9QxEs6=z>auBvSXYqDwf{vMV_$Uh@}tZahB5 z>p!g2fv_EA*#^*?vU-{9dEH|p+8p1QzC_;K&H?3~47l07EqI5t>end8UU4?t=jkG` z+$>8C675{d#H0G|5r^ql_g4m8uA$PzniN(2tRNGFgHG?>BhRNOFQD75o#U{+`!@05 z_*v7j*f_YVntqqb>_v3Uld)F6)?R<-bSga^QAQu#>9f%R_akC?#!(8)7p8)gOq%Es zkA#1WB>zs%pW$sfhDMF~UDGB49!ace(8h+a{&r0=sk@AI(iux zAo1{X;!C)qx+kMXb5Zf}6eQ9;(MhwO7#=Y3MreJQRTj~FRoL~cq=+=#xc{hWKGv6` z?_t~v?%sJJDX-q;FVb#d1{0GXf8cU?nyDGWG-+4&LR$m_=PTKp@U?@Jk|k{+6~>1| z{}bd)Jq4%-AVI9e+c%>e#CK`L)2;6u`|R;KcZUwRJ);M}N7SWXIz|?n{ zY+;M8r7TrBlSgN`F}uu-Ya8HBaopX4GcLH?Me#`eY|PT_PbcH%>qEq*xt>j9m%^z1 zL~X%J)+bxO6I%?PDvXgO6s|_O}|c zC;44zIemRokdvMD()NY&5H=mw;6H$NHg>qFlcb3jLt8yTMRw4EGo{|PY;2oVAa7&W zkO^IG3bRLUZp!0g>WZMiME8|UIRTbp9ciX`#gy?v<`44UtRr5XwqW@AcbcZx z#EniUmh$QsytvaQdYF}KoCRp4hb>V}(^0~)ryOVEv}U)XgALNO?MFxbd5oJIW=h$j z^@rD*EyR0`VyL(~T?|l*K1_>;lp90hT`!CLyep%GIu=iHeoCarekY0hV+5H_qT-H> zi&QBV6ibs417BT9PK>IDnt6+HehgGIaVRU@e|0psolK|Z^Dr1mR;%Fg(6pFmAZugV>*q| zO6+o$yk-X48z-k4JiYbd6BX#QM&HN7ucE5^!(3>a?QO%3_Y4b&NoxyYTU0J(VA1rV zGR+ayj+C}`R`qO)$O#3^h^YnhjZ&kvffg+A<~oU@|Ig8yGba5Nmv~z=wGGsWn9?>u z=&_u8+P5aW%~8gkTM$OvJ2MG@V5+fa_H#3Ro{DbKpAc*tLqvar5t6IHM!eG~X<@_$ zRboVBT0G$!$PTaNiQQkG^FC|j&}T@0TDKT|kiV@`FW5=y#&k9f?oaCqV7bl$-2sY~ zl>vI(lv$7Flwz#o@43{uiQ*YPgA_xC@Q43f6Aw7a42+9N`qPs?8L(?rV;(y-aV@zG zDgC96GC2rh4fCIbwe`xcj%-uMC*PG#T5T1}?{dIpkmjpCxh|KndzNupWI5_ z^!=&;h)37$SGG~0(oEOZ`N*1QF<5BoYCvIzw*Y_){^oAzHwC@zcp2dQwmST=0T7S&5~3oRK?_- z*!W=3!g@IRFkRusI7euD4=l>OWc?YBT6o_>F*NAHrZ=`<$7?NRF)zLOa9cro;8A)m zv{O@WSJ9q785+(nL=Lk4^eDN{HeVoqY3JzqcB6O2vvp;9M-8zPbhD0{%cVP|8RX+f2 zGy+8#KU#48B&<0`u7wRwJq90gKPRGIP{pmcClGKFag~S11w7Zcw3{ust^w5;0apck z#iaj;5Ulph8e*d(Zc9iQY;-kkq&fjQ`Dw--+STI{=PPzdzGf9$+x4dQ%vjk@eee@& zHBOoMaR%(i#CXWg40oW{rmkwA({JWe#1Ht;2E;|8kz8Pg;nJQF|75 zf^CYX>2fE=!%Cku@{oYz{lB332|&7XT8Bx8S_cT8r{l4_xDOIzF4YoE8q^(n*9|^9 z$eQ%^9|qr|)e~PBm>u3h#%F zy64laXUGI5N{D@pWmgUs{^Jsm)ASdZ(No}V%$iDN$J{x>ahqd?0T)?Hv78=9hb)=F z^2XVTM?;a2yM^U~7TXhkG@Q%n;|b~n)z@;1vNQ}i5shiB5f2-$EA@#TN13=(y}IAXT#brEgS zvoLItkRmw8dw0zTMQd?q=aSin6Pu$|v5`(GUW^*D4#_Zek}~>Tx#{Dmf*5p%1qTieezYXiC*)R~Igx=0#b0 zaqy>gFboXL6a9+&%~bzimFoTn%}*IW25m5P*jfvuma4HYmcy$DEa{cr(IS{T+xut{ z&@~98|Al5Ew!?eyr+&j-l)2Q2D`X^=5h!x?7c>U}Q4I{#mX*~^JHJ>xrsW-=-YV64 z`A!PQpcz(kjj?iY=q=6PCGP4&;Ui$GmzUu4*5x|Y40Fc-Rj*B9CmLV_&Ts#PlZ@J; zWfj1wugdiPP;h~%i{l|vp6qMcLsGz*30UHX&NUw0MFtWEmBZHILTp!M4(@9j$sI+f4wnq)d7ydc>v@cLPm~F@aicT9MKDo zOO$w>_&rrgs^x9%J6bCOyc*i;{lTVHmI}(;gnGFt)K`84z1nQ6@Or_|;U>e|DV2!u77lu`7EjQs&jQKC298_y(5>ZEn^<;RsL2h+sMt zML{1SNFOtIu&=EfjD|GHrCV+a&68427WMEPVa5 zP=vXQG1u_fsgp(S-Pd>JVE317?KvB}#lH2WO#AQ$5+4ITO7xn`qw{NTCY=J8#bu7! z8GDn*_l$iG441nyqE7NK9-`&R5U6h@vPfp4x;gJ--_eOvG$y zL6P~D_RX52Q~5T%>uQAW3xf+Sn@8(nEZ~3*(<#NM!!Ya@u~g8A|D~%RBvR+J$6S4Y zxU0V*P!{0MJqg(MjnueD*MS8V!B3%NDUd4ks3E0EX-bjDkE`RB5K2K1b@ua6&&AlD z8Hz*J8`!HLjhh@>A3?7fU02zvb~r65k~>|PUyY77_rDzz8%|I~dJ^l<;lydrnyex{ zhlIi1a9TLx50`Je6#&-jejna$ywqz2I0I$gKJ~9n9wgf0|2!$rZ-Pcs*z#!KC3R2A zG;~3M$pQ-?cu{&|bqNkmUL|1U?ywU2eVQ5j6UH zZsDIqDHhi5?!DhqH{;Ak-1vj_54lCRCA~Q{Yz7y1yge-N`#Yw84+VaRyzy6OjjFkR zoM0-&?3-73@G{eEB-kbhGSLESWIfFM;j<&fawc2gkF282n_m>~BaQ#-;kSljm&xxV zHgzOC%a}Am->;Avot|+&WO}%wQY|bwzcB)sH0Vh%m=7d#Xo?1~NaSH3R{aVi{~Mmz z3gpq?6zHHS<3hP!0r_;fz3BPHqr0iLR_xBT?0weNmoMZT%PcfWpYM1b7NxpjX%Dk8LR> zor-*%p;8(mKKlbEp^~T1$KVC)_$jAG)~1z+8gmI0+OgXi)A{`#ph*1$+7K1!B}xQ{ANd=y z=Bje2>(#F(d5Ikvh;}}|FARz76!$AwFKg3_RXf{jup!ewlpo6%RcqYvusj-Yf%sbe zJs1Q^F1a+rM_hFm$dklO6yv!ldfszZ7Qewd#WreINh~Zn4}WopT5f5FV3fGlVi&MkbV+u?Qulr@JT=GmuX>7M#Q*qbeg&zUg|%l;2R zOn==ETyG>IePGqL-Fs3CM&{J8^fmmwH_Ifii_+ESwrfye@{zdTA2%m5KuHFBg9b-? z6^Z5Nkbp*1*)={06L4lkqQ%~6J=gjFj^7vK*E;Q)-PU9LxoZ^0sLH|pU&1#+6=~1i zC6<_x(vaG(2};gpEhkPu!u`+bG$66^GRIiUs%|+3RS1KKDSP=op)(tM{snv6fi?cp zirQ2}q&uZywton}Mj`gJ!R9DQ=)9BzJy}v9;JCv!snSQ? zd$}WYDbK&a3|Zn}NKp~{L@;Al&!lAyE$nYAgQ(td9APPc5so{y3e?0QLWQ z0rOM1TrWrGUx>`86Oa?JW9Z%IH|zugM-Qc}7|alLtQv9WpB8uv$5pQ1d9^EOW8%#Y zzp4fpY%FKWQMfYOHNKf?!#?KE1=_wmjpQPMV=ZkB{FSi4gH^;b#9fbcG z4eTefh^fpFATcl{<8A;*IH2)gdlsfX!-fn>X7E|Jcs%lO_f6XQm41J*SJ-#^g%qL6 zEb%#LRdec;b8(gYaOY%j`uwWCuLjWTk0RL$D&DyE?TM0!DaPjW(`9xM*qSUAUtxeP ziM`a=8|lNjGuF8g3Oc7Oftn44e@W?l!S9iwrB|jp;Khgi-0D;uAqxj>bv6ml= zajP+P2N@s^R;;8*U+VXICOb*FP5*kL)w2-?aB8>S4sD}Cfe|<>!*puB!#DU9*}u3+ zN%<`<*v~0M233XwL?+8P3-I3v7ld zfdjEO?HgdgC-U#Lu$+La5i-D{lPbAH>LgONhI5KcHP%`8m0|x9fz0Jh8V7lX&ORJL z20ibi_&td?SgzNnV*Cj-1nhYV!dw6|txy1Wpw(X^@ht4+5pPzx&EdDbSqmZ440`xl z=)k#|4Sb?S(LDSA^e{CnxLau|3`ug`m76dZi>AHj5IYXozNOtYciZrOrqw5dcIO^c zb1ewgtkQFT0Py&*H8k4HiH#&PVJRz8o1yGjGX1$xgmL|`LSw;%+3>khGhe8I-UfwH zdCx||2Xzhc>TTLRjGtw4M@?v6XoMAS6G>Y|vrzVAJ4TJWju73(bF6Ac4GitciQPIp?ZFaz8_*a68v zFoC&lgRyRW15c5)21f_FnQ&0~SpT1UK#V~!&Bt4c^II zLQ!(=#u+uBpL2R~lzt0lYqVMoE&%8*LUwcljodNgGECD?{m;E3$m+&xN-?-m{y5Cb zwr*SCG{z-mrSsJOfNA2~x1K}(-_QRj@E-;KqriU@_>Thr|4^V=;t(zr!&9d;UwiiJ Os~@RoJ}gl-3;I8b5_CZT diff --git a/docs/chainscore.svg b/docs/chainscore.svg new file mode 100644 index 0000000..927b3cc --- /dev/null +++ b/docs/chainscore.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/cover.svg b/docs/cover.svg index 1d80e0e..d964289 100644 --- a/docs/cover.svg +++ b/docs/cover.svg @@ -1,45 +1,9 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + diff --git a/tests/benchmark/bench_ietf.py b/tests/benchmark/bench_ietf.py index b272cb8..4a58b66 100644 --- a/tests/benchmark/bench_ietf.py +++ b/tests/benchmark/bench_ietf.py @@ -10,20 +10,18 @@ - Reports min, mean, and std deviation """ -import json import statistics import time -from pathlib import Path from dot_ring import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.vrf.ietf.ietf import IETF_VRF def load_test_data(): - """Load test vector data.""" - vector_path = Path(__file__).parent / "vectors" / "ark-vrf" / "bandersnatch_ed_sha512_ell2_ietf.json" - with open(vector_path) as f: - return json.load(f)[0] + """Create deterministic benchmark data.""" + _, sk = secret_from_seed(bytes(32), Bandersnatch) + return {"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex(), "salt": ""} def benchmark_ietf_vrf(warmup_iters: int = 5, bench_iters: int = 100): diff --git a/tests/benchmark/bench_pedersen.py b/tests/benchmark/bench_pedersen.py index 661f223..4564513 100644 --- a/tests/benchmark/bench_pedersen.py +++ b/tests/benchmark/bench_pedersen.py @@ -10,20 +10,18 @@ - Reports min, mean, and std deviation """ -import json import statistics import time -from pathlib import Path from dot_ring import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.vrf.pedersen.pedersen import PedersenVRF def load_test_data(): - """Load test vector data.""" - vector_path = Path(__file__).parent / "vectors" / "ark-vrf" / "bandersnatch_ed_sha512_ell2_pedersen.json" - with open(vector_path) as f: - return json.load(f)[0] + """Create deterministic benchmark data.""" + _, sk = secret_from_seed(bytes(32), Bandersnatch) + return {"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex()} def benchmark_pedersen_vrf(warmup_iters: int = 5, bench_iters: int = 100): diff --git a/tests/benchmark/bench_ring_large.py b/tests/benchmark/bench_ring_large.py index 0676435..272e65d 100644 --- a/tests/benchmark/bench_ring_large.py +++ b/tests/benchmark/bench_ring_large.py @@ -21,7 +21,7 @@ def generate_ring_keys(ring_size: int) -> tuple[list[bytes], bytes, bytes]: # Generate keys deterministically from seeds for i in range(ring_size): - seed = f"ring_member_{i}".encode() + seed = i.to_bytes(32, "little") pk, sk = secret_from_seed(seed, Bandersnatch) keys.append(pk) diff --git a/tests/benchmark/bench_ring_proof.py b/tests/benchmark/bench_ring_proof.py index 65f290d..84a5353 100644 --- a/tests/benchmark/bench_ring_proof.py +++ b/tests/benchmark/bench_ring_proof.py @@ -10,7 +10,6 @@ - Reports min, mean, and std deviation """ -import json import statistics import sys import time @@ -20,16 +19,26 @@ sys.path.insert(0, str(Path(__file__).parent / "blst" / "bindings" / "python")) from dot_ring import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ring.ring_root import Ring, RingRoot from dot_ring.vrf.ring.ring_vrf import RingVRF def load_test_data(): - """Load test vector data.""" - vector_path = Path(__file__).parent.parent / "vectors" / "ark-vrf" / "bandersnatch_ed_sha512_ell2_ring.json" - with open(vector_path) as f: - return json.load(f)[0] + """Create deterministic benchmark data.""" + pk, sk = secret_from_seed(bytes(32), Bandersnatch) + keys = [] + for i in range(8): + member_pk, _ = secret_from_seed((i + 1).to_bytes(32, "little"), Bandersnatch) + keys.append(member_pk) + keys[3] = pk + return { + "sk": sk.hex(), + "alpha": b"bench input data".hex(), + "ad": b"ad".hex(), + "ring_pks": b"".join(keys).hex(), + } def benchmark_ring_proof(warmup_iters: int = 3, bench_iters: int = 10): diff --git a/tests/benchmark/test_bench_ietf.py b/tests/benchmark/test_bench_ietf.py index 4cf6e6b..efc7565 100644 --- a/tests/benchmark/test_bench_ietf.py +++ b/tests/benchmark/test_bench_ietf.py @@ -1,4 +1,3 @@ -import json import os import sys from pathlib import Path @@ -7,6 +6,7 @@ sys.path.insert(0, str(Path(__file__).parent)) from dot_ring.curve.specs.bandersnatch import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.vrf.ietf.ietf import IETF_VRF from ..utils.profiler import Profiler @@ -16,11 +16,9 @@ def load_test_data(): - """Load test vectors from JSON file - returns only first test case""" - file_path = os.path.join(HERE, "../vectors", "ark-vrf/bandersnatch_ed_sha512_ell2_ietf.json") - with open(file_path) as f: - data = json.load(f) - return [data[0]] # Return only first test case + """Create deterministic benchmark data.""" + pk, sk = secret_from_seed(bytes(32), Bandersnatch) + return [{"sk": sk.hex(), "pk": pk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex(), "salt": ""}] def test_bench_ietf_prove(): @@ -47,8 +45,7 @@ def test_bench_ietf_prove(): ): proof = IETF_VRF[Bandersnatch].prove(alpha, s_k, ad, salt) - # Verify correctness - assert proof.output_point.point_to_string().hex() == item["gamma"] + assert proof.verify(bytes.fromhex(item["pk"]), alpha, ad, salt) print("📊 Profile saved to: perf/results/") diff --git a/tests/benchmark/test_bench_pedersen.py b/tests/benchmark/test_bench_pedersen.py index f35826e..6d62259 100644 --- a/tests/benchmark/test_bench_pedersen.py +++ b/tests/benchmark/test_bench_pedersen.py @@ -1,4 +1,3 @@ -import json import os import sys from pathlib import Path @@ -7,6 +6,7 @@ sys.path.insert(0, str(Path(__file__).parent)) from dot_ring.curve.specs.bandersnatch import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.vrf.pedersen.pedersen import PedersenVRF from ..utils.profiler import Profiler @@ -16,11 +16,9 @@ def load_test_data(): - """Load test vectors from JSON file - returns only first test case""" - file_path = os.path.join(HERE, "../vectors", "ark-vrf/bandersnatch_ed_sha512_ell2_pedersen.json") - with open(file_path) as f: - data = json.load(f) - return [data[0]] # Return only first test case + """Create deterministic benchmark data.""" + _, sk = secret_from_seed(bytes(32), Bandersnatch) + return [{"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex()}] def test_bench_pedersen_prove(): @@ -46,8 +44,7 @@ def test_bench_pedersen_prove(): ): proof = PedersenVRF[Bandersnatch].prove(alpha, s_k, ad) - # Verify correctness - assert proof.output_point.point_to_string().hex() == item["gamma"] + assert proof.verify(alpha, ad) print("📊 Profile saved to: perf/results/") diff --git a/tests/benchmark/test_bench_ring.py b/tests/benchmark/test_bench_ring.py index 495b962..06aa3e2 100644 --- a/tests/benchmark/test_bench_ring.py +++ b/tests/benchmark/test_bench_ring.py @@ -1,4 +1,3 @@ -import json import os import sys from pathlib import Path @@ -7,6 +6,7 @@ sys.path.insert(0, str(Path(__file__).parent)) from dot_ring.curve.specs.bandersnatch import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ring.ring_root import Ring, RingRoot from dot_ring.vrf.ring.ring_vrf import RingVRF @@ -18,11 +18,22 @@ def load_test_data(): - """Load test vectors from JSON file - returns only first test case""" - file_path = os.path.join(HERE, "../vectors", "ark-vrf/bandersnatch_ed_sha512_ell2_ring.json") - with open(file_path) as f: - data = json.load(f) - return [data[0]] # Return only first test case + """Create deterministic benchmark data.""" + pk, sk = secret_from_seed(bytes(32), Bandersnatch) + keys = [] + for i in range(8): + member_pk, _ = secret_from_seed((i + 1).to_bytes(32, "little"), Bandersnatch) + keys.append(member_pk) + keys[3] = pk + return [ + { + "sk": sk.hex(), + "pk": pk.hex(), + "alpha": b"bench input data".hex(), + "ad": b"ad".hex(), + "ring_pks": b"".join(keys).hex(), + } + ] def test_bench_ring_prove(): @@ -55,9 +66,7 @@ def test_bench_ring_prove(): ): _ = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - # Verify correctness assert p_k.hex() == item["pk"], "Invalid Public Key" - assert ring_root.to_bytes().hex() == item["ring_pks_com"], "Invalid Ring Root" # expected_proof = ( # item["gamma"] # + item["proof_pk_com"] @@ -84,20 +93,13 @@ def test_bench_ring_verify(): alpha = bytes.fromhex(item["alpha"]) ad = bytes.fromhex(item["ad"]) keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(item["ring_pks"])) - ring_root_bytes = bytes.fromhex(item["ring_pks_com"]) # Construct ring and ring root params = RingProofParams() ring = Ring(keys, params) - ring_root = RingRoot.from_bytes(ring_root_bytes) - - proof_hex = ( - item["gamma"] + item["proof_pk_com"] + item["proof_r"] + item["proof_ok"] + item["proof_s"] + item["proof_sb"] + item["ring_proof"] - ) - proof_bytes = bytes.fromhex(proof_hex) - - # Parse proof from bytes - ring_vrf_proof = RingVRF[Bandersnatch].from_bytes(proof_bytes) + ring_root = RingRoot.from_ring(ring, params) + p_k = RingVRF[Bandersnatch].get_public_key(bytes.fromhex(item["sk"])) + ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, bytes.fromhex(item["sk"]), p_k, ring, ring_root) # Benchmark verification with profiling with Profiler( From 6783f5e25263c5b1bf0be26e0cc8f3e1235e8921 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 21:04:14 +0530 Subject: [PATCH 09/33] fix: satisfy lint import checks --- dot_ring/ring_proof/pcs/kzg.py | 4 +--- dot_ring/vrf/ring/__init__.py | 2 +- dot_ring/vrf/ring/ring_batch_verifier.py | 7 +++++-- dot_ring/vrf/ring/ring_context.py | 4 ++++ dot_ring/vrf/ring/ring_vrf.py | 18 ++++++++++++------ dot_ring/vrf/vrf.py | 1 - 6 files changed, 23 insertions(+), 13 deletions(-) diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index aa14283..7827dc0 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -237,7 +237,5 @@ def batch_verify( rhs = blst_miller_loop(rhs_point, g2_tau) return bool(blst_final_verify(lhs, rhs)) -from .bn254_kzg import BN254KZG -from .bn254_srs import BN254SRS -__all__ = ["BN254KZG", "BN254SRS", "KZG", "Opening"] +__all__ = ["KZG", "Opening"] diff --git a/dot_ring/vrf/ring/__init__.py b/dot_ring/vrf/ring/__init__.py index 6c146e0..655a2d6 100644 --- a/dot_ring/vrf/ring/__init__.py +++ b/dot_ring/vrf/ring/__init__.py @@ -1,7 +1,7 @@ +from .ring import Ring from .ring_batch_item import RingBatchItem from .ring_batch_verifier import RingBatchVerifier from .ring_context import RingContext, RingSetup -from .ring import Ring from .ring_root import RingRoot from .ring_verifier_key_builder import RingVerifierKeyBuilder from .ring_vrf import RingVRF diff --git a/dot_ring/vrf/ring/ring_batch_verifier.py b/dot_ring/vrf/ring/ring_batch_verifier.py index f69ead7..fef78b8 100644 --- a/dot_ring/vrf/ring/ring_batch_verifier.py +++ b/dot_ring/vrf/ring/ring_batch_verifier.py @@ -1,14 +1,17 @@ from __future__ import annotations -from typing import Any, cast +from typing import TYPE_CHECKING, Any, cast from dot_ring.vrf.pedersen.pedersen_batch_verifier import PedersenBatchVerifier from dot_ring.vrf.transcript import VrfIo -from .ring_batch_item import RingBatchItem from .ring import Ring +from .ring_batch_item import RingBatchItem from .ring_root import RingRoot +if TYPE_CHECKING: + from .ring_vrf import RingVRF + class RingBatchVerifier: def __init__(self) -> None: diff --git a/dot_ring/vrf/ring/ring_context.py b/dot_ring/vrf/ring/ring_context.py index 087e7e0..5402028 100644 --- a/dot_ring/vrf/ring/ring_context.py +++ b/dot_ring/vrf/ring/ring_context.py @@ -1,6 +1,7 @@ from __future__ import annotations from dataclasses import dataclass +from typing import TYPE_CHECKING from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.params import RingProofParams @@ -9,6 +10,9 @@ from .ring_keys import parse_concatenated_keys from .ring_root import RingRoot +if TYPE_CHECKING: + from .ring_verifier_key_builder import RingVerifierKeyBuilder + RingSetup = RingProofParams diff --git a/dot_ring/vrf/ring/ring_vrf.py b/dot_ring/vrf/ring/ring_vrf.py index ec29680..e9a98a6 100644 --- a/dot_ring/vrf/ring/ring_vrf.py +++ b/dot_ring/vrf/ring/ring_vrf.py @@ -18,15 +18,27 @@ from ..vrf import VRF from .ring import Ring +from .ring_batch_item import RingBatchItem +from .ring_batch_verifier import RingBatchVerifier +from .ring_context import RingContext, RingSetup from .ring_keys import parse_concatenated_keys as _parse_concatenated_keys from .ring_root import RingRoot from .ring_serialization import ( compress_g1 as _compress_g1, +) +from .ring_serialization import ( decompress_g1 as _decompress_g1, +) +from .ring_serialization import ( ring_proof_len as _ring_proof_len, +) +from .ring_serialization import ( transcript_g1 as _transcript_g1, +) +from .ring_serialization import ( transcript_vk as _transcript_vk, ) +from .ring_verifier_key_builder import RingVerifierKeyBuilder @dataclass @@ -463,12 +475,6 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) # type: ignore[misc] - -from .ring_batch_item import RingBatchItem -from .ring_batch_verifier import RingBatchVerifier -from .ring_context import RingContext, RingSetup -from .ring_verifier_key_builder import RingVerifierKeyBuilder - __all__ = [ "RingBatchItem", "RingBatchVerifier", diff --git a/dot_ring/vrf/vrf.py b/dot_ring/vrf/vrf.py index dd94b83..60a9f24 100644 --- a/dot_ring/vrf/vrf.py +++ b/dot_ring/vrf/vrf.py @@ -7,7 +7,6 @@ from ..curve.curve import CurveVariant from ..curve.point import CurvePoint from ..ring_proof.helpers import Helpers -from .vrf_protocol import VRFProtocol C = TypeVar("C", bound=CurveVariant) From 5aeb203ec1a00a149fbdc31d15e5204a9d0002dd Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Sun, 24 May 2026 21:07:37 +0530 Subject: [PATCH 10/33] fix: lint format --- dot_ring/ring_proof/pcs/kzg.py | 1 + dot_ring/vrf/ark_transcript.py | 5 +---- dot_ring/vrf/ietf/tiny_vrf.py | 6 +----- dot_ring/vrf/pedersen/pedersen_batch_verifier.py | 11 +++++++---- dot_ring/vrf/ring/ring_vrf.py | 1 + scripts/generate_test_vectors.py | 5 +---- tests/test_ark_vrf.py | 7 +------ tests/test_dot_ring_vectors.py | 7 +------ 8 files changed, 14 insertions(+), 29 deletions(-) diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index 7827dc0..df19f71 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -238,4 +238,5 @@ def batch_verify( return bool(blst_final_verify(lhs, rhs)) + __all__ = ["KZG", "Opening"] diff --git a/dot_ring/vrf/ark_transcript.py b/dot_ring/vrf/ark_transcript.py index 4c44eab..c04d8eb 100644 --- a/dot_ring/vrf/ark_transcript.py +++ b/dot_ring/vrf/ark_transcript.py @@ -55,10 +55,7 @@ def _squeeze_digest_xof(self, size: int) -> bytes: block_size = len(self._seed) first_block = start // block_size last_block = (end + block_size - 1) // block_size - stream = b"".join( - self._digest(self._seed + counter.to_bytes(8, "little")) - for counter in range(first_block, last_block) - ) + stream = b"".join(self._digest(self._seed + counter.to_bytes(8, "little")) for counter in range(first_block, last_block)) self._squeeze_offset = end return stream[start % block_size : start % block_size + size] diff --git a/dot_ring/vrf/ietf/tiny_vrf.py b/dot_ring/vrf/ietf/tiny_vrf.py index fd86e8a..66a3f70 100644 --- a/dot_ring/vrf/ietf/tiny_vrf.py +++ b/dot_ring/vrf/ietf/tiny_vrf.py @@ -57,11 +57,7 @@ def from_bytes(cls, proof_bytes: bytes) -> TinyVRF: return cls(output_point, c, s) def to_bytes(self) -> bytes: - return ( - self.output_point.point_to_string() - + self.c.to_bytes(CHALLENGE_LEN, "little") - + scalar_encode(self.cv, self.s) - ) + return self.output_point.point_to_string() + self.c.to_bytes(CHALLENGE_LEN, "little") + scalar_encode(self.cv, self.s) @classmethod def _io_from_alpha(cls, alpha: bytes, secret_scalar: int, salt: bytes = b"") -> VrfIo: diff --git a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py index a12dedd..57ab55b 100644 --- a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py +++ b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py @@ -69,7 +69,10 @@ def verify(self) -> bool: input_scalars.extend([coefficient * item.s, -coefficient * item.c, -coefficient]) commitment_points.extend([generator, blinding_base, item.pk_com, item.r]) commitment_scalars.extend([coefficient * item.s, coefficient * item.sb, -coefficient * item.c, -coefficient]) - return self.cv.point.msm(input_points, input_scalars).is_identity() and self.cv.point.msm( - commitment_points, - commitment_scalars, - ).is_identity() + return ( + self.cv.point.msm(input_points, input_scalars).is_identity() + and self.cv.point.msm( + commitment_points, + commitment_scalars, + ).is_identity() + ) diff --git a/dot_ring/vrf/ring/ring_vrf.py b/dot_ring/vrf/ring/ring_vrf.py index e9a98a6..2469a97 100644 --- a/dot_ring/vrf/ring/ring_vrf.py +++ b/dot_ring/vrf/ring/ring_vrf.py @@ -475,6 +475,7 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) # type: ignore[misc] + __all__ = [ "RingBatchItem", "RingBatchVerifier", diff --git a/scripts/generate_test_vectors.py b/scripts/generate_test_vectors.py index df5dabb..0d65da7 100644 --- a/scripts/generate_test_vectors.py +++ b/scripts/generate_test_vectors.py @@ -193,10 +193,7 @@ def scheme_vectors(suite: Suite, scheme: str) -> list[dict[str, Any]]: "pedersen": pedersen_vector, "ring": ring_vector, } - return [ - builders[scheme](suite, index, seed, alpha, ad) - for index, (seed, alpha, ad) in enumerate(VECTOR_CASES, start=1) - ] + return [builders[scheme](suite, index, seed, alpha, ad) for index, (seed, alpha, ad) in enumerate(VECTOR_CASES, start=1)] def write_vectors(out_dir: Path, suite: Suite, scheme: str) -> None: diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py index 77556c7..5238b3b 100644 --- a/tests/test_ark_vrf.py +++ b/tests/test_ark_vrf.py @@ -96,12 +96,7 @@ def test_pedersen_vectors(curve, prefix: str, _point_len: int) -> None: proof_bytes = proof.to_bytes() assert proof_bytes.hex() == ( - vector["gamma"] - + vector["proof_pk_com"] - + vector["proof_r"] - + vector["proof_ok"] - + vector["proof_s"] - + vector["proof_sb"] + vector["gamma"] + vector["proof_pk_com"] + vector["proof_r"] + vector["proof_ok"] + vector["proof_s"] + vector["proof_sb"] ) assert proof.verify(alpha, ad) assert PedersenVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes diff --git a/tests/test_dot_ring_vectors.py b/tests/test_dot_ring_vectors.py index d760313..b1050de 100644 --- a/tests/test_dot_ring_vectors.py +++ b/tests/test_dot_ring_vectors.py @@ -61,12 +61,7 @@ def test_dot_ring_pedersen_vectors(suite: Suite) -> None: alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) proof_bytes = bytes.fromhex( - vector["gamma"] - + vector["proof_pk_com"] - + vector["proof_r"] - + vector["proof_ok"] - + vector["proof_s"] - + vector["proof_sb"] + vector["gamma"] + vector["proof_pk_com"] + vector["proof_r"] + vector["proof_ok"] + vector["proof_s"] + vector["proof_sb"] ) proof = PedersenVRF[suite.curve].from_bytes(proof_bytes) From 3321bd69dea3eaa2d957de846040acfadd91eaf5 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:50:43 +0530 Subject: [PATCH 11/33] ci: set coverage floor --- .github/workflows/test.yml | 1 + codecov.yml | 11 +++++++++++ 2 files changed, 12 insertions(+) create mode 100644 codecov.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a8598e9..deed83b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -45,6 +45,7 @@ jobs: --cov=dot_ring \ --cov-report=term-missing \ --cov-report=xml \ + --cov-fail-under=80 \ -v \ --tb=short diff --git a/codecov.yml b/codecov.yml new file mode 100644 index 0000000..79bf282 --- /dev/null +++ b/codecov.yml @@ -0,0 +1,11 @@ +coverage: + status: + project: + default: + target: "80%" + threshold: "1%" + patch: + default: + target: "80%" + threshold: "5%" + informational: true From 5866a4e0e23244055111bee779707ecf0a219380 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:51:05 +0530 Subject: [PATCH 12/33] refactor: use spec transcript naming --- dot_ring/vrf/delinearize.py | 8 +++-- .../{ark_transcript.py => spec_transcript.py} | 9 ++--- dot_ring/vrf/transcript.py | 34 +++++++++---------- 3 files changed, 26 insertions(+), 25 deletions(-) rename dot_ring/vrf/{ark_transcript.py => spec_transcript.py} (92%) diff --git a/dot_ring/vrf/delinearize.py b/dot_ring/vrf/delinearize.py index 699d5c9..b1fabb5 100644 --- a/dot_ring/vrf/delinearize.py +++ b/dot_ring/vrf/delinearize.py @@ -1,14 +1,16 @@ from __future__ import annotations +from typing import cast + from dot_ring.curve.curve import CurveVariant -from .ark_transcript import ArkTranscript from .domain import DomSep +from .spec_transcript import SpecTranscript from .transcript_constants import CHALLENGE_LEN class DelinearizeScalars: - def __init__(self, curve: CurveVariant, transcript: ArkTranscript) -> None: + def __init__(self, curve: CurveVariant, transcript: SpecTranscript) -> None: self.curve = curve self.transcript = transcript.clone() self.transcript.absorb_raw(bytes([DomSep.DELINEARIZE])) @@ -18,7 +20,7 @@ def next(self) -> int: if self.first: self.first = False return 1 - return int.from_bytes(self.transcript.squeeze_raw(CHALLENGE_LEN), "little") % self.curve.curve.ORDER + return int.from_bytes(self.transcript.squeeze_raw(CHALLENGE_LEN), "little") % cast(int, self.curve.curve.ORDER) def take(self, n: int) -> list[int]: return [self.next() for _ in range(n)] diff --git a/dot_ring/vrf/ark_transcript.py b/dot_ring/vrf/spec_transcript.py similarity index 92% rename from dot_ring/vrf/ark_transcript.py rename to dot_ring/vrf/spec_transcript.py index c04d8eb..5f9a92c 100644 --- a/dot_ring/vrf/ark_transcript.py +++ b/dot_ring/vrf/spec_transcript.py @@ -1,24 +1,25 @@ from __future__ import annotations import hashlib +from typing import cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint def _scalar_len(curve: CurveVariant) -> int: - return (curve.curve.ORDER.bit_length() + 7) // 8 + return (cast(int, curve.curve.ORDER).bit_length() + 7) // 8 -class ArkTranscript: +class SpecTranscript: def __init__(self, label: bytes, hash_name: str = "sha512") -> None: self.hash_name = hash_name self._absorbed = bytearray(label) self._seed: bytes | None = None self._squeeze_offset = 0 - def clone(self) -> ArkTranscript: - other = ArkTranscript(b"", self.hash_name) + def clone(self) -> SpecTranscript: + other = SpecTranscript(b"", self.hash_name) other._absorbed = bytearray(self._absorbed) other._seed = self._seed other._squeeze_offset = self._squeeze_offset diff --git a/dot_ring/vrf/transcript.py b/dot_ring/vrf/transcript.py index 35f3b7a..b148375 100644 --- a/dot_ring/vrf/transcript.py +++ b/dot_ring/vrf/transcript.py @@ -1,22 +1,22 @@ from __future__ import annotations -from typing import Any +from typing import Any, cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.ark_transcript import ArkTranscript from dot_ring.vrf.delinearize import DelinearizeScalars from dot_ring.vrf.domain import DomSep +from dot_ring.vrf.spec_transcript import SpecTranscript from dot_ring.vrf.transcript_constants import CHALLENGE_LEN, SECURITY_PARAMETER from dot_ring.vrf.vrf_io import VrfIo def suite_id(curve: CurveVariant) -> bytes: - return curve.curve.SUITE_ID or curve.curve.SUITE_STRING + return cast(bytes, curve.curve.SUITE_ID or curve.curve.SUITE_STRING) def scalar_len(curve: CurveVariant) -> int: - return (curve.curve.ORDER.bit_length() + 7) // 8 + return (cast(int, curve.curve.ORDER).bit_length() + 7) // 8 def scalar_encode(curve: CurveVariant, value: int) -> bytes: @@ -28,22 +28,22 @@ def scalar_decode(curve: CurveVariant, value: bytes) -> int: def expanded_scalar_len(curve: CurveVariant, sec_bits: int = SECURITY_PARAMETER) -> int: - return (curve.curve.ORDER.bit_length() + sec_bits + 7) // 8 + return (cast(int, curve.curve.ORDER).bit_length() + sec_bits + 7) // 8 -def transcript_for(curve: CurveVariant) -> ArkTranscript: - return ArkTranscript(suite_id(curve), curve.curve.TRANSCRIPT_HASH) +def transcript_for(curve: CurveVariant) -> SpecTranscript: + return SpecTranscript(suite_id(curve), curve.curve.TRANSCRIPT_HASH) -def nonce_scalar(curve: CurveVariant, transcript: ArkTranscript) -> int: - return int.from_bytes(transcript.squeeze_raw(expanded_scalar_len(curve)), "little") % curve.curve.ORDER +def nonce_scalar(curve: CurveVariant, transcript: SpecTranscript) -> int: + return int.from_bytes(transcript.squeeze_raw(expanded_scalar_len(curve)), "little") % cast(int, curve.curve.ORDER) -def challenge_scalar(curve: CurveVariant, transcript: ArkTranscript) -> int: - return int.from_bytes(transcript.squeeze_raw(CHALLENGE_LEN), "little") % curve.curve.ORDER +def challenge_scalar(curve: CurveVariant, transcript: SpecTranscript) -> int: + return int.from_bytes(transcript.squeeze_raw(CHALLENGE_LEN), "little") % cast(int, curve.curve.ORDER) -def nonce(curve: CurveVariant, secret_scalar: int, transcript: ArkTranscript | None = None) -> int: +def nonce(curve: CurveVariant, secret_scalar: int, transcript: SpecTranscript | None = None) -> int: t = transcript.clone() if transcript is not None else transcript_for(curve) t_exp = t.clone() @@ -56,7 +56,7 @@ def nonce(curve: CurveVariant, secret_scalar: int, transcript: ArkTranscript | N return nonce_scalar(curve, t) -def challenge(curve: CurveVariant, points: list[CurvePoint], transcript: ArkTranscript | None = None) -> int: +def challenge(curve: CurveVariant, points: list[CurvePoint], transcript: SpecTranscript | None = None) -> int: t = transcript.clone() if transcript is not None else transcript_for(curve) t.absorb_raw(bytes([DomSep.CHALLENGE])) for point in points: @@ -76,7 +76,7 @@ def vrf_transcript( scheme: DomSep, ios: list[VrfIo], ad: bytes, -) -> tuple[ArkTranscript, VrfIo]: +) -> tuple[SpecTranscript, VrfIo]: t, scalars = vrf_transcript_scalars(curve, scheme, ios, ad) if not ios: zero = curve.point.identity() @@ -98,7 +98,7 @@ def vrf_transcript_scalars( scheme: DomSep, ios: list[VrfIo], ad: bytes, -) -> tuple[ArkTranscript, DelinearizeScalars]: +) -> tuple[SpecTranscript, DelinearizeScalars]: t = transcript_for(curve) t.absorb_raw(bytes([scheme])) t.absorb_raw(len(ios).to_bytes(8, "little")) @@ -136,7 +136,7 @@ def hash_to_curve_tai(point_cls: Any, data: bytes) -> CurvePoint: curve = point_cls.curve field_len = (curve.PRIME_FIELD.bit_length() + 7) // 8 - prefix = ArkTranscript(curve.SUITE_ID or curve.SUITE_STRING, curve.TRANSCRIPT_HASH) + prefix = SpecTranscript(curve.SUITE_ID or curve.SUITE_STRING, curve.TRANSCRIPT_HASH) prefix.absorb_raw(bytes([DomSep.HASH_TO_CURVE])) prefix.absorb_raw(len(data).to_bytes(8, "little")) prefix.absorb_raw(data) @@ -160,8 +160,6 @@ def hash_to_curve_tai(point_cls: Any, data: bytes) -> CurvePoint: point = point_cls.string_to_point(bytes(candidate)) except ValueError: continue - if isinstance(point, str): - continue if point.curve.COFACTOR > 1: point = point * point.curve.COFACTOR if not point.is_identity(): From 31414439b55b8454b6b68449551337dc5feb38d4 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:51:18 +0530 Subject: [PATCH 13/33] refactor: split ring proof internals --- dot_ring/ring_proof/columns/columns.py | 18 +- dot_ring/ring_proof/helpers.py | 15 +- dot_ring/ring_proof/params.py | 23 +- dot_ring/ring_proof/pcs/kzg.py | 47 ++- dot_ring/ring_proof/pcs/opening.py | 4 +- dot_ring/ring_proof/pcs/protocol.py | 40 +++ dot_ring/ring_proof/pcs/utils.py | 27 +- dot_ring/ring_proof/proof/aggregation_poly.py | 3 +- dot_ring/ring_proof/proof/quotient_poly.py | 7 +- dot_ring/ring_proof/verify.py | 3 +- dot_ring/scripts/export_python_proof.py | 19 +- dot_ring/vrf/ring/ring.py | 43 ++- dot_ring/vrf/ring/ring_context.py | 9 +- dot_ring/vrf/ring/ring_proof_builder.py | 155 ++++++++ dot_ring/vrf/ring/ring_proof_payload.py | 134 +++++++ dot_ring/vrf/ring/ring_root.py | 31 +- dot_ring/vrf/ring/ring_serialization.py | 13 +- dot_ring/vrf/ring/ring_vrf.py | 337 ++++-------------- tests/test_coverage/test_kzg.py | 5 +- 19 files changed, 565 insertions(+), 368 deletions(-) create mode 100644 dot_ring/ring_proof/pcs/protocol.py create mode 100644 dot_ring/vrf/ring/ring_proof_builder.py create mode 100644 dot_ring/vrf/ring/ring_proof_payload.py diff --git a/dot_ring/ring_proof/columns/columns.py b/dot_ring/ring_proof/columns/columns.py index a7ddb32..289553f 100644 --- a/dot_ring/ring_proof/columns/columns.py +++ b/dot_ring/ring_proof/columns/columns.py @@ -4,7 +4,7 @@ import os import secrets from dataclasses import dataclass -from typing import Any, cast +from typing import cast from dot_ring.curve.point import CurvePoint from dot_ring.curve.specs.bandersnatch import Bandersnatch @@ -12,13 +12,13 @@ from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft _H_VEC_DEFAULT = json.load(open(os.path.join(os.path.dirname(__file__), "h_vec.json"))) _H_VEC_DEFAULT = [tuple(pt) for pt in _H_VEC_DEFAULT] Scalar = int -G1Point = tuple @dataclass(slots=True) @@ -26,7 +26,7 @@ class Column: name: str evals: list[int] coeffs: list[int] | None = None - commitment: G1Point | None = None + commitment: G1Commitment | None = None size: int = DEFAULT_SIZE def interpolate( @@ -55,13 +55,19 @@ def interpolate( self.evals += [0] * (self.size - len(self.evals)) self.coeffs = poly_interpolate_fft(self.evals, domain_omega, prime) - def commit(self, pcs: Any = KZG) -> None: + def commit(self, pcs: type[PCS] = KZG) -> None: if self.coeffs is None: raise ValueError("call interpolate() first") if self.commitment is None: self.commitment = pcs.commit(self.coeffs) +def require_commitment(column: Column) -> G1Commitment: + if column.commitment is None: + raise ValueError(f"{column.name} commitment is missing") + return column.commitment + + @dataclass(slots=True) class WitnessColumnBuilder: ring_pk: list[tuple[int, int]] @@ -76,7 +82,7 @@ class WitnessColumnBuilder: test_vectors: bool = False seed_point: tuple[int, int] = SeedPoint point_cls: type[CurvePoint] = Bandersnatch.point - pcs: Any = KZG + pcs: type[PCS] = KZG @classmethod def from_params( @@ -135,7 +141,7 @@ def _conditional_sum_accumulator(self, b_vector: list[int]) -> tuple[list[int], for i in range(1, acc_len): next_pt = acc[i - 1] if b_vector[i - 1] == 0 else self._add(acc[i - 1], self.ring_pk[i - 1]) acc.append(next_pt) - return H.unzip(acc) + return cast(tuple[list[int], list[int]], H.unzip(acc)) def _inner_product_accumulator(self, b_vector: list[int]) -> list[int]: acc = [0] diff --git a/dot_ring/ring_proof/helpers.py b/dot_ring/ring_proof/helpers.py index 6b2f09f..0f1d071 100644 --- a/dot_ring/ring_proof/helpers.py +++ b/dot_ring/ring_proof/helpers.py @@ -27,7 +27,17 @@ def unzip(points: list[tuple[T, T]]) -> tuple[list[T], list[T]]: @staticmethod # bls point to string - def bls_g1_compress(bls_point: tuple) -> str: + def bls_g1_compress(bls_point: Any) -> str: + try: + from dot_ring import blst + + if isinstance(bls_point, blst.P1): + return bls_point.compress().hex() + if isinstance(bls_point, blst.P1_Affine): + return blst.P1(bls_point).compress().hex() + except (ImportError, AttributeError): + pass + if len(bls_point) == 2: point = (FQ(bls_point[0]), FQ(bls_point[1]), FQ(1)) else: @@ -69,7 +79,8 @@ def bls_g1_decompress(byte_array: bytes | str) -> tuple[FQ, FQ, FQ]: else: dcp_scalar = int(byte_array, 16) decompressed = point_compression.decompress_G1(dcp_scalar) - assert is_on_curve(decompressed, 4), "INVALID POINT" + if not is_on_curve(decompressed, 4): + raise ValueError("Invalid BLS12-381 G1 point") from None return cast(tuple[FQ, FQ, FQ], decompressed) @staticmethod diff --git a/dot_ring/ring_proof/params.py b/dot_ring/ring_proof/params.py index 4d5bee6..739e190 100644 --- a/dot_ring/ring_proof/params.py +++ b/dot_ring/ring_proof/params.py @@ -2,6 +2,7 @@ from dataclasses import dataclass, field from functools import lru_cache +from typing import cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint @@ -9,6 +10,7 @@ from dot_ring.ring_proof.constants import D_2048, DEFAULT_SIZE, MAX_RING_SIZE, OMEGA_2048, S_PRIME from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.protocol import PCS def _is_power_of_two(n: int) -> bool: @@ -133,7 +135,7 @@ class RingProofParams: prime: int = S_PRIME base_root: int = OMEGA_2048 base_root_size: int = 2048 - pcs: object = field(default=KZG, compare=False, hash=False, repr=False) + pcs: type[PCS] = field(default=KZG, compare=False, hash=False, repr=False) test_vectors: bool = False cv: CurveVariant = field(default_factory=lambda: Bandersnatch, compare=False, hash=False) @@ -187,17 +189,24 @@ def domain(self) -> list[int]: @property def radix_omega(self) -> int: - return _omega_for_domain(self.radix_domain_size, self.prime, self.base_root, self.base_root_size) + return _omega_for_domain(self._radix_domain_size, self.prime, self.base_root, self.base_root_size) @property def radix_domain(self) -> list[int]: - if self.radix_domain_size == 2048 and self.base_root_size == 2048 and self.base_root == OMEGA_2048: + radix_domain_size = self._radix_domain_size + if radix_domain_size == 2048 and self.base_root_size == 2048 and self.base_root == OMEGA_2048: return list(D_2048) - return list(_domain_for_size(self.radix_domain_size, self.prime, self.base_root, self.base_root_size)) + return list(_domain_for_size(radix_domain_size, self.prime, self.base_root, self.base_root_size)) @property def radix_shift(self) -> int: - return self.radix_domain_size // self.domain_size + return self._radix_domain_size // self.domain_size + + @property + def _radix_domain_size(self) -> int: + if self.radix_domain_size is None: + raise ValueError("radix_domain_size is not initialized") + return self.radix_domain_size @property def last_index(self) -> int: @@ -236,8 +245,8 @@ def point_to_ring_point(self, point: CurvePoint) -> tuple[int, int]: @property def ring_point_cls(self) -> type[CurvePoint]: if self.cv.name == "Bandersnatch_SW": - return Bandersnatch.point - return self.cv.point + return cast(type[CurvePoint], Bandersnatch.point) + return cast(type[CurvePoint], self.cv.point) @property def ring_edwards_a(self) -> int: diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index df19f71..9721998 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -10,8 +10,8 @@ from ..polynomial.poly_ops import poly_evaluate_single from .opening import Opening from .pairing import blst_final_verify, blst_miller_loop -from .srs import G1Point, srs -from .utils import CoeffVector, Scalar, blst_p1_to_fq_tuple, g1_to_blst, synthetic_div +from .srs import srs +from .utils import CoeffVector, Scalar, g1_to_blst, synthetic_div blst = cast(Any, _blst) Point_G1 = Any @@ -55,18 +55,36 @@ class KZG: @staticmethod def normalize_g1(point: Point_G1) -> tuple[int, int]: - if isinstance(point, blst.P1): - point = blst_p1_to_fq_tuple(point) + if isinstance(point, (blst.P1, blst.P1_Affine)): + raw = g1_to_blst(point).serialize() + return int.from_bytes(raw[:48], "big"), int.from_bytes(raw[48:], "big") x, y = bls.normalize(point) return int(x), int(y) + @classmethod + def compress_g1(cls, point: Point_G1) -> bytes: + return g1_to_blst(point).compress() + + @classmethod + def serialize_g1_uncompressed(cls, point: Point_G1) -> bytes: + return g1_to_blst(point).serialize() + + @classmethod + def decompress_g1(cls, data: bytes) -> Any: + if len(data) != cls.commitment_size: + raise ValueError(f"invalid BLS12-381 G1 length: expected {cls.commitment_size}, got {len(data)}") + try: + return blst.P1(blst.P1_Affine(data)) + except RuntimeError as exc: + raise ValueError("invalid BLS12-381 G1 encoding") from exc + @classmethod def msm_g1(cls, points: list[Any], scalars: list[int]) -> Any: - blst_points = [point if isinstance(point, blst.P1) else g1_to_blst(point) for point in points] + blst_points = [g1_to_blst(point) for point in points] return blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(blst_points), scalars) @classmethod - def commit(cls, coeffs: CoeffVector) -> G1Point: + def commit(cls, coeffs: CoeffVector) -> Any: """ Commit to a polynomial using Pippenger multi-scalar multiplication. @@ -92,7 +110,7 @@ def commit(cls, coeffs: CoeffVector) -> G1Point: else: # Use Pippenger multi-scalar multiplication result = blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(blst_points), active_scalars) - return blst_p1_to_fq_tuple(result) + return result @classmethod def open(cls, coeffs: CoeffVector, x: Scalar) -> Opening: @@ -131,15 +149,8 @@ def verify( Returns: True if proof is valid, False otherwise """ - if isinstance(commitment, blst.P1): - comm_blst = commitment - else: - comm_blst = g1_to_blst(commitment) - - if isinstance(proof, blst.P1): - proof_blst = proof - else: - proof_blst = g1_to_blst(proof) + comm_blst = g1_to_blst(commitment) + proof_blst = g1_to_blst(proof) g1_gen = srs.blst_g1[0] # [1]G1 g2_gen = srs.blst_g2[0] # [1]G2 @@ -208,8 +219,8 @@ def batch_verify( sum_v = 0 for coeff, (commitment, proof, point, value) in zip(coeffs, verifications, strict=False): - comm_blst = commitment - proof_blst = proof + comm_blst = g1_to_blst(commitment) + proof_blst = g1_to_blst(proof) # LHS terms lhs_points.append(comm_blst) diff --git a/dot_ring/ring_proof/pcs/opening.py b/dot_ring/ring_proof/pcs/opening.py index 667db25..7ddda04 100644 --- a/dot_ring/ring_proof/pcs/opening.py +++ b/dot_ring/ring_proof/pcs/opening.py @@ -1,12 +1,12 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any +from .protocol import G1Commitment from .utils import Scalar @dataclass(slots=True, frozen=True) class Opening: - proof: Any + proof: G1Commitment y: Scalar diff --git a/dot_ring/ring_proof/pcs/protocol.py b/dot_ring/ring_proof/pcs/protocol.py new file mode 100644 index 0000000..13c9131 --- /dev/null +++ b/dot_ring/ring_proof/pcs/protocol.py @@ -0,0 +1,40 @@ +from __future__ import annotations + +from typing import Any, ClassVar, Protocol + +from .utils import CoeffVector, Scalar + +G1Commitment = object + + +class PCS(Protocol): + commitment_size: ClassVar[int] + scalar_modulus: ClassVar[int] + srs: ClassVar[Any] + + @classmethod + def normalize_g1(cls, point: G1Commitment) -> tuple[int, int]: ... + + @classmethod + def compress_g1(cls, point: G1Commitment) -> bytes: ... + + @classmethod + def serialize_g1_uncompressed(cls, point: G1Commitment) -> bytes: ... + + @classmethod + def decompress_g1(cls, data: bytes) -> G1Commitment: ... + + @classmethod + def msm_g1(cls, points: list[G1Commitment], scalars: list[int]) -> G1Commitment: ... + + @classmethod + def commit(cls, coeffs: CoeffVector) -> G1Commitment: ... + + @classmethod + def open(cls, coeffs: CoeffVector, x: Scalar) -> Any: ... + + @classmethod + def verify(cls, commitment: G1Commitment, proof: G1Commitment, point: Scalar, value: Scalar) -> bool: ... + + @classmethod + def batch_verify(cls, verifications: list[tuple[G1Commitment, G1Commitment, Scalar, Scalar]]) -> bool: ... diff --git a/dot_ring/ring_proof/pcs/utils.py b/dot_ring/ring_proof/pcs/utils.py index fbf9e34..4fc5493 100644 --- a/dot_ring/ring_proof/pcs/utils.py +++ b/dot_ring/ring_proof/pcs/utils.py @@ -1,6 +1,5 @@ import py_ecc.optimized_bls12_381 as bls from py_ecc.bls import point_compression -from py_ecc.optimized_bls12_381 import FQ from dot_ring import blst @@ -21,26 +20,24 @@ def synthetic_div(poly: CoeffVector, x: Scalar, y: Scalar) -> CoeffVector: return q -def g1_to_blst(p: tuple) -> blst.P1: - """Convert py_ecc G1 point (Jacobian tuple) to blst.P1""" +def g1_to_blst(p: tuple | blst.P1 | blst.P1_Affine) -> blst.P1: + """Convert a G1 point to blst.P1.""" + if isinstance(p, blst.P1): + return p + if isinstance(p, blst.P1_Affine): + return blst.P1(p) compressed_int = point_compression.compress_G1(p) compressed_bytes = compressed_int.to_bytes(48, "big") return blst.P1(blst.P1_Affine(compressed_bytes)) -def g2_to_blst(p: tuple) -> blst.P2: - """Convert py_ecc G2 point to blst.P2""" +def g2_to_blst(p: tuple | blst.P2 | blst.P2_Affine) -> blst.P2: + """Convert a G2 point to blst.P2.""" + if isinstance(p, blst.P2): + return p + if isinstance(p, blst.P2_Affine): + return blst.P2(p) z1, z2 = point_compression.compress_G2(p) b1 = z1.to_bytes(48, "big") b2 = z2.to_bytes(48, "big") return blst.P2(blst.P2_Affine(b1 + b2)) - - -def blst_p1_to_fq_tuple(blst_point: blst.P1) -> tuple[FQ, FQ, FQ]: - """Convert blst.P1 point back to (FQ, FQ, FQ) tuple in Jacobian coordinates""" - point_bytes = blst_point.serialize() - x_bytes = point_bytes[:48] - y_bytes = point_bytes[48:96] - x_int = int.from_bytes(x_bytes, "big") - y_int = int.from_bytes(y_bytes, "big") - return (FQ(x_int), FQ(y_int), FQ(1)) diff --git a/dot_ring/ring_proof/proof/aggregation_poly.py b/dot_ring/ring_proof/proof/aggregation_poly.py index e471146..d377f1f 100644 --- a/dot_ring/ring_proof/proof/aggregation_poly.py +++ b/dot_ring/ring_proof/proof/aggregation_poly.py @@ -1,5 +1,6 @@ from dot_ring.ring_proof.constants import S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.protocol import PCS from dot_ring.ring_proof.polynomial.poly_ops import poly_add, poly_scalar_mul @@ -35,7 +36,7 @@ def proof_contents_phi( Q_p: list[int], cf_vectors: list[int], prime: int = S_PRIME, - pcs: object = KZG, + pcs: type[PCS] = KZG, ) -> tuple: """ input:agg_poly, liner_poly, zeta, zeta_omega diff --git a/dot_ring/ring_proof/proof/quotient_poly.py b/dot_ring/ring_proof/proof/quotient_poly.py index f25a545..f63bff6 100644 --- a/dot_ring/ring_proof/proof/quotient_poly.py +++ b/dot_ring/ring_proof/proof/quotient_poly.py @@ -1,9 +1,10 @@ from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment from dot_ring.ring_proof.polynomial.ops import poly_division_general class QuotientPoly: - def __init__(self, domain_size: int, pcs: object = KZG) -> None: + def __init__(self, domain_size: int, pcs: type[PCS] = KZG) -> None: self.domain_size = domain_size self.pcs = pcs @@ -15,7 +16,7 @@ def poly_vector_xn_minus_1(n: int) -> list[int]: # print("vect:", vec) return vec - def quotient_poly_commitment(self, q_x: list[int]) -> tuple: + def quotient_poly_commitment(self, q_x: list[int]) -> G1Commitment: """ input: quotient polynomial output: commitment to quotient polynomial @@ -23,7 +24,7 @@ def quotient_poly_commitment(self, q_x: list[int]) -> tuple: c_q = self.pcs.commit(q_x) return c_q - def quotient_poly(self, C_agg: list[int]) -> tuple[list[int], tuple]: + def quotient_poly(self, C_agg: list[int]) -> tuple[list[int], G1Commitment]: qnt_poly = poly_division_general(C_agg, self.domain_size) # print("q_p:", qnt_poly) C_qp = self.quotient_poly_commitment(qnt_poly) diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index 4ab0c66..b040a66 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -6,6 +6,7 @@ from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA from dot_ring.ring_proof.constants import OMEGA_2048, S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.protocol import PCS from dot_ring.ring_proof.pcs.utils import g1_to_blst from dot_ring.ring_proof.transcript.phases import ( phase1_alphas, @@ -87,7 +88,7 @@ def __init__( edwards_a: int = -5, prime: int = S_PRIME, omega: int | None = None, - pcs: Any = KZG, + pcs: type[PCS] = KZG, ) -> None: ( self.Cb, diff --git a/dot_ring/scripts/export_python_proof.py b/dot_ring/scripts/export_python_proof.py index 0d27e69..3f4a46a 100644 --- a/dot_ring/scripts/export_python_proof.py +++ b/dot_ring/scripts/export_python_proof.py @@ -22,8 +22,8 @@ from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.srs import srs +from dot_ring.vrf.ring.ring_proof_builder import RingProofBuilder from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF from tests.utils.python_to_rust_serde import ( serialize_bls12_381_g1, serialize_bls12_381_g2, @@ -149,12 +149,17 @@ def export_variant(variant: VariantSpec, output_dir: Path) -> dict[str, Any]: producer_key_point = keys_points[prover_index] # Generate ring proof using a Rust-test-compatible transcript label - proof_components = RingVRF[Bandersnatch].generate_bls_signature( - blinding_factor=blinding_factor, - producer_key=producer_key_bytes, - ring=ring, - transcript_challenge=b"w3f-ring-proof-test", - ring_root=ring_root, + proof_components = ( + RingProofBuilder( + curve=Bandersnatch, + blinding_factor=blinding_factor, + producer_key=producer_key_bytes, + ring=ring, + transcript_challenge=b"w3f-ring-proof-test", + ring_root=ring_root, + ) + .build() + .as_tuple() ) # Compute result point (blinded public key) diff --git a/dot_ring/vrf/ring/ring.py b/dot_ring/vrf/ring/ring.py index 0ec10e0..f6e9570 100644 --- a/dot_ring/vrf/ring/ring.py +++ b/dot_ring/vrf/ring/ring.py @@ -1,19 +1,27 @@ from __future__ import annotations +from collections.abc import Sequence + from dot_ring.ring_proof.constants import DEFAULT_SIZE from dot_ring.ring_proof.params import RingProofParams def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: """Return `[2⁰·H, 2¹·H, ...]` in short-Weierstrass coords.""" - return [params.mul_point(pow(2, i, params.prime), params.blinding_base) for i in range(size)] + point = params.blinding_base + points = [] + for _ in range(size): + points.append(point) + point = params.add_points(point, point) + return points class Ring: nm_points: list[tuple[int, int]] params: RingProofParams + key_index: dict[tuple[int, int], int] - def __init__(self, keys: list[bytes], params: RingProofParams | None = None) -> None: + def __init__(self, keys: Sequence[bytes | str], params: RingProofParams | None = None) -> None: """ Initialize a Ring from a list of public keys. @@ -30,12 +38,13 @@ def __init__(self, keys: list[bytes], params: RingProofParams | None = None) -> raise ValueError(f"ring size {len(keys)} exceeds max supported size {params.max_ring_size}") self.nm_points = [] + self.key_index = {} for key in keys: - if isinstance(key, (str, bytes)): - point = params.cv.point.string_to_point(key) - if isinstance(point, str): - continue - self.nm_points.append(params.point_to_ring_point(point)) + point = self._decode_key(key) + if point is None: + continue + self.key_index.setdefault(point, len(self.nm_points)) + self.nm_points.append(point) while len(self.nm_points) < params.max_ring_size: self.nm_points.append(params.padding_point) @@ -46,3 +55,23 @@ def __init__(self, keys: list[bytes], params: RingProofParams | None = None) -> self.nm_points.extend(h_vec[:fill_count]) if params.padding_rows > 0: self.nm_points.extend([(0, 0)] * params.padding_rows) + + def _decode_key(self, key: object) -> tuple[int, int] | None: + if not isinstance(key, (bytes, str)): + return None + try: + point = self.params.cv.point.string_to_point(key) + except ValueError: + return None + if point.is_identity(): + return self.params.padding_point + return self.params.point_to_ring_point(point) + + def index_of(self, key: bytes | str) -> int: + point = self._decode_key(key) + if point is None: + raise ValueError("invalid ring key") + try: + return self.key_index[point] + except KeyError as exc: + raise ValueError("producer key is not in ring") from exc diff --git a/dot_ring/vrf/ring/ring_context.py b/dot_ring/vrf/ring/ring_context.py index 5402028..a3a7b14 100644 --- a/dot_ring/vrf/ring/ring_context.py +++ b/dot_ring/vrf/ring/ring_context.py @@ -1,5 +1,6 @@ from __future__ import annotations +from collections.abc import Sequence from dataclasses import dataclass from typing import TYPE_CHECKING @@ -28,17 +29,17 @@ def from_ring_size(cls, ring_size: int, *, test_vectors: bool = False, cv: Curve def max_ring_size(self) -> int: return self.setup.max_ring_size - def ring(self, keys: list[bytes] | bytes) -> Ring: + def ring(self, keys: Sequence[bytes | str] | bytes) -> Ring: if isinstance(keys, bytes): - keys = parse_concatenated_keys(keys, self.setup.cv) + return Ring(parse_concatenated_keys(keys, self.setup.cv), self.setup) return Ring(keys, self.setup) - def ring_root(self, ring: Ring | list[bytes] | bytes) -> RingRoot: + def ring_root(self, ring: Ring | Sequence[bytes | str] | bytes) -> RingRoot: if not isinstance(ring, Ring): ring = self.ring(ring) return RingRoot.from_ring(ring, self.setup) - def verifier_key(self, ring: Ring | list[bytes] | bytes) -> RingRoot: + def verifier_key(self, ring: Ring | Sequence[bytes | str] | bytes) -> RingRoot: return self.ring_root(ring) def verifier_key_from_commitment(self, commitment: RingRoot | bytes) -> RingRoot: diff --git a/dot_ring/vrf/ring/ring_proof_builder.py b/dot_ring/vrf/ring/ring_proof_builder.py new file mode 100644 index 0000000..b302fbc --- /dev/null +++ b/dot_ring/vrf/ring/ring_proof_builder.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +from typing import cast + +from dot_ring.curve.curve import CurveVariant +from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder, require_commitment +from dot_ring.ring_proof.constraints.aggregation import aggregate_constraints +from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder +from dot_ring.ring_proof.pcs.protocol import G1Commitment +from dot_ring.ring_proof.proof.aggregation_poly import AggPoly +from dot_ring.ring_proof.proof.linearization_poly import LAggPoly +from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly +from dot_ring.ring_proof.transcript.phases import phase1_alphas, phase3_nu_vector +from dot_ring.ring_proof.transcript.transcript import Transcript + +from .ring import Ring +from .ring_proof_payload import RingProofPayload +from .ring_root import RingRoot +from .ring_serialization import transcript_g1, transcript_vk + + +class RingProofBuilder: + def __init__( + self, + curve: CurveVariant, + blinding_factor: int, + producer_key: bytes | str, + ring: Ring, + transcript_challenge: bytes | None = None, + ring_root: RingRoot | None = None, + ) -> None: + self.curve = curve + self.blinding_factor = blinding_factor + self.producer_key = producer_key + self.ring = ring + self.ring_root = ring_root + self.transcript_challenge = transcript_challenge or curve.curve.SUITE_ID or curve.curve.SUITE_STRING + + def build(self) -> RingProofPayload: + params = self.ring.params + ring_root = self.ring_root or RingRoot.from_ring(self.ring, params) + producer_index = self.ring.index_of(self.producer_key) + + witness_builder = WitnessColumnBuilder.from_params( + self.ring.nm_points, + ring_root.s.evals, + producer_index, + self.blinding_factor, + params, + ) + witness_columns = witness_builder.build() + relation_point = witness_builder.result(params.blinding_base) + relation_plus_seed = witness_builder.result_p_seed(relation_point) + + constraints = RingConstraintBuilder( + Result_plus_Seed=relation_plus_seed, + px=cast(list[int], ring_root.px.coeffs), + py=cast(list[int], ring_root.py.coeffs), + s=cast(list[int], ring_root.s.coeffs), + b=cast(list[int], witness_columns[0].coeffs), + acc_x=cast(list[int], witness_columns[1].coeffs), + acc_y=cast(list[int], witness_columns[2].coeffs), + acc_ip=cast(list[int], witness_columns[3].coeffs), + params=params, + seed_point=params.seed_point, + ) + transcript, alpha = self._phase1_transcript(ring_root, relation_point, witness_columns) + constraint_polys = list(constraints.compute().values()) + c_agg = aggregate_constraints(constraint_polys, alpha, params.radix_omega, params.prime, domain=params.domain) + + quotient_poly = QuotientPoly(params.domain_size, params.pcs) + q_poly, c_q = quotient_poly.quotient_poly(c_agg) + c_q_column = Column(name="C_q", evals=[], commitment=c_q) + + l_agg = LAggPoly( + transcript, + transcript_g1(params, c_q), + [ring_root.px, ring_root.py, ring_root.s], + list(witness_columns), + alpha, + domain=params.domain, + omega=params.omega, + prime=params.prime, + padding_rows=params.padding_rows, + edwards_a=params.ring_edwards_a, + ) + current_transcript, zeta, relation_evals, l_agg_poly, zeta_omega, l_zeta_omega = l_agg.l_agg_poly() + _, _, phi_zeta, phi_zeta_omega = AggPoly.proof_contents_phi( + zeta, + zeta_omega, + l_agg_poly, + [ring_root.px, ring_root.py, ring_root.s], + list(witness_columns), + q_poly, + phase3_nu_vector(current_transcript, list(relation_evals.values()), l_zeta_omega), + prime=params.prime, + pcs=params.pcs, + ) + ( + px_zeta, + py_zeta, + s_zeta, + b_zeta, + accip_zeta, + accx_zeta, + accy_zeta, + ) = relation_evals.values() + c_b, c_accx, c_accy, c_accip = witness_columns + + return RingProofPayload( + c_b=c_b, + c_accip=c_accip, + c_accx=c_accx, + c_accy=c_accy, + px_zeta=px_zeta, + py_zeta=py_zeta, + s_zeta=s_zeta, + b_zeta=b_zeta, + accip_zeta=accip_zeta, + accx_zeta=accx_zeta, + accy_zeta=accy_zeta, + c_q=c_q_column, + l_zeta_omega=l_zeta_omega, + open_agg_zeta=phi_zeta, + open_l_zeta_omega=phi_zeta_omega, + ) + + def _phase1_transcript( + self, + ring_root: RingRoot, + relation_point: tuple[int, int], + witness_columns: tuple[Column, Column, Column, Column], + ) -> tuple[Transcript, list[int]]: + params = self.ring.params + c_b, c_accx, c_accy, c_accip = witness_columns + witness_commitments = [ + transcript_g1(params, require_commitment(c_b)), + transcript_g1(params, require_commitment(c_accip)), + transcript_g1(params, require_commitment(c_accx)), + transcript_g1(params, require_commitment(c_accy)), + ] + transcript = Transcript(params.prime, self.transcript_challenge) + return cast( + tuple[Transcript, list[int]], + phase1_alphas( + transcript, + transcript_vk(params, _commitments(ring_root.px, ring_root.py, ring_root.s)), + relation_point, + witness_commitments, + ), + ) + + +def _commitments(*columns: Column) -> list[G1Commitment]: + return [require_commitment(column) for column in columns] diff --git a/dot_ring/vrf/ring/ring_proof_payload.py b/dot_ring/vrf/ring/ring_proof_payload.py new file mode 100644 index 0000000..050ecc7 --- /dev/null +++ b/dot_ring/vrf/ring/ring_proof_payload.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import cast + +from dot_ring.ring_proof.columns.columns import Column, require_commitment +from dot_ring.ring_proof.helpers import Helpers as H +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.ring_proof.pcs.opening import Opening +from dot_ring.ring_proof.pcs.protocol import G1Commitment + +from .ring_serialization import compress_g1, decompress_g1, ring_proof_len + + +@dataclass(slots=True) +class RingProofPayload: + c_b: Column + c_accip: Column + c_accx: Column + c_accy: Column + px_zeta: int + py_zeta: int + s_zeta: int + b_zeta: int + accip_zeta: int + accx_zeta: int + accy_zeta: int + c_q: Column + l_zeta_omega: int + open_agg_zeta: Opening + open_l_zeta_omega: Opening + + def as_tuple( + self, + ) -> tuple[ + Column, + Column, + Column, + Column, + int, + int, + int, + int, + int, + int, + int, + Column, + int, + Opening, + Opening, + ]: + return ( + self.c_b, + self.c_accip, + self.c_accx, + self.c_accy, + self.px_zeta, + self.py_zeta, + self.s_zeta, + self.b_zeta, + self.accip_zeta, + self.accx_zeta, + self.accy_zeta, + self.c_q, + self.l_zeta_omega, + self.open_agg_zeta, + self.open_l_zeta_omega, + ) + + def to_bytes(self, params: RingProofParams) -> bytes: + return b"".join( + ( + compress_g1(params, require_commitment(self.c_b)), + compress_g1(params, require_commitment(self.c_accip)), + compress_g1(params, require_commitment(self.c_accx)), + compress_g1(params, require_commitment(self.c_accy)), + H.to_l_endian(self.px_zeta), + H.to_l_endian(self.py_zeta), + H.to_l_endian(self.s_zeta), + H.to_l_endian(self.b_zeta), + H.to_l_endian(self.accip_zeta), + H.to_l_endian(self.accx_zeta), + H.to_l_endian(self.accy_zeta), + compress_g1(params, require_commitment(self.c_q)), + H.to_l_endian(self.l_zeta_omega), + compress_g1(params, self.open_agg_zeta.proof), + compress_g1(params, self.open_l_zeta_omega.proof), + ) + ) + + @classmethod + def from_bytes(cls, proof: bytes, params: RingProofParams) -> RingProofPayload: + expected = ring_proof_len(params) + if len(proof) != expected: + raise ValueError(f"invalid Ring VRF proof length: expected {expected}, got {len(proof)}") + + reader = _PayloadReader(proof, params) + return cls( + c_b=Column(name="c_b", evals=[], commitment=reader.commitment()), + c_accip=Column(name="c_accip", evals=[], commitment=reader.commitment()), + c_accx=Column(name="c_accx", evals=[], commitment=reader.commitment()), + c_accy=Column(name="c_accy", evals=[], commitment=reader.commitment()), + px_zeta=reader.scalar(), + py_zeta=reader.scalar(), + s_zeta=reader.scalar(), + b_zeta=reader.scalar(), + accip_zeta=reader.scalar(), + accx_zeta=reader.scalar(), + accy_zeta=reader.scalar(), + c_q=Column(name="c_q", evals=[], commitment=reader.commitment()), + l_zeta_omega=reader.scalar(), + open_agg_zeta=Opening(proof=reader.commitment(), y=0), + open_l_zeta_omega=Opening(proof=reader.commitment(), y=0), + ) + + +class _PayloadReader: + def __init__(self, data: bytes, params: RingProofParams) -> None: + self.data = data + self.params = params + self.offset = 0 + + def commitment(self) -> G1Commitment: + commitment_size = self.params.pcs.commitment_size + end = self.offset + commitment_size + commitment = decompress_g1(self.params, self.data[self.offset : end]) + self.offset = end + return commitment + + def scalar(self) -> int: + end = self.offset + 32 + value = cast(int, H.to_scalar_int(self.data[self.offset : end])) + self.offset = end + return value diff --git a/dot_ring/vrf/ring/ring_root.py b/dot_ring/vrf/ring/ring_root.py index 777d6b0..e13314e 100644 --- a/dot_ring/vrf/ring/ring_root.py +++ b/dot_ring/vrf/ring/ring_root.py @@ -1,10 +1,11 @@ from dataclasses import dataclass from typing import Any, cast -from dot_ring.ring_proof.columns.columns import Column +from dot_ring.ring_proof.columns.columns import Column, require_commitment from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG +from dot_ring.ring_proof.pcs.kzg import KZG from .ring import Ring @@ -17,7 +18,7 @@ class RingRoot: params: RingProofParams | None = None @classmethod - def from_ring(cls, ring: Ring, params: RingProofParams): + def from_ring(cls, ring: Ring, params: RingProofParams) -> "RingRoot": # Px, Py, s points px, py = H.unzip(ring.nm_points) selector_vec = [1 if i < params.max_ring_size else 0 for i in range(params.domain_size)] @@ -32,18 +33,18 @@ def from_ring(cls, ring: Ring, params: RingProofParams): def to_bytes(self) -> bytes: pcs = self.params.pcs if self.params is not None else None - if pcs is not None and getattr(pcs, "commitment_size", 48) == 32: + if pcs is not None and hasattr(pcs, "compress_g1"): return b"".join( ( - pcs.compress_g1(cast(Any, self.px.commitment)), - pcs.compress_g1(cast(Any, self.py.commitment)), - pcs.compress_g1(cast(Any, self.s.commitment)), + pcs.compress_g1(require_commitment(self.px)), + pcs.compress_g1(require_commitment(self.py)), + pcs.compress_g1(require_commitment(self.s)), ) ) comm_keys = ( - H.bls_g1_compress(cast(Any, self.px.commitment)), - H.bls_g1_compress(cast(Any, self.py.commitment)), - H.bls_g1_compress(cast(Any, self.s.commitment)), + H.bls_g1_compress(cast(Any, require_commitment(self.px))), + H.bls_g1_compress(cast(Any, require_commitment(self.py))), + H.bls_g1_compress(cast(Any, require_commitment(self.s))), ) return bytes.fromhex(comm_keys[0]) + bytes.fromhex(comm_keys[1]) + bytes.fromhex(comm_keys[2]) @@ -62,9 +63,15 @@ def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "Ring py_commitment = pcs.decompress_g1(data[32:64]) s_commitment = pcs.decompress_g1(data[64:96]) else: - px_commitment = H.bls_g1_decompress(data[0:48].hex()) - py_commitment = H.bls_g1_decompress(data[48:96].hex()) - s_commitment = H.bls_g1_decompress(data[96:144].hex()) + pcs = params.pcs if params is not None else KZG + if hasattr(pcs, "decompress_g1"): + px_commitment = pcs.decompress_g1(data[0:48]) + py_commitment = pcs.decompress_g1(data[48:96]) + s_commitment = pcs.decompress_g1(data[96:144]) + else: + px_commitment = H.bls_g1_decompress(data[0:48].hex()) + py_commitment = H.bls_g1_decompress(data[48:96].hex()) + s_commitment = H.bls_g1_decompress(data[96:144].hex()) px = Column(name="px", evals=[], commitment=px_commitment) py = Column(name="py", evals=[], commitment=py_commitment) diff --git a/dot_ring/vrf/ring/ring_serialization.py b/dot_ring/vrf/ring/ring_serialization.py index 95a652b..54516f6 100644 --- a/dot_ring/vrf/ring/ring_serialization.py +++ b/dot_ring/vrf/ring/ring_serialization.py @@ -4,6 +4,7 @@ from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams +from dot_ring.ring_proof.pcs.protocol import G1Commitment RING_SCALAR_LEN = 32 @@ -12,25 +13,25 @@ def ring_proof_len(params: RingProofParams) -> int: return 7 * params.pcs.commitment_size + 8 * RING_SCALAR_LEN -def compress_g1(params: RingProofParams, point: Any) -> bytes: - if params.pcs.commitment_size == 32: +def compress_g1(params: RingProofParams, point: G1Commitment) -> bytes: + if hasattr(params.pcs, "compress_g1"): return params.pcs.compress_g1(point) return bytes.fromhex(H.bls_g1_compress(cast(tuple, point))) -def decompress_g1(params: RingProofParams, data: bytes) -> Any: - if params.pcs.commitment_size == 32: +def decompress_g1(params: RingProofParams, data: bytes) -> G1Commitment: + if hasattr(params.pcs, "decompress_g1"): return params.pcs.decompress_g1(data) return H.bls_g1_decompress(data.hex()) -def transcript_g1(params: RingProofParams, point: Any) -> Any: +def transcript_g1(params: RingProofParams, point: G1Commitment) -> Any: if params.pcs.commitment_size == 32: return params.pcs.serialize_g1_uncompressed(point) return params.pcs.normalize_g1(point) -def transcript_vk(params: RingProofParams, commitments: list[Any]) -> dict[str, Any]: +def transcript_vk(params: RingProofParams, commitments: list[G1Commitment]) -> dict[str, Any]: if params.pcs.commitment_size == 32: return { "g1": params.pcs.srs.g1_uncompressed[0], diff --git a/dot_ring/vrf/ring/ring_vrf.py b/dot_ring/vrf/ring/ring_vrf.py index 2469a97..736448b 100644 --- a/dot_ring/vrf/ring/ring_vrf.py +++ b/dot_ring/vrf/ring/ring_vrf.py @@ -2,17 +2,10 @@ from typing import Any, cast from dot_ring.curve.point import CurvePoint -from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder -from dot_ring.ring_proof.constraints.aggregation import aggregate_constraints -from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder -from dot_ring.ring_proof.helpers import Helpers as H +from dot_ring.ring_proof.columns.columns import Column, require_commitment from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.opening import Opening -from dot_ring.ring_proof.proof.aggregation_poly import AggPoly -from dot_ring.ring_proof.proof.linearization_poly import LAggPoly -from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly -from dot_ring.ring_proof.transcript.phases import phase1_alphas, phase3_nu_vector -from dot_ring.ring_proof.transcript.transcript import Transcript +from dot_ring.ring_proof.pcs.protocol import G1Commitment from dot_ring.ring_proof.verify import Verify from dot_ring.vrf.pedersen.pedersen import PedersenVRF @@ -22,22 +15,11 @@ from .ring_batch_verifier import RingBatchVerifier from .ring_context import RingContext, RingSetup from .ring_keys import parse_concatenated_keys as _parse_concatenated_keys +from .ring_proof_builder import RingProofBuilder +from .ring_proof_payload import RingProofPayload from .ring_root import RingRoot -from .ring_serialization import ( - compress_g1 as _compress_g1, -) -from .ring_serialization import ( - decompress_g1 as _decompress_g1, -) -from .ring_serialization import ( - ring_proof_len as _ring_proof_len, -) -from .ring_serialization import ( - transcript_g1 as _transcript_g1, -) -from .ring_serialization import ( - transcript_vk as _transcript_vk, -) +from .ring_serialization import ring_proof_len +from .ring_serialization import transcript_vk as _transcript_vk from .ring_verifier_key_builder import RingVerifierKeyBuilder @@ -82,35 +64,9 @@ def to_bytes(self) -> bytes: Returns: bytes: Bytes representation of the Ring VRF proof """ - assert self.c_b.commitment is not None - assert self.c_accip.commitment is not None - assert self.c_accx.commitment is not None - assert self.c_accy.commitment is not None - assert self.c_q.commitment is not None - assert self.open_agg_zeta.proof is not None - assert self.open_l_zeta_omega.proof is not None - assert self.pedersen_proof is not None params = RingProofParams(cv=self.cv) - return self.pedersen_proof.to_bytes() + b"".join( - ( - _compress_g1(params, self.c_b.commitment), - _compress_g1(params, self.c_accip.commitment), - _compress_g1(params, self.c_accx.commitment), - _compress_g1(params, self.c_accy.commitment), - H.to_l_endian(self.px_zeta), - H.to_l_endian(self.py_zeta), - H.to_l_endian(self.s_zeta), - H.to_l_endian(self.b_zeta), - H.to_l_endian(self.accip_zeta), - H.to_l_endian(self.accx_zeta), - H.to_l_endian(self.accy_zeta), - _compress_g1(params, self.c_q.commitment), - H.to_l_endian(self.l_zeta_omega), - _compress_g1(params, self.open_agg_zeta.proof), - _compress_g1(params, self.open_l_zeta_omega.proof), - ) - ) + return cast(bytes, self.pedersen_proof.to_bytes()) + self._payload().to_bytes(params) @classmethod def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": @@ -126,221 +82,53 @@ def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) scalar_size = (cls.cv.curve.ORDER.bit_length() + 7) // 8 pedersen_len = 4 * point_len + 2 * scalar_size - pedersen_proof = PedersenVRF[cls.cv].from_bytes(proof[:pedersen_len]) # type: ignore[name-defined] + pedersen_proof = PedersenVRF[cast(Any, cls).cv].from_bytes(proof[:pedersen_len]) # type: ignore[misc] offset = pedersen_len else: pedersen_proof = None offset = 0 params = RingProofParams(cv=cls.cv) - expected = offset + _ring_proof_len(params) + expected = offset + ring_proof_len(params) if len(proof) != expected: raise ValueError(f"invalid Ring VRF proof length: expected {expected}, got {len(proof)}") - - commitment_size = params.pcs.commitment_size - - c_b_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - c_accip_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - c_accx_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - c_accy_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - - px_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - py_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - s_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - b_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - accip_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - accx_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - accy_zeta = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - - c_q_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - - l_zeta_omega = H.to_scalar_int(proof[offset : offset + 32]) - offset += 32 - - open_agg_zeta_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size - open_l_zeta_omega_commitment = _decompress_g1(params, proof[offset : offset + commitment_size]) - offset += commitment_size + payload = RingProofPayload.from_bytes(proof[offset:], params) return cls( pedersen_proof=pedersen_proof, - c_b=Column(name="c_b", evals=[], commitment=c_b_commitment), - c_accip=Column(name="c_accip", evals=[], commitment=c_accip_commitment), - c_accx=Column(name="c_accx", evals=[], commitment=c_accx_commitment), - c_accy=Column(name="c_accy", evals=[], commitment=c_accy_commitment), - px_zeta=px_zeta, - py_zeta=py_zeta, - s_zeta=s_zeta, - b_zeta=b_zeta, - accip_zeta=accip_zeta, - accx_zeta=accx_zeta, - accy_zeta=accy_zeta, - c_q=Column(name="c_q", evals=[], commitment=c_q_commitment), - l_zeta_omega=l_zeta_omega, - # TODO: Fix Opening initialization; unsafe scalar 0 used temporarily - open_agg_zeta=Opening(proof=open_agg_zeta_commitment, y=0), # We only need opening proof to verify - open_l_zeta_omega=Opening(proof=open_l_zeta_omega_commitment, y=0), # We only need opening proof to verify - ) - - @classmethod - def generate_bls_signature( - cls, - blinding_factor: int, - producer_key: bytes | str, - ring: Ring, - transcript_challenge: bytes | None = None, - ring_root: RingRoot | None = None, - ) -> tuple[ - Column, - Column, - Column, - Column, - int, - int, - int, - int, - int, - int, - int, - Column, - int, - Any, - Any, - ]: - """ - Returns the Ring Proof as an output. - - Args: - blinding_factor: Blinding factor from Pedersen VRF - producer_key: Public key of the prover - ring: Ring object containing member keys and params - transcript_challenge: Challenge for Fiat-Shamir - ring_root: Optional pre-computed ring root for performance - """ - if transcript_challenge is None: - transcript_challenge = cls.cv.curve.SUITE_ID or cls.cv.curve.SUITE_STRING - # Use params from the ring object - params = ring.params - producer_key_point = cls.cv.point.string_to_point(producer_key) - - if isinstance(producer_key_point, str) or producer_key_point.is_identity(): - producer_key_pt = params.padding_point - else: - producer_key_pt = params.point_to_ring_point(producer_key_point) - - if not ring_root: - ring_root = RingRoot.from_ring(ring, params) # ring_root builder - - s_v = ring_root.s.evals - try: - producer_index = ring.nm_points.index(producer_key_pt) - except ValueError as exc: - raise ValueError("producer key is not in ring") from exc - witness_obj = WitnessColumnBuilder.from_params( - ring.nm_points, - s_v, - producer_index, - blinding_factor, - params, - ) - witness_res = witness_obj.build() - witness_relation_res = witness_obj.result(params.blinding_base) - Result_plus_Seed = witness_obj.result_p_seed(witness_relation_res) - constraints = RingConstraintBuilder( - Result_plus_Seed=Result_plus_Seed, # type: ignore - px=cast(list[int], ring_root.px.coeffs), - py=cast(list[int], ring_root.py.coeffs), - s=cast(list[int], ring_root.s.coeffs), - b=cast(list[int], witness_res[0].coeffs), - acc_x=cast(list[int], witness_res[1].coeffs), - acc_y=cast(list[int], witness_res[2].coeffs), - acc_ip=cast(list[int], witness_res[3].coeffs), - params=params, - seed_point=params.seed_point, + c_b=payload.c_b, + c_accip=payload.c_accip, + c_accx=payload.c_accx, + c_accy=payload.c_accy, + px_zeta=payload.px_zeta, + py_zeta=payload.py_zeta, + s_zeta=payload.s_zeta, + b_zeta=payload.b_zeta, + accip_zeta=payload.accip_zeta, + accx_zeta=payload.accx_zeta, + accy_zeta=payload.accy_zeta, + c_q=payload.c_q, + l_zeta_omega=payload.l_zeta_omega, + open_agg_zeta=payload.open_agg_zeta, + open_l_zeta_omega=payload.open_l_zeta_omega, ) - constraint_dict = constraints.compute() - fixed_col_commits = [ring_root.px.commitment, ring_root.py.commitment, ring_root.s.commitment] - - ws = witness_res - witness_commitments = [ - _transcript_g1(params, ws[0].commitment), - _transcript_g1(params, ws[-1].commitment), - _transcript_g1(params, ws[1].commitment), - _transcript_g1(params, ws[2].commitment), - ] - - vk = _transcript_vk(params, fixed_col_commits) - t = Transcript(params.prime, transcript_challenge) - t, alpha = phase1_alphas(t, vk, witness_relation_res, witness_commitments) - - cd = constraint_dict - c_polys = [cd[val] for val in cd] - C_agg = aggregate_constraints(c_polys, alpha, params.radix_omega, params.prime, domain=params.domain) - qp = QuotientPoly(params.domain_size, params.pcs) - Q_p, C_q = qp.quotient_poly(C_agg) - C_q_commitment = Column(name="C_q", evals=[], commitment=C_q) - l_obj = LAggPoly( - t, - _transcript_g1(params, C_q), - list([ring_root.px, ring_root.py, ring_root.s]), - list(ws), - alpha, - domain=params.domain, - omega=params.omega, - prime=params.prime, - padding_rows=params.padding_rows, - edwards_a=params.ring_edwards_a, - ) - current_t, zeta, rel_poly_evals, l_agg, zeta_omega, l_zw = l_obj.l_agg_poly() - _, _, phi_z, phi_zw = AggPoly.proof_contents_phi( - zeta, - zeta_omega, - l_agg, - list([ring_root.px, ring_root.py, ring_root.s]), - list(ws), - Q_p, - phase3_nu_vector(current_t, list(rel_poly_evals.values()), l_zw), - prime=params.prime, - pcs=params.pcs, - ) - [ - p_x_zeta, - p_y_zeta, - s_zeta, - b_zeta, - acc_ip_zeta, - acc_x_zeta, - acc_y_zeta, - ] = list(rel_poly_evals.values()) - c_b, c_acc_x, c_acc_y, c_acc_ip = ws[0], ws[1], ws[2], ws[3] - return ( - c_b, - c_acc_ip, - c_acc_x, - c_acc_y, - p_x_zeta, - p_y_zeta, - s_zeta, - b_zeta, - acc_ip_zeta, - acc_x_zeta, - acc_y_zeta, - C_q_commitment, - l_zw, - phi_z, - phi_zw, + def _payload(self) -> RingProofPayload: + return RingProofPayload( + c_b=self.c_b, + c_accip=self.c_accip, + c_accx=self.c_accx, + c_accy=self.c_accy, + px_zeta=self.px_zeta, + py_zeta=self.py_zeta, + s_zeta=self.s_zeta, + b_zeta=self.b_zeta, + accip_zeta=self.accip_zeta, + accx_zeta=self.accx_zeta, + accy_zeta=self.accy_zeta, + c_q=self.c_q, + l_zeta_omega=self.l_zeta_omega, + open_agg_zeta=self.open_agg_zeta, + open_l_zeta_omega=self.open_l_zeta_omega, ) def _ring_proof_verifier( @@ -349,27 +137,18 @@ def _ring_proof_verifier( ring: Ring, ring_root: RingRoot, ) -> Verify: - fixed_cols_cmts = [ - ring_root.px.commitment, - ring_root.py.commitment, - ring_root.s.commitment, - ] + fixed_cols_cmts = _commitments(ring_root.px, ring_root.py, ring_root.s) if isinstance(message, bytes): - message_pt = ring.params.cv.point.string_to_point(message) - if isinstance(message_pt, str): - raise ValueError("Invalid message point") - message = message_pt + try: + message = ring.params.cv.point.string_to_point(message) + except ValueError as exc: + raise ValueError("Invalid message point") from exc rltn = ring.params.point_to_ring_point(message) res_plus_seed = ring.params.add_points(ring.params.seed_point, rltn) - # Ensure commitments are not None - fixed_cols_cmts_safe = [] - for c in fixed_cols_cmts: - assert c is not None - fixed_cols_cmts_safe.append(c) - verifier_key: dict[str, Any] = _transcript_vk(ring.params, fixed_cols_cmts_safe) + verifier_key: dict[str, Any] = _transcript_vk(ring.params, fixed_cols_cmts) return Verify( ( @@ -412,7 +191,7 @@ def verify_ring_proof( """ Verifies the Ring Proof """ - return self._ring_proof_verifier(message, ring, ring_root).is_valid() + return cast(bool, self._ring_proof_verifier(message, ring, ring_root).is_valid()) @classmethod def prove( @@ -444,9 +223,15 @@ def prove( """ pedersen_proof = PedersenVRF[cast(Any, cls).cv].prove(alpha, secret_key, ad) # type: ignore[misc] - ring_proof = cls.generate_bls_signature(pedersen_proof._blinding_factor, producer_key, ring=ring, ring_root=ring_root) + ring_proof = RingProofBuilder( + cls.cv, + pedersen_proof._blinding_factor, + producer_key, + ring, + ring_root=ring_root, + ).build() - return cls(pedersen_proof, *ring_proof) + return cls(pedersen_proof, *ring_proof.as_tuple()) @classmethod def parse_keys(cls, keys: bytes) -> list[bytes]: @@ -458,7 +243,7 @@ def parse_keys(cls, keys: bytes) -> list[bytes]: Returns: List[bytes]: A list of individual keys extracted from the input bytes object. """ - return _parse_concatenated_keys(keys, cls.cv) + return cast(list[bytes], _parse_concatenated_keys(keys, cls.cv)) def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) -> bool: """ @@ -473,7 +258,11 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) @classmethod def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: - return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) # type: ignore[misc] + return cast(bytes, PedersenVRF[cast(Any, cls).cv].proof_to_hash(gamma, mul_cofactor)) # type: ignore[misc] + + +def _commitments(*columns: Column) -> list[G1Commitment]: + return [require_commitment(column) for column in columns] __all__ = [ diff --git a/tests/test_coverage/test_kzg.py b/tests/test_coverage/test_kzg.py index 45328d5..ca23559 100644 --- a/tests/test_coverage/test_kzg.py +++ b/tests/test_coverage/test_kzg.py @@ -2,6 +2,7 @@ import pytest +from dot_ring import blst from dot_ring.ring_proof.pcs.kzg import ( KZG, Opening, @@ -87,9 +88,7 @@ def test_commit_simple_poly(self): commitment = KZG.commit(coeffs) assert commitment is not None - assert isinstance(commitment, tuple) - # Commitment is (x, y, z) in projective coordinates - assert len(commitment) in [2, 3] # (x, y) or (x, y, z) coordinates + assert isinstance(commitment, blst.P1) def test_commit_zero_poly(self): """Test commitment to zero polynomial.""" From a6719d90dba5fd194f793bc1879150f89df30800 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:51:40 +0530 Subject: [PATCH 14/33] perf: simplify batch verification --- dot_ring/vrf/ietf/thin_batch_item.py | 3 +- dot_ring/vrf/ietf/thin_batch_verifier.py | 24 ++++++--- dot_ring/vrf/pedersen/pedersen_batch_item.py | 5 +- .../vrf/pedersen/pedersen_batch_verifier.py | 52 ++++++++++++------- 4 files changed, 54 insertions(+), 30 deletions(-) diff --git a/dot_ring/vrf/ietf/thin_batch_item.py b/dot_ring/vrf/ietf/thin_batch_item.py index 618c85a..a9754f8 100644 --- a/dot_ring/vrf/ietf/thin_batch_item.py +++ b/dot_ring/vrf/ietf/thin_batch_item.py @@ -9,6 +9,7 @@ @dataclass class ThinBatchItem: c: int - merged: VrfIo + ios: list[VrfIo] + zs: list[int] r: CurvePoint s: int diff --git a/dot_ring/vrf/ietf/thin_batch_verifier.py b/dot_ring/vrf/ietf/thin_batch_verifier.py index 9348f6d..4accc37 100644 --- a/dot_ring/vrf/ietf/thin_batch_verifier.py +++ b/dot_ring/vrf/ietf/thin_batch_verifier.py @@ -5,7 +5,7 @@ from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, schnorr_ios, vrf_transcript +from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, schnorr_ios, vrf_transcript_scalars from .thin_batch_item import ThinBatchItem from .thin_vrf import ThinVRF @@ -41,14 +41,24 @@ def __init__(self) -> None: return _SpecializedThinBatchVerifier def push(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes, proof: ThinVRF) -> None: - transcript, merged = vrf_transcript(self.cv, DomSep.THIN_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) + chained_ios = schnorr_ios(self.cv, public_key, ios) + transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.THIN_VRF, chained_ios, additional_data) c = challenge(self.cv, [proof.r], transcript) - self.items.append(ThinBatchItem(c, merged, proof.r, proof.s)) + self.items.append(ThinBatchItem(c, chained_ios, scalar_stream.take(len(chained_ios)), proof.r, proof.s)) def verify(self) -> bool: - points = [] - scalars = [] + if not self.items: + return True + + points: list[CurvePoint] = [] + scalars: list[int] = [] for coefficient, item in zip(_batch_coefficients(len(self.items), self.cv.curve.ORDER), self.items, strict=False): - points.extend([item.merged.input, item.merged.output, item.r]) - scalars.extend([coefficient * item.s, -coefficient * item.c, -coefficient]) + weighted_c = coefficient * item.c + weighted_s = coefficient * item.s + + for io, z in zip(item.ios, item.zs, strict=True): + points.extend([io.input, io.output]) + scalars.extend([weighted_s * z, -(weighted_c * z)]) + points.append(item.r) + scalars.append(-coefficient) return self.cv.point.msm(points, scalars).is_identity() diff --git a/dot_ring/vrf/pedersen/pedersen_batch_item.py b/dot_ring/vrf/pedersen/pedersen_batch_item.py index 79e2c4f..01af170 100644 --- a/dot_ring/vrf/pedersen/pedersen_batch_item.py +++ b/dot_ring/vrf/pedersen/pedersen_batch_item.py @@ -3,13 +3,14 @@ from dataclasses import dataclass from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import VrfIo @dataclass class PedersenBatchItem: c: int - input: CurvePoint - output: CurvePoint + ios: list[VrfIo] + zs: list[int] pk_com: CurvePoint r: CurvePoint ok: CurvePoint diff --git a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py index 57ab55b..fe34eab 100644 --- a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py +++ b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py @@ -5,7 +5,7 @@ from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, vrf_transcript +from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, vrf_transcript_scalars from .pedersen_batch_item import PedersenBatchItem from .pedersen_vrf import PedersenVRF, _blinding_base @@ -41,14 +41,14 @@ def __init__(self) -> None: return _SpecializedPedersenBatchVerifier def push(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> None: - transcript, merged = vrf_transcript(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) + transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) transcript.absorb_point(proof.blinded_pk) c = challenge(self.cv, [proof.result_point, proof.ok], transcript) self.items.append( PedersenBatchItem( c=c, - input=merged.input, - output=merged.output, + ios=list(ios), + zs=scalar_stream.take(len(ios)), pk_com=proof.blinded_pk, r=proof.result_point, ok=proof.ok, @@ -58,21 +58,33 @@ def push(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> ) def verify(self) -> bool: + if not self.items: + return True + blinding_base = _blinding_base(self.cv) generator = self.cv.point.generator_point() - input_points: list[CurvePoint] = [] - input_scalars: list[int] = [] - commitment_points: list[CurvePoint] = [] - commitment_scalars: list[int] = [] - for coefficient, item in zip(_batch_coefficients(len(self.items), self.cv.curve.ORDER), self.items, strict=False): - input_points.extend([item.input, item.output, item.ok]) - input_scalars.extend([coefficient * item.s, -coefficient * item.c, -coefficient]) - commitment_points.extend([generator, blinding_base, item.pk_com, item.r]) - commitment_scalars.extend([coefficient * item.s, coefficient * item.sb, -coefficient * item.c, -coefficient]) - return ( - self.cv.point.msm(input_points, input_scalars).is_identity() - and self.cv.point.msm( - commitment_points, - commitment_scalars, - ).is_identity() - ) + points: list[CurvePoint] = [] + scalars: list[int] = [] + coefficients = _batch_coefficients(2 * len(self.items), self.cv.curve.ORDER) + + for index, item in enumerate(self.items): + io_weight = coefficients[2 * index] + commitment_weight = coefficients[2 * index + 1] + + for io, z in zip(item.ios, item.zs, strict=True): + points.extend([io.input, io.output]) + scalars.extend([io_weight * item.s * z, -(io_weight * item.c * z)]) + points.append(item.ok) + scalars.append(-io_weight) + + points.extend([generator, blinding_base, item.pk_com, item.r]) + scalars.extend( + [ + commitment_weight * item.s, + commitment_weight * item.sb, + -(commitment_weight * item.c), + -commitment_weight, + ] + ) + + return self.cv.point.msm(points, scalars).is_identity() From 51eac4aa3b4f9c55790f030696e8221969bec770 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:51:50 +0530 Subject: [PATCH 15/33] refactor: simplify point decoding --- dot_ring/curve/point.py | 34 ++++++++++++------- .../short_weierstrass/sw_affine_point.py | 7 ++-- dot_ring/curve/specs/bandersnatch_sw.py | 25 ++++++-------- dot_ring/curve/specs/p256.py | 2 +- .../curve/twisted_edwards/te_affine_point.py | 16 +++++---- dot_ring/vrf/ietf/thin_vrf.py | 16 +++++---- dot_ring/vrf/ietf/tiny_vrf.py | 14 ++++---- dot_ring/vrf/pedersen/pedersen_vrf.py | 21 ++++++------ dot_ring/vrf/vrf.py | 7 ++-- 9 files changed, 77 insertions(+), 65 deletions(-) diff --git a/dot_ring/curve/point.py b/dot_ring/curve/point.py index 4a93f28..01cf28d 100644 --- a/dot_ring/curve/point.py +++ b/dot_ring/curve/point.py @@ -203,14 +203,17 @@ def point_to_string(self) -> bytes: return bytes(y_bytes) @classmethod - def string_to_point(cls, octet_string: str | bytes) -> Self | str: + def string_to_point(cls, octet_string: str | bytes) -> Self: """ Convert compressed octet string back to point. Args: octet_string: Compressed point bytes Returns: - Point: Decoded point or returns "INVALID" If encoding is invalid + Point: Decoded point + + Raises: + ValueError: If encoding is invalid. """ if cls.curve.UNCOMPRESSED: @@ -218,6 +221,8 @@ def string_to_point(cls, octet_string: str | bytes) -> Self | str: if isinstance(octet_string, str): octet_string = bytes.fromhex(octet_string) + if not octet_string: + raise ValueError("Empty octet string") # Extract x sign bit from MSB of last byte x_sign_bit = (octet_string[-1] >> 7) & 1 @@ -229,7 +234,7 @@ def string_to_point(cls, octet_string: str | bytes) -> Self | str: x_candidates = cls._x_recover(y) if x_candidates is None: - return "INVALID" + raise ValueError("Invalid point encoding") if isinstance(x_candidates, int): x, neg_x = x_candidates, (-x_candidates) % cls.curve.PRIME_FIELD @@ -240,8 +245,8 @@ def string_to_point(cls, octet_string: str | bytes) -> Self | str: chosen_x = neg_x if x_sign_bit else x try: return cls(chosen_x, y) - except ValueError: - return "INVALID" # Needed for TAI_Case + except ValueError as exc: + raise ValueError("Invalid point encoding") from exc def uncompressed_p2s(self) -> bytes: p = self.curve.PRIME_FIELD @@ -320,25 +325,30 @@ def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Se TEAffinePoint: Resulting curve point """ ctr = 0 - H: Self | str = "INVALID" front = b"\x01" back = b"\x00" alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string salt = salt.encode() if isinstance(salt, str) else salt suite_string = cls.curve.SUITE_STRING - while H == "INVALID" or H == cast(Any, cls).identity_point(): + while True: ctr_string = ctr.to_bytes(1, "big") hash_input = suite_string + front + salt + alpha_string + ctr_string + back if cls.__name__ == "P256PointVariant": hash_output = hashlib.sha256(hash_input).digest() - H = cls.string_to_point(b"\x02" + hash_output[:32]) + candidate = b"\x02" + hash_output[:32] else: hash_output = hashlib.sha512(hash_input).digest() - H = cls.string_to_point(hash_output[:32]) - if H != "INVALID" and cls.curve.COFACTOR > 1: - H = cast(Any, H).clear_cofactor() + candidate = hash_output[:32] + try: + point = cls.string_to_point(candidate) + except ValueError: + ctr += 1 + continue + if cls.curve.COFACTOR > 1: + point = cast(Any, point).clear_cofactor() + if point != cast(Any, cls).identity_point(): + return point ctr += 1 - return cast(Self, H) def __hash__(self) -> int: if self.x is None or self.y is None: diff --git a/dot_ring/curve/short_weierstrass/sw_affine_point.py b/dot_ring/curve/short_weierstrass/sw_affine_point.py index 0377657..361cd46 100644 --- a/dot_ring/curve/short_weierstrass/sw_affine_point.py +++ b/dot_ring/curve/short_weierstrass/sw_affine_point.py @@ -181,7 +181,7 @@ def point_to_string(self, compressed: bool = True) -> bytes: return b"\x04" + x_bytes + y_bytes @classmethod - def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint | str: + def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: if isinstance(octet_string, str): octet_string = bytes.fromhex(octet_string) @@ -221,10 +221,7 @@ def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint | str: # Compute square root using Tonelli-Shanks y = cls.tonelli_shanks(y_squared, p) if y is None: - # raise ValueError( - # f"Point decompression failed: no square root exists for y² ≡ {y_squared} (mod {p})" - # ) - return "INVALID" + raise ValueError("Invalid point encoding") # Choose correct square root based on parity indicated by prefix # prefix 0x02: y should be even diff --git a/dot_ring/curve/specs/bandersnatch_sw.py b/dot_ring/curve/specs/bandersnatch_sw.py index c0b240d..c61b519 100644 --- a/dot_ring/curve/specs/bandersnatch_sw.py +++ b/dot_ring/curve/specs/bandersnatch_sw.py @@ -170,30 +170,30 @@ def string_to_point(cls, data: str | bytes) -> Self: x = int.from_bytes(x_bytes, cast(Literal["little", "big"], cls.curve.ENDIAN)) y_candidates = cls._y_recover(x) if not y_candidates: - raise ValueError("INVALID point: no y-coordinate found for x") + raise ValueError("Invalid point: no y-coordinate found for x") y, y_neg = y_candidates flag = data[-1] if flag & 0x3F: - raise ValueError("INVALID: invalid canonical point flags") + raise ValueError("Invalid canonical point flags") is_negative = (flag >> 7) & 1 is_infinity = (flag >> 6) & 1 if is_infinity: if is_negative: - raise ValueError("INVALID: Infinity point cannot be negative") - raise ValueError("INVALID: Infinity point not supported") + raise ValueError("Invalid infinity point: negative flag is set") + raise ValueError("Invalid infinity point: not supported") if is_negative: try: return cls(x, y_neg) except ValueError: - raise ValueError("INVALID point") from None + raise ValueError("Invalid point") from None else: try: return cls(x, y) except ValueError: - raise ValueError("INVALID point") from None + raise ValueError("Invalid point") from None @classmethod # modified def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Self: @@ -210,7 +210,6 @@ def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Se ctr = 0 import hashlib - H: Self | None = None front = b"\x01" back = b"\x00" alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string @@ -222,20 +221,16 @@ def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Se hash_input = suite_string + front + salt + alpha_string + ctr_string + back hash_output = hashlib.sha512(hash_input).digest() try: - H = cls.string_to_point(hash_output[:33]) + point = cls.string_to_point(hash_output[:33]) except ValueError: ctr += 1 continue - # Check if H is valid (not raising ValueError) if cls.curve.COFACTOR > 1: - # H is Self | None, but here it must be Self (point) - if H is None: # Should not happen if string_to_point works - continue - H = cast(Self, H * cls.curve.COFACTOR) # type: ignore[operator] + point = cast(Self, point * cls.curve.COFACTOR) # type: ignore[operator] - if H != cls.identity_point(): - return H + if point != cls.identity_point(): + return point ctr += 1 diff --git a/dot_ring/curve/specs/p256.py b/dot_ring/curve/specs/p256.py index 40b86ac..9811af0 100644 --- a/dot_ring/curve/specs/p256.py +++ b/dot_ring/curve/specs/p256.py @@ -187,7 +187,7 @@ def _string_to_canonical_point(cls, data: bytes): raise ValueError("x-coordinate is not in field") y_candidates = cls._y_recover(x) if y_candidates is None: - raise ValueError("INVALID point") + raise ValueError("Invalid point") y, y_neg = y_candidates return cls(x, y_neg if is_negative else y) diff --git a/dot_ring/curve/twisted_edwards/te_affine_point.py b/dot_ring/curve/twisted_edwards/te_affine_point.py index c1dc4ff..452d422 100644 --- a/dot_ring/curve/twisted_edwards/te_affine_point.py +++ b/dot_ring/curve/twisted_edwards/te_affine_point.py @@ -282,21 +282,25 @@ def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Se TEAffinePoint: Resulting curve point """ ctr = 0 - H: Self | str = "INVALID" front = b"\x01" back = b"\x00" alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string salt = salt.encode() if isinstance(salt, str) else salt suite_string = cls.curve.SUITE_STRING - while H == "INVALID" or H == cls.identity_point(): + while True: ctr_string = ctr.to_bytes(1, "big") hash_input = suite_string + front + salt + alpha_string + ctr_string + back hash_output = hashlib.sha512(hash_input).digest() - H = cls.string_to_point(hash_output[:32]) - if isinstance(H, TEAffinePoint) and cls.curve.COFACTOR > 1: - H = H.clear_cofactor() + try: + point = cls.string_to_point(hash_output[:32]) + except ValueError: + ctr += 1 + continue + if cls.curve.COFACTOR > 1: + point = point.clear_cofactor() + if point != cls.identity_point(): + return point ctr += 1 - return cast(Self, H) def clear_cofactor(self) -> Self: """ diff --git a/dot_ring/vrf/ietf/thin_vrf.py b/dot_ring/vrf/ietf/thin_vrf.py index e662ee9..6d5b835 100644 --- a/dot_ring/vrf/ietf/thin_vrf.py +++ b/dot_ring/vrf/ietf/thin_vrf.py @@ -35,10 +35,11 @@ def from_bytes(cls, proof_bytes: bytes) -> ThinVRF: expected = 2 * point_len + scalar_size if len(proof_bytes) != expected: raise ValueError(f"invalid Thin VRF proof length: expected {expected}, got {len(proof_bytes)}") - output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) - r = cls.cv.point.string_to_point(proof_bytes[point_len : 2 * point_len]) - if isinstance(output_point, str) or isinstance(r, str): - raise ValueError("Invalid point in proof") + try: + output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) + r = cls.cv.point.string_to_point(proof_bytes[point_len : 2 * point_len]) + except ValueError as exc: + raise ValueError("Invalid point in proof") from exc s = scalar_decode(cls.cv, proof_bytes[2 * point_len :]) if s >= cls.cv.curve.ORDER: raise ValueError("Response scalar s is not less than the curve order") @@ -79,9 +80,10 @@ def prove_ios( def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) - public_key_pt = self.cv.point.string_to_point(public_key) - if isinstance(public_key_pt, str): - raise ValueError("Invalid public key") + try: + public_key_pt = self.cv.point.string_to_point(public_key) + except ValueError as exc: + raise ValueError("Invalid public key") from exc return self.verify_ios(public_key_pt, [VrfIo(input_point, self.output_point)], additional_data) def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: diff --git a/dot_ring/vrf/ietf/tiny_vrf.py b/dot_ring/vrf/ietf/tiny_vrf.py index 66a3f70..312decd 100644 --- a/dot_ring/vrf/ietf/tiny_vrf.py +++ b/dot_ring/vrf/ietf/tiny_vrf.py @@ -47,9 +47,10 @@ def from_bytes(cls, proof_bytes: bytes) -> TinyVRF: expected = point_len + CHALLENGE_LEN + scalar_size if len(proof_bytes) != expected: raise ValueError(f"invalid Tiny VRF proof length: expected {expected}, got {len(proof_bytes)}") - output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) - if isinstance(output_point, str): - raise ValueError("Invalid output point") + try: + output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) + except ValueError as exc: + raise ValueError("Invalid output point") from exc c = int.from_bytes(proof_bytes[point_len : point_len + CHALLENGE_LEN], "little") % cls.cv.curve.ORDER s = scalar_decode(cls.cv, proof_bytes[point_len + CHALLENGE_LEN :]) if s >= cls.cv.curve.ORDER: @@ -102,9 +103,10 @@ def generate_nonce(cls, secret_scalar: int, transcript: Any) -> int: def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) - public_key_pt = self.cv.point.string_to_point(public_key) - if isinstance(public_key_pt, str): - raise ValueError("Invalid public key") + try: + public_key_pt = self.cv.point.string_to_point(public_key) + except ValueError as exc: + raise ValueError("Invalid public key") from exc io = VrfIo(input_point, self.output_point) return self.verify_ios(public_key_pt, [io], additional_data) diff --git a/dot_ring/vrf/pedersen/pedersen_vrf.py b/dot_ring/vrf/pedersen/pedersen_vrf.py index d27563e..086d27d 100644 --- a/dot_ring/vrf/pedersen/pedersen_vrf.py +++ b/dot_ring/vrf/pedersen/pedersen_vrf.py @@ -59,16 +59,16 @@ def from_bytes(cls, proof: bytes) -> PedersenVRF: if len(proof) != expected: raise ValueError(f"invalid Pedersen VRF proof length: expected {expected}, got {len(proof)}") - output_point = cls.cv.point.string_to_point(proof[0:point_length]) - public_key_cp = cls.cv.point.string_to_point(proof[point_length : 2 * point_length]) - r = cls.cv.point.string_to_point(proof[2 * point_length : 3 * point_length]) - ok = cls.cv.point.string_to_point(proof[3 * point_length : 4 * point_length]) + try: + output_point = cls.cv.point.string_to_point(proof[0:point_length]) + public_key_cp = cls.cv.point.string_to_point(proof[point_length : 2 * point_length]) + r = cls.cv.point.string_to_point(proof[2 * point_length : 3 * point_length]) + ok = cls.cv.point.string_to_point(proof[3 * point_length : 4 * point_length]) + except ValueError as exc: + raise ValueError("Invalid point in proof") from exc s = scalar_decode(cls.cv, proof[4 * point_length : 4 * point_length + scalar_size]) sb = scalar_decode(cls.cv, proof[4 * point_length + scalar_size :]) - if isinstance(output_point, str) or isinstance(public_key_cp, str) or isinstance(r, str) or isinstance(ok, str): - raise ValueError("Invalid point in proof") - return cls( output_point=output_point, blinded_pk=public_key_cp, @@ -181,7 +181,8 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: def ecvrf_proof_to_hash(cls, output_point_bytes: bytes | str) -> bytes: if not isinstance(output_point_bytes, bytes): output_point_bytes = bytes.fromhex(output_point_bytes) - output_point = cls.cv.point.string_to_point(output_point_bytes) - if isinstance(output_point, str): - raise ValueError("Invalid output point") + try: + output_point = cls.cv.point.string_to_point(output_point_bytes) + except ValueError as exc: + raise ValueError("Invalid output point") from exc return cls.proof_to_hash(output_point) diff --git a/dot_ring/vrf/vrf.py b/dot_ring/vrf/vrf.py index 60a9f24..36d91b6 100644 --- a/dot_ring/vrf/vrf.py +++ b/dot_ring/vrf/vrf.py @@ -177,9 +177,10 @@ def ecvrf_decode_proof(cls, pi_string: bytes | str) -> tuple[CurvePoint, int, in s_string = pi_string[c_end:s_end] # Convert to appropriate types] - gamma = cls.cv.point.string_to_point(gamma_string) - if isinstance(gamma, str): - raise ValueError("Invalid gamma point") + try: + gamma = cls.cv.point.string_to_point(gamma_string) + except ValueError as exc: + raise ValueError("Invalid gamma point") from exc C = Helpers.str_to_int(c_string, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) S = Helpers.str_to_int(s_string, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) From 287d6f6267621f25c60f9bc36d9246bfb52d50e1 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:52:00 +0530 Subject: [PATCH 16/33] test: share vector helpers --- tests/test_ark_vrf.py | 59 ++++++---------------------------- tests/test_dot_ring_vectors.py | 24 +++----------- tests/vector_helpers.py | 30 +++++++++++++++++ 3 files changed, 45 insertions(+), 68 deletions(-) create mode 100644 tests/vector_helpers.py diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py index 5238b3b..dbeaa9e 100644 --- a/tests/test_ark_vrf.py +++ b/tests/test_ark_vrf.py @@ -18,6 +18,7 @@ from dot_ring.vrf.transcript import VrfIo from scripts.generate_test_vectors import SUITES as GENERATED_SUITES from scripts.generate_test_vectors import scheme_vectors +from tests.vector_helpers import bytes_from_fields, load_json_vectors, pedersen_proof_bytes, ring_proof_bytes, thin_proof_bytes, tiny_proof_bytes VECTORS = Path(__file__).parent / "vectors" / "ark-vrf" @@ -33,19 +34,7 @@ def load(name: str) -> list[dict[str, str]]: - return json.loads((VECTORS / name).read_text()) - - -def ring_proof_bytes(vector: dict[str, str]) -> bytes: - return bytes.fromhex( - vector["gamma"] - + vector["proof_pk_com"] - + vector["proof_r"] - + vector["proof_ok"] - + vector["proof_s"] - + vector["proof_sb"] - + vector["ring_proof"] - ) + return load_json_vectors(VECTORS, name) def test_generated_vectors_match_ark_vrf_tiny_thin_pedersen() -> None: @@ -65,7 +54,7 @@ def test_tiny_vectors(curve, prefix: str, point_len: int) -> None: assert TinyVRF[curve].get_public_key(sk).hex() == vector["pk"] assert curve.point.encode_to_curve(alpha).point_to_string().hex() == vector["h"] - assert proof_bytes.hex() == vector["gamma"] + vector["proof_c"] + vector["proof_s"] + assert proof_bytes == tiny_proof_bytes(vector) assert TinyVRF[curve].proof_to_hash(proof.output_point).hex() == vector["beta"] assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) assert TinyVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes @@ -81,7 +70,7 @@ def test_thin_vectors(curve, prefix: str, _point_len: int) -> None: proof = ThinVRF[curve].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() - assert proof_bytes.hex() == vector["gamma"] + vector["proof_r"] + vector["proof_s"] + assert proof_bytes == thin_proof_bytes(vector) assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) assert ThinVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes @@ -95,9 +84,7 @@ def test_pedersen_vectors(curve, prefix: str, _point_len: int) -> None: proof = PedersenVRF[curve].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() - assert proof_bytes.hex() == ( - vector["gamma"] + vector["proof_pk_com"] + vector["proof_r"] + vector["proof_ok"] + vector["proof_s"] + vector["proof_sb"] - ) + assert proof_bytes == pedersen_proof_bytes(vector) assert proof.verify(alpha, ad) assert PedersenVRF[curve].from_bytes(proof_bytes).to_bytes() == proof_bytes @@ -107,23 +94,17 @@ def test_rejects_invalid_point_encodings(curve, prefix: str, point_len: int) -> invalid_point = b"\xff" * point_len tiny_vector = load(f"{prefix}_tiny.json")[0] - tiny_bytes = invalid_point + bytes.fromhex(tiny_vector["proof_c"] + tiny_vector["proof_s"]) + tiny_bytes = invalid_point + bytes_from_fields(tiny_vector, "proof_c", "proof_s") with pytest.raises(ValueError, match="INVALID|Invalid"): TinyVRF[curve].from_bytes(tiny_bytes) thin_vector = load(f"{prefix}_thin.json")[0] - thin_bytes = invalid_point + bytes.fromhex(thin_vector["proof_r"] + thin_vector["proof_s"]) + thin_bytes = invalid_point + bytes_from_fields(thin_vector, "proof_r", "proof_s") with pytest.raises(ValueError, match="INVALID|Invalid"): ThinVRF[curve].from_bytes(thin_bytes) pedersen_vector = load(f"{prefix}_pedersen.json")[0] - pedersen_bytes = invalid_point + bytes.fromhex( - pedersen_vector["proof_pk_com"] - + pedersen_vector["proof_r"] - + pedersen_vector["proof_ok"] - + pedersen_vector["proof_s"] - + pedersen_vector["proof_sb"] - ) + pedersen_bytes = invalid_point + bytes_from_fields(pedersen_vector, "proof_pk_com", "proof_r", "proof_ok", "proof_s", "proof_sb") with pytest.raises(ValueError, match="INVALID|Invalid"): PedersenVRF[curve].from_bytes(pedersen_bytes) @@ -142,15 +123,7 @@ def test_ring_vectors(curve, filename: str) -> None: for vector in load(filename): alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) - proof_bytes = bytes.fromhex( - vector["gamma"] - + vector["proof_pk_com"] - + vector["proof_r"] - + vector["proof_ok"] - + vector["proof_s"] - + vector["proof_sb"] - + vector["ring_proof"] - ) + proof_bytes = ring_proof_bytes(vector) keys = RingVRF[curve].parse_keys(bytes.fromhex(vector["ring_pks"])) params = RingProofParams(test_vectors=True, cv=curve) ring = Ring(keys, params) @@ -168,7 +141,6 @@ def test_multi_input_apis() -> None: public_key = TinyVRF[Bandersnatch].get_public_key(secret) secret_scalar = int.from_bytes(secret, "little") public_key_point = Bandersnatch.point.string_to_point(public_key) - assert not isinstance(public_key_point, str) ios = [] for alpha in (b"first", b"second"): @@ -193,7 +165,6 @@ def test_batch_apis() -> None: alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) - assert not isinstance(pk_point, str) thin = ThinVRF[Bandersnatch].prove(alpha, bytes.fromhex(vector["sk"]), ad) input_point = Bandersnatch.point.encode_to_curve(alpha) thin_batch.push(pk_point, [VrfIo(input_point, thin.output_point)], ad, thin) @@ -211,7 +182,6 @@ def test_batch_rejects_invalid_items() -> None: ad = bytes.fromhex(vector["ad"]) secret = bytes.fromhex(vector["sk"]) pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) - assert not isinstance(pk_point, str) input_point = Bandersnatch.point.encode_to_curve(alpha) thin = ThinVRF[Bandersnatch].prove(alpha, secret, ad) @@ -311,7 +281,6 @@ def test_negative_and_malformed_proofs() -> None: tiny = TinyVRF[Bandersnatch].prove(alpha, sk, ad) pk_point = Bandersnatch.point.string_to_point(pk) - assert not isinstance(pk_point, str) wrong_output = Bandersnatch.point.encode_to_curve(b"wrong-output") assert not tiny.verify(pk, alpha, b"wrong-ad") @@ -332,15 +301,7 @@ def test_negative_and_malformed_proofs() -> None: ring_vector = load("bandersnatch_sha-512_ell2_ring.json")[0] ring_alpha = bytes.fromhex(ring_vector["alpha"]) ring_ad = bytes.fromhex(ring_vector["ad"]) - proof_bytes = bytes.fromhex( - ring_vector["gamma"] - + ring_vector["proof_pk_com"] - + ring_vector["proof_r"] - + ring_vector["proof_ok"] - + ring_vector["proof_s"] - + ring_vector["proof_sb"] - + ring_vector["ring_proof"] - ) + proof_bytes = ring_proof_bytes(ring_vector) keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(ring_vector["ring_pks"])) params = RingProofParams(test_vectors=True) ring = Ring(keys, params) diff --git a/tests/test_dot_ring_vectors.py b/tests/test_dot_ring_vectors.py index b1050de..83fc6af 100644 --- a/tests/test_dot_ring_vectors.py +++ b/tests/test_dot_ring_vectors.py @@ -1,4 +1,3 @@ -import json from pathlib import Path from typing import Any @@ -9,24 +8,13 @@ from dot_ring.vrf.pedersen import PedersenVRF from dot_ring.vrf.ring import Ring, RingRoot, RingVRF from scripts.generate_test_vectors import RING_SUITES, SUITES, Suite +from tests.vector_helpers import load_json_vectors, pedersen_proof_bytes, ring_proof_bytes, thin_proof_bytes, tiny_proof_bytes VECTORS = Path(__file__).parent / "vectors" / "dot-ring" def load(suite: Suite, scheme: str) -> list[dict[str, Any]]: - return json.loads((VECTORS / f"{suite.prefix}_{scheme}.json").read_text()) - - -def ring_proof_bytes(vector: dict[str, Any]) -> bytes: - return bytes.fromhex( - vector["gamma"] - + vector["proof_pk_com"] - + vector["proof_r"] - + vector["proof_ok"] - + vector["proof_s"] - + vector["proof_sb"] - + vector["ring_proof"] - ) + return load_json_vectors(VECTORS, f"{suite.prefix}_{scheme}.json") @pytest.mark.parametrize("suite", SUITES) @@ -34,7 +22,7 @@ def test_dot_ring_tiny_vectors(suite: Suite) -> None: for vector in load(suite, "tiny"): alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) - proof_bytes = bytes.fromhex(vector["gamma"] + vector["proof_c"] + vector["proof_s"]) + proof_bytes = tiny_proof_bytes(vector) proof = TinyVRF[suite.curve].from_bytes(proof_bytes) assert TinyVRF[suite.curve].get_public_key(bytes.fromhex(vector["sk"])).hex() == vector["pk"] @@ -48,7 +36,7 @@ def test_dot_ring_thin_vectors(suite: Suite) -> None: for vector in load(suite, "thin"): alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) - proof_bytes = bytes.fromhex(vector["gamma"] + vector["proof_r"] + vector["proof_s"]) + proof_bytes = thin_proof_bytes(vector) proof = ThinVRF[suite.curve].from_bytes(proof_bytes) assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) @@ -60,9 +48,7 @@ def test_dot_ring_pedersen_vectors(suite: Suite) -> None: for vector in load(suite, "pedersen"): alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) - proof_bytes = bytes.fromhex( - vector["gamma"] + vector["proof_pk_com"] + vector["proof_r"] + vector["proof_ok"] + vector["proof_s"] + vector["proof_sb"] - ) + proof_bytes = pedersen_proof_bytes(vector) proof = PedersenVRF[suite.curve].from_bytes(proof_bytes) assert proof.verify(alpha, ad) diff --git a/tests/vector_helpers.py b/tests/vector_helpers.py new file mode 100644 index 0000000..5b27849 --- /dev/null +++ b/tests/vector_helpers.py @@ -0,0 +1,30 @@ +import json +from collections.abc import Mapping +from pathlib import Path +from typing import Any + +Vector = Mapping[str, Any] + + +def load_json_vectors(base_path: Path, name: str) -> list[dict[str, Any]]: + return json.loads((base_path / name).read_text()) + + +def bytes_from_fields(vector: Vector, *fields: str) -> bytes: + return bytes.fromhex("".join(str(vector[field]) for field in fields)) + + +def tiny_proof_bytes(vector: Vector) -> bytes: + return bytes_from_fields(vector, "gamma", "proof_c", "proof_s") + + +def thin_proof_bytes(vector: Vector) -> bytes: + return bytes_from_fields(vector, "gamma", "proof_r", "proof_s") + + +def pedersen_proof_bytes(vector: Vector) -> bytes: + return bytes_from_fields(vector, "gamma", "proof_pk_com", "proof_r", "proof_ok", "proof_s", "proof_sb") + + +def ring_proof_bytes(vector: Vector) -> bytes: + return bytes_from_fields(vector, "gamma", "proof_pk_com", "proof_r", "proof_ok", "proof_s", "proof_sb", "ring_proof") From 3da7143005e0dcd68b8a4697694240abb119cf7d Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Mon, 25 May 2026 19:52:08 +0530 Subject: [PATCH 17/33] docs: update benchmark summary --- docs/BENCHMARK.md | 47 +++++++++++++++++++++++++++++++---------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/docs/BENCHMARK.md b/docs/BENCHMARK.md index c62b5da..aed1f29 100644 --- a/docs/BENCHMARK.md +++ b/docs/BENCHMARK.md @@ -7,6 +7,7 @@ Benchmark results for `dot-ring` VRF implementations on Bandersnatch curve. - **Suite**: `Bandersnatch-SHA512-ELL2-v1` - **Vectors**: `tests/vectors/ark-vrf/bandersnatch_*_*.json` - **Baseline**: ark-vrf `benches/SUMMARY.md`, quick mode, AMD Ryzen Threadripper 3970X +- **Local run**: 2026-05-25 --- @@ -15,9 +16,22 @@ Benchmark results for `dot-ring` VRF implementations on Bandersnatch curve. IETF VRF-AD proof. | Operation | Min | Mean | Stddev | ark-vrf | x | -|-----------|--------------|---------------|-----------------|---------|--------------------| -| Proof Generation | 1.67 ms | 1.78 ms | 0.09 ms | 185.4 us | 9.6x | -| Verification | 1.58 ms | 1.66 ms | 0.06 ms | 194.5 us | 8.5x | +|-----------|-----|------|--------|---------|---| +| Proof Generation | 2.21 ms | 2.65 ms | 0.85 ms | 185.4 us | 14.3x | +| Verification | 1.97 ms | 2.28 ms | 0.59 ms | 194.5 us | 11.7x | + +**Proof size**: 80 bytes + +--- + +## Thin VRF + +Thin VRF with `(R, s)` proofs. + +| Operation | Min | Mean | Stddev | ark-vrf | x | +|-----------|-----|------|--------|---------|---| +| Proof Generation | 2.21 ms | 2.55 ms | 0.56 ms | 184.8 us | 13.8x | +| Verification | 1.99 ms | 2.15 ms | 0.20 ms | 192.4 us | 11.2x | **Proof size**: 96 bytes @@ -28,9 +42,9 @@ IETF VRF-AD proof. VRF with Pedersen commitment for public key blinding. | Operation | Min | Mean | Stddev | ark-vrf | x | -|-----------|--------------|---------------|-----------------|---------|--------------------| -| Proof Generation | 2.30 ms | 2.38 ms | 0.07 ms | 374.6 us | 6.4x | -| Verification | 1.88 ms | 1.97 ms | 0.06 ms | 215.4 us | 9.1x | +|-----------|-----|------|--------|---------|---| +| Proof Generation | 2.40 ms | 2.64 ms | 0.68 ms | 374.6 us | 7.1x | +| Verification | 1.74 ms | 1.83 ms | 0.07 ms | 215.4 us | 8.5x | **Proof size**: 192 bytes @@ -45,20 +59,18 @@ Ring VRF with SNARK-based ring membership proof. ### 8-member ring (domain size: 512) | Operation | Min | Mean | Stddev | -|-----------|--------------|---------------|-----------------| -| Ring Root Construction | 28.07 ms | 28.28 ms | 0.14 ms | -| Proof Generation | 153.35 ms | 155.18 ms | 1.42 ms | -| Verification | 4.05 ms | 4.35 ms | 0.19 ms | - -ark-vrf's published summary does not include an 8-member ring row. +|-----------|-----|------|--------| +| Ring Root Construction | 48.87 ms | 50.56 ms | 1.50 ms | +| Proof Generation | 158.37 ms | 171.04 ms | 11.74 ms | +| Verification | 3.92 ms | 4.12 ms | 0.19 ms | ### 1023-member ring (domain size: 2048) | Operation | Min | Mean | Stddev | ark-vrf | x | -|-----------|--------------|---------------|-----------------|---------|--------------------| -| Ring Root Construction | 330.76 ms | 334.71 ms | 5.07 ms | 138.5 ms | 2.4x | -| Proof Generation | 525.28 ms | 543.04 ms | 29.13 ms | 482.2 ms | 1.1x | -| Verification | 4.09 ms | 4.22 ms | 0.14 ms | 3.37 ms | 1.3x | +|-----------|-----|------|--------|---------|---| +| Ring Root Construction | 409.70 ms | 411.42 ms | 1.83 ms | 138.5 ms | 3.0x | +| Proof Generation | 513.06 ms | 514.72 ms | 1.76 ms | 482.2 ms | 1.1x | +| Verification | 3.62 ms | 3.84 ms | 0.32 ms | 3.37 ms | 1.1x | --- @@ -68,6 +80,9 @@ ark-vrf's published summary does not include an 8-member ring row. # IETF / Tiny VRF uv run python tests/benchmark/bench_ietf.py +# Thin VRF +uv run python scripts/benchmark_rust_baseline.py --output /tmp/dot-ring-benchmark.md --ring-batch-max 0 + # Pedersen VRF uv run python tests/benchmark/bench_pedersen.py From 4f70e9cbf20ebc97a01933b4c0b075b1854e52c2 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Tue, 26 May 2026 02:39:51 +0530 Subject: [PATCH 18/33] Cache ring blinding vector construction --- docs/BENCHMARK.md | 12 ++++++------ dot_ring/vrf/ring/ring.py | 23 ++++++++++++++++++----- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/docs/BENCHMARK.md b/docs/BENCHMARK.md index aed1f29..44e156e 100644 --- a/docs/BENCHMARK.md +++ b/docs/BENCHMARK.md @@ -60,17 +60,17 @@ Ring VRF with SNARK-based ring membership proof. | Operation | Min | Mean | Stddev | |-----------|-----|------|--------| -| Ring Root Construction | 48.87 ms | 50.56 ms | 1.50 ms | -| Proof Generation | 158.37 ms | 171.04 ms | 11.74 ms | -| Verification | 3.92 ms | 4.12 ms | 0.19 ms | +| Ring Root Construction | 27.03 ms | 27.80 ms | 0.75 ms | +| Proof Generation | 152.31 ms | 154.03 ms | 1.29 ms | +| Verification | 3.70 ms | 3.95 ms | 0.19 ms | ### 1023-member ring (domain size: 2048) | Operation | Min | Mean | Stddev | ark-vrf | x | |-----------|-----|------|--------|---------|---| -| Ring Root Construction | 409.70 ms | 411.42 ms | 1.83 ms | 138.5 ms | 3.0x | -| Proof Generation | 513.06 ms | 514.72 ms | 1.76 ms | 482.2 ms | 1.1x | -| Verification | 3.62 ms | 3.84 ms | 0.32 ms | 3.37 ms | 1.1x | +| Ring Root Construction | 327.11 ms | 334.16 ms | 9.18 ms | 138.5 ms | 2.4x | +| Proof Generation | 527.04 ms | 534.57 ms | 12.48 ms | 482.2 ms | 1.1x | +| Verification | 3.81 ms | 3.99 ms | 0.24 ms | 3.37 ms | 1.2x | --- diff --git a/dot_ring/vrf/ring/ring.py b/dot_ring/vrf/ring/ring.py index f6e9570..c19e9ce 100644 --- a/dot_ring/vrf/ring/ring.py +++ b/dot_ring/vrf/ring/ring.py @@ -1,19 +1,32 @@ from __future__ import annotations from collections.abc import Sequence +from functools import lru_cache +from typing import Any from dot_ring.ring_proof.constants import DEFAULT_SIZE from dot_ring.ring_proof.params import RingProofParams -def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: - """Return `[2⁰·H, 2¹·H, ...]` in short-Weierstrass coords.""" - point = params.blinding_base +@lru_cache(maxsize=32) +def _h_vector_cached( + blinding_base: tuple[int, int], + size: int, + point_cls: type[Any], +) -> tuple[tuple[int, int], ...]: + point = blinding_base points = [] for _ in range(size): points.append(point) - point = params.add_points(point, point) - return points + point_obj = point_cls(point[0], point[1]) + result = point_obj + point_obj + point = int(result.x), int(result.y) + return tuple(points) + + +def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: + """Return `[2⁰·H, 2¹·H, ...]` in short-Weierstrass coords.""" + return list(_h_vector_cached(params.blinding_base, size, params.ring_point_cls)) class Ring: From 33e703a9c4df2125008c28ac7fcfbf5e41751e36 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:36:59 +0530 Subject: [PATCH 19/33] reshape curve params --- dot_ring/curve/curve.py | 336 +++++----- dot_ring/curve/specs/baby_jubjub.py | 143 ++-- dot_ring/curve/specs/bandersnatch.py | 377 ++++++----- dot_ring/curve/specs/bandersnatch_sw.py | 236 ++----- dot_ring/curve/specs/bls12_381_G1.py | 435 ++++-------- dot_ring/curve/specs/bls12_381_G2.py | 625 +++++++----------- dot_ring/curve/specs/curve25519.py | 179 ++--- dot_ring/curve/specs/curve448.py | 169 ++--- dot_ring/curve/specs/ed25519.py | 199 ++---- dot_ring/curve/specs/ed448.py | 259 ++------ dot_ring/curve/specs/jubjub.py | 143 ++-- dot_ring/curve/specs/p256.py | 204 ++---- dot_ring/curve/specs/p384.py | 148 ++--- dot_ring/curve/specs/p521.py | 161 ++--- dot_ring/curve/specs/parameters.py | 109 +++ dot_ring/curve/specs/secp256k1.py | 242 ++----- tests/test_coverage/test_curve_base.py | 22 +- tests/test_curve_ops/test_curves_s2p_p2s.py | 16 +- .../test_bls12_381_G2_ssw_nu.py | 69 +- .../test_bls12_381_G2_ssw_ro.py | 96 +-- .../test_h2c_suites/test_e2c_bandersnatch.py | 8 +- tests/test_h2c_suites/test_h2c_nu.py | 53 +- tests/test_h2c_suites/test_h2c_ro.py | 45 +- 23 files changed, 1506 insertions(+), 2768 deletions(-) create mode 100644 dot_ring/curve/specs/parameters.py diff --git a/dot_ring/curve/curve.py b/dot_ring/curve/curve.py index 5956eb7..03e5164 100644 --- a/dot_ring/curve/curve.py +++ b/dot_ring/curve/curve.py @@ -2,19 +2,25 @@ import hashlib import math -from dataclasses import dataclass, field -from typing import TYPE_CHECKING, Any, cast +from dataclasses import dataclass +from typing import TYPE_CHECKING, Generic, Literal, TypeVar, cast + +from gmpy2 import invert as _invert +from gmpy2 import mpz as _mpz +from gmpy2 import powmod as _powmod from dot_ring.curve.e2c import E2C_Variant -from dot_ring.curve.fast_math import invert, powmod, sqrt_mod -from dot_ring.curve.fast_math import is_square as fast_is_square +from dot_ring.curve.fp2 import Fp2 +from dot_ring.curve.specs.parameters import CurveParams, HashConstructor if TYPE_CHECKING: from dot_ring.curve.point import CurvePoint +CoordT = TypeVar("CoordT", int, Fp2) + -@dataclass(frozen=True) -class Curve: +@dataclass(frozen=True, kw_only=True) +class Curve(Generic[CoordT]): """ Base implementation of an elliptic curve. @@ -23,109 +29,75 @@ class Curve: IETF draft-irtf-cfrg-hash-to-curve. Attributes: - PRIME_FIELD: The prime field characteristic - ORDER: The order of the curve - GENERATOR_X: X-coordinate of the generator point - GENERATOR_Y: Y-coordinate of the generator point - COFACTOR: The cofactor of the curve - Z: The Z parameter for the curve + params: Stable curve-suite constants. + e2c_variant: Selected hash-to-curve/runtime variant. """ - # Curve Parameters - PRIME_FIELD: int - ORDER: int - GENERATOR_X: int | tuple[int, int] - GENERATOR_Y: int | tuple[int, int] - COFACTOR: int - Z: int | tuple[int, int] - E2C: E2C_Variant - - M: int # 1 - K: int # 128 - L: int - S_in_bytes: int | None - H_A: Any # Callable or str - ENDIAN: str - - CHALLENGE_LENGTH: int - - # Isogeny - Requires_Isogeny: bool - Isogeny_Coeffs: dict[str, list[int]] | None - - # Suite String Parameters - SUITE_STRING: bytes - DST: bytes - - # Blinding Base For Pedersen - BBx: int | tuple[int, int] | None - BBy: int | tuple[int, int] | None - UNCOMPRESSED: bool - POINT_LEN: int - SUITE_ID: bytes | None = field(default=None, kw_only=True) - TRANSCRIPT_HASH: str = field(default="sha512", kw_only=True) - HASH_TO_CURVE: str | None = field(default=None, kw_only=True) - ACCUMULATOR_BASE_X: int | tuple[int, int] | None = field(default=None, kw_only=True) - ACCUMULATOR_BASE_Y: int | tuple[int, int] | None = field(default=None, kw_only=True) - PADDING_X: int | tuple[int, int] | None = field(default=None, kw_only=True) - PADDING_Y: int | tuple[int, int] | None = field(default=None, kw_only=True) + params: CurveParams[CoordT] + e2c_variant: E2C_Variant def __post_init__(self) -> None: """Validate curve parameters after initialization.""" - if not self._validate_parameters(): - raise ValueError("Invalid curve parameters") - - def _validate_parameters(self) -> bool: + self._validate() + + def hash_to_curve_dst(self) -> bytes: + """Return the DST for the selected hash-to-curve variant.""" + if self.e2c_variant == E2C_Variant.TAI: + return b"" + dst = self.params.hash_to_curve.dst + if self.e2c_variant.value.endswith("_NU_"): + return dst.replace(b"_RO_", b"_NU_") + return dst + + def encoding_endian(self) -> Literal["little", "big"]: + """Return point/scalar endianness for the selected variant.""" + if self.e2c_variant == E2C_Variant.TAI: + return "little" + return self.params.encoding.endian + + def _validate(self): """ Validate that the curve parameters are correct. Returns: - bool: True if parameters are valid, False otherwise + None """ - # For extension fields (like Fp2), we need to check each component - if hasattr(self.GENERATOR_X, "__iter__"): - # Handle Fp2 points (tuples of two integers) - if not ( - isinstance(self.GENERATOR_X, (tuple, list)) - and len(self.GENERATOR_X) == 2 - and all(isinstance(x, int) for x in self.GENERATOR_X) - and all(0 <= x < self.PRIME_FIELD for x in self.GENERATOR_X) - ): - return False + generator_x, generator_y = self.params.generator + field_modulus = self.params.field_modulus + if isinstance(generator_x, Fp2) or isinstance(generator_y, Fp2): if not ( - isinstance(self.GENERATOR_Y, (tuple, list)) - and len(self.GENERATOR_Y) == 2 - and all(isinstance(y, int) for y in self.GENERATOR_Y) - and all(0 <= y < self.PRIME_FIELD for y in self.GENERATOR_Y) + isinstance(generator_x, Fp2) + and isinstance(generator_y, Fp2) + and generator_x.p == field_modulus + and generator_y.p == field_modulus ): - return False - - # Convert to a point for the on-curve check - from dot_ring.curve.short_weierstrass.sw_affine_point import SWAffinePoint - - point = SWAffinePoint(self.GENERATOR_X, self.GENERATOR_Y, self) - if not point.is_on_curve(): - return False + raise ValueError("Generator coordinates must be Fp2 elements with the correct field modulus") + if not self.is_on_curve((generator_x, generator_y)): # type: ignore[attr-defined] + raise ValueError("Generator point is not on the curve") else: # Original scalar field validation # Allow int or custom Scalar types - if isinstance(self.GENERATOR_X, int) and isinstance(self.GENERATOR_Y, int): - if not (0 <= self.GENERATOR_X < self.PRIME_FIELD and 0 <= self.GENERATOR_Y < self.PRIME_FIELD): - return False - elif not (isinstance(self.GENERATOR_X, (tuple, list)) or isinstance(self.GENERATOR_Y, (tuple, list))): - # Assume custom field elements (like Scalar) - # We can't easily check bounds against int PRIME_FIELD if they are opaque, - # but we assume they are valid if they are passed. + if isinstance(generator_x, int) and isinstance(generator_y, int): + if not (0 <= generator_x < field_modulus and 0 <= generator_y < field_modulus): + raise ValueError("Generator coordinates must be in the field range") + elif not (isinstance(generator_x, (tuple, list)) or isinstance(generator_y, (tuple, list))): + # Opaque field elements are accepted here; point classes validate curve membership. pass else: - return False - - # if not self.is_on_curve(self.GENERATOR_X, self.GENERATOR_Y): #already given in point class - # return False + raise ValueError("Generator coordinates must be either int, Fp2, or opaque field elements") + + if not ( + self.params.field_modulus > 2 + and self.params.subgroup_order > 2 + and self.params.cofactor > 0 + and self.params.field_modulus != self.params.subgroup_order + ): + raise ValueError("Invalid curve parameters") - return self.PRIME_FIELD > 2 and self.ORDER > 2 and self.COFACTOR > 0 and self.PRIME_FIELD != self.ORDER + def _hash_to_curve_fn(self) -> HashConstructor: + return self.params.hash_to_curve.hash_fn or self.params.hash_fn def hash_to_field(self, msg: bytes, count: int) -> list[int]: """ @@ -146,18 +118,19 @@ def hash_to_field(self, msg: bytes, count: int) -> list[int]: if msg is None: raise ValueError("Message cannot be None") - len_in_bytes = count * self.M * self.L + hash_to_curve = self.params.hash_to_curve + len_in_bytes = count * hash_to_curve.field_extension_degree * hash_to_curve.field_length if self._uses_xof(): uniform_bytes = self.expand_message_xof(msg, len_in_bytes) else: uniform_bytes = self.expand_message_xmd(msg, len_in_bytes) u_values: list[int] = [] for i in range(count): - for j in range(self.M): - elm_offset = self.L * (j + i * self.M) - tv = uniform_bytes[elm_offset : elm_offset + self.L] - e_j = int.from_bytes(tv, "big") % self.PRIME_FIELD - u_values.append(e_j % self.PRIME_FIELD) # modulo + for j in range(hash_to_curve.field_extension_degree): + elm_offset = hash_to_curve.field_length * (j + i * hash_to_curve.field_extension_degree) + tv = uniform_bytes[elm_offset : elm_offset + hash_to_curve.field_length] + e_j = int.from_bytes(tv, "big") % self.params.field_modulus + u_values.append(e_j % self.params.field_modulus) # modulo return u_values @@ -174,54 +147,32 @@ def expand_message_xmd(self, msg: bytes, len_in_bytes: int) -> bytes: Raises: ValueError: If the input parameters are invalid """ - b_in_bytes = self.H_A().digest_size + hash_to_curve = self.params.hash_to_curve + hash_fn = self._hash_to_curve_fn() + b_in_bytes = hash_fn().digest_size ell = math.ceil(len_in_bytes / b_in_bytes) - if (ell > 255 and self.PRIME_FIELD.bit_length() < 384) or len_in_bytes > 65535 or len(self.DST) > 255: - # Relax ell check for large curves like P-521 where len_in_bytes might be large - # But strictly, RFC 9380 says ell <= 255. - # If len_in_bytes is huge, maybe we should just allow it if the curve is large? - # However, 26596 bytes is suspiciously large. - # Let's just relax the check if it's P521 (521 bits). - # But wait, 26596 bytes is way more than needed for P521 (approx 132 bytes). - # The issue might be how len_in_bytes is calculated. - # len_in_bytes = count * M * L. - # For P521, L=66 (ceil(521/8) + 128/8 ? No, L is usually larger). - # If L is around 66-100 bytes, and count=2, M=1. len_in_bytes should be ~200. - # Why is it 26596? - # 26596 / 2 = 13298. - # This suggests L is huge or M is huge. - # I should check P521 params in specs/p521.py before patching this. - pass - - if ell > 255 and len_in_bytes < 65535: - # If len_in_bytes is within u16 range but ell > 255, it means b_in_bytes is small. - # For SHA-512, b=64. 255*64 = 16320. - # If len_in_bytes > 16320, ell > 255. - # P521 might need more than 16320 bytes? No. - pass - - if ell > 255 or len_in_bytes > 65535 or len(self.DST) > 255: - # We will relax this check for now to unblock P521 if it really needs it, - # but 26KB seems wrong. - # Let's assume the user knows what they are doing if they request such length. - # But XMD structure relies on 1 byte for 'i' in loop. So ell cannot exceed 255. - raise ValueError(f"Invalid input size parameters: ell={ell}, len={len_in_bytes}, dst_len={len(self.DST)}") - - DST_prime = self.DST + self.I2OSP(len(self.DST), 1) - Z_pad = self.I2OSP(0, cast(int, self.S_in_bytes)) + dst = self.hash_to_curve_dst() + if ell > 255 or len_in_bytes > 65535 or len(dst) > 255: + raise ValueError( + "Invalid XMD input size parameters: " + f"ell={ell}, len_in_bytes={len_in_bytes}, dst_len={len(dst)}" + ) + + DST_prime = dst + self.I2OSP(len(dst), 1) + Z_pad = self.I2OSP(0, cast(int, hash_to_curve.expand_len)) l_i_b_str = self.I2OSP(len_in_bytes, 2) msg_prime = Z_pad + msg + l_i_b_str + self.I2OSP(0, 1) + DST_prime - b_0 = self.H_A(msg_prime).digest() + b_0 = hash_fn(msg_prime).digest() - b_1 = self.H_A(b_0 + self.I2OSP(1, 1) + DST_prime).digest() + b_1 = hash_fn(b_0 + self.I2OSP(1, 1) + DST_prime).digest() b_values = [b_1] for i in range(2, ell + 1): - b_i = self.H_A(self.strxor(b_0, b_values[-1]) + self.I2OSP(i, 1) + DST_prime).digest() + b_i = hash_fn(self.strxor(b_0, b_values[-1]) + self.I2OSP(i, 1) + DST_prime).digest() b_values.append(b_i) uniform_bytes = b"".join(b_values) @@ -230,17 +181,17 @@ def expand_message_xmd(self, msg: bytes, len_in_bytes: int) -> bytes: def _uses_xof(self) -> bool: """Return True when the curve suite requires XOF-based expansion.""" - if self.S_in_bytes in (None, 0): + hash_to_curve = self.params.hash_to_curve + if hash_to_curve.expand_len in (None, 0): return True - suite = self.SUITE_STRING or b"" - if b"_XOF" in suite: + if b"_XOF" in self.params.suite_id or b"_XOF" in hash_to_curve.dst: return True - hash_name = getattr(getattr(self.H_A, "__name__", ""), "lower", lambda: "")() + hash_name = getattr(getattr(self._hash_to_curve_fn(), "__name__", ""), "lower", lambda: "")() return "shake" in hash_name def _default_xof_len(self) -> int: - scalar_len = (self.ORDER.bit_length() + 7) // 8 - return max(self.L, self.CHALLENGE_LENGTH, 2 * scalar_len) + scalar_len = (self.params.subgroup_order.bit_length() + 7) // 8 + return max(self.params.hash_to_curve.field_length, self.params.encoding.challenge_len, 2 * scalar_len) def expand_message_xof(self, msg: bytes, len_in_bytes: int) -> bytes: # 1.ABORT if len_in_bytes > 65535 or len(DST) > 255 @@ -251,17 +202,18 @@ def expand_message_xof(self, msg: bytes, len_in_bytes: int) -> bytes: if len_in_bytes > 65535: raise ValueError("len_in_bytes too large") - if len(self.DST) > 255: + dst = self.hash_to_curve_dst() + if len(dst) > 255: raise ValueError("DST too long") # Step 2: DST_prime = DST || I2OSP(len(DST), 1) - DST_prime = self.DST + self.I2OSP(len(self.DST), 1) + DST_prime = dst + self.I2OSP(len(dst), 1) # Step 3: msg_prime = msg || I2OSP(len_in_bytes, 2) || DST_prime msg_prime = msg + self.I2OSP(len_in_bytes, 2) + DST_prime # Step 4: uniform_bytes = SHAKE256(msg_prime, len_in_bytes) - xof = self.H_A() + xof = self._hash_to_curve_fn()() xof.update(msg_prime) uniform_bytes = xof.digest(len_in_bytes) # Step 5: return uniform_bytes @@ -271,11 +223,11 @@ def hash(self, data: bytes, out_len: int | None = None) -> bytes: """Hash helper that handles both XOF and XMD suites.""" if self._uses_xof(): length = out_len or self._default_xof_len() - xof = self.H_A() + xof = self.params.hash_fn() xof.update(data) return cast(bytes, xof.digest(length)) - hasher = self.H_A() + hasher = self.params.hash_fn() hasher.update(data) digest = hasher.digest() if out_len is not None: @@ -295,9 +247,11 @@ def mod_inverse(self, val: int) -> int: Raises: ValueError: If inverse doesn't exist """ - if powmod(val, self.PRIME_FIELD - 1, self.PRIME_FIELD) != 1: + field_modulus = self.params.field_modulus + modulus = _mpz(field_modulus) + if _powmod(_mpz(val), _mpz(field_modulus - 1), modulus) != 1: raise ValueError("No inverse exists") - return cast(int, invert(val, self.PRIME_FIELD)) + return int(_invert(_mpz(val), modulus)) @staticmethod def CMOV(a: int, b: int, cond: int) -> int: @@ -314,7 +268,11 @@ def find_z_ell2(self) -> int: def is_square(self, val: int) -> bool: """Check if val is a quadratic residue mod p using gmpy2 if available.""" - return cast(bool, fast_is_square(val, self.PRIME_FIELD)) + if val == 0: + return True + modulus = _mpz(self.params.field_modulus) + exponent = _mpz((self.params.field_modulus - 1) // 2) + return _powmod(_mpz(val), exponent, modulus) == 1 def mod_sqrt(self, val: int) -> int: """ @@ -329,17 +287,56 @@ def mod_sqrt(self, val: int) -> int: Raises: ValueError: If no square root exists """ - result = sqrt_mod(val, self.PRIME_FIELD) - if result is None: + p = self.params.field_modulus + if val == 0: + return 0 + + modulus = _mpz(p) + value = _mpz(val) + if _powmod(value, _mpz((p - 1) // 2), modulus) != 1: raise ValueError("No square root exists") - return cast(int, result) + + q = _mpz(p - 1) + s = 0 + while q % 2 == 0: + q //= 2 + s += 1 + + z = _mpz(2) + while self.is_square(int(z)): + z += 1 + + c = _powmod(z, q, modulus) + m = s + t = _powmod(value, q, modulus) + r = _powmod(value, _mpz((q + 1) // 2), modulus) + + while True: + if t == 0: + return 0 + if t == 1: + return int(r) + + i = 1 + temp = (t * t) % p + while temp != 1: + temp = (temp * temp) % p + i += 1 + + b = c + for _ in range(m - i - 1): + b = (b * b) % p + m = i + c = (b * b) % p + t = (t * c) % p + r = (r * b) % p def inv(self, x: int) -> int: # modular inverse in GF(p) - return pow(x, self.PRIME_FIELD - 2, self.PRIME_FIELD) + return pow(x, self.params.field_modulus - 2, self.params.field_modulus) def legendre_symbol(self, x: int) -> int: - return pow(x, (self.PRIME_FIELD - 1) // 2, self.PRIME_FIELD) + return pow(x, (self.params.field_modulus - 1) // 2, self.params.field_modulus) @staticmethod def sha512(data: bytes) -> bytes: @@ -358,11 +355,40 @@ def OS2IP(octets: bytearray) -> int: @staticmethod def strxor(s1: bytes, s2: bytes) -> bytes: - return bytes(a ^ b for a, b in zip(s1, s2, strict=False)) + n = min(len(s1), len(s2)) + if n == 0: + return b"" + if len(s1) != n: + s1 = s1[:n] + if len(s2) != n: + s2 = s2[:n] + return (int.from_bytes(s1, "big") ^ int.from_bytes(s2, "big")).to_bytes(n, "big") @dataclass -class CurveVariant: +class CurveVariant(Generic[CoordT]): name: str - curve: Curve - point: type[CurvePoint] + curve: Curve[CoordT] + point_type: type[CurvePoint[Curve[CoordT], CoordT]] + + def point(self, x: CoordT, y: CoordT) -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type(x, y, self.curve) + + def identity(self) -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type.identity(self.curve) + + def generator_point(self) -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type.generator_point(self.curve) + + def string_to_point(self, data: str | bytes) -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type.string_to_point(data, self.curve) + + def encode_to_curve(self, alpha_string: bytes | str, salt: bytes | str = b"") -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type.encode_to_curve(alpha_string, salt, self.curve) + + def msm( + self, + points: list[CurvePoint[Curve[CoordT], CoordT]], + scalars: list[int], + ) -> CurvePoint[Curve[CoordT], CoordT]: + return self.point_type.msm(points, scalars, self.curve) diff --git a/dot_ring/curve/specs/baby_jubjub.py b/dot_ring/curve/specs/baby_jubjub.py index dc3caba..d8ca964 100644 --- a/dot_ring/curve/specs/baby_jubjub.py +++ b/dot_ring/curve/specs/baby_jubjub.py @@ -1,110 +1,59 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..twisted_edwards.te_affine_point import TEAffinePoint from ..twisted_edwards.te_curve import TECurve - - -@dataclass(frozen=True) -class BabyJubJubParams: - """ - Baby JubJub curve parameters. - - Specification of the Baby JubJub curve in Twisted Edwards form. - """ - - SUITE_STRING = b"BabyJubJub-SHA512-TAI-v1" - SUITE_ID = b"BabyJubJub-SHA512-TAI-v1" - DST = b"" - - # Curve parameters - PRIME_FIELD: Final[int] = 21888242871839275222246405745257275088548364400416034343698204186575808495617 - ORDER: Final[int] = 2736030358979909402780800718157159386076813972158567259200215660948447373041 - COFACTOR: Final[int] = 8 - - # Generator point - GENERATOR_X: Final[int] = 19698561148652590122159747500897617769866003486955115824547446575314762165298 - GENERATOR_Y: Final[int] = 19298250018296453272277890825869354524455968081175474282777126169995084727839 - # Edwards curve parameters - EDWARDS_A: Final[int] = 1 - EDWARDS_D: Final[int] = 9706598848417545097372247223557719406784115219466060233080913168975159366771 - - # Z - Z: Final[int] = 5 - M: Final[int] = 1 - K: Final[int] = 128 - L: Final[int] = 32 # can define func as well - S_in_bytes: Final[int] = 128 # can be taken as hsh_fn.block_size #not sure as its supposed to be 128 for sha512 - H_A = hashlib.sha512 - ENDIAN = "little" - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - # Challenge length in bytes for VRF (aligned with 128-bit security level) - CHALLENGE_LENGTH: Final[int] = 16 - - # Blinding Base For Pedersen - BBx: Final[int] = 15549380791300914366206471199568039679131690710803662429646809536753521087193 - BBy: Final[int] = 15218614024055502695611547593111691164731001864276292210438920202280814188379 - ACCUMULATOR_BASE_X: Final[int] = 6402374321243162085389111671722843560682527921646684137786768606010797479351 - ACCUMULATOR_BASE_Y: Final[int] = 9735581299071570006712034490635195155689931359428941496570758703259384062170 - PADDING_X: Final[int] = 11167490195257431015694161063225325511805242064780376648595733691987293447528 - PADDING_Y: Final[int] = 18403369502642103292159933062507105566469227524991433735553439433605496057425 - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - TRANSCRIPT_HASH = "sha512" - HASH_TO_CURVE = "tai" - - -BabyJubJub_TE_Curve: Final[TECurve] = TECurve( - PRIME_FIELD=BabyJubJubParams.PRIME_FIELD, - ORDER=BabyJubJubParams.ORDER, - GENERATOR_X=BabyJubJubParams.GENERATOR_X, - GENERATOR_Y=BabyJubJubParams.GENERATOR_Y, - COFACTOR=BabyJubJubParams.COFACTOR, - Z=BabyJubJubParams.Z, - EdwardsA=BabyJubJubParams.EDWARDS_A, - EdwardsD=BabyJubJubParams.EDWARDS_D, - SUITE_STRING=BabyJubJubParams.SUITE_STRING, - DST=BabyJubJubParams.DST, - E2C=E2C_Variant.TAI, - BBx=BabyJubJubParams.BBx, - BBy=BabyJubJubParams.BBy, - M=BabyJubJubParams.M, - K=BabyJubJubParams.K, - L=BabyJubJubParams.L, - S_in_bytes=BabyJubJubParams.S_in_bytes, - H_A=BabyJubJubParams.H_A, - Requires_Isogeny=BabyJubJubParams.Requires_Isogeny, - Isogeny_Coeffs=BabyJubJubParams.Isogeny_Coeffs, - UNCOMPRESSED=BabyJubJubParams.UNCOMPRESSED, - ENDIAN=BabyJubJubParams.ENDIAN, - POINT_LEN=BabyJubJubParams.POINT_LEN, - CHALLENGE_LENGTH=BabyJubJubParams.CHALLENGE_LENGTH, - SUITE_ID=BabyJubJubParams.SUITE_ID, - TRANSCRIPT_HASH=BabyJubJubParams.TRANSCRIPT_HASH, - HASH_TO_CURVE=BabyJubJubParams.HASH_TO_CURVE, - ACCUMULATOR_BASE_X=BabyJubJubParams.ACCUMULATOR_BASE_X, - ACCUMULATOR_BASE_Y=BabyJubJubParams.ACCUMULATOR_BASE_Y, - PADDING_X=BabyJubJubParams.PADDING_X, - PADDING_Y=BabyJubJubParams.PADDING_Y, +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + TwistedEdwardsCurveParams, ) - -class BabyJubJubPoint(TEAffinePoint): - """ - Point on the Bandersnatch curve. - - Implements optimized point operations specific to the Bandersnatch curve, - including GLV scalar multiplication. - """ - - curve: TECurve = BabyJubJub_TE_Curve +BABY_JUBJUB_PARAMS = TwistedEdwardsCurveParams( + field_modulus=21888242871839275222246405745257275088548364400416034343698204186575808495617, + subgroup_order=2736030358979909402780800718157159386076813972158567259200215660948447373041, + cofactor=8, + suite_id=b"BabyJubJub-SHA512-TAI-v1", + hash_fn=hashlib.sha512, + generator=( + 19698561148652590122159747500897617769866003486955115824547446575314762165298, + 19298250018296453272277890825869354524455968081175474282777126169995084727839, + ), + a=1, + d=9706598848417545097372247223557719406784115219466060233080913168975159366771, + hash_to_curve=HashToCurveParams( + dst=b"", + z=5, + field_extension_degree=1, + security_level=128, + field_length=32, + expand_len=128, + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 15549380791300914366206471199568039679131690710803662429646809536753521087193, + 15218614024055502695611547593111691164731001864276292210438920202280814188379, + ), + accumulator_base=( + 6402374321243162085389111671722843560682527921646684137786768606010797479351, + 9735581299071570006712034490635195155689931359428941496570758703259384062170, + ), + padding_point=( + 11167490195257431015694161063225325511805242064780376648595733691987293447528, + 18403369502642103292159933062507105566469227524991433735553439433605496057425, + ), + ), +) -BabyJubJub = CurveVariant(name="BabyJubJub", curve=BabyJubJub_TE_Curve, point=BabyJubJubPoint) +BabyJubJub = CurveVariant( + name="BabyJubJub", + curve=TECurve(params=BABY_JUBJUB_PARAMS, e2c_variant=E2C_Variant.TAI), + point_type=TEAffinePoint, +) diff --git a/dot_ring/curve/specs/bandersnatch.py b/dot_ring/curve/specs/bandersnatch.py index 183a7de..b18d412 100644 --- a/dot_ring/curve/specs/bandersnatch.py +++ b/dot_ring/curve/specs/bandersnatch.py @@ -2,114 +2,166 @@ import hashlib from dataclasses import dataclass -from typing import Any, ClassVar, Final, Self, cast +from typing import Self, cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant -from dot_ring.curve.native_field.scalar import Scalar +from dot_ring.curve.native_field.bandersnatch_te import sqrt_mod_bls_scalar_cy as _native_sqrt from ..glv import GLV from ..twisted_edwards.te_affine_point import TEAffinePoint from ..twisted_edwards.te_curve import TECurve +from .parameters import ( + AuxiliaryPointParams, + Elligator2MontgomeryMap, + EncodingParams, + HashToCurveParams, + TwistedEdwardsCurveParams, +) -@dataclass(frozen=True) -class BandersnatchParams: - """ - Bandersnatch curve parameters. +def _pippenger_window_bits(point_count: int) -> int: + if point_count < 8: + return 2 + if point_count < 96: + return 3 + if point_count < 192: + return 4 + if point_count < 384: + return 5 + if point_count < 768: + return 6 + if point_count < 1024: + return 7 + return 8 + + +def elligator2_map_from_edwards(a: int, d: int, p: int) -> Elligator2MontgomeryMap: + """Derive the Elligator 2 Montgomery map associated with a twisted Edwards curve.""" + denom_inv = pow((a - d) % p, -1, p) + return Elligator2MontgomeryMap( + a=(2 * (a + d) * denom_inv) % p, + b=(4 * denom_inv) % p, + ) + + +@dataclass(frozen=True, kw_only=True) +class BandersnatchParams(TwistedEdwardsCurveParams): + """Bandersnatch curve parameters.""" + + glv_lambda: int + glv_b: int + glv_c: int + + +BANDERSNATCH_FIELD_MODULUS = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 +BANDERSNATCH_SUBGROUP_ORDER = 0x1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E1 +BANDERSNATCH_GENERATOR = ( + 18886178867200960497001835917649091219057080094937609519140440539760939937304, + 19188667384257783945677642223292697773471335439753913231509108946878080696678, +) +BANDERSNATCH_A = -5 +BANDERSNATCH_D = 0x6389C12633C267CBC66E3BF86BE3B6D8CB66677177E54F92B369F2F5188D58E7 +BANDERSNATCH_GLV_LAMBDA = 0x13B4F3DC4A39A493EDF849562B38C72BCFC49DB970A5056ED13D21408783DF05 +BANDERSNATCH_GLV_B = 0x52C9F28B828426A561F00D3A63511A882EA712770D9AF4D6EE0F014D172510B4 +BANDERSNATCH_GLV_C = 0x6CC624CF865457C3A97C6EFD6C17D1078456ABCFFF36F4E9515C806CDF650B3D + + +BANDERSNATCH_PARAMS = BandersnatchParams( + field_modulus=BANDERSNATCH_FIELD_MODULUS, + subgroup_order=BANDERSNATCH_SUBGROUP_ORDER, + cofactor=4, + suite_id=b"Bandersnatch-SHA512-ELL2-v1", + hash_fn=hashlib.sha512, + generator=BANDERSNATCH_GENERATOR, + a=BANDERSNATCH_A, + d=BANDERSNATCH_D, + hash_to_curve=HashToCurveParams( + dst=b"Bandersnatch-SHA512-ELL2-v1\x60", + z=5, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=48, + elligator2_map=elligator2_map_from_edwards(BANDERSNATCH_A, BANDERSNATCH_D, BANDERSNATCH_FIELD_MODULUS), + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 23335687741101763108036518445642207119627658113885888016488710494487028845889, + 5552214580375038693022409684979828600325210968745774080859660443337357929963, + ), + accumulator_base=( + 14056632001415368875257708737821299882600475929746323097150942355715730684350, + 10322661992765989500407719465917595459409463902187386706652408883505670839210, + ), + padding_point=( + 26913883415342152801331916189968962157924271221160514298872262294143390094043, + 30874728313203001508631936119690348239461579770372782660098261717479009115354, + ), + ), + glv_lambda=BANDERSNATCH_GLV_LAMBDA, + glv_b=BANDERSNATCH_GLV_B, + glv_c=BANDERSNATCH_GLV_C, +) + +BANDERSNATCH_SHAKE128_PARAMS = BandersnatchParams( + field_modulus=BANDERSNATCH_FIELD_MODULUS, + subgroup_order=BANDERSNATCH_SUBGROUP_ORDER, + cofactor=4, + suite_id=b"Bandersnatch-SHAKE128-ELL2-v1", + hash_fn=hashlib.shake_128, + generator=BANDERSNATCH_GENERATOR, + a=BANDERSNATCH_A, + d=BANDERSNATCH_D, + hash_to_curve=HashToCurveParams( + dst=b"Bandersnatch-SHAKE128-ELL2-v1\x60", + z=5, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=None, + elligator2_map=elligator2_map_from_edwards(BANDERSNATCH_A, BANDERSNATCH_D, BANDERSNATCH_FIELD_MODULUS), + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 6153734995852631824944342602386415873379775188383988340041079006556670120775, + 27204351599954061630605768787803524395123895650061061132592995395630473050754, + ), + accumulator_base=( + 27631238720955528589004064829276283990465032040945349648037876197995278250917, + 37605358688136619817560700742505556266961225274493904038881144193539047100140, + ), + padding_point=( + 1834402953989431481748983728202937234471322740714585873803966488035889514523, + 52100941849053769665273763352270294131006971127418863694682093199651869272752, + ), + ), + glv_lambda=BANDERSNATCH_GLV_LAMBDA, + glv_b=BANDERSNATCH_GLV_B, + glv_c=BANDERSNATCH_GLV_C, +) - The Bandersnatch curve is a Twisted Edwards curve designed for efficient - implementation of zero-knowledge proofs and VRFs. - """ - SUITE_STRING = b"Bandersnatch-SHA512-ELL2-v1" - SUITE_ID = b"Bandersnatch-SHA512-ELL2-v1" - DST = SUITE_ID + b"\x60" - - # Curve parameters - PRIME_FIELD: Final[int] = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 - ORDER: Final[int] = 0x1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E1 - COFACTOR: Final[int] = 4 - - # Generator point - GENERATOR_X: ClassVar[Scalar] = Scalar(18886178867200960497001835917649091219057080094937609519140440539760939937304) - GENERATOR_Y: ClassVar[Scalar] = Scalar(19188667384257783945677642223292697773471335439753913231509108946878080696678) - - # Edwards curve parameters - EDWARDS_A: ClassVar[Scalar] = Scalar(-5) - EDWARDS_D: ClassVar[Scalar] = Scalar(0x6389C12633C267CBC66E3BF86BE3B6D8CB66677177E54F92B369F2F5188D58E7) - - # GLV parameters - GLV_LAMBDA: Final[int] = 0x13B4F3DC4A39A493EDF849562B38C72BCFC49DB970A5056ED13D21408783DF05 - GLV_B: Final[int] = 0x52C9F28B828426A561F00D3A63511A882EA712770D9AF4D6EE0F014D172510B4 - GLV_C: Final[int] = 0x6CC624CF865457C3A97C6EFD6C17D1078456ABCFFF36F4E9515C806CDF650B3D - - # Challenge length in bytes for VRF (aligned with 256-bit security level) - CHALLENGE_LENGTH: Final[int] = 16 - - # Z - Z: Final[int] = 5 - M: Final[int] = 1 - K: Final[int] = 128 - L: Final[int] = 48 # can define func as well - S_in_bytes: Final[int] = 48 # can be taken as hsh_fn.block_size #not sure as its supposed to be 128 for sha512 - H_A = hashlib.sha512 - ENDIAN = "little" - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - - BBx: Final[int] = 23335687741101763108036518445642207119627658113885888016488710494487028845889 - BBy: Final[int] = 5552214580375038693022409684979828600325210968745774080859660443337357929963 - ACCUMULATOR_BASE_X: Final[int] = 14056632001415368875257708737821299882600475929746323097150942355715730684350 - ACCUMULATOR_BASE_Y: Final[int] = 10322661992765989500407719465917595459409463902187386706652408883505670839210 - PADDING_X: Final[int] = 26913883415342152801331916189968962157924271221160514298872262294143390094043 - PADDING_Y: Final[int] = 30874728313203001508631936119690348239461579770372782660098261717479009115354 - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - TRANSCRIPT_HASH = "sha512" - HASH_TO_CURVE = "ell2-xmd" - - -"""GLV endomorphism parameters for Bandersnatch curve.""" BandersnatchGLV = GLV( - lambda_param=BandersnatchParams.GLV_LAMBDA, - constant_b=BandersnatchParams.GLV_B, - constant_c=BandersnatchParams.GLV_C, + lambda_param=BANDERSNATCH_PARAMS.glv_lambda, + constant_b=BANDERSNATCH_PARAMS.glv_b, + constant_c=BANDERSNATCH_PARAMS.glv_c, ) -Bandersnatch_TE_Curve: Final[TECurve] = TECurve( - PRIME_FIELD=BandersnatchParams.PRIME_FIELD, - ORDER=BandersnatchParams.ORDER, - GENERATOR_X=BandersnatchParams.GENERATOR_X, - GENERATOR_Y=BandersnatchParams.GENERATOR_Y, - COFACTOR=BandersnatchParams.COFACTOR, - Z=BandersnatchParams.Z, - EdwardsA=BandersnatchParams.EDWARDS_A, - EdwardsD=BandersnatchParams.EDWARDS_D, - SUITE_STRING=BandersnatchParams.SUITE_STRING, - DST=BandersnatchParams.DST, - E2C=E2C_Variant.ELL2, - BBx=BandersnatchParams.BBx, - BBy=BandersnatchParams.BBy, - M=BandersnatchParams.M, - K=BandersnatchParams.K, - L=BandersnatchParams.L, - S_in_bytes=BandersnatchParams.S_in_bytes, - H_A=BandersnatchParams.H_A, - Requires_Isogeny=BandersnatchParams.Requires_Isogeny, - Isogeny_Coeffs=BandersnatchParams.Isogeny_Coeffs, - UNCOMPRESSED=BandersnatchParams.UNCOMPRESSED, - ENDIAN=BandersnatchParams.ENDIAN, - POINT_LEN=BandersnatchParams.POINT_LEN, - CHALLENGE_LENGTH=BandersnatchParams.CHALLENGE_LENGTH, - SUITE_ID=BandersnatchParams.SUITE_ID, - TRANSCRIPT_HASH=BandersnatchParams.TRANSCRIPT_HASH, - HASH_TO_CURVE=BandersnatchParams.HASH_TO_CURVE, - ACCUMULATOR_BASE_X=BandersnatchParams.ACCUMULATOR_BASE_X, - ACCUMULATOR_BASE_Y=BandersnatchParams.ACCUMULATOR_BASE_Y, - PADDING_X=BandersnatchParams.PADDING_X, - PADDING_Y=BandersnatchParams.PADDING_Y, -) + +class BandersnatchCurve(TECurve): + """Bandersnatch curve with native base-field square roots.""" + + def mod_sqrt(self, val: int) -> int: + try: + return int(_native_sqrt(val)) + except ValueError as exc: + raise ValueError("No square root exists") from exc + + +Bandersnatch_TE_Curve = BandersnatchCurve(params=BANDERSNATCH_PARAMS, e2c_variant=E2C_Variant.ELL2) class BandersnatchPoint(TEAffinePoint[TECurve]): @@ -120,8 +172,6 @@ class BandersnatchPoint(TEAffinePoint[TECurve]): including GLV scalar multiplication. """ - curve: TECurve = Bandersnatch_TE_Curve - def __mul__(self, scalar: int) -> Self: """ GLV scalar multiplication using endomorphism. @@ -132,25 +182,25 @@ def __mul__(self, scalar: int) -> Self: Returns: TEAffinePoint: Scalar multiplication result """ - n = self.curve.ORDER + n = self.curve.params.subgroup_order k1, k2 = BandersnatchGLV.decompose_scalar(scalar % n, n) phi = BandersnatchGLV.compute_endomorphism(self) return cast(Self, BandersnatchGLV.windowed_simultaneous_mult(k1, k2, self, phi, w=2)) - def __add__(self, other: Any) -> Self: - return cast(Self, super().__add__(other)) - @classmethod - def msm(cls, points: list[Self], scalars: list[int]) -> Self: + def msm(cls, points: list[Self], scalars: list[int], curve: TECurve) -> Self: """ Optimized multi-scalar multiplication using GLV. """ + if len(points) != len(scalars): + raise ValueError("Points and scalars must have same length") if not points: - return cls.identity() + return cls.identity(curve) # Normalize scalars to [0, ORDER) for GLV - n = cls.curve.ORDER + n = curve.params.subgroup_order + raw_scalars = scalars scalars = [s % n for s in scalars] if len(points) == 2: @@ -170,81 +220,80 @@ def msm(cls, points: list[Self], scalars: list[int]) -> Self: ) if len(points) == 4: - return BandersnatchGLV.multi_scalar_mult_4( - scalars[0], - scalars[1], - scalars[2], - scalars[3], - points[0], - points[1], - points[2], - points[3], + if any(point.is_identity() for point in points): + return super().msm(points, scalars, curve) + + return cast( + Self, + BandersnatchGLV.multi_scalar_mult_4( + scalars[0], + scalars[1], + scalars[2], + scalars[3], + points[0], + points[1], + points[2], + points[3], + ), ) if len(points) == 3: - # Pad to 4 with identity and 0 scalar - return BandersnatchGLV.multi_scalar_mult_4( - scalars[0], - scalars[1], - scalars[2], - 0, - points[0], - points[1], - points[2], - cls.identity(), + if any(point.is_identity() for point in points): + return super().msm(points, scalars, curve) + + decomposed = [BandersnatchGLV.decompose_scalar(scalar, n) for scalar in scalars] + phi_points = [BandersnatchGLV.compute_endomorphism(point) for point in points] + return cast( + Self, + BandersnatchGLV.multi_scalar_mult_6( + [ + decomposed[0][0], + decomposed[0][1], + decomposed[1][0], + decomposed[1][1], + decomposed[2][0], + decomposed[2][1], + ], + [ + points[0], + phi_points[0], + points[1], + phi_points[1], + points[2], + phi_points[2], + ], + ), ) - return super().msm(points, scalars) + if len(points) >= 5: + work_points = [] + work_scalars = [] + for point, scalar in zip(points, raw_scalars, strict=True): + scalar %= n + if scalar > n >> 1: + scalar -= n + if scalar != 0 and not point.is_identity(): + work_points.append(point) + work_scalars.append(scalar) + if not work_points: + return cls.identity(curve) + if len(work_points) >= 5: + window_bits = _pippenger_window_bits(len(work_points)) + return cast(Self, BandersnatchGLV.multi_scalar_mult_pippenger(work_scalars, work_points, window_bits=window_bits)) + + return super().msm(points, scalars, curve) Bandersnatch = CurveVariant( name="Bandersnatch", curve=Bandersnatch_TE_Curve, - point=BandersnatchPoint, -) - - -Bandersnatch_SHAKE128_TE_Curve: Final[TECurve] = TECurve( - PRIME_FIELD=BandersnatchParams.PRIME_FIELD, - ORDER=BandersnatchParams.ORDER, - GENERATOR_X=BandersnatchParams.GENERATOR_X, - GENERATOR_Y=BandersnatchParams.GENERATOR_Y, - COFACTOR=BandersnatchParams.COFACTOR, - Z=BandersnatchParams.Z, - EdwardsA=BandersnatchParams.EDWARDS_A, - EdwardsD=BandersnatchParams.EDWARDS_D, - SUITE_STRING=b"Bandersnatch-SHAKE128-ELL2-v1", - DST=b"Bandersnatch-SHAKE128-ELL2-v1\x60", - E2C=E2C_Variant.ELL2, - BBx=6153734995852631824944342602386415873379775188383988340041079006556670120775, - BBy=27204351599954061630605768787803524395123895650061061132592995395630473050754, - M=BandersnatchParams.M, - K=BandersnatchParams.K, - L=BandersnatchParams.L, - S_in_bytes=None, - H_A=hashlib.shake_128, - Requires_Isogeny=BandersnatchParams.Requires_Isogeny, - Isogeny_Coeffs=BandersnatchParams.Isogeny_Coeffs, - UNCOMPRESSED=BandersnatchParams.UNCOMPRESSED, - ENDIAN=BandersnatchParams.ENDIAN, - POINT_LEN=BandersnatchParams.POINT_LEN, - CHALLENGE_LENGTH=BandersnatchParams.CHALLENGE_LENGTH, - SUITE_ID=b"Bandersnatch-SHAKE128-ELL2-v1", - TRANSCRIPT_HASH="shake128", - HASH_TO_CURVE="ell2-xof", - ACCUMULATOR_BASE_X=27631238720955528589004064829276283990465032040945349648037876197995278250917, - ACCUMULATOR_BASE_Y=37605358688136619817560700742505556266961225274493904038881144193539047100140, - PADDING_X=1834402953989431481748983728202937234471322740714585873803966488035889514523, - PADDING_Y=52100941849053769665273763352270294131006971127418863694682093199651869272752, + point_type=BandersnatchPoint, ) - -class BandersnatchSHAKE128Point(BandersnatchPoint): - curve: TECurve = Bandersnatch_SHAKE128_TE_Curve - +Bandersnatch_SHAKE128_TE_Curve = BandersnatchCurve(params=BANDERSNATCH_SHAKE128_PARAMS, e2c_variant=E2C_Variant.ELL2) Bandersnatch_SHAKE128 = CurveVariant( name="Bandersnatch_SHAKE128", curve=Bandersnatch_SHAKE128_TE_Curve, - point=BandersnatchSHAKE128Point, + point_type=BandersnatchPoint, ) diff --git a/dot_ring/curve/specs/bandersnatch_sw.py b/dot_ring/curve/specs/bandersnatch_sw.py index c61b519..d0244fc 100644 --- a/dot_ring/curve/specs/bandersnatch_sw.py +++ b/dot_ring/curve/specs/bandersnatch_sw.py @@ -1,150 +1,89 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final, Literal, Self, cast +from typing import Literal, Self, cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve - - -@dataclass(frozen=True) -class BandersnatchSWParams: - SUITE_STRING = b"Bandersnatch-SW-SHA512-TAI-v1" - SUITE_ID = b"Bandersnatch-SW-SHA512-TAI-v1" - DST = b"" - - PRIME_FIELD: Final[int] = 52435875175126190479447740508185965837690552500527637822603658699938581184513 - ORDER: Final[int] = 0x1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E1 - COFACTOR: Final[int] = 4 - - WEIERSTRASS_A: Final[int] = 10773120815616481058602537765553212789256758185246796157495669123169359657269 - WEIERSTRASS_B: Final[int] = 29569587568322301171008055308580903175558631321415017492731745847794083609535 - - GENERATOR_X: Final[int] = 30900340493481298850216505686589334086208278925799850409469406976849338430199 - GENERATOR_Y: Final[int] = 12663882780877899054958035777720958383845500985908634476792678820121468453298 - - Z: Final[int] = -11 - M: Final[int] = 1 - L: Final[int] = 64 - K: Final[int] = 1 - S_in_bytes: Final[int] = 64 - H_A = hashlib.sha512 - ENDIAN = "little" - BBx: Final[int] = 28115362618644671219696075022370511395136332234538034358311199318506963235315 - BBy: Final[int] = 3900851469868158154936962463930962496000252801946757953905982128670530185313 - ACCUMULATOR_BASE_X: Final[int] = 13189182432637108534251278524663360416811744717379968387043749958796254980045 - ACCUMULATOR_BASE_Y: Final[int] = 14483286006782706188671626508232161325054303360192563232232823772738911894793 - PADDING_X: Final[int] = 20496180070424734470560955314776462366297546779079302509428101119888111900885 - PADDING_Y: Final[int] = 8839106592405352067483360946162273985142890146060814748321063063028225641813 - - CHALLENGE_LENGTH: Final[int] = 16 - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 33 - TRANSCRIPT_HASH = "sha512" - HASH_TO_CURVE = "tai" - - -Bandersnatch_SW_SW_Curve: Final[SWCurve] = SWCurve( - PRIME_FIELD=BandersnatchSWParams.PRIME_FIELD, - ORDER=BandersnatchSWParams.ORDER, - GENERATOR_X=BandersnatchSWParams.GENERATOR_X, - GENERATOR_Y=BandersnatchSWParams.GENERATOR_Y, - COFACTOR=BandersnatchSWParams.COFACTOR, - Z=BandersnatchSWParams.Z, - WeierstrassA=BandersnatchSWParams.WEIERSTRASS_A, - WeierstrassB=BandersnatchSWParams.WEIERSTRASS_B, - SUITE_STRING=BandersnatchSWParams.SUITE_STRING, - DST=BandersnatchSWParams.DST, - E2C=E2C_Variant.TAI, - BBx=BandersnatchSWParams.BBx, - BBy=BandersnatchSWParams.BBy, - M=BandersnatchSWParams.M, - K=BandersnatchSWParams.K, - L=BandersnatchSWParams.L, - S_in_bytes=BandersnatchSWParams.S_in_bytes, - H_A=BandersnatchSWParams.H_A, - Requires_Isogeny=BandersnatchSWParams.Requires_Isogeny, - Isogeny_Coeffs=BandersnatchSWParams.Isogeny_Coeffs, - UNCOMPRESSED=BandersnatchSWParams.UNCOMPRESSED, - ENDIAN=BandersnatchSWParams.ENDIAN, - POINT_LEN=BandersnatchSWParams.POINT_LEN, - CHALLENGE_LENGTH=BandersnatchSWParams.CHALLENGE_LENGTH, - SUITE_ID=BandersnatchSWParams.SUITE_ID, - TRANSCRIPT_HASH=BandersnatchSWParams.TRANSCRIPT_HASH, - HASH_TO_CURVE=BandersnatchSWParams.HASH_TO_CURVE, - ACCUMULATOR_BASE_X=BandersnatchSWParams.ACCUMULATOR_BASE_X, - ACCUMULATOR_BASE_Y=BandersnatchSWParams.ACCUMULATOR_BASE_Y, - PADDING_X=BandersnatchSWParams.PADDING_X, - PADDING_Y=BandersnatchSWParams.PADDING_Y, +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + ShortWeierstrassCurveParams, ) +BANDERSNATCH_SW_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=52435875175126190479447740508185965837690552500527637822603658699938581184513, + subgroup_order=0x1CFB69D4CA675F520CCE760202687600FF8F87007419047174FD06B52876E7E1, + cofactor=4, + suite_id=b"Bandersnatch-SW-SHA512-TAI-v1", + hash_fn=hashlib.sha512, + generator=( + 30900340493481298850216505686589334086208278925799850409469406976849338430199, + 12663882780877899054958035777720958383845500985908634476792678820121468453298, + ), + a=10773120815616481058602537765553212789256758185246796157495669123169359657269, + b=29569587568322301171008055308580903175558631321415017492731745847794083609535, + hash_to_curve=HashToCurveParams( + dst=b"", + z=-11, + field_extension_degree=1, + security_level=1, + field_length=64, + expand_len=64, + ), + encoding=EncodingParams(endian="little", point_len=33, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 28115362618644671219696075022370511395136332234538034358311199318506963235315, + 3900851469868158154936962463930962496000252801946757953905982128670530185313, + ), + accumulator_base=( + 13189182432637108534251278524663360416811744717379968387043749958796254980045, + 14483286006782706188671626508232161325054303360192563232232823772738911894793, + ), + padding_point=( + 20496180070424734470560955314776462366297546779079302509428101119888111900885, + 8839106592405352067483360946162273985142890146060814748321063063028225641813, + ), + ), +) -class Bandersnatch_SW_Point(SWAffinePoint): - curve: SWCurve = Bandersnatch_SW_SW_Curve - - @classmethod - def identity_point(cls) -> None: - return None - @classmethod - def _x_recover(cls, y: int) -> tuple[int, int]: - return SWAffinePoint._x_recover(y) +class BandersnatchSWPoint(SWAffinePoint): + """Point on Bandersnatch in short-Weierstrass form.""" def point_to_string(self, compressed: bool = False) -> bytes: - """ """ - - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus field_bit_len = p.bit_length() - flag_bit_len = 2 # SWFlags: YIsNegative + PointAtInfinity - total_bits = field_bit_len + flag_bit_len - - # Compute number of bytes to hold field + flags - output_byte_len = (total_bits + 7) // 8 # ceil(total_bits/8) + output_byte_len = (field_bit_len + 2 + 7) // 8 - # Handle point at infinity if self.x is None and self.y is None: - # field = 0, flags = 0b01 << 6 = 0x40 result = bytearray(output_byte_len) - result[-1] |= 1 << 6 # PointAtInfinity flag + result[-1] |= 1 << 6 return bytes(result) - # Determine flags - if self.y is None: + if self.x is None or self.y is None: raise ValueError("Cannot serialize identity point") - y_int = cast(int, self.y) - if y_int <= (-y_int % p): - flag = 0 # Y positive - else: - flag = 1 << 7 # Y negative - # Serialize x-coordinate - if self.x is None: - raise ValueError("Cannot serialize identity point") - x_bytes = int(cast(int, self.x)).to_bytes((field_bit_len + 7) // 8, cast(Literal["little", "big"], self.curve.ENDIAN)) + y_int = cast(int, self.y) + flag = 0 if y_int <= (-y_int % p) else 1 << 7 + x_bytes = int(cast(int, self.x)).to_bytes((field_bit_len + 7) // 8, cast(Literal["little", "big"], self.curve.encoding_endian())) - # Copy x_bytes into buffer of total length result = bytearray(output_byte_len) result[: len(x_bytes)] = x_bytes - - # Merge flag bits into last byte result[-1] |= flag - return bytes(result) @classmethod - def _y_recover(cls, x: int) -> tuple[int, int] | None: - p = cls.curve.PRIME_FIELD - A = cast(int, cls.curve.WeierstrassA) - B = cast(int, cls.curve.WeierstrassB) - y_square = (pow(x, 3, p) + A * x + B) % p + def _y_recover(cls, x: int, curve: SWCurve) -> tuple[int, int] | None: + p = curve.params.field_modulus + y_square = (pow(x, 3, p) + curve.params.a * x + curve.params.b) % p try: - y = cls.curve.mod_sqrt(y_square) + y = curve.mod_sqrt(y_square) except ValueError: return None @@ -156,19 +95,16 @@ def _y_recover(cls, x: int) -> tuple[int, int] | None: return neg_y, y @classmethod - def string_to_point(cls, data: str | bytes) -> Self: + def string_to_point(cls, data: str | bytes, curve: SWCurve) -> Self: if isinstance(data, str): data = bytes.fromhex(data) if len(data) == 0: raise ValueError("Empty octet string") - # Assuming the input `data` is still an octet string as per original logic - # and the `x_str`, `y_str`, `Helpers` part was a mis-paste or incomplete change. - # Reverting to original parsing logic but using `data` instead of `octet_string`. x_bytes = data[:-1] - x = int.from_bytes(x_bytes, cast(Literal["little", "big"], cls.curve.ENDIAN)) - y_candidates = cls._y_recover(x) + x = int.from_bytes(x_bytes, curve.encoding_endian()) + y_candidates = cls._y_recover(x, curve) if not y_candidates: raise ValueError("Invalid point: no y-coordinate found for x") y, y_neg = y_candidates @@ -184,58 +120,14 @@ def string_to_point(cls, data: str | bytes) -> Self: raise ValueError("Invalid infinity point: negative flag is set") raise ValueError("Invalid infinity point: not supported") - if is_negative: - try: - return cls(x, y_neg) - except ValueError: - raise ValueError("Invalid point") from None - else: - try: - return cls(x, y) - except ValueError: - raise ValueError("Invalid point") from None - - @classmethod # modified - def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Self: - """ - Encode a string to a curve point using try-and-increment method for ECVRF. - - Args: - alpha: String to encode - salt: Optional salt for the encoding - - Returns: - TEAffinePoint: Resulting curve point - """ - ctr = 0 - import hashlib - - front = b"\x01" - back = b"\x00" - alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string - salt = salt.encode() if isinstance(salt, str) else salt - suite_string = cls.curve.SUITE_STRING - - while True: - ctr_string = ctr.to_bytes(1, "big") - hash_input = suite_string + front + salt + alpha_string + ctr_string + back - hash_output = hashlib.sha512(hash_input).digest() - try: - point = cls.string_to_point(hash_output[:33]) - except ValueError: - ctr += 1 - continue - - if cls.curve.COFACTOR > 1: - point = cast(Self, point * cls.curve.COFACTOR) # type: ignore[operator] - - if point != cls.identity_point(): - return point - ctr += 1 + try: + return cls(x, y_neg if is_negative else y, curve) + except ValueError: + raise ValueError("Invalid point") from None Bandersnatch_SW = CurveVariant( name="Bandersnatch_SW", - curve=Bandersnatch_SW_SW_Curve, - point=Bandersnatch_SW_Point, + curve=SWCurve(params=BANDERSNATCH_SW_PARAMS, e2c_variant=E2C_Variant.TAI), + point_type=BandersnatchSWPoint, ) diff --git a/dot_ring/curve/specs/bls12_381_G1.py b/dot_ring/curve/specs/bls12_381_G1.py index c28221d..b9846d5 100644 --- a/dot_ring/curve/specs/bls12_381_G1.py +++ b/dot_ring/curve/specs/bls12_381_G1.py @@ -1,345 +1,132 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Any, Final, cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve +from .parameters import ( + EncodingParams, + HashToCurveParams, + RationalIsogeny, + ShortWeierstrassCurveParams, + ShortWeierstrassModel, +) -@dataclass(frozen=True) -class BLS12_381_G1Params: - """ - Parameters for the BLS12-381 G1 curve (Weierstrass form). - """ - - # Domain separation / hash-to-curve strings (RFC drafts / implementations) - SUITE_STRING: Final[bytes] = b"BLS12381G1_XMD:SHA-256_SSWU_RO_" - DST: Final[bytes] = b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_" # common default DST - - # Prime field p (F_q) for BLS12-381 - PRIME_FIELD: Final[int] = 0x1A0111EA397FE69A4B1BA7B6434BACD7_64774B84F38512BF6730D2A0F6B0F624_1EABFFFEB153FFFFB9FEFFFFFFFFAAAB - - # Order r of the prime-order subgroup (G1 and G2 share the same r) - ORDER: Final[int] = 0x73EDA753299D7D483339D80809A1D805_53BDA402FFFE5BFEFFFFFFFF00000001 - - # Cofactor for G1 (h1) - COFACTOR: Final[int] = 0xD201000000010001 - - # Generator point (affine coordinates) for G1 (from standard definitions) - GENERATOR_X: Final[int] = 0x17F1D3A73197D7942695638C4FA9AC0F_C3688C4F9774B905A14E3A3F171BAC58_6C55E83FF97A1AEFFB3AF00ADB22C6BB - GENERATOR_Y: Final[int] = 0x08B3F481E3AAA0F1A09E30ED741D8AE4_FCF5E095D5D00AF600DB18CB2C04B3ED_D03CC744A2888AE40CAA232946C5E7E1 - - # Short Weierstrass parameters for y^2 = x^3 + a*x + b - WEIERSTRASS_A: Final[int] = 0x00 - WEIERSTRASS_B: Final[int] = 0x04 - - Z: Final[int] = 11 - - # Field/encoding sizes - M: Final[int] = 1 # extension degree for G1 (over Fp) - K: Final[int] = 128 # approximate security level in bits - L: Final[int] = 64 # field element encoded length in bytes (381 bits -> 48 bytes) - S_IN_BYTES: Final[int] = 64 - - # Hash used by default in hash-to-curve suites for this curve - H_A = hashlib.sha256 - ENDIAN = "little" - - # Challenge length for VRF / challenge bytes (48 bytes is typical for 381-bit) - CHALLENGE_LENGTH: Final[int] = 48 - - # Flags / optional fields - Requires_Isogeny: Final[bool] = True - BBx: Final[int | None] = None - BBy: Final[int | None] = None - Isogeny_Coeffs: Final[object | None] = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - - -class BLS12_381_G1Curve(SWCurve): - """ - BLS12-381 G1 curve implementation (short Weierstrass form). - - This class wraps the curve parameters and passes them to the SWCurve base - implementation used in this codebase. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - SUITE_STRING = BLS12_381_G1Params.SUITE_STRING - DST = BLS12_381_G1Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - super().__init__( - PRIME_FIELD=BLS12_381_G1Params.PRIME_FIELD, - ORDER=BLS12_381_G1Params.ORDER, - GENERATOR_X=BLS12_381_G1Params.GENERATOR_X, - GENERATOR_Y=BLS12_381_G1Params.GENERATOR_Y, - COFACTOR=BLS12_381_G1Params.COFACTOR, - Z=BLS12_381_G1Params.Z, - WeierstrassA=BLS12_381_G1Params.WEIERSTRASS_A, - WeierstrassB=BLS12_381_G1Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=BLS12_381_G1Params.BBx, - BBy=BLS12_381_G1Params.BBy, - M=BLS12_381_G1Params.M, - K=BLS12_381_G1Params.K, - L=BLS12_381_G1Params.L, - S_in_bytes=BLS12_381_G1Params.S_IN_BYTES, - H_A=BLS12_381_G1Params.H_A, - Requires_Isogeny=BLS12_381_G1Params.Requires_Isogeny, - Isogeny_Coeffs=cast(dict[str, list[int]] | None, BLS12_381_G1Params.Isogeny_Coeffs), - UNCOMPRESSED=BLS12_381_G1Params.UNCOMPRESSED, - ENDIAN=BLS12_381_G1Params.ENDIAN, - POINT_LEN=BLS12_381_G1Params.POINT_LEN, - CHALLENGE_LENGTH=BLS12_381_G1Params.CHALLENGE_LENGTH, - ) - - -# Singleton instance for convenience -BLS12_381_G1_SW_Curve: Final[BLS12_381_G1Curve] = BLS12_381_G1Curve() - - -def nu_variant( - e2c_variant: E2C_Variant = E2C_Variant.SSWU_NU, -) -> type[BLS12_381_G1Point]: - class BLS12_381_G1PointVariant(BLS12_381_G1Point): - """Point on BLS12_381_G1 with custom E2C variant""" - - curve: SWCurve = BLS12_381_G1Curve(e2c_variant) - - return BLS12_381_G1PointVariant - - -class BLS12_381_G1Point(SWAffinePoint): - """ - Affine point on BLS12-381 G1. - """ - - curve: SWCurve = BLS12_381_G1_SW_Curve - - @classmethod - def map_to_curve_simple_swu(cls, u: int) -> BLS12_381_G1Point | Any: - """Implements simplified SWU mapping""" - # 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) - # 2. x1 = (-B / A) * (1 + tv1) - # 3. If tv1 == 0, set x1 = B / (Z * A) - # 4. gx1 = x1^3 + A * x1 + B - # 5. x2 = Z * u^2 * x1 - # 6. gx2 = x2^3 + A * x2 + B - # 7. If is_square(gx1), set x = x1 and y = sqrt(gx1) - # 8. Else set x = x2 and y = sqrt(gx2) - # 9. If sgn0(u) != sgn0(y), set y = -y - # 10. return (x, y) - Z = cast(int, cls.curve.Z) - A = cast(int, cls.curve.WeierstrassA) - B = cast(int, cls.curve.WeierstrassB) - p = cls.curve.PRIME_FIELD - - if cls.curve.Requires_Isogeny: # E' vals, used only for secp256k1 as its A=0 - A = 0x144698A3B8E9433D693A02C96D4982B0EA985383EE66A8D8E8981AEFD881AC98936F8DA0E0F97F5CF428082D584C1D - B = 0x12E2908D11688030018B12E8753EEE3B2016C1F0F24F4070A0B9C14FCEF35EF55A23215A316CEAA5D1CC48E98E172BE0 - - # 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) - u_sq = (u * u) % p - tv1 = (Z * Z * ((u_sq * u_sq) % p) + Z * u_sq) % p - - # Handle special case when tv1 is 0 - if tv1 == 0: - # 3. If tv1 == 0, set x1 = B / (Z * A) - x1 = (B * cls.curve.inv((Z * A) % p)) % p - else: - # 2. x1 = (-B / A) * (1 + tv1) - tv1 = cls.curve.inv(tv1) - x1 = (-B * cls.curve.inv(A)) % p - x1 = (x1 * (1 + tv1)) % p - - # 4. gx1 = x1^3 + A * x1 + B - gx1 = (pow(x1, 3, p) + (A * x1) % p + B) % p - - # 5. x2 = Z * u^2 * x1 - x2 = (Z * u_sq % p) * x1 % p - - # 6. gx2 = x2^3 + A * x2 + B - gx2 = (pow(x2, 3, p) + (A * x2) % p + B) % p - - # 7-8. Find a valid x and y - x, y = x1, None - if cls.curve.is_square(gx1): - y = cls.curve.mod_sqrt(gx1) - else: - x = x2 - y = cls.curve.mod_sqrt(gx2) - - # 9. Fix sign of y - if cls.curve.sgn0(u) != cls.curve.sgn0(y): - y = (-y) % p - - params = { - "k_1": [ - "0x11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7", - "0x17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb", - "0xd54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0", - "0x1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861", - "0xe99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9", - "0x1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983", - "0xd6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84", - "0x17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e", - "0x80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317", - "0x169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e", - "0x10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b", - "0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229", - ], - "k_2": [ - "0x8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c", - "0x12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff", - "0xb2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19", - "0x3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8", - "0x13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e", - "0xe7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5", - "0x772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a", - "0x14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e", - "0xa10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641", - "0x95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a", - ], - "k_3": [ - "0x90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33", - "0x134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696", - "0xcc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6", - "0x1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb", - "0x8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb", - "0x16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0", - "0x4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2", - "0x987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29", - "0x9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587", - "0xe1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30", - "0x19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132", - "0x18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e", - "0xb182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8", - "0x245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133", - "0x5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b", - "0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604", - ], - "k_4": [ - "0x16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1", - "0x1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d", - "0x58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2", - "0x16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416", - "0xbe0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d", - "0x8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac", - "0x166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c", - "0x16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9", - "0x1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a", - "0x167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55", - "0x4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8", - "0xaccbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092", - "0xad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc", - "0x2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7", - "0xe0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f", - ], - } - - if cls.curve.Requires_Isogeny: - k1 = [int(c, 16) for c in params["k_1"][::-1]] # k_(1,11) .. k_(1,0) - k2 = [int(c, 16) for c in params["k_2"][::-1]] # k_(2,9) .. k_(2,0) - k3 = [int(c, 16) for c in params["k_3"][::-1]] # k_(3,15) .. k_(3,0) - k4 = [int(c, 16) for c in params["k_4"][::-1]] # k_(4,14) .. k_(4,0) - xp, yp = x, y - - # Compute x numerator - x_num = ( - k1[0] * xp**11 - + k1[1] * xp**10 - + k1[2] * xp**9 - + k1[3] * xp**8 - + k1[4] * xp**7 - + k1[5] * xp**6 - + k1[6] * xp**5 - + k1[7] * xp**4 - + k1[8] * xp**3 - + k1[9] * xp**2 - + k1[10] * xp - + k1[11] - ) % p - - # Compute x denominator - x_den = ( - xp**10 - + k2[0] * xp**9 - + k2[1] * xp**8 - + k2[2] * xp**7 - + k2[3] * xp**6 - + k2[4] * xp**5 - + k2[5] * xp**4 - + k2[6] * xp**3 - + k2[7] * xp**2 - + k2[8] * xp - + k2[9] - ) % p - - x = (x_num * pow(x_den, -1, p)) % p # multiply by modular inverse - - # Compute y numerator - y_num = ( - k3[0] * xp**15 - + k3[1] * xp**14 - + k3[2] * xp**13 - + k3[3] * xp**12 - + k3[4] * xp**11 - + k3[5] * xp**10 - + k3[6] * xp**9 - + k3[7] * xp**8 - + k3[8] * xp**7 - + k3[9] * xp**6 - + k3[10] * xp**5 - + k3[11] * xp**4 - + k3[12] * xp**3 - + k3[13] * xp**2 - + k3[14] * xp - + k3[15] - ) % p +def _descending_hex(*coefficients: str) -> tuple[int, ...]: + return tuple(int(coefficient, 16) for coefficient in reversed(coefficients)) + + +BLS12_381_G1_ISOGENY = RationalIsogeny[int]( + map_curve=ShortWeierstrassModel( + a=0x144698A3B8E9433D693A02C96D4982B0EA985383EE66A8D8E8981AEFD881AC98936F8DA0E0F97F5CF428082D584C1D, + b=0x12E2908D11688030018B12E8753EEE3B2016C1F0F24F4070A0B9C14FCEF35EF55A23215A316CEAA5D1CC48E98E172BE0, + ), + x_numerator=_descending_hex( + "0x11a05f2b1e833340b809101dd99815856b303e88a2d7005ff2627b56cdb4e2c85610c2d5f2e62d6eaeac1662734649b7", + "0x17294ed3e943ab2f0588bab22147a81c7c17e75b2f6a8417f565e33c70d1e86b4838f2a6f318c356e834eef1b3cb83bb", + "0xd54005db97678ec1d1048c5d10a9a1bce032473295983e56878e501ec68e25c958c3e3d2a09729fe0179f9dac9edcb0", + "0x1778e7166fcc6db74e0609d307e55412d7f5e4656a8dbf25f1b33289f1b330835336e25ce3107193c5b388641d9b6861", + "0xe99726a3199f4436642b4b3e4118e5499db995a1257fb3f086eeb65982fac18985a286f301e77c451154ce9ac8895d9", + "0x1630c3250d7313ff01d1201bf7a74ab5db3cb17dd952799b9ed3ab9097e68f90a0870d2dcae73d19cd13c1c66f652983", + "0xd6ed6553fe44d296a3726c38ae652bfb11586264f0f8ce19008e218f9c86b2a8da25128c1052ecaddd7f225a139ed84", + "0x17b81e7701abdbe2e8743884d1117e53356de5ab275b4db1a682c62ef0f2753339b7c8f8c8f475af9ccb5618e3f0c88e", + "0x80d3cf1f9a78fc47b90b33563be990dc43b756ce79f5574a2c596c928c5d1de4fa295f296b74e956d71986a8497e317", + "0x169b1f8e1bcfa7c42e0c37515d138f22dd2ecb803a0c5c99676314baf4bb1b7fa3190b2edc0327797f241067be390c9e", + "0x10321da079ce07e272d8ec09d2565b0dfa7dccdde6787f96d50af36003b14866f69b771f8c285decca67df3f1605fb7b", + "0x6e08c248e260e70bd1e962381edee3d31d79d7e22c837bc23c0bf1bc24c6b68c24b1b80b64d391fa9c8ba2e8ba2d229", + ), + x_denominator=( + 1, + *_descending_hex( + "0x8ca8d548cff19ae18b2e62f4bd3fa6f01d5ef4ba35b48ba9c9588617fc8ac62b558d681be343df8993cf9fa40d21b1c", + "0x12561a5deb559c4348b4711298e536367041e8ca0cf0800c0126c2588c48bf5713daa8846cb026e9e5c8276ec82b3bff", + "0xb2962fe57a3225e8137e629bff2991f6f89416f5a718cd1fca64e00b11aceacd6a3d0967c94fedcfcc239ba5cb83e19", + "0x3425581a58ae2fec83aafef7c40eb545b08243f16b1655154cca8abc28d6fd04976d5243eecf5c4130de8938dc62cd8", + "0x13a8e162022914a80a6f1d5f43e7a07dffdfc759a12062bb8d6b44e833b306da9bd29ba81f35781d539d395b3532a21e", + "0xe7355f8e4e667b955390f7f0506c6e9395735e9ce9cad4d0a43bcef24b8982f7400d24bc4228f11c02df9a29f6304a5", + "0x772caacf16936190f3e0c63e0596721570f5799af53a1894e2e073062aede9cea73b3538f0de06cec2574496ee84a3a", + "0x14a7ac2a9d64a8b230b3f5b074cf01996e7f63c21bca68a81996e1cdf9822c580fa5b9489d11e2d311f7d99bbdcc5a5e", + "0xa10ecf6ada54f825e920b3dafc7a3cce07f8d1d7161366b74100da67f39883503826692abba43704776ec3a79a1d641", + "0x95fc13ab9e92ad4476d6e3eb3a56680f682b4ee96f7d03776df533978f31c1593174e4b4b7865002d6384d168ecdd0a", + ), + ), + y_numerator=_descending_hex( + "0x90d97c81ba24ee0259d1f094980dcfa11ad138e48a869522b52af6c956543d3cd0c7aee9b3ba3c2be9845719707bb33", + "0x134996a104ee5811d51036d776fb46831223e96c254f383d0f906343eb67ad34d6c56711962fa8bfe097e75a2e41c696", + "0xcc786baa966e66f4a384c86a3b49942552e2d658a31ce2c344be4b91400da7d26d521628b00523b8dfe240c72de1f6", + "0x1f86376e8981c217898751ad8746757d42aa7b90eeb791c09e4a3ec03251cf9de405aba9ec61deca6355c77b0e5f4cb", + "0x8cc03fdefe0ff135caf4fe2a21529c4195536fbe3ce50b879833fd221351adc2ee7f8dc099040a841b6daecf2e8fedb", + "0x16603fca40634b6a2211e11db8f0a6a074a7d0d4afadb7bd76505c3d3ad5544e203f6326c95a807299b23ab13633a5f0", + "0x4ab0b9bcfac1bbcb2c977d027796b3ce75bb8ca2be184cb5231413c4d634f3747a87ac2460f415ec961f8855fe9d6f2", + "0x987c8d5333ab86fde9926bd2ca6c674170a05bfe3bdd81ffd038da6c26c842642f64550fedfe935a15e4ca31870fb29", + "0x9fc4018bd96684be88c9e221e4da1bb8f3abd16679dc26c1e8b6e6a1f20cabe69d65201c78607a360370e577bdba587", + "0xe1bba7a1186bdb5223abde7ada14a23c42a0ca7915af6fe06985e7ed1e4d43b9b3f7055dd4eba6f2bafaaebca731c30", + "0x19713e47937cd1be0dfd0b8f1d43fb93cd2fcbcb6caf493fd1183e416389e61031bf3a5cce3fbafce813711ad011c132", + "0x18b46a908f36f6deb918c143fed2edcc523559b8aaf0c2462e6bfe7f911f643249d9cdf41b44d606ce07c8a4d0074d8e", + "0xb182cac101b9399d155096004f53f447aa7b12a3426b08ec02710e807b4633f06c851c1919211f20d4c04f00b971ef8", + "0x245a394ad1eca9b72fc00ae7be315dc757b3b080d4c158013e6632d3c40659cc6cf90ad1c232a6442d9d3f5db980133", + "0x5c129645e44cf1102a159f748c4a3fc5e673d81d7e86568d9ab0f5d396a7ce46ba1049b6579afb7866b1e715475224b", + "0x15e6be4e990f03ce4ea50b3b42df2eb5cb181d8f84965a3957add4fa95af01b2b665027efec01c7704b456be69c8b604", + ), + y_denominator=( + 1, + *_descending_hex( + "0x16112c4c3a9c98b252181140fad0eae9601a6de578980be6eec3232b5be72e7a07f3688ef60c206d01479253b03663c1", + "0x1962d75c2381201e1a0cbd6c43c348b885c84ff731c4d59ca4a10356f453e01f78a4260763529e3532f6102c2e49a03d", + "0x58df3306640da276faaae7d6e8eb15778c4855551ae7f310c35a5dd279cd2eca6757cd636f96f891e2538b53dbf67f2", + "0x16b7d288798e5395f20d23bf89edb4d1d115c5dbddbcd30e123da489e726af41727364f2c28297ada8d26d98445f5416", + "0xbe0e079545f43e4b00cc912f8228ddcc6d19c9f0f69bbb0542eda0fc9dec916a20b15dc0fd2ededda39142311a5001d", + "0x8d9e5297186db2d9fb266eaac783182b70152c65550d881c5ecd87b6f0f5a6449f38db9dfa9cce202c6477faaf9b7ac", + "0x166007c08a99db2fc3ba8734ace9824b5eecfdfa8d0cf8ef5dd365bc400a0051d5fa9c01a58b1fb93d1a1399126a775c", + "0x16a3ef08be3ea7ea03bcddfabba6ff6ee5a4375efa1f4fd7feb34fd206357132b920f5b00801dee460ee415a15812ed9", + "0x1866c8ed336c61231a1be54fd1d74cc4f9fb0ce4c6af5920abc5750c4bf39b4852cfe2f7bb9248836b233d9d55535d4a", + "0x167a55cda70a6e1cea820597d94a84903216f763e13d87bb5308592e7ea7d4fbc7385ea3d529b35e346ef48bb8913f55", + "0x4d2f259eea405bd48f010a01ad2911d9c6dd039bb61a6290e591b36e636a5c871a5c29f4f83060400f8b49cba8f6aa8", + "0xaccbb67481d033ff5852c1e48c50c477f94ff8aefce42d28c0f9a88cea7913516f968986f7ebbea9684b529e2561092", + "0xad6b9514c767fe3c3613144b45f1496543346d98adf02267d5ceef9a00d9b8693000763e3b90ac11e99b138573345cc", + "0x2660400eb2e4f3b628bdd0d53cd76f2bf565b94e72927c1cb748df27942480e420517bd8714cc80d1fadc1326ed06f7", + "0xe0fa1d816ddc03e6b24255e0d7819c171c40f65e273b853324efcd6356caa205ca2f570f13497804415473a1d634b8f", + ), + ), +) - # Compute y denominator - y_den = ( - xp**15 - + k4[0] * xp**14 - + k4[1] * xp**13 - + k4[2] * xp**12 - + k4[3] * xp**11 - + k4[4] * xp**10 - + k4[5] * xp**9 - + k4[6] * xp**8 - + k4[7] * xp**7 - + k4[8] * xp**6 - + k4[9] * xp**5 - + k4[10] * xp**4 - + k4[11] * xp**3 - + k4[12] * xp**2 - + k4[13] * xp - + k4[14] - ) % p - y = (yp * y_num * pow(y_den, -1, p)) % p - return cls(x=x, y=y) +BLS12_381_G1_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=0x1A0111EA397FE69A4B1BA7B6434BACD7_64774B84F38512BF6730D2A0F6B0F624_1EABFFFEB153FFFFB9FEFFFFFFFFAAAB, + subgroup_order=0x73EDA753299D7D483339D80809A1D805_53BDA402FFFE5BFEFFFFFFFF00000001, + cofactor=0xD201000000010001, + suite_id=b"BLS12381G1_XMD:SHA-256_SSWU_RO_", + hash_fn=hashlib.sha256, + generator=( + 0x17F1D3A73197D7942695638C4FA9AC0F_C3688C4F9774B905A14E3A3F171BAC58_6C55E83FF97A1AEFFB3AF00ADB22C6BB, + 0x08B3F481E3AAA0F1A09E30ED741D8AE4_FCF5E095D5D00AF600DB18CB2C04B3ED_D03CC744A2888AE40CAA232946C5E7E1, + ), + a=0, + b=4, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-BLS12381G1_XMD:SHA-256_SSWU_RO_", + z=11, + field_extension_degree=1, + security_level=128, + field_length=64, + expand_len=64, + isogeny=BLS12_381_G1_ISOGENY, + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=48), +) BLS12_381_G1_NU = CurveVariant( name="BLS12_381_G1_NU", - curve=BLS12_381_G1Curve(e2c_variant=E2C_Variant.SSWU_NU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU_NU), + curve=SWCurve(params=BLS12_381_G1_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=SWAffinePoint, ) BLS12_381_G1_RO = CurveVariant( name="BLS12_381_G1_RO", - curve=BLS12_381_G1Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=BLS12_381_G1_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=SWAffinePoint, ) diff --git a/dot_ring/curve/specs/bls12_381_G2.py b/dot_ring/curve/specs/bls12_381_G2.py index b7973d1..a25489c 100644 --- a/dot_ring/curve/specs/bls12_381_G2.py +++ b/dot_ring/curve/specs/bls12_381_G2.py @@ -1,156 +1,196 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Any, Final, Self, cast +from typing import Self, cast from py_ecc.bls12_381 import FQ2, add, multiply from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant +from dot_ring.curve.fp2 import Fp2 +from dot_ring.curve.point import CurvePoint -from ..field_element import FieldElement -from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve +from .parameters import ( + EncodingParams, + HashToCurveParams, + RationalIsogeny, + ShortWeierstrassCurveParams, + ShortWeierstrassModel, +) -Fp2 = tuple[int, int] +BLS12_381_G2_FIELD_MODULUS = 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB -@dataclass(frozen=True) -class BLS12_381_G2Params: - """ - BLS12-381 G2 curve parameters. +def _fp2(re: int, im: int) -> Fp2: + return Fp2(re, im, BLS12_381_G2_FIELD_MODULUS) - The BLS12-381 curve is a pairing-friendly curve that is part of the BLS family. - This implementation follows RFC 9380 Section 8.8.2 for hash-to-curve operations. - """ - # From RFC 9380 Section 8.8.2: BLS12-381 G2 - SUITE_STRING = b"BLS12381G2_XMD:SHA-256_SSWU_RO_" - DST = b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_" # Use the suite string as DST by default per RFC 9380 - # Base field characteristic (modulus) - PRIME_FIELD: Final[int] = 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAAAB - - # Subgroup order (r) - ORDER: Final[int] = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 - - # Cofactor (h) - COFACTOR: Final[int] = ( - 0xBC69F08F2EE75B3584C6A0EA91B352888E2A8E9145AD7689986FF031508FFE1329C2F178731DB956D82BF015D1212B02EC0EC69D7477C1AE954CBC06689F6A359894C0ADEBBF6B4E8020005AAA95551 # noqa: E501 - ) - - # Generator point (G2) - GENERATOR_X: Final[Fp2] = ( - 0x024AA2B2F08F0A91260805272DC51051C6E47AD4FA403B02B4510B647AE3D1770BAC0326A805BBEFD48056C8C121BDB8, # noqa: E501 - 0x13E02B6052719F607DACD3A088274F65596BD0D09920B61AB5DA61BBDC7F5049334CF11213945D57E5AC7D055D042B7E, # noqa: E501 - ) - GENERATOR_Y: Final[Fp2] = ( - 0x0CE5D527727D6E118CC9CDC6DA2E351AADFD9BAA8CBDD3A76D429A695160D12C923AC9CC3BACA289E193548608B82801, # noqa: E501 - 0x0606C4A02EA734CC32ACD2B02BC28B99CB3E287E85A763AF267492AB572E99AB3F370D275CEC1DA1AAA9075FF05F79BE, # noqa: E501 - ) - - # Curve equation: y² = x³ + 4(1 + i) - WEIERSTRASS_A: Final[Fp2] = (0, 0) - WEIERSTRASS_B: Final[Fp2] = (4, 4) # 4 * (1 + i) - - # Z parameter for SSWU mapping (from RFC 9380 Section 8.8.2) - Z: Final[Fp2] = (-2, -1) # 1 + i - - # Security parameters - M: Final[int] = 2 # Extension field degree (Fp2) - K: Final[int] = 128 # Security level in bits - L: Final[int] = 64 # Number of bytes for XMD - S_in_bytes: Final[int] = 64 # Length of domain separation tag - H_A = hashlib.sha256 - ENDIAN = "little" - # VRF parameters - CHALLENGE_LENGTH: Final[int] = 32 # 256-bit challenge - - # Isogeny parameters - Requires_Isogeny: Final[bool] = True - Isogeny_Coeffs = None - - # Blinding base for Pedersen VRF (not used in basic implementation) - BBx: Final[Fp2 | None] = None - BBy: Final[Fp2 | None] = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - - -class BLS12_381_G2Curve(SWCurve): - """ - BLS12-381 G2 curve implementation. +def _fp2_values(values: tuple[tuple[int, int], ...]) -> tuple[Fp2, ...]: + return tuple(_fp2(re, im) for re, im in values) - This curve is defined over a quadratic extension field Fp2 and is primarily - used for cryptographic pairings in the BLS signature scheme. - """ - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - SUITE_STRING = BLS12_381_G2Params.SUITE_STRING - DST = BLS12_381_G2Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - super().__init__( - PRIME_FIELD=BLS12_381_G2Params.PRIME_FIELD, - ORDER=BLS12_381_G2Params.ORDER, - GENERATOR_X=BLS12_381_G2Params.GENERATOR_X, - GENERATOR_Y=BLS12_381_G2Params.GENERATOR_Y, - COFACTOR=BLS12_381_G2Params.COFACTOR, - Z=BLS12_381_G2Params.Z, - WeierstrassA=BLS12_381_G2Params.WEIERSTRASS_A, - WeierstrassB=BLS12_381_G2Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=BLS12_381_G2Params.BBx, - BBy=BLS12_381_G2Params.BBy, - M=BLS12_381_G2Params.M, - K=BLS12_381_G2Params.K, - L=BLS12_381_G2Params.L, - S_in_bytes=BLS12_381_G2Params.S_in_bytes, - H_A=BLS12_381_G2Params.H_A, - Requires_Isogeny=BLS12_381_G2Params.Requires_Isogeny, - Isogeny_Coeffs=BLS12_381_G2Params.Isogeny_Coeffs, - UNCOMPRESSED=BLS12_381_G2Params.UNCOMPRESSED, - ENDIAN=BLS12_381_G2Params.ENDIAN, - POINT_LEN=BLS12_381_G2Params.POINT_LEN, - CHALLENGE_LENGTH=BLS12_381_G2Params.CHALLENGE_LENGTH, - ) +BLS12_381_G2_ISOGENY = RationalIsogeny[Fp2]( + map_curve=ShortWeierstrassModel(a=_fp2(0, 240), b=_fp2(1012, 1012)), + x_numerator=_fp2_values(( + ( + 0x171D6541FA38CCFAED6DEA691F5FB614CB14B4E7F4E810AA22D6108F142B85757098E38D0F671C7188E2AAAAAAAA5ED1, + 0, + ), + ( + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71E, + 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38D, + ), + ( + 0, + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71A, + ), + ( + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, + ), + )), + x_denominator=_fp2_values(( + (1, 0), + ( + 0xC, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA9F, + ), + ( + 0, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA63, + ), + )), + y_numerator=_fp2_values(( + ( + 0x124C9AD43B6CF79BFBF7043DE3811AD0761B0F37A1E26286B0E977C69AA274524E79097A56DC4BD9E1B371C71C718B10, + 0, + ), + ( + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71C, + 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38F, + ), + ( + 0, + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97BE, + ), + ( + 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, + 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, + ), + )), + y_denominator=_fp2_values(( + (1, 0), + ( + 0x12, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA99, + ), + ( + 0, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA9D3, + ), + ( + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, + ), + )), +) + +BLS12_381_G2_PARAMS = ShortWeierstrassCurveParams[Fp2]( + field_modulus=BLS12_381_G2_FIELD_MODULUS, + subgroup_order=0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001, + cofactor=0xBC69F08F2EE75B3584C6A0EA91B352888E2A8E9145AD7689986FF031508FFE1329C2F178731DB956D82BF015D1212B02EC0EC69D7477C1AE954CBC06689F6A359894C0ADEBBF6B4E8020005AAA95551, + suite_id=b"BLS12381G2_XMD:SHA-256_SSWU_RO_", + hash_fn=hashlib.sha256, + generator=( + _fp2( + 0x024AA2B2F08F0A91260805272DC51051C6E47AD4FA403B02B4510B647AE3D1770BAC0326A805BBEFD48056C8C121BDB8, + 0x13E02B6052719F607DACD3A088274F65596BD0D09920B61AB5DA61BBDC7F5049334CF11213945D57E5AC7D055D042B7E, + ), + _fp2( + 0x0CE5D527727D6E118CC9CDC6DA2E351AADFD9BAA8CBDD3A76D429A695160D12C923AC9CC3BACA289E193548608B82801, + 0x0606C4A02EA734CC32ACD2B02BC28B99CB3E287E85A763AF267492AB572E99AB3F370D275CEC1DA1AAA9075FF05F79BE, + ), + ), + a=_fp2(0, 0), + b=_fp2(4, 4), + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-BLS12381G2_XMD:SHA-256_SSWU_RO_", + z=_fp2(-2, -1), + field_extension_degree=2, + security_level=128, + field_length=64, + expand_len=64, + isogeny=BLS12_381_G2_ISOGENY, + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=32), +) -# Singleton instance for convenience -BLS12_381_G2_SW_Curve: Final[BLS12_381_G2Curve] = BLS12_381_G2Curve() +class BLS12_381_G2Point(CurvePoint[SWCurve[Fp2], Fp2]): + """ + Point on the BLS12-381 G2 curve. -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> type[BLS12_381_G2Point]: - # Create curve with the specified variant - curve = BLS12_381_G2Curve(e2c_variant) + Implements point operations specific to the BLS12-381 G2 curve. + """ - # Create and return a point class with this curve - class BLS12_381_G2PointVariant(BLS12_381_G2Point): - """Point on BLS12_381_G1 with custom E2C variant""" + def __init__(self, x: Fp2 | tuple[int, int] | None, y: Fp2 | tuple[int, int] | None, curve: SWCurve[Fp2]) -> None: + super().__init__(self._coord(x, curve), self._coord(y, curve), curve) - def __init__(self, x: int, y: int) -> None: - """Initialize a point with the variant curve.""" - SWAffinePoint.__init__(self, x, y, curve) + @staticmethod + def _coord(value: Fp2 | tuple[int, int] | None, curve: SWCurve[Fp2]) -> Fp2 | None: + if value is None: + return None + if isinstance(value, Fp2): + if value.p != curve.params.field_modulus: + raise ValueError("Fp2 coordinate uses the wrong field") + return value + if isinstance(value, tuple) and len(value) == 2: + return Fp2(value[0], value[1], curve.params.field_modulus) + raise TypeError("BLS12-381 G2 coordinates must be Fp2 values") + + def _py_ecc_point(self) -> tuple[FQ2, FQ2]: + if self.is_identity() or self.x is None or self.y is None: + raise ValueError("Cannot convert identity point to py_ecc affine point") + return self.x.to_fq2(), self.y.to_fq2() - # Set the curve as a class attribute - BLS12_381_G2PointVariant.curve = curve + @classmethod + def _from_py_ecc_point(cls, point: tuple[FQ2, FQ2] | None, curve: SWCurve[Fp2]) -> BLS12_381_G2Point: + if point is None: + return cls.identity(curve) + return cls(Fp2.from_fq2(point[0], curve.params.field_modulus), Fp2.from_fq2(point[1], curve.params.field_modulus), curve) + + def _validate_coordinates(self) -> bool: + if self.x is None and self.y is None: + return True + if self.x is None or self.y is None: + return False + return self.x.p == self.curve.params.field_modulus and self.y.p == self.curve.params.field_modulus + + def is_on_curve(self) -> bool: + if self.is_identity(): + return True + if self.x is None or self.y is None: + return False + return self.curve.is_on_curve((self.x, self.y)) - return BLS12_381_G2PointVariant + def is_identity(self) -> bool: + return self.x is None and self.y is None + @classmethod + def identity(cls, curve: SWCurve[Fp2]) -> BLS12_381_G2Point: + return cls(None, None, curve) -class BLS12_381_G2Point(SWAffinePoint): - """ - Point on the BLS12-381 G2 curve. + def clear_cofactor(self) -> BLS12_381_G2Point: + return self * self.curve.params.cofactor - Implements point operations specific to the BLS12-381 G2 curve. - """ + def point_to_string(self) -> bytes: + raise NotImplementedError("BLS12-381 G2 point serialization is not implemented") - curve: BLS12_381_G2Curve = BLS12_381_G2_SW_Curve + @classmethod + def string_to_point(cls, data: str | bytes, curve: SWCurve[Fp2]) -> BLS12_381_G2Point: + raise NotImplementedError("BLS12-381 G2 point deserialization is not implemented") def __add__(self, other: BLS12_381_G2Point) -> BLS12_381_G2Point: # type: ignore[override] """ @@ -171,43 +211,7 @@ def __add__(self, other: BLS12_381_G2Point) -> BLS12_381_G2Point: # type: ignor if other.is_identity(): return self - # Helper function to convert coordinates to FQ2 format - def to_fq2( - x: int | tuple[int, int] | FieldElement, - y: int | tuple[int, int] | FieldElement, - ) -> tuple[FQ2, FQ2]: - # If x is a tuple, use it directly - if isinstance(x, tuple) and isinstance(y, tuple): - return (FQ2([x[0], x[1]]), FQ2([y[0], y[1]])) - # If x is a FieldElement, use its re and im attributes - elif hasattr(x, "re") and hasattr(x, "im"): - x = cast(FieldElement, x) - y = cast(FieldElement, y) - return (FQ2([x.re, x.im]), FQ2([y.re, y.im])) - # Fallback for other cases (shouldn't happen) - else: - return (FQ2([cast(int, x), 0]), FQ2([cast(int, y), 0])) - - # Convert points to FQ2 format for py_ecc - p1 = to_fq2( - cast(int | tuple[int, int] | FieldElement, self.x), - cast(int | tuple[int, int] | FieldElement, self.y), - ) - p2 = to_fq2( - cast(int | tuple[int, int] | FieldElement, other.x), - cast(int | tuple[int, int] | FieldElement, other.y), - ) - - # Perform addition using py_ecc - result = add(p1, p2) - if result is None: - return self.identity() - - # Convert back to tuple format - x = (int(result[0].coeffs[0]), int(result[0].coeffs[1])) - y = (int(result[1].coeffs[0]), int(result[1].coeffs[1])) - - return self.__class__(x, y) + return self._from_py_ecc_point(add(self._py_ecc_point(), other._py_ecc_point()), self.curve) def __neg__(self) -> BLS12_381_G2Point: """ @@ -220,25 +224,9 @@ def __neg__(self) -> BLS12_381_G2Point: if self.is_identity(): return self - # Use FieldElement's negation if y is a FieldElement - if hasattr(self.y, "__neg__"): - neg_y = -self.y # type: ignore[operator] - # Handle tuple case (Fp2 elements) - elif isinstance(self.y, tuple): - from ..field_element import FieldElement - - p = self.curve.PRIME_FIELD - # Convert tuple to FieldElement, negate, then convert back - y_fe = FieldElement(self.y[0], self.y[1], p) - neg_y_fe = -y_fe - neg_y = (neg_y_fe.re, neg_y_fe.im) - else: - # Fallback for regular integers - if self.y is None: - raise ValueError("Cannot negate identity point") - neg_y = (-self.y) % self.curve.PRIME_FIELD - - return self.__class__(self.x, neg_y) + if self.x is None or self.y is None: + raise ValueError("Invalid G2 point coordinate") + return self.__class__(self.x, -self.y, self.curve) def __sub__(self, other: BLS12_381_G2Point) -> BLS12_381_G2Point: # type: ignore[override] """ @@ -267,149 +255,119 @@ def __mul__(self, scalar: int) -> BLS12_381_G2Point: BLS12_381_G2Point: The result of scalar multiplication """ if scalar == 0: - return self.identity() + return self.identity(self.curve) if scalar < 0: return (-self) * (-scalar) - try: - # Convert point to FQ2 format for py_ecc - # Try treating as tuple/list first - p = ( - FQ2([cast(Any, self.x)[0], cast(Any, self.x)[1]]), - FQ2([cast(Any, self.y)[0], cast(Any, self.y)[1]]), - ) - - except (TypeError, IndexError): - # Fallback to .re/.im attributes (FieldElement) - x_num = [cast(Any, self.x).re, cast(Any, self.x).im] - y_num = [cast(Any, self.y).re, cast(Any, self.y).im] - p = (FQ2(x_num), FQ2(y_num)) - - # Perform scalar multiplication using py_ecc - result = multiply(p, scalar) - - if result is None: - return self.identity() - - # Convert back to tuple format - x = (int(result[0].coeffs[0]), int(result[0].coeffs[1])) - y = (int(result[1].coeffs[0]), int(result[1].coeffs[1])) - - return self.__class__(x, y) + return self._from_py_ecc_point(multiply(self._py_ecc_point(), scalar), self.curve) @classmethod - def sswu_hash2_curve_ro(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> dict | Self: - """ - Encode a string to a curve point using SSWU map with 3-isogeny (Random Oracle variant). - - Args: - alpha_string: String to encode - salt: Optional salt for domain separation - General_Check: Flag for test suite compatibility + def encode_to_curve( + cls, + alpha_string: bytes | str, + salt: bytes | str = b"", + curve: SWCurve[Fp2] | None = None, + ) -> BLS12_381_G2Point: + if curve is None: + raise ValueError("curve is required") + if not isinstance(alpha_string, bytes): + alpha_string = bytes.fromhex(alpha_string) + if not isinstance(salt, bytes): + salt = bytes.fromhex(salt) + + if curve.e2c_variant == E2C_Variant.SSWU_NU: + return cls._encode_sswu_nu(alpha_string, curve, salt) + if curve.e2c_variant == E2C_Variant.SSWU: + return cls._encode_sswu_ro(alpha_string, curve, salt) + raise ValueError(f"Unexpected E2C Variant: {curve.e2c_variant}") - Returns: - dict: Dictionary containing the resulting point and raw U values - """ + @classmethod + def _encode_sswu_ro( + cls, + alpha_string: bytes, + curve: SWCurve[Fp2], + salt: bytes = b"", + ) -> Self: + """Encode with the random-oracle simplified-SWU hash-to-curve variant.""" string_to_hash = salt + alpha_string # Get field elements - this returns [re0, im0, re1, im1] - u = cls.curve.hash_to_field(string_to_hash, 2) - u0 = FieldElement(u[0], u[1], cls.curve.PRIME_FIELD) - u1 = FieldElement(u[2], u[3], cls.curve.PRIME_FIELD) + u_raw = curve.hash_to_field(string_to_hash, 2) + u = ( + Fp2(u_raw[0], u_raw[1], curve.params.field_modulus), + Fp2(u_raw[2], u_raw[3], curve.params.field_modulus), + ) - q0 = cls.map_to_curve_simple_swu(u0) + q0 = cls.map_to_curve_simple_swu(u[0], curve) - q1 = cls.map_to_curve_simple_swu(u1) + q1 = cls.map_to_curve_simple_swu(u[1], curve) R = q0 + q1 - R = R * cls.curve.COFACTOR - if General_Check: - return {"u": [u0, u1], "Q0": q0, "Q1": q1, "R": R} - return cast(Self, R) + return cast(Self, R * curve.params.cofactor) @classmethod - def sswu_hash2_curve_nu(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> Self | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding - General_Check:Just for printing all test suites - - Returns: - TEAffinePoint: Resulting curve point - """ + def _encode_sswu_nu( + cls, + alpha_string: bytes, + curve: SWCurve[Fp2], + salt: bytes = b"", + ) -> Self: + """Encode with the nonuniform simplified-SWU hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 1) # for nu + u_raw = curve.hash_to_field(string_to_hash, 1) # for nu - u0 = FieldElement(u[0], u[1], cls.curve.PRIME_FIELD) - q0 = cls.map_to_curve_simple_swu(u0) - R = q0 * cls.curve.COFACTOR - if General_Check: - return {"u": u, "Q0": q0, "R": R} - - return R + u0 = Fp2(u_raw[0], u_raw[1], curve.params.field_modulus) + q0 = cls.map_to_curve_simple_swu(u0, curve) + return cast(Self, q0 * curve.params.cofactor) @classmethod - def map_to_curve_simple_swu(cls, u: FieldElement) -> BLS12_381_G2Point: # type: ignore[override] + def map_to_curve_simple_swu(cls, u: Fp2, curve: SWCurve) -> BLS12_381_G2Point: # type: ignore[override] """ Simplified SWU map with 3-isogeny for BLS12-381 G2 Combines SSWU map and 3-isogeny map in one function """ # 1. Map to the isogenous curve E' - point_on_e_prime = cls._sswu_map_to_e_prime(u) + point_on_e_prime = cls._sswu_map_to_e_prime(u, curve) # 2. Apply 3-isogeny map from E' to E - x, y = cls._apply_3_isogeny(point_on_e_prime) + x, y = cls._apply_3_isogeny(point_on_e_prime, curve) - # 3. Wrap into a BLS12_381_G2Point object - # Convert FieldElements to tuples - x_tuple = (x.re, x.im) - y_tuple = (y.re, y.im) - point = cls(x_tuple, y_tuple) + point = cls(x, y, curve) if not point.is_on_curve(): - print("ERROR: Point is not on the curve after mapping!") - # Print curve equation and point for debugging left = y * y - right = x * x * x + FieldElement(4, 4, x.p) # 4 * (1 + i) + right = x * x * x + cast(Fp2, curve.params.b) assert left == right, "point is not on the curve" raise ValueError("Mapped point is not on the curve") return point @staticmethod - def _sgn0(x: FieldElement) -> int: - """ - Return the sign of x: 1 if odd, 0 if even. + def _sgn0(x: Fp2) -> int: + return x.sgn0() - Args: - x: Field element (Fp2) - - Returns: - int: 1 if the integer representation is odd, 0 if even - """ - # For Fp2, we use the lexicographic order as specified in RFC 9380 - # sgn0(x) = sgn0(x0) if x0 != 0 else sgn0(x1) - x0, x1 = x.re, x.im - - # sgn0 for Fp: 1 if odd, 0 if even - def sgn0_fp(a: int) -> int: - return a % 2 + @staticmethod + def _require_isogeny(curve: SWCurve) -> RationalIsogeny[Fp2]: + isogeny = curve.params.hash_to_curve.isogeny + if isogeny is None: + raise ValueError("Missing isogeny") + return cast(RationalIsogeny[Fp2], isogeny) - if x0 != 0: - return sgn0_fp(x0) - return sgn0_fp(x1) + @classmethod + def _evaluate_fp2_polynomial(cls, coefficients: tuple[Fp2, ...], x: Fp2) -> Fp2: + value = Fp2(0, 0, x.p) + for coefficient in coefficients: + value = value * x + coefficient + return value @classmethod - def _sswu_map_to_e_prime(cls, u: FieldElement) -> tuple[FieldElement, FieldElement]: - p = u.p - Z = FieldElement(-2, -1, p) # -(2 + I) - A_prime = FieldElement(0, 240, p) # 240*I - B_prime = FieldElement(1012, 1012, p) # 1012*(1+I) + def _sswu_map_to_e_prime(cls, u: Fp2, curve: SWCurve) -> tuple[Fp2, Fp2]: + isogeny = cls._require_isogeny(curve) + Z = cast(Fp2, curve.params.hash_to_curve.z) + A_prime = isogeny.map_curve.a + B_prime = isogeny.map_curve.b u_sq = u * u u_4 = u_sq * u_sq tv1 = Z * Z * u_4 + Z * u_sq - tv1 = tv1.inv() + tv1 = Fp2(0, 0, u.p) if tv1.is_zero() else tv1.inv() if tv1.is_zero(): x1 = B_prime * ((Z * A_prime).inv()) @@ -435,109 +393,38 @@ def _sswu_map_to_e_prime(cls, u: FieldElement) -> tuple[FieldElement, FieldEleme x, y = x2, y2 # Step 9: Ensure sgn0(u) == sgn0(y) - if cls._sgn0(u) != cls._sgn0(cast(FieldElement, y)): - y = -cast(FieldElement, y) - return x, cast(FieldElement, y) + if cls._sgn0(u) != cls._sgn0(cast(Fp2, y)): + y = -cast(Fp2, y) + return x, cast(Fp2, y) @classmethod - def _apply_3_isogeny(cls, point: tuple[FieldElement, FieldElement]) -> tuple[FieldElement, FieldElement]: + def _apply_3_isogeny(cls, point: tuple[Fp2, Fp2], curve: SWCurve) -> tuple[Fp2, Fp2]: x_prime, y_prime = point - p = cls.curve.PRIME_FIELD - - # Constants from RFC 9380 - k_1_0 = FieldElement( - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, - p, - ) - k_1_1 = FieldElement( - 0, - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71A, - p, - ) - k_1_2 = FieldElement( - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71E, - 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38D, - p, - ) - k_1_3 = FieldElement( - 0x171D6541FA38CCFAED6DEA691F5FB614CB14B4E7F4E810AA22D6108F142B85757098E38D0F671C7188E2AAAAAAAA5ED1, - 0, - p, - ) - - k_2_0 = FieldElement( - 0, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA63, - p, - ) - k_2_1 = FieldElement( - 0xC, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA9F, - p, - ) - - k_3_0 = FieldElement( - 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, - 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, - p, - ) - k_3_1 = FieldElement( - 0, - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97BE, - p, - ) - k_3_2 = FieldElement( - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71C, - 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38F, - p, - ) - k_3_3 = FieldElement( - 0x124C9AD43B6CF79BFBF7043DE3811AD0761B0F37A1E26286B0E977C69AA274524E79097A56DC4BD9E1B371C71C718B10, - 0, - p, - ) - - k_4_0 = FieldElement( - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, - p, - ) - k_4_1 = FieldElement( - 0, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA9D3, - p, - ) - k_4_2 = FieldElement( - 0x12, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA99, - p, - ) + isogeny = cls._require_isogeny(curve) - x_num = k_1_3 * (x_prime**3) + k_1_2 * (x_prime**2) + k_1_1 * x_prime + k_1_0 - x_den = x_prime**2 + k_2_1 * x_prime + k_2_0 - x = x_num / x_den # can use inv as well + x_num = cls._evaluate_fp2_polynomial(isogeny.x_numerator, x_prime) + x_den = cls._evaluate_fp2_polynomial(isogeny.x_denominator, x_prime) + x = x_num / x_den - # Calculate y numerator and denominator - y_num = k_3_3 * (x_prime**3) + k_3_2 * (x_prime**2) + k_3_1 * x_prime + k_3_0 - y_den = x_prime**3 + k_4_2 * (x_prime**2) + k_4_1 * x_prime + k_4_0 - y = y_prime * (y_num / y_den) # can u inv() as well + y_num = cls._evaluate_fp2_polynomial(isogeny.y_numerator, x_prime) + y_den = cls._evaluate_fp2_polynomial(isogeny.y_denominator, x_prime) + y = y_prime * (y_num / y_den) # Verify the point is on the curve left = y * y - right = x * x * x + FieldElement(4, 4, p) # 4 * (1 + i) + right = x * x * x + cast(Fp2, curve.params.b) assert left == right, "Mapped point is not on the curve" return x, y BLS12_381_G2_NU = CurveVariant( name="BLS12_381_G2_NU", - curve=BLS12_381_G2Curve(e2c_variant=E2C_Variant.SSWU_NU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU_NU), + curve=SWCurve(params=BLS12_381_G2_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=BLS12_381_G2Point, ) BLS12_381_G2_RO = CurveVariant( name="BLS12_381_G2_RO", - curve=BLS12_381_G2Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=BLS12_381_G2_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=BLS12_381_G2Point, ) diff --git a/dot_ring/curve/specs/curve25519.py b/dot_ring/curve/specs/curve25519.py index b076bba..b0c4618 100644 --- a/dot_ring/curve/specs/curve25519.py +++ b/dot_ring/curve/specs/curve25519.py @@ -1,160 +1,57 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..montgomery.mg_affine_point import MGAffinePoint from ..montgomery.mg_curve import MGCurve +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + MontgomeryCurveParams, +) - -@dataclass(frozen=True) -class Curve25519Params: - """ - Curve25519 parameters (Montgomery form of edwards25519). - - Curve25519 is a Montgomery curve defined by: v² = u³ + 486662u² + u - over the prime field 2^255 - 19. - """ - - # From RFC 9380 Section 4.1: curve25519_XMD:SHA-512_ELL2_RO_ - SUITE_STRING = b"curve25519_XMD:SHA-512_ELL2_RO_" - DST = b"QUUX-V01-CS02-with-curve25519_XMD:SHA-512_ELL2_RO_" # Default DST is the same as SUITE_STRING - - # Curve parameters - PRIME_FIELD: Final[int] = 2**255 - 19 - ORDER: Final[int] = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED - COFACTOR: Final[int] = 8 - # Generator point (u, v) - corresponds to the base point of edwards25519 - GENERATOR_U: Final[int] = 9 - GENERATOR_V: Final[int] = 14781619447589544791020593568409986887264606134616475288964881837755586237401 - - # Montgomery curve parameters: v² = u³ + Au² + u - A: Final[int] = 486662 - B: Final[int] = 1 # B = 1 for Curve25519 - - # Z parameter for Elligator 2 mapping (from RFC 9380 Section 4.1) - Z: Final[int] = 2 # Curve25519 uses Z = 2 for Elligator 2 mapping - L: Final[int] = 48 - H_A = hashlib.sha512 - ENDIAN = "little" - M: Final[int] = 1 - K: Final[int] = 128 - S_in_bytes: Final[int] = 128 # 48 64 136 172\ - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - - # Challenge length in bytes for VRF - CHALLENGE_LENGTH: Final[int] = 16 - - # Blinding base for Pedersen VRF (project-specific: keep if you need them) - BBu: Final[int] = GENERATOR_U # 0x2a4f9ef57d59ee131c7c4e1d9b4e3a1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1e1 - BBv: Final[int] = GENERATOR_V # 0x1a8d1d5a5f9e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8e8 - UNCOMPRESSED = True - POINT_LEN: Final[int] = 32 - - -class Curve25519Curve(MGCurve): - """ - Curve25519 implementation (Montgomery form). - - A high-performance curve used in X25519 key exchange. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: - """Initialize Curve255198 with its parameters.""" - # Start with default RO suite and dst - SUITE_STRING = Curve25519Params.SUITE_STRING - DST = Curve25519Params.DST - - # Adjust SUITE_STRING and DST based on variant - # Handle ELL2 variants (default is ELL2_RO) - if e2c_variant == E2C_Variant.ELL2_NU: - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - # Initialize with proper dataclass pattern for MGCurve - # Note: This assumes MGCurve is a dataclass with these fields - super().__init__( - PRIME_FIELD=Curve25519Params.PRIME_FIELD, - ORDER=Curve25519Params.ORDER, - GENERATOR_X=Curve25519Params.GENERATOR_U, - GENERATOR_Y=Curve25519Params.GENERATOR_V, - COFACTOR=Curve25519Params.COFACTOR, - Z=Curve25519Params.Z, - A=Curve25519Params.A, - B=Curve25519Params.B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=Curve25519Params.BBu, - BBy=Curve25519Params.BBv, - L=Curve25519Params.L, - M=Curve25519Params.M, - K=Curve25519Params.K, - H_A=Curve25519Params.H_A, - S_in_bytes=Curve25519Params.S_in_bytes, - Requires_Isogeny=Curve25519Params.Requires_Isogeny, - Isogeny_Coeffs=Curve25519Params.Isogeny_Coeffs, - UNCOMPRESSED=Curve25519Params.UNCOMPRESSED, - ENDIAN=Curve25519Params.ENDIAN, - POINT_LEN=Curve25519Params.POINT_LEN, - CHALLENGE_LENGTH=Curve25519Params.CHALLENGE_LENGTH, - ) - - def __post_init__(self) -> None: - """Skip parent validation since Curve25519 parameters are known to be valid.""" - # Override the validation from the fixed MGCurve to avoid redundant checks - pass - - -# Alternative simpler implementation if the above constructor doesn't work -class Curve25519CurveSimple(MGCurve): - """ - Simplified Curve25519 implementation using direct dataclass initialization. - """ - - PRIME_FIELD: int = Curve25519Params.PRIME_FIELD - A: int = Curve25519Params.A - B: int = Curve25519Params.B - - def __post_init__(self) -> None: - """Skip validation for known good parameters.""" - pass - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> type[MGAffinePoint]: - """ - Factory function to create a Curve25519Point class with a specific E2C variant. - This is the recommended way for library users to work with different hash-to-curve variants. - - Args: - e2c_variant: The E2C variant to use (ELL2, ELL2_NU) - Returns: - A Curve25519Point class configured with the specified variant - Example: - """ - - class Curve25519PointVariant(MGAffinePoint): - """Point on Curve25519 with custom E2C variant""" - - curve: Curve25519Curve = Curve25519Curve(e2c_variant) - pass - - return Curve25519PointVariant +CURVE25519_PARAMS = MontgomeryCurveParams( + field_modulus=2**255 - 19, + subgroup_order=2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED, + cofactor=8, + suite_id=b"curve25519_XMD:SHA-512_ELL2_RO_", + hash_fn=hashlib.sha512, + generator=( + 9, + 14781619447589544791020593568409986887264606134616475288964881837755586237401, + ), + a=486662, + b=1, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-curve25519_XMD:SHA-512_ELL2_RO_", + z=2, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=128, + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16, uncompressed=True), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 9, + 14781619447589544791020593568409986887264606134616475288964881837755586237401, + ), + ), +) Curve25519_NU = CurveVariant( name="Curve25519_NU", - curve=Curve25519Curve(e2c_variant=E2C_Variant.ELL2_NU), - point=nu_variant(e2c_variant=E2C_Variant.ELL2_NU), + curve=MGCurve(params=CURVE25519_PARAMS, e2c_variant=E2C_Variant.ELL2_NU), + point_type=MGAffinePoint, ) Curve25519_RO = CurveVariant( name="Curve25519_RO", - curve=Curve25519Curve(e2c_variant=E2C_Variant.ELL2), - point=nu_variant(e2c_variant=E2C_Variant.ELL2), + curve=MGCurve(params=CURVE25519_PARAMS, e2c_variant=E2C_Variant.ELL2), + point_type=MGAffinePoint, ) diff --git a/dot_ring/curve/specs/curve448.py b/dot_ring/curve/specs/curve448.py index 4e8d34b..1318f3d 100644 --- a/dot_ring/curve/specs/curve448.py +++ b/dot_ring/curve/specs/curve448.py @@ -1,150 +1,59 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..montgomery.mg_affine_point import MGAffinePoint from ..montgomery.mg_curve import MGCurve +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + MontgomeryCurveParams, +) +CURVE448_GENERATOR = ( + 5, + 355293926785568175264127502063783334808976399387714271831880898435169088786967410002932673765864550910142774147268105838985595290606362, +) -@dataclass(frozen=True) -class Curve448Params: - """ - Curve448 parameters (Montgomery form of edwards448). - - Curve448 is a Montgomery curve defined by: v² = u³ + 156326u² + u - over the prime field 2^448 - 2^224 - 1. - """ - - SUITE_STRING = b"curve448_XOF:SHAKE256_ELL2_RO_" - DST = b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_" - - # Curve parameters - PRIME_FIELD: Final[int] = 2**448 - 2**224 - 1 - ORDER: Final[int] = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D - COFACTOR: Final[int] = 4 - - # Generator point (u, v) - corresponds to the base point of edwards448 - # Generator point u-coordinate (from RFC 7748) - GENERATOR_U: Final[int] = 5 - - # v-coordinate is derived from the curve equation v^2 = u^3 + A*u^2 + u mod p - # Using the positive square root that has even least significant bit (LSB) - GENERATOR_V: Final[int] = ( - 355293926785568175264127502063783334808976399387714271831880898435169088786967410002932673765864550910142774147268105838985595290606362 - ) - - # Montgomery curve parameters: v² = u³ + Au² + u - A: Final[int] = 156326 - B: Final[int] = 1 # B = 1 for Curve448 - - # Z parameter for SSWU mapping - Z: Final[int] = -1 - L: Final[int] = 84 - H_A = hashlib.shake_256 - ENDIAN = "little" - M: Final[int] = 1 - K: Final[int] = 224 - S_in_bytes: Final[int | None] = None - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - - # Challenge length in bytes for VRF (aligned with 224-bit security level) - CHALLENGE_LENGTH: Final[int] = 28 # 224 bits for Curve448 (corrected from 24) - - # Blinding base for Pedersen VRF - BBu: Final[int] = GENERATOR_U - BBv: Final[int] = GENERATOR_V - UNCOMPRESSED = True - POINT_LEN: Final[int] = (PRIME_FIELD.bit_length() + 7) // 8 - - -class Curve448Curve(MGCurve): - """ - Curve448 implementation (Montgomery form). - - A high-security curve used in X448 key exchange. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: - """Initialize Curve448 with its parameters.""" - # Default suite and dst - SUITE_STRING = Curve448Params.SUITE_STRING - DST = Curve448Params.DST - - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - # Initialize with proper dataclass pattern for MGCurve - super().__init__( - PRIME_FIELD=Curve448Params.PRIME_FIELD, - ORDER=Curve448Params.ORDER, - GENERATOR_X=Curve448Params.GENERATOR_U, - GENERATOR_Y=Curve448Params.GENERATOR_V, - COFACTOR=Curve448Params.COFACTOR, - Z=Curve448Params.Z, - A=Curve448Params.A, - B=Curve448Params.B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=Curve448Params.BBu, - BBy=Curve448Params.BBv, - L=Curve448Params.L, - M=Curve448Params.M, - K=Curve448Params.K, - H_A=Curve448Params.H_A, - S_in_bytes=Curve448Params.S_in_bytes, - Requires_Isogeny=Curve448Params.Requires_Isogeny, - Isogeny_Coeffs=Curve448Params.Isogeny_Coeffs, - UNCOMPRESSED=Curve448Params.UNCOMPRESSED, - ENDIAN=Curve448Params.ENDIAN, - POINT_LEN=Curve448Params.POINT_LEN, - CHALLENGE_LENGTH=Curve448Params.CHALLENGE_LENGTH, - ) - - def __post_init__(self) -> None: - """Skip parent validation since Curve448 parameters are known to be valid.""" - # Override the validation from the fixed MGCurve to avoid redundant checks - pass - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> type[MGAffinePoint]: - """ - Factory function to create a Curve448Point class with a specific E2C variant. - - This is the recommended way for library users to work with different hash-to-curve variants. - - Args: - e2c_variant: The E2C variant to use (ELL2, ELL2_NU) - - Returns: - A Curve448Point class configured with the specified variant - """ - - class Curve448PointVariant(MGAffinePoint): - """Point on Curve448 with custom E2C variant""" - - curve = Curve448Curve(e2c_variant) - pass - - return Curve448PointVariant +CURVE448_PARAMS = MontgomeryCurveParams( + field_modulus=2**448 - 2**224 - 1, + subgroup_order=2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D, + cofactor=4, + suite_id=b"curve448_XOF:SHAKE256_ELL2_RO_", + hash_fn=hashlib.shake_256, + generator=CURVE448_GENERATOR, + a=156326, + b=1, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-curve448_XOF:SHAKE256_ELL2_RO_", + z=-1, + field_extension_degree=1, + security_level=224, + field_length=84, + expand_len=None, + ), + encoding=EncodingParams( + endian="little", + point_len=((2**448 - 2**224 - 1).bit_length() + 7) // 8, + challenge_len=28, + uncompressed=True, + ), + auxiliary_points=AuxiliaryPointParams(blinding_base=CURVE448_GENERATOR), +) Curve448_NU = CurveVariant( name="Curve448_NU", - curve=Curve448Curve(e2c_variant=E2C_Variant.ELL2_NU), - point=nu_variant(e2c_variant=E2C_Variant.ELL2_NU), + curve=MGCurve(params=CURVE448_PARAMS, e2c_variant=E2C_Variant.ELL2_NU), + point_type=MGAffinePoint, ) Curve448_RO = CurveVariant( name="Curve448_RO", - curve=Curve448Curve(e2c_variant=E2C_Variant.ELL2), - point=nu_variant(e2c_variant=E2C_Variant.ELL2), + curve=MGCurve(params=CURVE448_PARAMS, e2c_variant=E2C_Variant.ELL2), + point_type=MGAffinePoint, ) diff --git a/dot_ring/curve/specs/ed25519.py b/dot_ring/curve/specs/ed25519.py index be992e4..8ea7878 100644 --- a/dot_ring/curve/specs/ed25519.py +++ b/dot_ring/curve/specs/ed25519.py @@ -1,168 +1,83 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final, Self +from typing import Self from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..twisted_edwards.te_affine_point import TEAffinePoint from ..twisted_edwards.te_curve import TECurve +from .parameters import ( + AuxiliaryPointParams, + Elligator2MontgomeryMap, + EncodingParams, + HashToCurveParams, + TwistedEdwardsCurveParams, +) +ED25519_PARAMS = TwistedEdwardsCurveParams( + field_modulus=0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED, + subgroup_order=2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED, + cofactor=8, + suite_id=b"Ed25519-SHA512-TAI-v1", + hash_fn=hashlib.sha512, + generator=( + 0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A, + 0x6666666666666666666666666666666666666666666666666666666666666658, + ), + a=-1, + d=0x52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_", + z=2, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=128, + elligator2_map=Elligator2MontgomeryMap(a=486662, b=1), + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 45003173884697328536089278691112838614164406922820087464913813433380838325453, + 31256014272390301975555524011230972931324093235775711248505761870355310252869, + ), + ), +) -@dataclass(frozen=True) -class Ed25519Params: - """ - JubJub curve parameters. - - Specification of the JubJub curve in Twisted Edwards form. - """ - - SUITE_STRING = b"Ed25519-SHA512-TAI-v1" - SUITE_ID = b"Ed25519-SHA512-TAI-v1" - DST = b"QUUX-V01-CS02-with-edwards25519_XMD:SHA-512_ELL2_RO_" - - # Curve parameters - PRIME_FIELD: Final[int] = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED - ORDER: Final[int] = 2**252 + 0x14DEF9DEA2F79CD65812631A5CF5D3ED - COFACTOR: Final[int] = 8 - # Generator point - GENERATOR_X: Final[int] = 0x216936D3CD6E53FEC0A4E231FDD6DC5C692CC7609525A7B2C9562D608F25D51A - GENERATOR_Y: Final[int] = 0x6666666666666666666666666666666666666666666666666666666666666658 - # Edwards curve parameters - EDWARDS_A: Final[int] = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEC - EDWARDS_D: Final[int] = 0x52036CEE2B6FFE738CC740797779E89800700A4D4141D8AB75EB4DCA135978A3 - - # Z parameter for Elligator 2 mapping (from RFC 9380 Section 4.1) - Z: Final[int] = 2 # Curve25519 uses Z = 2 for Elligator 2 mapping - L: Final[int] = 48 - H_A = hashlib.sha512 - ENDIAN = "little" - M: Final = 1 - K: Final = 128 - S_in_bytes: Final = 128 # 48 64 136 172\ - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 16 # 128 bits - - BBx: Final[int] = 45003173884697328536089278691112838614164406922820087464913813433380838325453 - BBy: Final[int] = 31256014272390301975555524011230972931324093235775711248505761870355310252869 - - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - TRANSCRIPT_HASH = "sha512" - HASH_TO_CURVE = "tai" - - -class Ed25519Curve(TECurve): - """ - Bandersnatch curve implementation. - - A high-performance curve designed for zero-knowledge proofs and VRFs, - offering both efficiency and security. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: - """Initialize Ed25519 curve with RFC-compliant parameters.""" - # Default suite and dst - SUITE_STRING = Ed25519Params.SUITE_STRING - DST = Ed25519Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - if e2c_variant.value == "TryAndIncrement": - SUITE_STRING = Ed25519Params.SUITE_ID - DST = b"" - super().__init__( - PRIME_FIELD=Ed25519Params.PRIME_FIELD, - ORDER=Ed25519Params.ORDER, - GENERATOR_X=Ed25519Params.GENERATOR_X, - GENERATOR_Y=Ed25519Params.GENERATOR_Y, - COFACTOR=Ed25519Params.COFACTOR, - Z=Ed25519Params.Z, - EdwardsA=Ed25519Params.EDWARDS_A, - EdwardsD=Ed25519Params.EDWARDS_D, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=Ed25519Params.BBx, - BBy=Ed25519Params.BBy, - L=Ed25519Params.L, - H_A=Ed25519Params.H_A, - M=Ed25519Params.M, - K=Ed25519Params.K, - S_in_bytes=Ed25519Params.S_in_bytes, - Requires_Isogeny=Ed25519Params.Requires_Isogeny, - Isogeny_Coeffs=Ed25519Params.Isogeny_Coeffs, - UNCOMPRESSED=Ed25519Params.UNCOMPRESSED, - ENDIAN=Ed25519Params.ENDIAN, - POINT_LEN=Ed25519Params.POINT_LEN, - CHALLENGE_LENGTH=Ed25519Params.CHALLENGE_LENGTH, - SUITE_ID=Ed25519Params.SUITE_ID if e2c_variant == E2C_Variant.TAI else None, - TRANSCRIPT_HASH=Ed25519Params.TRANSCRIPT_HASH, - HASH_TO_CURVE=Ed25519Params.HASH_TO_CURVE if e2c_variant == E2C_Variant.TAI else None, - ) - - def calculate_j_k(self) -> tuple[int, int]: - """ - Calculate curve parameters J and K for Elligator 2. - - Returns: - Tuple[int, int]: J and K parameters - """ - return (486662, 1) # As Curve25519 is its equivalent MGC - - -class Ed25519Point(TEAffinePoint): - """ - Point on the Bandersnatch curve. - Implements optimized point operations specific to the Bandersnatch curve, - including GLV scalar multiplication. - """ +class Ed25519Point(TEAffinePoint[TECurve]): + """Point on Ed25519.""" @classmethod - def map_to_curve(cls, u: int) -> Self: - # Use a different mapping specifically for Ed25519 - s, t = cls.curve.map_to_curve_ell2(u) - return cls.mont_to_ed25519(s, t) + def map_to_curve(cls, u: int, curve: TECurve) -> Self: + s, t = curve.map_to_curve_ell2(u) + return cls.mont_to_ed25519(s, t, curve) @classmethod - def mont_to_ed25519(cls, u: int, v: int) -> Self: - """ - Convert a point (u, v) from Montgomery form to Edwards form (x, y). - Returns (x, y). - """ - p = cls.curve.PRIME_FIELD - # Precompute sqrt(-486664) mod p - sqrt_neg_A_minus_2 = cls.curve.mod_sqrt(-486664 % p) - # y = (u - 1) / (u + 1) mod p + def mont_to_ed25519(cls, u: int, v: int, curve: TECurve) -> Self: + p = curve.params.field_modulus + sqrt_neg_a_minus_2 = curve.mod_sqrt(-486664 % p) y = ((u - 1) * pow(u + 1, -1, p)) % p - # x = sqrt(-486664) * u / v mod p - x = (sqrt_neg_A_minus_2 * u * pow(v, -1, p)) % p - return cls(x, y) - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.ELL2_NU) -> type[Ed25519Point]: - class Ed25519PointVariant(Ed25519Point): - """Point on Ed25519 with custom E2C variant""" - - curve: TECurve = Ed25519Curve(e2c_variant) - - return Ed25519PointVariant + x = (sqrt_neg_a_minus_2 * u * pow(v, -1, p)) % p + return cls(x, y, curve) Ed25519_RO = CurveVariant( name="Ed25519_RO", - curve=Ed25519Curve(e2c_variant=E2C_Variant.ELL2), - point=nu_variant(e2c_variant=E2C_Variant.ELL2), + curve=TECurve(params=ED25519_PARAMS, e2c_variant=E2C_Variant.ELL2), + point_type=Ed25519Point, ) Ed25519_NU = CurveVariant( + name="Ed25519_NU", + curve=TECurve(params=ED25519_PARAMS, e2c_variant=E2C_Variant.ELL2_NU), + point_type=Ed25519Point, +) + +Ed25519_TAI = CurveVariant( name="Ed25519_TAI", - curve=Ed25519Curve(e2c_variant=E2C_Variant.TAI), - point=nu_variant(e2c_variant=E2C_Variant.TAI), + curve=TECurve(params=ED25519_PARAMS, e2c_variant=E2C_Variant.TAI), + point_type=Ed25519Point, ) diff --git a/dot_ring/curve/specs/ed448.py b/dot_ring/curve/specs/ed448.py index e33acea..3f8bc7b 100644 --- a/dot_ring/curve/specs/ed448.py +++ b/dot_ring/curve/specs/ed448.py @@ -1,237 +1,88 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final, Self +from typing import Self from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..twisted_edwards.te_affine_point import TEAffinePoint from ..twisted_edwards.te_curve import TECurve +from .parameters import ( + AuxiliaryPointParams, + Elligator2MontgomeryMap, + EncodingParams, + HashToCurveParams, + TwistedEdwardsCurveParams, +) +ED448_GENERATOR = ( + 117812161263436946737282484343310064665180535357016373416879082147939404277809514858788439644911793978499419995990477371552926308078495, + 19, +) -@dataclass(frozen=True) -class Ed448Params: - """ - Ed448 curve parameters (edwards448). - - The Ed448 curve is a high-security Twisted Edwards curve providing ~224-bit security. - Defined in RFC 8032 and hash-to-curve parameters from RFC 9380. - """ - - # RFC 9380 compliant suite string and DST for edwards448_XOF:SHAKE256_ELL2_RO_ - SUITE_STRING = b"edwards448_XOF:SHAKE256_ELL2_RO_" - DST = b"QUUX-V01-CS02-with-edwards448_XOF:SHAKE256_ELL2_RO_" - - # Curve parameters from RFC 8032 - PRIME_FIELD: Final[int] = 2**448 - 2**224 - 1 - ORDER: Final[int] = 2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D - COFACTOR: Final[int] = 4 - - # Generator point (x, y) - Valid Ed448 base point that satisfies the curve equation - # This point is on the curve: x² + y² = 1 + (-39081)*x²*y² (mod p) - # GENERATOR_X: Final[int] = ( - # 3 - # ) - # GENERATOR_Y: Final[int] = ( - # 60824814231572554857908961302747 - GENERATOR_X: Final[int] = ( - 117812161263436946737282484343310064665180535357016373416879082147939404277809514858788439644911793978499419995990477371552926308078495 - ) - GENERATOR_Y: Final[int] = 19 - - # Twisted Edwards parameters: ax² + y² = 1 + dx²y² (mod p) - # From RFC 8032: Ed448 uses a = 1 and d = -39081 - EDWARDS_A: Final[int] = 1 # a = 1 for Ed448 (untwisted Edwards form) - EDWARDS_D: Final[int] = -39081 # d = -39081 - - # Z parameter for Elligator 2 mapping (RFC 9380) - Z: Final[int] = -1 - L: Final[int] = 84 - H_A = hashlib.shake_256 - ENDIAN = "little" - M: Final[int] = 1 - K: Final[int] = 224 - S_in_bytes: Final[int] = 0 - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 64 # 512 bits for Ed448 (higher security) - - # Independent blinding base for Pedersen VRF - # Generated using a deterministic method from a different seed point - # These should be cryptographically independent from the generator - BBx: Final[int] = GENERATOR_X - BBy: Final[int] = GENERATOR_Y - ELL2_C1 = 0x570470F1767EA6DE324A3D3A46412AE1AF72AB66511433B80E18B00938E2626A82BC70CC05F # noqa: E501 - ELL2_C2 = 0x3736CA3984087789C1E05A0C2D73AD3FF1CE67C39C4FDBD132C4ED7C8AD9808795BF230FA16 # noqa: E501 - UNCOMPRESSED = True - POINT_LEN: Final[int] = (PRIME_FIELD.bit_length() + 7) // 8 - - -class Ed448Curve(TECurve): - """ - Ed448 curve implementation. - - A high-security Twisted Edwards curve providing ~224-bit security. - Defined in RFC 8032 with hash-to-curve support per RFC 9380. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.ELL2) -> None: - """Initialize Ed448 curve with RFC-compliant parameters.""" - # Default suite and dst - SUITE_STRING = Ed448Params.SUITE_STRING - DST = Ed448Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - if e2c_variant.value == "TryAndIncrement": - SUITE_STRING = b"Ed25519_SHA-512_TAI" # as per davxy - DST = b"" + SUITE_STRING - - super().__init__( - PRIME_FIELD=Ed448Params.PRIME_FIELD, - ORDER=Ed448Params.ORDER, - GENERATOR_X=Ed448Params.GENERATOR_X, - GENERATOR_Y=Ed448Params.GENERATOR_Y, - COFACTOR=Ed448Params.COFACTOR, - Z=Ed448Params.Z, - EdwardsA=Ed448Params.EDWARDS_A, - EdwardsD=Ed448Params.EDWARDS_D, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=Ed448Params.BBx, - BBy=Ed448Params.BBy, - L=Ed448Params.L, - H_A=Ed448Params.H_A, - M=Ed448Params.M, - K=Ed448Params.K, - S_in_bytes=Ed448Params.S_in_bytes, - Requires_Isogeny=Ed448Params.Requires_Isogeny, - Isogeny_Coeffs=Ed448Params.Isogeny_Coeffs, - UNCOMPRESSED=Ed448Params.UNCOMPRESSED, - ENDIAN=Ed448Params.ENDIAN, - POINT_LEN=Ed448Params.POINT_LEN, - CHALLENGE_LENGTH=Ed448Params.CHALLENGE_LENGTH, - ) - - def is_on_curve(self, x: int, y: int) -> bool: - """ - Check if point (x, y) is on the Ed448 curve. - - Ed448 equation: x² + y² = 1 + d*x²*y² (mod p) - where a = 1 (untwisted Edwards form) - - Args: - x: x-coordinate - y: y-coordinate - - Returns: - bool: True if point is on curve - """ - p = self.PRIME_FIELD - d = Ed448Params.EDWARDS_D - - # Compute left side: x² + y² - left = (x * x + y * y) % p - - # Compute right side: 1 + d*x²*y² - right = (1 + d * x * x * y * y) % p - - return left == right - - def calculate_j_k(self) -> tuple[int, int]: - """ - Calculate curve parameters J and K for Elligator 2. - - Returns: - Tuple[int, int]: J and K parameters - """ - return (156326, 1) # As Curve448 is its equivalent MGC - +ED448_PARAMS = TwistedEdwardsCurveParams( + field_modulus=2**448 - 2**224 - 1, + subgroup_order=2**446 - 0x8335DC163BB124B65129C96FDE933D8D723A70AADC873D6D54A7BB0D, + cofactor=4, + suite_id=b"edwards448_XOF:SHAKE256_ELL2_RO_", + hash_fn=hashlib.shake_256, + generator=ED448_GENERATOR, + a=1, + d=-39081, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-edwards448_XOF:SHAKE256_ELL2_RO_", + z=-1, + field_extension_degree=1, + security_level=224, + field_length=84, + expand_len=None, + elligator2_map=Elligator2MontgomeryMap(a=156326, b=1), + ), + encoding=EncodingParams( + endian="little", + point_len=((2**448 - 2**224 - 1).bit_length() + 7) // 8, + challenge_len=64, + uncompressed=True, + ), + auxiliary_points=AuxiliaryPointParams(blinding_base=ED448_GENERATOR), +) -class Ed448Point(TEAffinePoint): - """ - Point on the Ed448 curve. - Implements point operations specific to the Ed448 curve - with RFC 8032 and RFC 9380 compliance. - """ +class Ed448Point(TEAffinePoint[TECurve]): + """Point on Ed448.""" @classmethod - def blinding_base(cls) -> Self: - """ - Get the blinding base point for VRF operations. - - This point is cryptographically independent from the generator - for secure Pedersen VRF implementations. - - Returns: - Ed448Point: Blinding base point - """ - return cls(Ed448Params.BBx, Ed448Params.BBy) + def blinding_base(cls, curve: TECurve) -> Self: + x, y = curve.params.auxiliary_points.blinding_base or curve.params.generator + return cls(x, y, curve) @classmethod - def map_to_curve(cls, u: int) -> Self: - # Use a different mapping specifically for Ed25519 - s, t = cls.curve.map_to_curve_ell2(u) - return cls.mont_to_ed448(s, t) + def map_to_curve(cls, u: int, curve: TECurve) -> Self: + s, t = curve.map_to_curve_ell2(u) + return cls.mont_to_ed448(s, t, curve) @classmethod - def mont_to_ed448(cls, u: int, v: int) -> Self: - """ - Convert a point (u, v) from Curve448 (Montgomery form) - to Ed448 (Twisted Edwards form). - - Args: - u (int): Montgomery u-coordinate (mod p) - v (int): Montgomery v-coordinate (mod p) - - Returns: - (x, y): Edwards coordinates as integers mod p - """ - p = cls.curve.PRIME_FIELD - - # x numerator: 4 * v * (u^2 - 1) + def mont_to_ed448(cls, u: int, v: int, curve: TECurve) -> Self: + p = curve.params.field_modulus x_num = (4 * v * ((u * u - 1) % p)) % p - - # x denominator: u^4 - 2u^2 + 4v^2 + 1 x_den = (pow(u, 4, p) - 2 * pow(u, 2, p) + 4 * pow(v, 2, p) + 1) % p + x = (x_num * curve.inv(x_den)) % p - x = (x_num * cls.curve.inv(x_den)) % p - - # y numerator: -(u^5 - 2u^3 - 4uv^2 + u) y_num = -(pow(u, 5, p) - 2 * pow(u, 3, p) - 4 * u * pow(v, 2, p) + u) % p - - # y denominator: u^5 - 2u^2v^2 - 2u^3 - 2v^2 + u y_den = (pow(u, 5, p) - 2 * pow(u, 2, p) * pow(v, 2, p) - 2 * pow(u, 3, p) - 2 * pow(v, 2, p) + u) % p - - y = (y_num * cls.curve.inv(y_den)) % p - return cls(x, y) - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.ELL2_NU) -> type[Ed448Point]: - class Ed448PointVariant(Ed448Point): - """Point on Ed448 with custom E2C variant""" - - curve: TECurve = Ed448Curve(e2c_variant) - - return Ed448PointVariant + y = (y_num * curve.inv(y_den)) % p + return cls(x, y, curve) Ed448_NU = CurveVariant( name="Ed448_NU", - curve=Ed448Curve(e2c_variant=E2C_Variant.ELL2_NU), - point=nu_variant(e2c_variant=E2C_Variant.ELL2_NU), + curve=TECurve(params=ED448_PARAMS, e2c_variant=E2C_Variant.ELL2_NU), + point_type=Ed448Point, ) Ed448_RO = CurveVariant( name="Ed448_RO", - curve=Ed448Curve(e2c_variant=E2C_Variant.ELL2), - point=nu_variant(e2c_variant=E2C_Variant.ELL2), + curve=TECurve(params=ED448_PARAMS, e2c_variant=E2C_Variant.ELL2), + point_type=Ed448Point, ) diff --git a/dot_ring/curve/specs/jubjub.py b/dot_ring/curve/specs/jubjub.py index aeb96a4..4ffa8db 100644 --- a/dot_ring/curve/specs/jubjub.py +++ b/dot_ring/curve/specs/jubjub.py @@ -1,110 +1,59 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..twisted_edwards.te_affine_point import TEAffinePoint from ..twisted_edwards.te_curve import TECurve - - -@dataclass(frozen=True) -class JubJubParams: - """ - JubJub curve parameters. - - Specification of the JubJub curve in Twisted Edwards form. - """ - - SUITE_STRING = b"JubJub-SHA512-TAI-v1" - SUITE_ID = b"JubJub-SHA512-TAI-v1" - DST = b"" - # f_len=q_len=32 - # Curve parameters - PRIME_FIELD: Final[int] = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 - ORDER: Final[int] = 0x0E7DB4EA6533AFA906673B0101343B00A6682093CCC81082D0970E5ED6F72CB7 - COFACTOR: Final[int] = 8 - - # Generator point - GENERATOR_X: Final[int] = 8076246640662884909881801758704306714034609987455869804520522091855516602923 - GENERATOR_Y: Final[int] = 13262374693698910701929044844600465831413122818447359594527400194675274060458 - - # Edwards curve parameters - EDWARDS_A: Final[int] = -1 - EDWARDS_D: Final[int] = 19257038036680949359750312669786877991949435402254120286184196891950884077233 - - # Z - Z: Final[int] = 5 - M: Final[int] = 1 - K: Final[int] = 128 - L: Final[int] = 48 # can define func as well - S_in_bytes: Final[int] = 48 # can be taken as hsh_fn.block_size #not sure as its supposed to be 128 for sha512 - H_A = hashlib.sha512 - ENDIAN = "little" - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - CHALLENGE_LENGTH: Final[int] = 16 - - # Blinding Base For Pedersen - BBx: Final[int] = 38206460563694846719174258613922853630278999941532690543235578292520143148532 - BBy: Final[int] = 34254498978062207918041301829525626783549813531091321004550549786528984401675 - ACCUMULATOR_BASE_X: Final[int] = 48142684311216766702182564801462043940571084233680216669499475549492432046964 - ACCUMULATOR_BASE_Y: Final[int] = 34380560660182334518990118617091967209302636551264477863958902286043397647879 - PADDING_X: Final[int] = 17348704025397475127937572481155408456556065464328870407269802701696798733683 - PADDING_Y: Final[int] = 24318278422173803457621119807961883607097742387673491974779969503617097905596 - UNCOMPRESSED = False - POINT_LEN: Final[int] = 32 - TRANSCRIPT_HASH = "sha512" - HASH_TO_CURVE = "tai" - - -JubJub_TE_Curve: Final[TECurve] = TECurve( - PRIME_FIELD=JubJubParams.PRIME_FIELD, - ORDER=JubJubParams.ORDER, - GENERATOR_X=JubJubParams.GENERATOR_X, - GENERATOR_Y=JubJubParams.GENERATOR_Y, - COFACTOR=JubJubParams.COFACTOR, - Z=JubJubParams.Z, - EdwardsA=JubJubParams.EDWARDS_A, - EdwardsD=JubJubParams.EDWARDS_D, - SUITE_STRING=JubJubParams.SUITE_STRING, - DST=JubJubParams.DST, - E2C=E2C_Variant.TAI, - BBx=JubJubParams.BBx, - BBy=JubJubParams.BBy, - M=JubJubParams.M, - K=JubJubParams.K, - L=JubJubParams.L, - S_in_bytes=JubJubParams.S_in_bytes, - H_A=JubJubParams.H_A, - Requires_Isogeny=JubJubParams.Requires_Isogeny, - Isogeny_Coeffs=JubJubParams.Isogeny_Coeffs, - UNCOMPRESSED=JubJubParams.UNCOMPRESSED, - ENDIAN=JubJubParams.ENDIAN, - POINT_LEN=JubJubParams.POINT_LEN, - CHALLENGE_LENGTH=JubJubParams.CHALLENGE_LENGTH, - SUITE_ID=JubJubParams.SUITE_ID, - TRANSCRIPT_HASH=JubJubParams.TRANSCRIPT_HASH, - HASH_TO_CURVE=JubJubParams.HASH_TO_CURVE, - ACCUMULATOR_BASE_X=JubJubParams.ACCUMULATOR_BASE_X, - ACCUMULATOR_BASE_Y=JubJubParams.ACCUMULATOR_BASE_Y, - PADDING_X=JubJubParams.PADDING_X, - PADDING_Y=JubJubParams.PADDING_Y, +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + TwistedEdwardsCurveParams, ) - -class JubJubPoint(TEAffinePoint): - """ - Point on the Bandersnatch curve. - - Implements optimized point operations specific to the Bandersnatch curve, - including GLV scalar multiplication. - """ - - curve: TECurve = JubJub_TE_Curve +JUBJUB_PARAMS = TwistedEdwardsCurveParams( + field_modulus=0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001, + subgroup_order=0x0E7DB4EA6533AFA906673B0101343B00A6682093CCC81082D0970E5ED6F72CB7, + cofactor=8, + suite_id=b"JubJub-SHA512-TAI-v1", + hash_fn=hashlib.sha512, + generator=( + 8076246640662884909881801758704306714034609987455869804520522091855516602923, + 13262374693698910701929044844600465831413122818447359594527400194675274060458, + ), + a=-1, + d=19257038036680949359750312669786877991949435402254120286184196891950884077233, + hash_to_curve=HashToCurveParams( + dst=b"", + z=5, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=48, + ), + encoding=EncodingParams(endian="little", point_len=32, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 38206460563694846719174258613922853630278999941532690543235578292520143148532, + 34254498978062207918041301829525626783549813531091321004550549786528984401675, + ), + accumulator_base=( + 48142684311216766702182564801462043940571084233680216669499475549492432046964, + 34380560660182334518990118617091967209302636551264477863958902286043397647879, + ), + padding_point=( + 17348704025397475127937572481155408456556065464328870407269802701696798733683, + 24318278422173803457621119807961883607097742387673491974779969503617097905596, + ), + ), +) -JubJub = CurveVariant(name="JubJub", curve=JubJub_TE_Curve, point=JubJubPoint) +JubJub = CurveVariant( + name="JubJub", + curve=TECurve(params=JUBJUB_PARAMS, e2c_variant=E2C_Variant.TAI), + point_type=TEAffinePoint, +) diff --git a/dot_ring/curve/specs/p256.py b/dot_ring/curve/specs/p256.py index 9811af0..b1ee70e 100644 --- a/dot_ring/curve/specs/p256.py +++ b/dot_ring/curve/specs/p256.py @@ -1,125 +1,47 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + ShortWeierstrassCurveParams, +) - -@dataclass(frozen=True) -class P256Params: - """ - NIST P-256 (secp256r1) curve parameters. - - The P-256 curve is a NIST-standardized Short Weierstrass curve widely used - in TLS, digital signatures, and other cryptographic applications. - """ - - SUITE_STRING = b"Secp256r1-SHA256-TAI-v1" - SUITE_ID = b"Secp256r1-SHA256-TAI-v1" - DST = b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_" # Default DST is the same as SUITE_STRING - - # Curve parameters for y² = x³ - 3x + b - PRIME_FIELD: Final[int] = 0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF - ORDER: Final[int] = 0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551 - COFACTOR: Final[int] = 1 - - # Generator point - GENERATOR_X: Final[int] = 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296 - GENERATOR_Y: Final[int] = 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5 - - # Short Weierstrass parameters: y² = x³ + ax + b - WEIERSTRASS_A: Final[int] = -3 # a = -3 - WEIERSTRASS_B: Final[int] = 0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B - - # Z parameter for SSWU mapping (from RFC 9380 Section 8.1) - Z: Final[int] = -10 # P-256 uses Z = -10 for SSWU mapping - M: Final[int] = 1 # Field Extension Degree - L: Final[int] = 48 # can define func as well - K: Final[int] = 128 # Security level - # expand_message: Final[str]="XMD" - S_in_bytes: Final[int] = 64 - H_A = hashlib.sha256 - ENDIAN = "big" - # Blinding Base For Pedersen VRF - # These are arbitrary points on the curve for blinding - BBx: Final[int] = 100063053743935619201936855760019111820847755970243670581468062459849338000 - BBy: Final[int] = 113675507039234898358330549589155441528265243038226986303017485279501143145422 - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 16 # 128 bits - Requires_Isogeny: Final[bool] = False - Isogeny_Coeffs = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 33 - TRANSCRIPT_HASH = "sha256" - HASH_TO_CURVE = "tai" - - -class P256Curve(SWCurve): - """ - NIST P-256 (secp256r1) curve implementation. - - A widely standardized curve used in many cryptographic protocols. - Defined by the equation y² = x³ - 3x + b over the prime field. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - """Initialize P-256 curve with its parameters.""" - # Default suite and dst - SUITE_STRING = P256Params.SUITE_STRING - DST = P256Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - elif e2c_variant.value == "TryAndIncrement": - SUITE_STRING = P256Params.SUITE_ID - DST = b"" - ENDIAN = "little" if e2c_variant == E2C_Variant.TAI else P256Params.ENDIAN - - super().__init__( - PRIME_FIELD=P256Params.PRIME_FIELD, - ORDER=P256Params.ORDER, - GENERATOR_X=P256Params.GENERATOR_X, - GENERATOR_Y=P256Params.GENERATOR_Y, - COFACTOR=P256Params.COFACTOR, - Z=P256Params.Z, - WeierstrassA=P256Params.WEIERSTRASS_A, - WeierstrassB=P256Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=P256Params.BBx, - BBy=P256Params.BBy, - M=P256Params.M, - K=P256Params.K, - L=P256Params.L, - S_in_bytes=P256Params.S_in_bytes, - H_A=P256Params.H_A, - Requires_Isogeny=P256Params.Requires_Isogeny, - Isogeny_Coeffs=P256Params.Isogeny_Coeffs, - UNCOMPRESSED=P256Params.UNCOMPRESSED, - ENDIAN=ENDIAN, - POINT_LEN=P256Params.POINT_LEN, - CHALLENGE_LENGTH=P256Params.CHALLENGE_LENGTH, - SUITE_ID=P256Params.SUITE_ID if e2c_variant == E2C_Variant.TAI else None, - TRANSCRIPT_HASH=P256Params.TRANSCRIPT_HASH, - HASH_TO_CURVE=P256Params.HASH_TO_CURVE if e2c_variant == E2C_Variant.TAI else None, - ) - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> type[P256Point]: - class P256PointVariant(P256Point): - """Point on P256 with custom E2C variant""" - - curve: P256Curve = P256Curve(e2c_variant) - - return P256PointVariant +P256_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=0xFFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF, + subgroup_order=0xFFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551, + cofactor=1, + suite_id=b"Secp256r1-SHA256-TAI-v1", + hash_fn=hashlib.sha256, + generator=( + 0x6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296, + 0x4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5, + ), + a=-3, + b=0x5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-P256_XMD:SHA-256_SSWU_RO_", + z=-10, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=64, + ), + encoding=EncodingParams(endian="big", point_len=33, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 100063053743935619201936855760019111820847755970243670581468062459849338000, + 113675507039234898358330549589155441528265243038226986303017485279501143145422, + ), + ), +) class P256Point(SWAffinePoint): @@ -129,49 +51,39 @@ class P256Point(SWAffinePoint): Implements point operations specific to the P-256 curve. """ - @classmethod - def identity_point(cls) -> None: - """ - Get the identity point (0, 1) of the curve. - Returns: - Ed25519Point: Identity point - """ - # The identity point - return None - def point_to_string(self, compressed: bool = True) -> bytes: - if getattr(self.curve, "HASH_TO_CURVE", None) != "tai": + if self.curve.e2c_variant != E2C_Variant.TAI: return super().point_to_string(compressed) if self.x is None and self.y is None: return bytes([0] * 32 + [0x40]) - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus x_bytes = int(self.x).to_bytes(32, "little") flag = 0x80 if int(self.y) > (-int(self.y) % p) else 0x00 return x_bytes + bytes([flag]) @classmethod - def string_to_point(cls, data: str | bytes): + def string_to_point(cls, data: str | bytes, curve: SWCurve): if isinstance(data, str): data = bytes.fromhex(data) - if getattr(cls.curve, "HASH_TO_CURVE", None) != "tai": - return super().string_to_point(data) + if curve.e2c_variant != E2C_Variant.TAI: + return super().string_to_point(data, curve) elif len(data) == 33 and data[0] in (0x02, 0x03): # Canonical SW encodings put flags in the final byte, so external # vectors can coincidentally start with SEC1 marker bytes. try: - return cls._string_to_canonical_point(data) + return cls._string_to_canonical_point(data, curve) except ValueError: - return super().string_to_point(data) + return super().string_to_point(data, curve) if len(data) != 33: raise ValueError(f"Invalid compressed point length: expected 33, got {len(data)}") - return cls._string_to_canonical_point(data) + return cls._string_to_canonical_point(data, curve) @classmethod - def _string_to_canonical_point(cls, data: bytes): + def _string_to_canonical_point(cls, data: bytes, curve: SWCurve): flag = data[-1] is_negative = (flag >> 7) & 1 is_infinity = (flag >> 6) & 1 @@ -180,23 +92,23 @@ def _string_to_canonical_point(cls, data: bytes): if is_infinity: if is_negative or any(data[:-1]): raise ValueError("Invalid infinity encoding") - return cls.identity() + return cls.identity(curve) x = int.from_bytes(data[:-1], "little") - if x >= cls.curve.PRIME_FIELD: + if x >= curve.params.field_modulus: raise ValueError("x-coordinate is not in field") - y_candidates = cls._y_recover(x) + y_candidates = cls._y_recover(x, curve) if y_candidates is None: raise ValueError("Invalid point") y, y_neg = y_candidates - return cls(x, y_neg if is_negative else y) + return cls(x, y_neg if is_negative else y, curve) @classmethod - def _y_recover(cls, x: int) -> tuple[int, int] | None: - p = cls.curve.PRIME_FIELD - y_square = (pow(x, 3, p) + cls.curve.WeierstrassA * x + cls.curve.WeierstrassB) % p + def _y_recover(cls, x: int, curve: SWCurve) -> tuple[int, int] | None: + p = curve.params.field_modulus + y_square = (pow(x, 3, p) + curve.params.a * x + curve.params.b) % p try: - y = cls.curve.mod_sqrt(y_square) + y = curve.mod_sqrt(y_square) except ValueError: return None neg_y = -y % p @@ -205,12 +117,18 @@ def _y_recover(cls, x: int) -> tuple[int, int] | None: P256_RO = CurveVariant( name="P256_RO", - curve=P256Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=P256_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=P256Point, ) P256_NU = CurveVariant( + name="P256_NU", + curve=SWCurve(params=P256_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=P256Point, +) + +P256_TAI = CurveVariant( name="P256_TAI", - curve=P256Curve(e2c_variant=E2C_Variant.TAI), - point=nu_variant(e2c_variant=E2C_Variant.TAI), + curve=SWCurve(params=P256_PARAMS, e2c_variant=E2C_Variant.TAI), + point_type=P256Point, ) diff --git a/dot_ring/curve/specs/p384.py b/dot_ring/curve/specs/p384.py index 5ec4415..019b085 100644 --- a/dot_ring/curve/specs/p384.py +++ b/dot_ring/curve/specs/p384.py @@ -1,129 +1,57 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + ShortWeierstrassCurveParams, +) - -@dataclass(frozen=True) -class P384Params: - """ - NIST P-384 curve parameters. - - The P-384 curve is a NIST-standardized Short Weierstrass curve providing - 192-bit security level, widely used in high-security applications. - """ - - # From RFC 9380 Section 8.3: P384_XMD:SHA-384_SSWU_RO_ - SUITE_STRING = b"P384_XMD:SHA-384_SSWU_RO_" - DST = b"QUUX-V01-CS02-with-P384_XMD:SHA-384_SSWU_RO_" # Default DST is the same as SUITE_STRING - - # Curve parameters for y² = x³ - 3x + b - PRIME_FIELD: Final[int] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF - ORDER: Final[int] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973 - COFACTOR: Final[int] = 1 - - # Generator point - GENERATOR_X: Final[int] = 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7 - GENERATOR_Y: Final[int] = 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F - - # Short Weierstrass parameters: y² = x³ + ax + b - WEIERSTRASS_A: Final[int] = -3 # a = -3 - WEIERSTRASS_B: Final[int] = 0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF - - # Z parameter for SSWU mapping (from RFC 9380 Section 8.3) - Z: Final[int] = -12 # P-384 uses Z = -12 for SSWU mapping - M: Final[int] = 1 # Field Extension Degree - K: Final[int] = 192 # Security level - # expand_message: Final[str] = "XMD" - H_A = hashlib.sha384 - ENDIAN = "little" - L: Final[int] = 72 - S_in_bytes: Final[int] = 128 - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 24 # 192 bits for P-384 - Requires_Isogeny: Final[bool] = False - # Blinding Base For Pedersen VRF - # These are arbitrary points on the curve for blinding - # sample blinding base - BBx: Final[int] = 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7 # noqa: E501 - ELL2_C1 = 0x1C9B74C1A04954B78B4B6035E97A5E078A5A0F28EC96D547BFEE9ACE803AC012345678901234567890123456789012 # noqa: E501 - BBy: Final[int] = 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F # noqa: E501 - ELL2_C2 = 0x2D3C6863973926E049E637CB1B5F40A36DAC28AF1766968C30C2313F3A38945678901234567890123456789012345 # noqa: E501 - Isogeny_Coeffs = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 49 - - -class P384Curve(SWCurve): - """ - NIST P-384 curve implementation. - - A high-security standardized curve providing 192-bit security level. - Defined by the equation y² = x³ - 3x + b over the prime field. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - """Initialize P-256 curve with its parameters.""" - # Default suite and dst - SUITE_STRING = P384Params.SUITE_STRING - DST = P384Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - super().__init__( - PRIME_FIELD=P384Params.PRIME_FIELD, - ORDER=P384Params.ORDER, - GENERATOR_X=P384Params.GENERATOR_X, - GENERATOR_Y=P384Params.GENERATOR_Y, - COFACTOR=P384Params.COFACTOR, - Z=P384Params.Z, - WeierstrassA=P384Params.WEIERSTRASS_A, - WeierstrassB=P384Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=P384Params.BBx, - BBy=P384Params.BBy, - M=P384Params.M, - K=P384Params.K, - L=P384Params.L, - S_in_bytes=P384Params.S_in_bytes, - H_A=P384Params.H_A, - Requires_Isogeny=P384Params.Requires_Isogeny, - Isogeny_Coeffs=P384Params.Isogeny_Coeffs, - UNCOMPRESSED=P384Params.UNCOMPRESSED, - ENDIAN=P384Params.ENDIAN, - POINT_LEN=P384Params.POINT_LEN, - CHALLENGE_LENGTH=P384Params.CHALLENGE_LENGTH, - ) - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> type[SWAffinePoint]: - class P384PointVariant(SWAffinePoint): - """Point on P384 with custom E2C variant""" - - curve: P384Curve = P384Curve(e2c_variant) - - return P384PointVariant +P384_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF, + subgroup_order=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973, + cofactor=1, + suite_id=b"P384_XMD:SHA-384_SSWU_RO_", + hash_fn=hashlib.sha384, + generator=( + 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7, + 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F, + ), + a=-3, + b=0xB3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-P384_XMD:SHA-384_SSWU_RO_", + z=-12, + field_extension_degree=1, + security_level=192, + field_length=72, + expand_len=128, + ), + encoding=EncodingParams(endian="little", point_len=49, challenge_len=24), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 0xAA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7, + 0x3617DE4A96262C6F5D9E98BF9292DC29F8F41DBD289A147CE9DA3113B5F0B8C00A60B1CE1D7E819D7A431D7C90EA0E5F, + ), + ), +) P384_RO = CurveVariant( name="P384_RO", - curve=P384Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=P384_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=SWAffinePoint, ) P384_NU = CurveVariant( name="P384_NU", - curve=P384Curve(e2c_variant=E2C_Variant.SSWU_NU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU_NU), + curve=SWCurve(params=P384_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=SWAffinePoint, ) diff --git a/dot_ring/curve/specs/p521.py b/dot_ring/curve/specs/p521.py index 040bff5..fe47307 100644 --- a/dot_ring/curve/specs/p521.py +++ b/dot_ring/curve/specs/p521.py @@ -1,142 +1,57 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + ShortWeierstrassCurveParams, +) - -@dataclass(frozen=True) -class P521Params: - """ - NIST P-521 curve parameters. - - The P-521 curve is a NIST-standardized Short Weierstrass curve providing - 256-bit security level, the highest security level in the NIST suite. - """ - - # From RFC 9380 Section 8.4: P521_XMD:SHA-512_SSWU_RO_ - SUITE_STRING = b"P521_XMD:SHA-512_SSWU_RO_" - DST = b"QUUX-V01-CS02-with-P521_XMD:SHA-512_SSWU_RO_" # Default DST is the same as SUITE_STRING - - # Curve parameters for y² = x³ - 3x + b - PRIME_FIELD: Final[int] = ( - 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF - ) - ORDER: Final[int] = ( - 0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409 - ) - COFACTOR: Final[int] = 1 - - # Generator point - GENERATOR_X: Final[int] = ( - 0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66 - ) - GENERATOR_Y: Final[int] = ( - 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650 - ) - - # Short Weierstrass parameters: y² = x³ + ax + b - WEIERSTRASS_A: Final[int] = -3 # a = -3 - WEIERSTRASS_B: Final[int] = ( - 0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00 - ) - - # Z parameter for SSWU mapping (from RFC 9380 Section 8.4) - Z: Final[int] = -4 # P-521 uses Z = -4 for SSWU mapping - M: Final[int] = 1 # Field Extension Degree - K: Final[int] = 256 # Security level - # expand_message: Final[str] = "XMD" - H_A = hashlib.sha512 - ENDIAN = "little" - L: Final[int] = 98 - S_in_bytes: Final[int] = 128 # 64 128 136 72 - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 32 # 256 bits for P-521 - Requires_Isogeny: Final[bool] = False - # Blinding Base For Pedersen VRF - # These are arbitrary points on the curve for blinding - BBx: Final[int] = ( - 0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66 # noqa: E501 - ) - ELL2_C1 = 0x01C9B74C1A04954B78B4B6035E97A5E078A5A0F28EC96D547BFEE9ACE803AC012345678901234567890123456789012345678901234567890123456789012345678 # noqa: E501 - BBy: Final[int] = ( - 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650 # noqa: E501 - ) - ELL2_C2 = 0x02D3C6863973926E049E637CB1B5F40A36DAC28AF1766968C30C2313F3A38945678901234567890123456789012345678901234567890123456789012345678901 # noqa: E501 - Isogeny_Coeffs = None - UNCOMPRESSED = False - POINT_LEN: Final[int] = 67 - - -class P521Curve(SWCurve): - """ - NIST P-521 curve implementation. - - The highest security level standardized curve in the NIST suite. - Defined by the equation y² = x³ - 3x + b over the prime field. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - """Initialize P-521 curve with its parameters.""" - # Default suite and dst - SUITE_STRING = P521Params.SUITE_STRING - DST = P521Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - super().__init__( - PRIME_FIELD=P521Params.PRIME_FIELD, - ORDER=P521Params.ORDER, - GENERATOR_X=P521Params.GENERATOR_X, - GENERATOR_Y=P521Params.GENERATOR_Y, - COFACTOR=P521Params.COFACTOR, - Z=P521Params.Z, - WeierstrassA=P521Params.WEIERSTRASS_A, - WeierstrassB=P521Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=P521Params.BBx, - BBy=P521Params.BBy, - M=P521Params.M, - K=P521Params.K, - L=P521Params.L, - S_in_bytes=P521Params.S_in_bytes, - H_A=P521Params.H_A, - Requires_Isogeny=P521Params.Requires_Isogeny, - Isogeny_Coeffs=P521Params.Isogeny_Coeffs, - UNCOMPRESSED=P521Params.UNCOMPRESSED, - ENDIAN=P521Params.ENDIAN, - POINT_LEN=P521Params.POINT_LEN, - CHALLENGE_LENGTH=P521Params.CHALLENGE_LENGTH, - ) - - -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> type[SWAffinePoint]: - class P521PointVariant(SWAffinePoint): - """Point on P521 with custom E2C variant""" - - curve: P521Curve = P521Curve(e2c_variant) - - return P521PointVariant +P521_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + subgroup_order=0x01FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409, + cofactor=1, + suite_id=b"P521_XMD:SHA-512_SSWU_RO_", + hash_fn=hashlib.sha512, + generator=( + 0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66, + 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650, + ), + a=-3, + b=0x0051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-P521_XMD:SHA-512_SSWU_RO_", + z=-4, + field_extension_degree=1, + security_level=256, + field_length=98, + expand_len=128, + ), + encoding=EncodingParams(endian="little", point_len=67, challenge_len=32), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 0x00C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66, + 0x011839296A789A3BC0045C8A5FB42C7D1BD998F54449579B446817AFBD17273E662C97EE72995EF42640C550B9013FAD0761353C7086A272C24088BE94769FD16650, + ), + ), +) P521_RO = CurveVariant( name="P521_RO", - curve=P521Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=P521_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=SWAffinePoint, ) P521_NU = CurveVariant( name="P521_NU", - curve=P521Curve(e2c_variant=E2C_Variant.SSWU_NU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU_NU), + curve=SWCurve(params=P521_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=SWAffinePoint, ) diff --git a/dot_ring/curve/specs/parameters.py b/dot_ring/curve/specs/parameters.py new file mode 100644 index 0000000..2f01b05 --- /dev/null +++ b/dot_ring/curve/specs/parameters.py @@ -0,0 +1,109 @@ +from __future__ import annotations + +from collections.abc import Callable +from dataclasses import dataclass +from typing import Any, Generic, Literal, TypeVar + +Coord = TypeVar("Coord") +HashConstructor = Callable[[], Any] + + +@dataclass(frozen=True, kw_only=True) +class ShortWeierstrassModel(Generic[Coord]): + """Short-Weierstrass coefficients for an auxiliary map curve.""" + + a: Coord + b: Coord + + +@dataclass(frozen=True, kw_only=True) +class Elligator2MontgomeryMap: + """Montgomery curve used by Elligator 2 before target-curve conversion.""" + + a: int + b: int + + + +@dataclass(frozen=True, kw_only=True) +class RationalIsogeny(Generic[Coord]): + """Rational isogeny map from an auxiliary curve to the target curve.""" + + map_curve: ShortWeierstrassModel[Coord] + x_numerator: tuple[Coord, ...] + x_denominator: tuple[Coord, ...] + y_numerator: tuple[Coord, ...] + y_denominator: tuple[Coord, ...] + + +@dataclass(frozen=True, kw_only=True) +class HashToCurveParams(Generic[Coord]): + """Hash-to-curve constants for a curve suite.""" + + dst: bytes + z: Coord + field_extension_degree: int + security_level: int + field_length: int + expand_len: int | None + hash_fn: HashConstructor | None = None + isogeny: RationalIsogeny[Coord] | None = None + elligator2_map: Elligator2MontgomeryMap | None = None + + +@dataclass(frozen=True, kw_only=True) +class EncodingParams: + """Point/scalar encoding constants.""" + + endian: Literal["little", "big"] + point_len: int + challenge_len: int + uncompressed: bool = False + + +@dataclass(frozen=True, kw_only=True) +class AuxiliaryPointParams(Generic[Coord]): + """Non-generator points used by VRF/ring-proof protocols.""" + + blinding_base: tuple[Coord, Coord] | None = None + accumulator_base: tuple[Coord, Coord] | None = None + padding_point: tuple[Coord, Coord] | None = None + + +@dataclass(frozen=True, kw_only=True) +class CurveParams(Generic[Coord]): + """Base constants shared by all curve forms.""" + + field_modulus: int + subgroup_order: int + cofactor: int + suite_id: bytes + hash_fn: HashConstructor + generator: tuple[Coord, Coord] + hash_to_curve: HashToCurveParams[Coord] + encoding: EncodingParams + auxiliary_points: AuxiliaryPointParams[Coord] = AuxiliaryPointParams() + + +@dataclass(frozen=True, kw_only=True) +class ShortWeierstrassCurveParams(CurveParams[Coord]): + """Parameters for curves of the form y^2 = x^3 + ax + b.""" + + a: Coord + b: Coord + + +@dataclass(frozen=True, kw_only=True) +class TwistedEdwardsCurveParams(CurveParams[int]): + """Parameters for curves of the form ax^2 + y^2 = 1 + dx^2y^2.""" + + a: int + d: int + + +@dataclass(frozen=True, kw_only=True) +class MontgomeryCurveParams(CurveParams[int]): + """Parameters for curves of the form Bv^2 = u^3 + Au^2 + u.""" + + a: int + b: int diff --git a/dot_ring/curve/specs/secp256k1.py b/dot_ring/curve/specs/secp256k1.py index ee5d07b..8303c12 100644 --- a/dot_ring/curve/specs/secp256k1.py +++ b/dot_ring/curve/specs/secp256k1.py @@ -1,202 +1,106 @@ from __future__ import annotations import hashlib -from dataclasses import dataclass -from typing import Final, Self, cast +from typing import Self, cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.e2c import E2C_Variant -from ..glv import GLV from ..short_weierstrass.sw_affine_point import SWAffinePoint from ..short_weierstrass.sw_curve import SWCurve - - -@dataclass(frozen=True) -class Secp256k1Params: - """ - secp256k1 curve parameters. - - The secp256k1 curve is a Short Weierstrass curve widely used in Bitcoin - and other cryptocurrencies. It's defined by y² = x³ + 7. - """ - - SUITE_STRING = b"secp256k1_XMD:SHA-256_SSWU_RO_" - DST = b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_" - - # Curve parameters for y² = x³ + 7 - - PRIME_FIELD: Final[int] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F - ORDER: Final[int] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - COFACTOR: Final[int] = 1 - # Generator point - GENERATOR_X: Final[int] = 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798 - GENERATOR_Y: Final[int] = 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8 - - # Short Weierstrass parameters: y² = x³ + ax + b - WEIERSTRASS_A: Final[int] = 0 # a = 0 - WEIERSTRASS_B: Final[int] = 7 # b = 7 - - # GLV parameters for secp256k1 - GLV_LAMBDA: Final[int] = 0x5363AD4CC05C30E0A5261C028812645A122E22EA20816678DF02967C1B23BD72 - GLV_B: Final[int] = 0x3086D221A7D46BCDE86C90E49284EB15 - GLV_C: Final[int] = 0xE4437ED6010E88286F547FA90ABFE4C3 - - # Challenge length in bytes for VRF (from RFC 9381) - CHALLENGE_LENGTH: Final[int] = 16 # 128 bits - - # Z parameter for SSWU mapping - # Z: Final[int] = 1 - Z: Final[int] = -11 # P-256 uses Z = -11 for SSWU mapping - M: Final[int] = 1 # Field Extension Degree - K: Final[int] = 128 # Security level - # expand_message: Final[str] = "XMD" - H_A = hashlib.sha256 - ENDIAN = "little" - L: Final[int] = 48 - S_in_bytes: Final[int] = 64 - # Blinding Base For Pedersen VRF - # These are arbitrary points on the curve for blinding - BBx: Final[int] = 0x50929B74C1A04954B78B4B6035E97A5E078A5A0F28EC96D547BFEE9ACE803AC0 - BBy: Final[int] = 0x31D3C6863973926E049E637CB1B5F40A36DAC28AF1766968C30C2313F3A38904 - Requires_Isogeny: Final[bool] = True - Isogeny_Coeffs = { - "x_num": [ - 0x8E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38DAAAAA88C, - 0x534C328D23F234E6E2A413DECA25CAECE4506144037C40314ECBD0B53D9DD262, - 0x7D3D4C80BC321D5B9F315CEA7FD44C5D595D2FC0BF63B92DFFF1044F17C6581, - 0x8E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38DAAAAA8C7, - ], - "x_den": [ - 0x1, # leading coefficient is 1 for x'^2 term - 0xEDADC6F64383DC1DF7C4B2D51B54225406D36B641F5E41BBC52A56612A8C6D14, - 0xD35771193D94918A9CA34CCBB7B640DD86CD409542F8487D9FE6B745781EB49B, - ], - "y_num": [ - 0x2F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F38E38D84, - 0x29A6194691F91A73715209EF6512E576722830A201BE2018A765E85A9ECEE931, - 0xC75E0C32D5CB7C0FA9D0A54B12A0A6D5647AB046D686DA6FDFFC90FC201D71A3, - 0x4BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684B8E38E23C, - ], - "y_den": [ - 0x1, # leading coefficient is 1 for x'^3 term - 0x6484AA716545CA2CF3A70C3FA8FE337E0A3D21162F0D6299A7BF8192BFD2A76F, - 0x7A06534BB8BDB49FD5E9E6632722C2989467C1BFC8E8D978DFB425D2685C2573, - 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFF93B, - ], - } - UNCOMPRESSED = False - POINT_LEN: Final[int] = 33 - - -"""GLV endomorphism parameters for secp256k1 curve.""" -Secp256k1GLVSpecs = GLV( - lambda_param=Secp256k1Params.GLV_LAMBDA, - constant_b=Secp256k1Params.GLV_B, - constant_c=Secp256k1Params.GLV_C, +from .parameters import ( + AuxiliaryPointParams, + EncodingParams, + HashToCurveParams, + RationalIsogeny, + ShortWeierstrassCurveParams, + ShortWeierstrassModel, ) - -class Secp256k1Curve(SWCurve): - """ - secp256k1 curve implementation. - - The curve used by Bitcoin and many other cryptocurrencies. - Defined by the equation y² = x³ + 7 over the prime field. - """ - - def __init__(self, e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> None: - """Initialize secp256k1 curve with its parameters.""" - # Default suite and dst - SUITE_STRING = Secp256k1Params.SUITE_STRING - DST = Secp256k1Params.DST - # Replace RO with NU automatically if variant endswith "NU_" - if e2c_variant.value.endswith("NU_"): - SUITE_STRING = SUITE_STRING.replace(b"_RO_", b"_NU_") - DST = DST.replace(b"_RO_", b"_NU_") - - super().__init__( - PRIME_FIELD=Secp256k1Params.PRIME_FIELD, - ORDER=Secp256k1Params.ORDER, - GENERATOR_X=Secp256k1Params.GENERATOR_X, - GENERATOR_Y=Secp256k1Params.GENERATOR_Y, - COFACTOR=Secp256k1Params.COFACTOR, - Z=Secp256k1Params.Z, - WeierstrassA=Secp256k1Params.WEIERSTRASS_A, - WeierstrassB=Secp256k1Params.WEIERSTRASS_B, - SUITE_STRING=SUITE_STRING, - DST=DST, - E2C=e2c_variant, - BBx=Secp256k1Params.BBx, - BBy=Secp256k1Params.BBy, - M=Secp256k1Params.M, - K=Secp256k1Params.K, - L=Secp256k1Params.L, - S_in_bytes=Secp256k1Params.S_in_bytes, - H_A=Secp256k1Params.H_A, - Requires_Isogeny=Secp256k1Params.Requires_Isogeny, - Isogeny_Coeffs=Secp256k1Params.Isogeny_Coeffs, - UNCOMPRESSED=Secp256k1Params.UNCOMPRESSED, - ENDIAN=Secp256k1Params.ENDIAN, - POINT_LEN=Secp256k1Params.POINT_LEN, - CHALLENGE_LENGTH=Secp256k1Params.CHALLENGE_LENGTH, - ) +SECP256K1_ISOGENY = RationalIsogeny[int]( + map_curve=ShortWeierstrassModel( + a=0x3F8731ABDD661ADCA08A5558F0F5D272E953D363CB6F0E5D405447C01A444533, + b=1771, + ), + x_numerator=( + 0x8E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38DAAAAA88C, + 0x534C328D23F234E6E2A413DECA25CAECE4506144037C40314ECBD0B53D9DD262, + 0x7D3D4C80BC321D5B9F315CEA7FD44C5D595D2FC0BF63B92DFFF1044F17C6581, + 0x8E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38E38DAAAAA8C7, + ), + x_denominator=( + 0x1, + 0xEDADC6F64383DC1DF7C4B2D51B54225406D36B641F5E41BBC52A56612A8C6D14, + 0xD35771193D94918A9CA34CCBB7B640DD86CD409542F8487D9FE6B745781EB49B, + ), + y_numerator=( + 0x2F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F38E38D84, + 0x29A6194691F91A73715209EF6512E576722830A201BE2018A765E85A9ECEE931, + 0xC75E0C32D5CB7C0FA9D0A54B12A0A6D5647AB046D686DA6FDFFC90FC201D71A3, + 0x4BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684BDA12F684B8E38E23C, + ), + y_denominator=( + 0x1, + 0x6484AA716545CA2CF3A70C3FA8FE337E0A3D21162F0D6299A7BF8192BFD2A76F, + 0x7A06534BB8BDB49FD5E9E6632722C2989467C1BFC8E8D978DFB425D2685C2573, + 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFF93B, + ), +) -Secp256k1_SW_Curve: Final[Secp256k1Curve] = Secp256k1Curve() +SECP256K1_PARAMS = ShortWeierstrassCurveParams[int]( + field_modulus=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F, + subgroup_order=0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141, + cofactor=1, + suite_id=b"secp256k1_XMD:SHA-256_SSWU_RO_", + hash_fn=hashlib.sha256, + generator=( + 0x79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798, + 0x483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8, + ), + a=0, + b=7, + hash_to_curve=HashToCurveParams( + dst=b"QUUX-V01-CS02-with-secp256k1_XMD:SHA-256_SSWU_RO_", + z=-11, + field_extension_degree=1, + security_level=128, + field_length=48, + expand_len=64, + isogeny=SECP256K1_ISOGENY, + ), + encoding=EncodingParams(endian="little", point_len=33, challenge_len=16), + auxiliary_points=AuxiliaryPointParams( + blinding_base=( + 0x50929B74C1A04954B78B4B6035E97A5E078A5A0F28EC96D547BFEE9ACE803AC0, + 0x31D3C6863973926E049E637CB1B5F40A36DAC28AF1766968C30C2313F3A38904, + ), + ), +) class Secp256k1Point(SWAffinePoint): - """ - Point on the secp256k1 curve. - - Implements optimized point operations specific to the secp256k1 curve, - including GLV scalar multiplication for enhanced performance. - """ + """Point on secp256k1.""" def __mul__(self, scalar: int) -> Self: - """ - GLV-optimized scalar multiplication for secp256k1. - - Args: - k: Scalar to multiply by - - Returns: - Secp256k1Point: Result of scalar multiplication - """ if scalar == 0: - return cast(Self, self.identity()) + return cast(Self, self.identity(self.curve)) if scalar < 0: return cast(Self, (-self).__mul__(-scalar)) - # GLV decomposition: k = k1 + k2*λ (mod n) - # where λ is the GLV parameter - scalar %= self.curve.ORDER - - # Simple GLV decomposition (can be optimized further) - # For now, fall back to standard multiplication - # TODO: Implement full GLV decomposition + scalar %= self.curve.params.subgroup_order return cast(Self, super().__mul__(scalar)) -def nu_variant(e2c_variant: E2C_Variant = E2C_Variant.SSWU) -> type[SWAffinePoint]: - class Secp256k1PointVariant(Secp256k1Point): - """Point on Secp256k1 with custom E2C variant""" - - curve: Secp256k1Curve = Secp256k1Curve(e2c_variant) - - return Secp256k1PointVariant - - Secp256k1_RO = CurveVariant( name="Secp256k1_RO", - curve=Secp256k1Curve(e2c_variant=E2C_Variant.SSWU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU), + curve=SWCurve(params=SECP256K1_PARAMS, e2c_variant=E2C_Variant.SSWU), + point_type=Secp256k1Point, ) Secp256k1_NU = CurveVariant( name="Secp256k1_NU", - curve=Secp256k1Curve(e2c_variant=E2C_Variant.SSWU_NU), - point=nu_variant(e2c_variant=E2C_Variant.SSWU_NU), + curve=SWCurve(params=SECP256K1_PARAMS, e2c_variant=E2C_Variant.SSWU_NU), + point_type=Secp256k1Point, ) diff --git a/tests/test_coverage/test_curve_base.py b/tests/test_coverage/test_curve_base.py index 1a589a4..fbd92e6 100644 --- a/tests/test_coverage/test_curve_base.py +++ b/tests/test_coverage/test_curve_base.py @@ -1,7 +1,7 @@ """Additional tests for curve.py module to improve coverage.""" from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve -from dot_ring.curve.specs.ed25519 import Ed25519_RO, Ed25519Curve +from dot_ring.curve.specs.ed25519 import Ed25519_RO class TestCurveVariant: @@ -15,8 +15,8 @@ def test_bandersnatch_te_curve_params(self): """Test Bandersnatch TE curve parameters.""" curve = Bandersnatch_TE_Curve - assert curve.PRIME_FIELD is not None - assert curve.ORDER is not None + assert curve.params.field_modulus is not None + assert curve.params.subgroup_order is not None class TestEdwardsCurve: @@ -24,21 +24,21 @@ class TestEdwardsCurve: def test_ed25519_curve_params(self): """Test Ed25519 curve parameters.""" - curve = Ed25519Curve() + curve = Ed25519_RO.curve - assert curve.PRIME_FIELD is not None - assert curve.ORDER is not None - assert curve.GENERATOR_X is not None - assert curve.GENERATOR_Y is not None + assert curve.params.field_modulus is not None + assert curve.params.subgroup_order is not None + assert curve.params.generator[0] is not None + assert curve.params.generator[1] is not None def test_ed25519_curve_cofactor(self): """Test Ed25519 cofactor.""" - curve = Ed25519Curve() + curve = Ed25519_RO.curve - assert curve.COFACTOR == 8 + assert curve.params.cofactor == 8 def test_ed25519_curve_name(self): """Test Ed25519 curve has name-like attribute.""" - curve = Ed25519Curve() + curve = Ed25519_RO.curve # Just test it doesn't crash str(curve) diff --git a/tests/test_curve_ops/test_curves_s2p_p2s.py b/tests/test_curve_ops/test_curves_s2p_p2s.py index 3a3be28..00090b7 100644 --- a/tests/test_curve_ops/test_curves_s2p_p2s.py +++ b/tests/test_curve_ops/test_curves_s2p_p2s.py @@ -5,21 +5,21 @@ def test_s2p_p2s_correctness(): # test for Bandersnatch curve - point = Bandersnatch.point.generator_point() + point = Bandersnatch.generator_point() S_repr = point.point_to_string() - assert point == Bandersnatch.point.string_to_point(S_repr), "Invalid Conversion" + assert point == Bandersnatch.string_to_point(S_repr), "Invalid Conversion" # Test for P256 curve - point = P256_RO.point.generator_point() + point = P256_RO.generator_point() S_repr = point.point_to_string() - assert point == P256_RO.point.string_to_point(S_repr), "Invalid Conversion" + assert point == P256_RO.string_to_point(S_repr), "Invalid Conversion" # Test for P256 Uncompressed - point = P256_RO.point.generator_point() + point = P256_RO.generator_point() S_repr = point.point_to_string(compressed=True) - assert point == P256_RO.point.string_to_point(S_repr), "Invalid Conversion" + assert point == P256_RO.string_to_point(S_repr), "Invalid Conversion" # Test for Curve25519 curve - point = Curve25519_RO.point.generator_point() + point = Curve25519_RO.generator_point() S_repr = point.point_to_string() - assert point == Curve25519_RO.point.string_to_point(S_repr), "Invalid Conversion" + assert point == Curve25519_RO.string_to_point(S_repr), "Invalid Conversion" diff --git a/tests/test_h2c_suites/test_bls12_381_G2_ssw_nu.py b/tests/test_h2c_suites/test_bls12_381_G2_ssw_nu.py index fe18006..f03e16f 100644 --- a/tests/test_h2c_suites/test_bls12_381_G2_ssw_nu.py +++ b/tests/test_h2c_suites/test_bls12_381_G2_ssw_nu.py @@ -2,11 +2,8 @@ import os import unittest -from dot_ring.curve.e2c import E2C_Variant -from dot_ring.curve.field_element import FieldElement -from dot_ring.curve.specs.bls12_381_G2 import nu_variant - -BLS12_381_G2Point = nu_variant(E2C_Variant.SSWU_NU) +from dot_ring.curve.fp2 import Fp2 +from dot_ring.curve.specs.bls12_381_G2 import BLS12_381_G2_NU class TestBLS12_381_G2_SSWU_RO(unittest.TestCase): @@ -17,75 +14,29 @@ def setUpClass(cls): with open(test_vectors_path) as f: cls.test_vectors = json.load(f) - def hex_to_field_element(self, hex_str, p): - """Convert hex string to integer and create FieldElement""" - # Remove '0x' prefix if present + def hex_to_int(self, hex_str, p): hex_str = hex_str.lower().replace("0x", "") - # Convert to integer and create FieldElement return int(hex_str, 16) % p + def hex_to_fp2(self, value, p): + return Fp2(self.hex_to_int(value["re"], p), self.hex_to_int(value["im"], p), p) + def test_sswu_hash2_curve(self): """Test SSWU hash-to-curve for BLS12-381 G2""" for i, vector in enumerate(self.test_vectors["vectors"]): with self.subTest(f"Test Vector {i + 1}"): msg = vector["msg"] expected_P = vector["P"] - expected_Q0 = vector["Q0"] - expected_u = vector["u"] # Get the prime modulus for Fp - p = BLS12_381_G2Point.curve.PRIME_FIELD - - # Convert sample.py values to FieldElements - expected_u0 = ( - self.hex_to_field_element(expected_u[0]["re"], p), - self.hex_to_field_element(expected_u[0]["im"], p), - ) + p = BLS12_381_G2_NU.curve.params.field_modulus # Encode message to curve - result = BLS12_381_G2Point.encode_to_curve(msg.encode("utf-8"), b"", True) - - # Extract computed values - computed_P = result["R"] - computed_Q0 = result["Q0"] - computed_u0 = tuple(result["u"]) - - # Create sample.py FieldElement objects for comparison - expected_Q0_x = FieldElement( - self.hex_to_field_element(expected_Q0["x"]["re"], p), - self.hex_to_field_element(expected_Q0["x"]["im"], p), - p, - ) - expected_Q0_y = FieldElement( - self.hex_to_field_element(expected_Q0["y"]["re"], p), - self.hex_to_field_element(expected_Q0["y"]["im"], p), - p, - ) - # # Assert u values match - self.assertEqual(computed_u0, expected_u0, "u0 does not match") - - # Assert Q0 matches - # computed_Q0.x is a tuple (re, im), expected_Q0_x is FieldElement - self.assertEqual( - computed_Q0.x, - (expected_Q0_x.re, expected_Q0_x.im), - "Q0.x does not match", - ) - self.assertEqual( - computed_Q0.y, - (expected_Q0_y.re, expected_Q0_y.im), - "Q0.y does not match", - ) + computed_P = BLS12_381_G2_NU.encode_to_curve(msg.encode("utf-8"), b"") # Create sample.py P point - expected_P_x = ( - self.hex_to_field_element(expected_P["x"]["re"], p), - self.hex_to_field_element(expected_P["x"]["im"], p), - ) - expected_P_y = ( - self.hex_to_field_element(expected_P["y"]["re"], p), - self.hex_to_field_element(expected_P["y"]["im"], p), - ) + expected_P_x = self.hex_to_fp2(expected_P["x"], p) + expected_P_y = self.hex_to_fp2(expected_P["y"], p) assert expected_P_x == computed_P.x, "P.x does not match" assert expected_P_y == computed_P.y, "P.y does not match" diff --git a/tests/test_h2c_suites/test_bls12_381_G2_ssw_ro.py b/tests/test_h2c_suites/test_bls12_381_G2_ssw_ro.py index df545b1..10fb9e5 100644 --- a/tests/test_h2c_suites/test_bls12_381_G2_ssw_ro.py +++ b/tests/test_h2c_suites/test_bls12_381_G2_ssw_ro.py @@ -2,7 +2,7 @@ import os import unittest -from dot_ring.curve.field_element import FieldElement +from dot_ring.curve.fp2 import Fp2 from dot_ring.curve.specs.bls12_381_G2 import BLS12_381_G2_RO @@ -14,107 +14,29 @@ def setUpClass(cls): with open(test_vectors_path) as f: cls.test_vectors = json.load(f) - def hex_to_field_element(self, hex_str, p): - """Convert hex string to integer and create FieldElement""" - # Remove '0x' prefix if present + def hex_to_int(self, hex_str, p): hex_str = hex_str.lower().replace("0x", "") - # Convert to integer and create FieldElement return int(hex_str, 16) % p + def hex_to_fp2(self, value, p): + return Fp2(self.hex_to_int(value["re"], p), self.hex_to_int(value["im"], p), p) + def test_sswu_hash2_curve(self): """Test SSWU hash-to-curve for BLS12-381 G2""" for i, vector in enumerate(self.test_vectors["vectors"]): with self.subTest(f"Test Vector {i + 1}"): msg = vector["msg"] expected_P = vector["P"] - expected_Q0 = vector["Q0"] - expected_Q1 = vector["Q1"] - expected_u = vector["u"] # Get the prime modulus for Fp - p = BLS12_381_G2_RO.curve.PRIME_FIELD - - # Convert sample.py values to FieldElements - expected_u0 = FieldElement( - self.hex_to_field_element(expected_u[0]["re"], p), - self.hex_to_field_element(expected_u[0]["im"], p), - p, - ) - expected_u1 = FieldElement( - self.hex_to_field_element(expected_u[1]["re"], p), - self.hex_to_field_element(expected_u[1]["im"], p), - p, - ) + p = BLS12_381_G2_RO.curve.params.field_modulus # Encode message to curve - result = BLS12_381_G2_RO.point.encode_to_curve(msg.encode("utf-8"), b"", True) - - # Extract computed values - computed_P = result["R"] - computed_Q0 = result["Q0"] - computed_Q1 = result["Q1"] - computed_u0, computed_u1 = result["u"] - - # Create sample.py FieldElement objects for comparison - expected_Q0_x = FieldElement( - self.hex_to_field_element(expected_Q0["x"]["re"], p), - self.hex_to_field_element(expected_Q0["x"]["im"], p), - p, - ) - expected_Q0_y = FieldElement( - self.hex_to_field_element(expected_Q0["y"]["re"], p), - self.hex_to_field_element(expected_Q0["y"]["im"], p), - p, - ) - expected_Q1_x = FieldElement( - self.hex_to_field_element(expected_Q1["x"]["re"], p), - self.hex_to_field_element(expected_Q1["x"]["im"], p), - p, - ) - expected_Q1_y = FieldElement( - self.hex_to_field_element(expected_Q1["y"]["re"], p), - self.hex_to_field_element(expected_Q1["y"]["im"], p), - p, - ) - - # # Assert u values match - self.assertEqual(computed_u0, expected_u0, "u0 does not match") - self.assertEqual(computed_u1, expected_u1, "u1 does not match") - - # Assert Q0 matches - # computed_Q0.x is a tuple (re, im), expected_Q0_x is FieldElement - self.assertEqual( - computed_Q0.x, - (expected_Q0_x.re, expected_Q0_x.im), - "Q0.x does not match", - ) - self.assertEqual( - computed_Q0.y, - (expected_Q0_y.re, expected_Q0_y.im), - "Q0.y does not match", - ) - - # Assert Q1 matches - self.assertEqual( - computed_Q1.x, - (expected_Q1_x.re, expected_Q1_x.im), - "Q1.x does not match", - ) - self.assertEqual( - computed_Q1.y, - (expected_Q1_y.re, expected_Q1_y.im), - "Q1.y does not match", - ) + computed_P = BLS12_381_G2_RO.encode_to_curve(msg.encode("utf-8"), b"") # Create sample.py P point - expected_P_x = ( - self.hex_to_field_element(expected_P["x"]["re"], p), - self.hex_to_field_element(expected_P["x"]["im"], p), - ) - expected_P_y = ( - self.hex_to_field_element(expected_P["y"]["re"], p), - self.hex_to_field_element(expected_P["y"]["im"], p), - ) + expected_P_x = self.hex_to_fp2(expected_P["x"], p) + expected_P_y = self.hex_to_fp2(expected_P["y"], p) assert expected_P_x == computed_P.x, "P.x does not match" assert expected_P_y == computed_P.y, "P.y does not match" diff --git a/tests/test_h2c_suites/test_e2c_bandersnatch.py b/tests/test_h2c_suites/test_e2c_bandersnatch.py index a22a328..798eb3c 100644 --- a/tests/test_h2c_suites/test_e2c_bandersnatch.py +++ b/tests/test_h2c_suites/test_e2c_bandersnatch.py @@ -1,4 +1,4 @@ -from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchPoint +from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_TE_Curve, BandersnatchPoint def test_h2f(): @@ -16,8 +16,8 @@ def test_m2c(): data = bytes("foo", "utf-8") u = Bandersnatch_TE_Curve.hash_to_field(data, 2) - p0 = BandersnatchPoint.map_to_curve(u[0]) - p1 = BandersnatchPoint.map_to_curve(u[1]) + p0 = BandersnatchPoint.map_to_curve(u[0], Bandersnatch_TE_Curve) + p1 = BandersnatchPoint.map_to_curve(u[1], Bandersnatch_TE_Curve) assert p0.x == 8864805491392651408849860969071502422330330403516577719645408615048305804698 assert p0.y == 3141991639291324936022779954882288522159181883763336030616570191472121730763 @@ -28,7 +28,7 @@ def test_m2c(): def test_e2c(): data = bytes("foo", "utf-8") - u = BandersnatchPoint.encode_to_curve(data) + u = Bandersnatch.encode_to_curve(data) assert u.x == 41706851287321768980670436615954402659160947743433584884323702829779219804533 assert u.y == 45261115535002764022712885934321790255618221679857277845409327068867281988279 diff --git a/tests/test_h2c_suites/test_h2c_nu.py b/tests/test_h2c_suites/test_h2c_nu.py index 4775ace..14e0218 100644 --- a/tests/test_h2c_suites/test_h2c_nu.py +++ b/tests/test_h2c_suites/test_h2c_nu.py @@ -3,38 +3,35 @@ import pytest -from dot_ring.curve.e2c import E2C_Variant -from dot_ring.curve.specs.bls12_381_G1 import nu_variant as bls12_381_G1_nu_variant -from dot_ring.curve.specs.curve448 import nu_variant as curve448_nu_variant -from dot_ring.curve.specs.curve25519 import nu_variant as curve25519_nu_variant -from dot_ring.curve.specs.ed448 import nu_variant as ed448_nu_variant -from dot_ring.curve.specs.ed25519 import nu_variant as ed25519_nu_variant -from dot_ring.curve.specs.p256 import nu_variant as p256_nu_variant -from dot_ring.curve.specs.p384 import nu_variant as p384_nu_variant -from dot_ring.curve.specs.p521 import nu_variant as p521_nu_variant -from dot_ring.curve.specs.secp256k1 import nu_variant as secp256k1_nu_variant +from dot_ring.curve.specs.bls12_381_G1 import BLS12_381_G1_NU +from dot_ring.curve.specs.curve448 import Curve448_NU +from dot_ring.curve.specs.curve25519 import Curve25519_NU +from dot_ring.curve.specs.ed448 import Ed448_NU +from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.p256 import P256_NU +from dot_ring.curve.specs.p384 import P384_NU +from dot_ring.curve.specs.p521 import P521_NU +from dot_ring.curve.specs.secp256k1 import Secp256k1_NU HERE = os.path.dirname(__file__) -# (point_factory, json_file, byte_size, variant) +# (curve_variant, json_file, byte_size) TEST_CASES = [ - (lambda: bls12_381_G1_nu_variant(E2C_Variant.SSWU_NU), "bls12_381_G1_nu.json", 48), - (lambda: curve25519_nu_variant(E2C_Variant.ELL2_NU), "curve25519_nu.json", 32), - (lambda: curve448_nu_variant(E2C_Variant.ELL2_NU), "curve448_nu.json", 56), - (lambda: ed25519_nu_variant(E2C_Variant.ELL2_NU), "ed25519_nu.json", 32), - (lambda: ed448_nu_variant(E2C_Variant.ELL2_NU), "ed448_nu.json", 56), - (lambda: p256_nu_variant(E2C_Variant.SSWU_NU), "p256_nu.json", 32), - (lambda: p384_nu_variant(E2C_Variant.SSWU_NU), "p384_nu.json", 48), - (lambda: p521_nu_variant(E2C_Variant.SSWU_NU), "p521_nu.json", 66), - (lambda: secp256k1_nu_variant(E2C_Variant.SSWU_NU), "secp256k1_nu.json", 32), + (BLS12_381_G1_NU, "bls12_381_G1_nu.json", 48), + (Curve25519_NU, "curve25519_nu.json", 32), + (Curve448_NU, "curve448_nu.json", 56), + (Ed25519_NU, "ed25519_nu.json", 32), + (Ed448_NU, "ed448_nu.json", 56), + (P256_NU, "p256_nu.json", 32), + (P384_NU, "p384_nu.json", 48), + (P521_NU, "p521_nu.json", 66), + (Secp256k1_NU, "secp256k1_nu.json", 32), ] -@pytest.mark.parametrize("point_factory, json_file, byte_size", TEST_CASES) -def test_h2c_nu(point_factory, json_file, byte_size): +@pytest.mark.parametrize("curve_variant, json_file, byte_size", TEST_CASES) +def test_h2c_nu(curve_variant, json_file, byte_size): """Test hash-to-curve NU (non-uniform) variant""" - point_class = point_factory() - json_path = os.path.join(HERE, "../vectors/h2c", json_file) with open(json_path) as f: data = json.load(f) @@ -43,12 +40,10 @@ def test_h2c_nu(point_factory, json_file, byte_size): for i, t in enumerate(vectors, start=1): msg_bytes = t["msg"].encode("utf-8") - out = point_class.encode_to_curve(msg_bytes, b"", True) + point = curve_variant.encode_to_curve(msg_bytes, b"") - u0_bytes = out["u"][0].to_bytes(byte_size, "big").hex() - Px_bytes = out["P"][0].to_bytes(byte_size, "big").hex() - Py_bytes = out["P"][1].to_bytes(byte_size, "big").hex() + Px_bytes = point.x.to_bytes(byte_size, "big").hex() + Py_bytes = point.y.to_bytes(byte_size, "big").hex() - assert u0_bytes == t["u"][0], f"u0 mismatch at vector {i}" assert Px_bytes == t["P"]["x"], f"P.x mismatch at vector {i}" assert Py_bytes == t["P"]["y"], f"P.y mismatch at vector {i}" diff --git a/tests/test_h2c_suites/test_h2c_ro.py b/tests/test_h2c_suites/test_h2c_ro.py index 447a56a..e13b6ff 100644 --- a/tests/test_h2c_suites/test_h2c_ro.py +++ b/tests/test_h2c_suites/test_h2c_ro.py @@ -15,22 +15,22 @@ HERE = os.path.dirname(__file__) -# (point_class, json_file, byte_size, has_Q_points) +# (curve_variant, json_file, byte_size) TEST_CASES = [ - (BLS12_381_G1_RO.point, "bls12_381_G1_ro.json", 48, False), - (Curve25519_RO.point, "curve25519_ro.json", 32, False), - (Curve448_RO.point, "curve448_ro.json", 56, False), - (Ed25519_RO.point, "ed25519_ro.json", 32, True), - (Ed448_RO.point, "ed448_ro.json", 56, False), - (P256_RO.point, "p256_ro.json", 32, False), - (P384_RO.point, "p384_ro.json", 48, False), - (P521_RO.point, "p521_ro.json", 66, False), - (Secp256k1_RO.point, "secp256k1_ro.json", 32, False), + (BLS12_381_G1_RO, "bls12_381_G1_ro.json", 48), + (Curve25519_RO, "curve25519_ro.json", 32), + (Curve448_RO, "curve448_ro.json", 56), + (Ed25519_RO, "ed25519_ro.json", 32), + (Ed448_RO, "ed448_ro.json", 56), + (P256_RO, "p256_ro.json", 32), + (P384_RO, "p384_ro.json", 48), + (P521_RO, "p521_ro.json", 66), + (Secp256k1_RO, "secp256k1_ro.json", 32), ] -@pytest.mark.parametrize("point_class, json_file, byte_size, has_Q_points", TEST_CASES) -def test_h2c_ro(point_class, json_file, byte_size, has_Q_points): +@pytest.mark.parametrize("curve_variant, json_file, byte_size", TEST_CASES) +def test_h2c_ro(curve_variant, json_file, byte_size): """Test hash-to-curve RO (random oracle) variant""" json_path = os.path.join(HERE, "../vectors/h2c", json_file) with open(json_path) as f: @@ -40,25 +40,10 @@ def test_h2c_ro(point_class, json_file, byte_size, has_Q_points): for i, t in enumerate(vectors, start=1): msg_bytes = t["msg"].encode("utf-8") - out = point_class.encode_to_curve(msg_bytes, b"", True) + point = curve_variant.encode_to_curve(msg_bytes, b"") - u0_bytes = out["u"][0].to_bytes(byte_size, "big").hex() - u1_bytes = out["u"][1].to_bytes(byte_size, "big").hex() - Px_bytes = out["P"][0].to_bytes(byte_size, "big").hex() - Py_bytes = out["P"][1].to_bytes(byte_size, "big").hex() + Px_bytes = point.x.to_bytes(byte_size, "big").hex() + Py_bytes = point.y.to_bytes(byte_size, "big").hex() - assert u0_bytes == t["u"][0], f"u0 mismatch at vector {i}" - assert u1_bytes == t["u"][1], f"u1 mismatch at vector {i}" assert Px_bytes == t["P"]["x"], f"P.x mismatch at vector {i}" assert Py_bytes == t["P"]["y"], f"P.y mismatch at vector {i}" - - if has_Q_points: - Q0x_bytes = out["Q0"][0].to_bytes(byte_size, "big").hex() - Q0y_bytes = out["Q0"][1].to_bytes(byte_size, "big").hex() - Q1x_bytes = out["Q1"][0].to_bytes(byte_size, "big").hex() - Q1y_bytes = out["Q1"][1].to_bytes(byte_size, "big").hex() - - assert Q0x_bytes == t["Q0"]["x"], f"Q0.x mismatch at vector {i}" - assert Q0y_bytes == t["Q0"]["y"], f"Q0.y mismatch at vector {i}" - assert Q1x_bytes == t["Q1"]["x"], f"Q1.x mismatch at vector {i}" - assert Q1y_bytes == t["Q1"]["y"], f"Q1.y mismatch at vector {i}" From cf079cbc0261bfdbbb07064d25a8779995adaaba Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:37:30 +0530 Subject: [PATCH 20/33] clean point model --- dot_ring/curve/fp2.py | 186 ++++++++++ dot_ring/curve/montgomery/mg_affine_point.py | 163 ++++---- dot_ring/curve/montgomery/mg_curve.py | 38 +- dot_ring/curve/point.py | 228 ++++-------- .../short_weierstrass/sw_affine_point.py | 312 +++++++--------- dot_ring/curve/short_weierstrass/sw_curve.py | 60 ++- .../curve/twisted_edwards/te_affine_point.py | 262 ++++++------- dot_ring/curve/twisted_edwards/te_curve.py | 120 +++--- .../twisted_edwards/te_projective_point.py | 28 +- tests/test_coverage/test_bls12_381_g2.py | 27 +- tests/test_coverage/test_sw_curve.py | 42 +-- tests/test_curve_ops/test_curve.py | 350 ++++++------------ tests/test_curve_ops/test_curve_ops.py | 64 ++-- tests/test_curve_ops/test_mg_curve.py | 83 ++--- 14 files changed, 878 insertions(+), 1085 deletions(-) create mode 100644 dot_ring/curve/fp2.py diff --git a/dot_ring/curve/fp2.py b/dot_ring/curve/fp2.py new file mode 100644 index 0000000..0b623ba --- /dev/null +++ b/dot_ring/curve/fp2.py @@ -0,0 +1,186 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any, Self + + +def _sqrt_fp(value: int, p: int) -> int | None: + value %= p + if value == 0: + return 0 + if pow(value, (p - 1) // 2, p) != 1: + return None + if p % 4 == 3: + return pow(value, (p + 1) // 4, p) + + q = p - 1 + s = 0 + while q % 2 == 0: + q //= 2 + s += 1 + + z = 2 + while pow(z, (p - 1) // 2, p) != p - 1: + z += 1 + + m = s + c = pow(z, q, p) + t = pow(value, q, p) + r = pow(value, (q + 1) // 2, p) + while t != 1: + i = 1 + t2i = (t * t) % p + while i < m and t2i != 1: + t2i = (t2i * t2i) % p + i += 1 + if i == m: + return None + b = pow(c, 1 << (m - i - 1), p) + m = i + c = (b * b) % p + t = (t * c) % p + r = (r * b) % p + return r + + +def _is_square_fp(value: int, p: int) -> bool: + value %= p + return value == 0 or pow(value, (p - 1) // 2, p) == 1 + + +@dataclass(frozen=True, slots=True) +class Fp2: + """Element of Fp2 represented as `re + im * i`, where `i^2 = -1`.""" + + re: int + im: int + p: int + + def __post_init__(self) -> None: + if self.p <= 2: + raise ValueError("Fp2 modulus must be an odd prime") + object.__setattr__(self, "re", self.re % self.p) + object.__setattr__(self, "im", self.im % self.p) + + def _coerce(self, other: Fp2 | int) -> Fp2: + if isinstance(other, int): + return Fp2(other, 0, self.p) + if isinstance(other, Fp2): + if self.p != other.p: + raise ValueError("Cannot mix Fp2 elements from different fields") + return other + return NotImplemented + + def __add__(self, other: Fp2 | int) -> Fp2: + rhs = self._coerce(other) + return Fp2(self.re + rhs.re, self.im + rhs.im, self.p) + + def __radd__(self, other: int) -> Fp2: + return self + other + + def __sub__(self, other: Fp2 | int) -> Fp2: + rhs = self._coerce(other) + return Fp2(self.re - rhs.re, self.im - rhs.im, self.p) + + def __rsub__(self, other: int) -> Fp2: + return Fp2(other, 0, self.p) - self + + def __mul__(self, other: Fp2 | int) -> Fp2: + rhs = self._coerce(other) + return Fp2( + self.re * rhs.re - self.im * rhs.im, + self.re * rhs.im + self.im * rhs.re, + self.p, + ) + + def __rmul__(self, other: int) -> Fp2: + return self * other + + def __truediv__(self, other: Fp2 | int) -> Fp2: + return self * self._coerce(other).inv() + + def __neg__(self) -> Fp2: + return Fp2(-self.re, -self.im, self.p) + + def __pow__(self, exponent: int) -> Fp2: + if not isinstance(exponent, int): + raise TypeError("Exponent must be an integer") + if exponent < 0: + return self.inv() ** (-exponent) + result = Fp2(1, 0, self.p) + base = self + while exponent: + if exponent & 1: + result *= base + base *= base + exponent >>= 1 + return result + + def __eq__(self, other: object) -> bool: + if isinstance(other, int): + return self.im == 0 and self.re == other % self.p + if not isinstance(other, Fp2): + return NotImplemented + return self.re == other.re and self.im == other.im and self.p == other.p + + def inv(self) -> Fp2: + if self.is_zero(): + raise ZeroDivisionError("Cannot invert zero in Fp2") + denom = (self.re * self.re + self.im * self.im) % self.p + inv_denom = pow(denom, -1, self.p) + return Fp2(self.re * inv_denom, -self.im * inv_denom, self.p) + + def is_zero(self) -> bool: + return self.re == 0 and self.im == 0 + + def norm(self) -> int: + return (self.re * self.re + self.im * self.im) % self.p + + def is_square(self) -> bool: + return self.is_zero() or _is_square_fp(self.norm(), self.p) + + def sqrt(self) -> Fp2 | None: + if self.is_zero(): + return Fp2(0, 0, self.p) + + if self.im == 0: + root = _sqrt_fp(self.re, self.p) + if root is not None: + return Fp2(root, 0, self.p) + imaginary_root = _sqrt_fp(-self.re, self.p) + if imaginary_root is not None: + return Fp2(0, imaginary_root, self.p) + return None + + sqrt_norm = _sqrt_fp(self.norm(), self.p) + if sqrt_norm is None: + return None + + inv2 = pow(2, -1, self.p) + for candidate in ((self.re + sqrt_norm) * inv2, (self.re - sqrt_norm) * inv2): + candidate %= self.p + if not _is_square_fp(candidate, self.p): + continue + real = _sqrt_fp(candidate, self.p) + if real is None or real == 0: + continue + imag = (self.im * pow(2 * real, -1, self.p)) % self.p + root = Fp2(real, imag, self.p) + if root * root == self: + return root + return None + + def sgn0(self) -> int: + return self.re % 2 if self.re != 0 else self.im % 2 + + def to_tuple(self) -> tuple[int, int]: + return self.re, self.im + + def to_fq2(self) -> Any: + from py_ecc.bls12_381 import FQ2 + + return FQ2([self.re, self.im]) + + @classmethod + def from_fq2(cls, value: Any, p: int) -> Self: + return cls(int(value.coeffs[0]), int(value.coeffs[1]), p) diff --git a/dot_ring/curve/montgomery/mg_affine_point.py b/dot_ring/curve/montgomery/mg_affine_point.py index 14b177c..7c74e90 100644 --- a/dot_ring/curve/montgomery/mg_affine_point.py +++ b/dot_ring/curve/montgomery/mg_affine_point.py @@ -1,6 +1,6 @@ from __future__ import annotations -from typing import Any, Literal, TypeVar, cast +from typing import Literal, TypeVar, cast from dot_ring.curve.e2c import E2C_Variant @@ -10,7 +10,7 @@ C = TypeVar("C", bound=MGCurve) -class MGAffinePoint(CurvePoint[C]): +class MGAffinePoint(CurvePoint[C, int]): """ Affine point on a Montgomery curve. @@ -58,14 +58,14 @@ def __add__(self, other: MGAffinePoint[C]) -> MGAffinePoint[C]: if other.is_identity(): return self - p = self.curve.PRIME_FIELD - A = self.curve.A - B = self.curve.B + p = self.curve.params.field_modulus + A = self.curve.params.a + B = self.curve.params.b # ensure coords are ints mod p if self.x is None or self.y is None or other.x is None or other.y is None: # Should be handled by is_identity checks above, but for mypy: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) x1, y1 = cast(int, self.x) % p, cast(int, self.y) % p x2, y2 = cast(int, other.x) % p, cast(int, other.y) % p @@ -74,21 +74,21 @@ def __add__(self, other: MGAffinePoint[C]) -> MGAffinePoint[C]: if x1 == x2 and y1 == y2: # if y == 0 then slope denominator = 0 => result is identity if y1 % p == 0: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) numerator = (3 * cast(int, x1) * cast(int, x1) + 2 * cast(int, A) * cast(int, x1) + 1) % p denominator = (2 * cast(int, B) * cast(int, y1)) % p # Check if denominator is zero before computing inverse if denominator == 0: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) lam = (numerator * pow(denominator, -1, p)) % p x3 = (cast(int, B) * (lam * lam % p) - cast(int, A) - 2 * cast(int, x1)) % p y3 = (lam * (cast(int, x1) - x3) - cast(int, y1)) % p - return self.__class__(x3, y3) + return self.__class__(x3, y3, self.curve) # Addition for distinct points if x1 == x2: # vertical line -> identity (x1 == x2 but y1 != y2) - return self.__class__(None, None) + return self.__class__(None, None, self.curve) numerator = (cast(int, y2) - cast(int, y1)) % p denominator = (cast(int, x2) - cast(int, x1)) % p @@ -100,15 +100,16 @@ def __add__(self, other: MGAffinePoint[C]) -> MGAffinePoint[C]: x3 = (cast(int, B) * lam * lam - cast(int, A) - cast(int, x1) - cast(int, x2)) % p # Corrected formula for y3 y3 = (lam * (cast(int, x1) - x3) - cast(int, y1)) % p - return self.__class__(x3, y3) + return self.__class__(x3, y3, self.curve) def __neg__(self) -> MGAffinePoint[C]: """Negate a point (x, y) -> (x, -y).""" if self.is_identity() or self.x is None or self.y is None: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) return self.__class__( - cast(int, self.x) % self.curve.PRIME_FIELD, - (-cast(int, self.y)) % self.curve.PRIME_FIELD, + cast(int, self.x) % self.curve.params.field_modulus, + (-cast(int, self.y)) % self.curve.params.field_modulus, + self.curve, ) def __sub__(self, other: MGAffinePoint[C]) -> MGAffinePoint[C]: @@ -121,7 +122,7 @@ def _sqrt_mod_p(self, n: int) -> int | None: Tonelli-Shanks. Returns one square root or None if none exists. """ - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus n = n % p # Ensure n is in range [0, p) if n == 0: @@ -190,14 +191,14 @@ def __mul__(self, scalar: int) -> MGAffinePoint[C]: by using the same underlying operations. """ if scalar == 0: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) # Handle negative scalar if scalar < 0: return (-self) * (-scalar) if self.is_identity(): - return self.__class__(None, None) + return self.__class__(None, None, self.curve) # Use double-and-add for consistency with point addition formulas return self._scalar_mult_double_add(scalar) @@ -210,16 +211,16 @@ def _scalar_mult_double_add(self, scalar: int) -> MGAffinePoint[C]: by using the same underlying operations. """ if scalar == 0: - return self.__class__(None, None) + return self.__class__(None, None, self.curve) # Handle negative scalar if scalar < 0: return (-self)._scalar_mult_double_add(-scalar) if self.is_identity(): - return self.__class__(None, None) + return self.__class__(None, None, self.curve) - result = self.__class__(None, None) # Start with identity + result = self.__class__(None, None, self.curve) # Start with identity current = self # Convert scalar to binary and process each bit @@ -239,20 +240,22 @@ def is_identity(self) -> bool: return self.x is None or self.y is None def clear_cofactor(self) -> MGAffinePoint[C]: - return self * self.curve.COFACTOR + return self * self.curve.params.cofactor @classmethod - def identity(cls) -> MGAffinePoint: + def identity(cls, curve: C) -> MGAffinePoint: """Get the identity element (point at infinity).""" - return cls(None, None) + return cls(None, None, curve) @classmethod def encode_to_curve( cls, alpha_string: bytes | str, salt: bytes | str = b"", - General_Check: bool = False, - ) -> MGAffinePoint[C] | Any: + curve: C | None = None, + ) -> MGAffinePoint[C]: + if curve is None: + raise ValueError("curve is required") if not isinstance(alpha_string, bytes): alpha_string = bytes.fromhex(alpha_string) @@ -260,59 +263,43 @@ def encode_to_curve( salt = bytes.fromhex(salt) # Check if it's an ELL2 variant (ELL2 or ELL2_NU) - if cls.curve.E2C in (E2C_Variant.ELL2, E2C_Variant.ELL2_NU): - if cls.curve.E2C.value.endswith("_NU_"): - return cls.encode_to_curve_hash2_suite_nu(alpha_string, salt, General_Check) + if curve.e2c_variant in (E2C_Variant.ELL2, E2C_Variant.ELL2_NU): + if curve.e2c_variant.value.endswith("_NU_"): + return cls._encode_elligator2_nu(alpha_string, curve, salt) - return cls.encode_to_curve_hash2_suite_ro(alpha_string, salt, General_Check) - else: - raise ValueError(f"Unexpected E2C Variant: {cls.curve.E2C}") + return cls._encode_elligator2_ro(alpha_string, curve, salt) - @classmethod - def encode_to_curve_hash2_suite_nu(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> MGAffinePoint[C] | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding + raise ValueError(f"Unexpected E2C Variant: {curve.e2c_variant}") - Returns: - TEAffinePoint: Resulting curve point - """ + @classmethod + def _encode_elligator2_nu( + cls, + alpha_string: bytes, + curve: C, + salt: bytes = b"", + ) -> MGAffinePoint[C]: + """Encode with the nonuniform Elligator2 hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 1) - R = cls.map_to_curve(u[0]) # ELL2 - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [R.x, R.y], "P": [P.x, P.y]} - return R.clear_cofactor() + (u0,) = curve.hash_to_field(string_to_hash, 1) + return cls.map_to_curve(u0, curve).clear_cofactor() @classmethod - def encode_to_curve_hash2_suite_ro(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> MGAffinePoint[C] | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding - - Returns: - TEAffinePoint: Resulting curve point - """ + def _encode_elligator2_ro( + cls, + alpha_string: bytes, + curve: C, + salt: bytes = b"", + ) -> MGAffinePoint[C]: + """Encode with the random-oracle Elligator2 hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 2) - - q0 = cls.map_to_curve(u[0]) # ELL2 - q1 = cls.map_to_curve(u[1]) # ELL2 + u0, u1 = curve.hash_to_field(string_to_hash, 2) + q0 = cls.map_to_curve(u0, curve) + q1 = cls.map_to_curve(u1, curve) R = q0 + q1 - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [q0.x, q0.y], "Q1": [q1.x, q1.y], "P": [P.x, P.y]} return R.clear_cofactor() @classmethod - def map_to_curve(cls, u: int) -> MGAffinePoint[C]: + def map_to_curve(cls, u: int, curve: C) -> MGAffinePoint[C]: """ Map a field element to a curve point using Elligator 2. @@ -333,32 +320,32 @@ def map_to_curve(cls, u: int) -> MGAffinePoint[C]: # 9. t = y * K # 10.return (s, t) - p = cls.curve.PRIME_FIELD - J = cls.curve.A - K = cls.curve.B - Z = cast(int, cls.curve.Z) + p = curve.params.field_modulus + J = curve.params.a + K = curve.params.b + Z = cast(int, curve.params.hash_to_curve.z) - c1 = (J * cls.curve.mod_inverse(K)) % p - c2 = cls.curve.mod_inverse(K * K) % p + c1 = (J * curve.mod_inverse(K)) % p + c2 = curve.mod_inverse(K * K) % p # Main mapping computation tv1 = (Z * u * u) % p e1 = tv1 == -1 tv1 = 0 if e1 else tv1 - x1 = (-c1 * cls.curve.mod_inverse(tv1 + 1)) % p + x1 = (-c1 * curve.mod_inverse(tv1 + 1)) % p gx1 = (((x1 + c1) * x1 + c2) * x1) % p x2 = -x1 - c1 gx2 = (tv1 * gx1) % p # Choose correct values - e2 = cls.curve.is_square(gx1) + e2 = curve.is_square(gx1) x = x2 if not e2 else x1 y2 = gx2 if not e2 else gx1 # Compute square root - y = cls.curve.mod_sqrt(y2) + y = curve.mod_sqrt(y2) # Adjust sign e3 = (y & 1) == 1 @@ -368,41 +355,41 @@ def map_to_curve(cls, u: int) -> MGAffinePoint[C]: s = (x * K) % p t = (y * K) % p - return cls(s, t) + return cls(s, t, curve) def point_to_string(self) -> bytes: if self.is_identity(): raise ValueError("Cannot serialize point at infinity") - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus field_byte_len = (p.bit_length() + 7) // 8 - if self.curve.UNCOMPRESSED: + if self.curve.params.encoding.uncompressed: # Encode u and v coordinates as little-endian bytes if self.x is None or self.y is None: raise ValueError("Cannot serialize identity point") - x_bytes = int(cast(int, self.x)).to_bytes(field_byte_len, cast(Literal["little", "big"], self.curve.ENDIAN)) - y_bytes = int(cast(int, self.y)).to_bytes(field_byte_len, cast(Literal["little", "big"], self.curve.ENDIAN)) + x_bytes = int(cast(int, self.x)).to_bytes(field_byte_len, cast(Literal["little", "big"], self.curve.encoding_endian())) + y_bytes = int(cast(int, self.y)).to_bytes(field_byte_len, cast(Literal["little", "big"], self.curve.encoding_endian())) return x_bytes + y_bytes else: raise NotImplementedError("Compressed encoding not implemented") @classmethod - def string_to_point(cls, data: str | bytes) -> MGAffinePoint: + def string_to_point(cls, data: str | bytes, curve: C) -> MGAffinePoint: if isinstance(data, str): data = bytes.fromhex(data) - p = cls.curve.PRIME_FIELD + p = curve.params.field_modulus byte_length = (p.bit_length() + 7) // 8 - if cls.curve.UNCOMPRESSED: + if curve.params.encoding.uncompressed: # Split into u and v coordinates u_bytes = data[:byte_length] v_bytes = data[byte_length:] - u = int.from_bytes(u_bytes, "little") - v = int.from_bytes(v_bytes, "little") + u = int.from_bytes(u_bytes, curve.encoding_endian()) + v = int.from_bytes(v_bytes, curve.encoding_endian()) # Create the point - point = cls(u, v) + point = cls(u, v, curve) else: ... @@ -414,5 +401,5 @@ def string_to_point(cls, data: str | bytes) -> MGAffinePoint: return point @classmethod - def _x_recover(cls, y: int) -> int: + def _x_recover(cls, y: int, curve: C) -> int: raise NotImplementedError("Must be implemented by subclass") diff --git a/dot_ring/curve/montgomery/mg_curve.py b/dot_ring/curve/montgomery/mg_curve.py index ef9af39..ec6884c 100644 --- a/dot_ring/curve/montgomery/mg_curve.py +++ b/dot_ring/curve/montgomery/mg_curve.py @@ -2,10 +2,10 @@ from dataclasses import dataclass -# Forward reference fix for circular imports from typing import TYPE_CHECKING, Any, TypeVar from ..curve import Curve +from ..specs.parameters import MontgomeryCurveParams if TYPE_CHECKING: from .mg_affine_point import MGAffinePoint @@ -14,8 +14,8 @@ P = TypeVar("P", bound="MGAffinePoint") -@dataclass(frozen=True) -class MGCurve(Curve): +@dataclass(frozen=True, kw_only=True) +class MGCurve(Curve[int]): """ Base class for Montgomery curves of the form: Bv² = u³ + Au² + u @@ -24,19 +24,18 @@ class MGCurve(Curve): - B: coefficient of v² term (typically 1 for most curves) """ - A: int - B: int + params: MontgomeryCurveParams def __post_init__(self) -> None: """Validate curve parameters after initialization.""" super().__post_init__() if hasattr(super(), "__post_init__") else None # Validate that B is not zero (would make curve degenerate) - if self.B % self.PRIME_FIELD == 0: + if self.params.b % self.params.field_modulus == 0: raise ValueError("B coefficient cannot be zero mod p") # Validate that A² - 4 is not zero (would make curve singular) - discriminant = (self.A * self.A - 4) % self.PRIME_FIELD + discriminant = (self.params.a * self.params.a - 4) % self.params.field_modulus if discriminant == 0: raise ValueError("Curve is singular: A² - 4 ≡ 0 (mod p)") @@ -45,13 +44,13 @@ def is_on_curve(self, point: tuple[int, int]) -> bool: Check if point (u, v) satisfies the Montgomery curve equation: Bv² = u³ + Au² + u """ u, v = point - p = self.PRIME_FIELD + p = self.params.field_modulus # Reduce coordinates modulo p u, v = u % p, v % p - left = (self.B * v * v) % p - right = (u * u * u + self.A * u * u + u) % p + left = (self.params.b * v * v) % p + right = (u * u * u + self.params.a * u * u + u) % p return left == right def point_at_infinity(self) -> MGAffinePoint: @@ -73,7 +72,7 @@ def random_point(self, rng: Any = None) -> MGAffinePoint: from .mg_affine_point import MGAffinePoint - p = self.PRIME_FIELD + p = self.params.field_modulus max_attempts = 100 for _ in range(max_attempts): @@ -83,10 +82,10 @@ def random_point(self, rng: Any = None) -> MGAffinePoint: # Compute y² = (x³ + Ax² + x) / B x_cubed = (x * x * x) % p x_squared = (x * x) % p - numerator = (x_cubed + (self.A * x_squared) % p + x) % p + numerator = (x_cubed + (self.params.a * x_squared) % p + x) % p try: - inv_B = pow(self.B, -1, p) + inv_B = pow(self.params.b, -1, p) y_squared = (numerator * inv_B) % p # Check if y_squared is a quadratic residue @@ -115,7 +114,7 @@ def validate_point(self, point: Any) -> bool: return True # Identity point # Check coordinates are in valid range - p = self.PRIME_FIELD + p = self.params.field_modulus if not (0 <= point.x < p and 0 <= point.y < p): return False @@ -126,16 +125,19 @@ def __eq__(self, other: object) -> bool: """Check if two curves are equal.""" if not isinstance(other, MGCurve): return False - return self.PRIME_FIELD == other.PRIME_FIELD and self.A == other.A and self.B == other.B + return self.params.field_modulus == other.params.field_modulus and self.params.a == other.params.a and self.params.b == other.params.b def __hash__(self) -> int: """Hash for use as dictionary keys.""" - return hash((self.PRIME_FIELD, self.A, self.B)) + return hash((self.params.field_modulus, self.params.a, self.params.b)) def __str__(self) -> str: """String representation of the curve.""" - return f"MGCurve(p={self.PRIME_FIELD}, A={self.A}, B={self.B})" + return f"MGCurve(p={self.params.field_modulus}, A={self.params.a}, B={self.params.b})" def __repr__(self) -> str: """Detailed string representation.""" - return f"MGCurve(PRIME_FIELD={self.PRIME_FIELD}, A={self.A}, B={self.B}, equation: {self.B}*v² = u³ + {self.A}*u² + u)" + return ( + f"MGCurve(field_modulus={self.params.field_modulus}, A={self.params.a}, B={self.params.b}, " + f"equation: {self.params.b}*v² = u³ + {self.params.a}*u² + u)" + ) diff --git a/dot_ring/curve/point.py b/dot_ring/curve/point.py index 01cf28d..451e07e 100644 --- a/dot_ring/curve/point.py +++ b/dot_ring/curve/point.py @@ -1,52 +1,20 @@ from __future__ import annotations -import hashlib from abc import abstractmethod -from dataclasses import dataclass -from typing import ( - TYPE_CHECKING, - Any, - ClassVar, - Generic, - Literal, - Protocol, - Self, - TypeVar, - cast, -) +from typing import TYPE_CHECKING, Generic, Self, TypeVar, cast + +from dot_ring.curve.fp2 import Fp2 if TYPE_CHECKING: from dot_ring.curve.curve import Curve # TypeVar for curve types -C = TypeVar("C", bound="Curve") - - -@dataclass -class Point: - x: int - y: int - - -class PointProtocol(Protocol[C]): - """Protocol defining the interface for curve points.""" - - x: int - y: int - curve: C - - def __add__(self, other: PointProtocol[C]) -> PointProtocol[C]: ... - - def __mul__(self, scalar: int) -> PointProtocol[C]: ... +Coord = int | Fp2 +CoordT = TypeVar("CoordT", int, Fp2) +C = TypeVar("C", bound="Curve[Coord]") - def __rmul__(self, scalar: int) -> PointProtocol[C]: ... - def is_on_curve(self) -> bool: ... - - def is_identity(self) -> bool: ... - - -class CurvePoint(Generic[C]): +class CurvePoint(Generic[C, CoordT]): """ Base implementation of an elliptic curve point. @@ -59,20 +27,19 @@ class CurvePoint(Generic[C]): curve: The curve this point belongs to """ - x: int | tuple[int, int] | None - y: int | tuple[int, int] | None + x: CoordT | None + y: CoordT | None curve: C def __init__( self, - x: int | tuple[int, int] | None, - y: int | tuple[int, int] | None, - curve: C | None = None, + x: CoordT | None, + y: CoordT | None, + curve: C, ) -> None: self.x = x self.y = y - if curve is not None: - self.curve = curve + self.curve = curve super().__init__() self.__post_init__() @@ -93,11 +60,10 @@ def __mul__(self, scalar: int) -> Self: def __rmul__(self, scalar: int) -> Self: return self.__mul__(scalar) - # Class constants - ENCODING_LENGTH: ClassVar[int] = 32 - def __post_init__(self) -> None: """Validate point after initialization.""" + if self.is_identity(): + return if not self._validate_coordinates(): raise ValueError("Invalid point coordinates") if not self.is_on_curve(): @@ -112,10 +78,11 @@ def _validate_coordinates(self) -> bool: """ if self.is_identity(): return True - return 0 <= cast(int, self.x) < self.curve.PRIME_FIELD and 0 <= cast(int, self.y) < self.curve.PRIME_FIELD + field_modulus = self.curve.params.field_modulus + return 0 <= cast(int, self.x) < field_modulus and 0 <= cast(int, self.y) < field_modulus @classmethod - def msm(cls, points: list[Self], scalars: list[int]) -> Self: + def msm(cls, points: list[Self], scalars: list[int], curve: C) -> Self: """ Compute sum(P_i * s_i) for pairs (P_i, s_i). Default implementation uses naive summation. @@ -125,23 +92,24 @@ def msm(cls, points: list[Self], scalars: list[int]) -> Self: raise ValueError("Points and scalars must have same length") if not points: - return cls.identity() + return cls.identity(curve) - result = cls.identity() + result = cls.identity(curve) for point, scalar in zip(points, scalars, strict=False): result = result + (point * scalar) return result @classmethod - def generator_point(cls) -> Self: + def generator_point(cls, curve: C) -> Self: """ Get the generator point of the curve. Returns: BandersnatchPoint: Generator point """ - return cls(cls.curve.GENERATOR_X, cls.curve.GENERATOR_Y) + generator_x, generator_y = curve.params.generator + return cls(generator_x, generator_y, curve) @abstractmethod def is_on_curve(self) -> bool: @@ -168,7 +136,7 @@ def is_identity(self) -> bool: @classmethod @abstractmethod - def identity(cls) -> Self: + def identity(cls, curve: C) -> Self: """ Get the identity element. @@ -190,20 +158,22 @@ def point_to_string(self) -> bytes: bytes: The compressed point representation """ - if self.curve.UNCOMPRESSED: - return self.uncompressed_p2s() + if self.curve.params.encoding.uncompressed: + encoded = self.uncompressed_p2s() + return encoded - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus n_bytes = (p.bit_length() + 7) // 8 - y_bytes = bytearray(cast(int, self.y).to_bytes(n_bytes, cast(Literal["little", "big"], self.curve.ENDIAN))) + y_bytes = bytearray(cast(int, self.y).to_bytes(n_bytes, self.curve.encoding_endian())) # Compute x sign bit x_sign_bit = 1 if cast(int, self.x) > (-cast(int, self.x) % p) else 0 y_bytes[-1] |= x_sign_bit << 7 - return bytes(y_bytes) + encoded = bytes(y_bytes) + return encoded @classmethod - def string_to_point(cls, octet_string: str | bytes) -> Self: + def string_to_point(cls, octet_string: str | bytes, curve: C) -> Self: """ Convert compressed octet string back to point. Args: @@ -216,72 +186,61 @@ def string_to_point(cls, octet_string: str | bytes) -> Self: ValueError: If encoding is invalid. """ - if cls.curve.UNCOMPRESSED: - return cls.uncompressed_s2p(octet_string) + if curve.params.encoding.uncompressed: + return cls.uncompressed_s2p(octet_string, curve) if isinstance(octet_string, str): octet_string = bytes.fromhex(octet_string) if not octet_string: raise ValueError("Empty octet string") - # Extract x sign bit from MSB of last byte x_sign_bit = (octet_string[-1] >> 7) & 1 - - # Mask out MSB to recover y y_bytes = bytearray(octet_string) y_bytes[-1] &= 0x7F - y = int.from_bytes(y_bytes, cls.curve.ENDIAN) - x_candidates = cls._x_recover(y) + y = int.from_bytes(y_bytes, curve.encoding_endian()) + if y >= curve.params.field_modulus: + raise ValueError("Invalid point encoding") + x_candidates = cls._x_recover(y, curve) if x_candidates is None: raise ValueError("Invalid point encoding") if isinstance(x_candidates, int): - x, neg_x = x_candidates, (-x_candidates) % cls.curve.PRIME_FIELD + x, neg_x = x_candidates, (-x_candidates) % curve.params.field_modulus else: x, neg_x = x_candidates - # Pick x using x_sign_bit - chosen_x = neg_x if x_sign_bit else x - try: - return cls(chosen_x, y) - except ValueError as exc: - raise ValueError("Invalid point encoding") from exc + x, y = neg_x if x_sign_bit else x, y + return cls(x, y, curve) def uncompressed_p2s(self) -> bytes: - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus byte_length = (p.bit_length() + 7) // 8 - endian = self.curve.ENDIAN - if endian != "little" and endian != "big": - raise ValueError("Invalid endianness") # Encode u and v coordinates as little-endian bytes - x_bytes = cast(int, self.x).to_bytes(byte_length, cast(Literal["little", "big"], endian)) - y_bytes = cast(int, self.y).to_bytes(byte_length, cast(Literal["little", "big"], endian)) + x_bytes = cast(int, self.x).to_bytes(byte_length, self.curve.encoding_endian()) + y_bytes = cast(int, self.y).to_bytes(byte_length, self.curve.encoding_endian()) return x_bytes + y_bytes @classmethod - def uncompressed_s2p(cls, octet_string: str | bytes) -> Self: + def uncompressed_s2p(cls, octet_string: str | bytes, curve: C) -> Self: if isinstance(octet_string, str): octet_string = bytes.fromhex(octet_string) - p = cls.curve.PRIME_FIELD + p = curve.params.field_modulus byte_length = (p.bit_length() + 7) // 8 - endian = cls.curve.ENDIAN - if endian != "little" and endian != "big": - raise ValueError("Invalid endianness") # Split into u and v coordinates x_bytes = octet_string[:byte_length] y_bytes = octet_string[byte_length:] - x = int.from_bytes(x_bytes, endian) - y = int.from_bytes(y_bytes, endian) + x = int.from_bytes(x_bytes, curve.encoding_endian()) + y = int.from_bytes(y_bytes, curve.encoding_endian()) # Create the point - point = cls(x % cls.curve.PRIME_FIELD, y % cls.curve.PRIME_FIELD) + point = cls(x % curve.params.field_modulus, y % curve.params.field_modulus, curve) # Verify the point is on the curve if not point.is_on_curve(): raise ValueError("Point is not on the curve") return point @classmethod - def _x_recover(cls, y: int) -> int | tuple[int, int] | None: + def _x_recover(cls, y: int, curve: C) -> int | tuple[int, int] | None: """ Recover x-coordinate from y. @@ -289,66 +248,21 @@ def _x_recover(cls, y: int) -> int | tuple[int, int] | None: y: y-coordinate Returns: - int | tuple[int, int] | None: Recovered x-coordinate + int | tuple[int, int] | None: Recovered x candidate(s) Raises: ValueError: If x cannot be recovered """ raise NotImplementedError("Must be implemented by subclass") - @staticmethod - def _get_bit(data: bytes, bit_index: int) -> int: - """ - Get specific bit from byte sequence. - - Args: - data: Byte sequence - bit_index: Index of bit to retrieve - - Returns: - int: Bit value (0 or 1) - """ - byte_index = bit_index // 8 - bit_offset = bit_index % 8 - return (data[byte_index] >> bit_offset) & 1 - - @classmethod # modified - def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Self: - """ - Encode a string to a curve point using try-and-increment method for ECVRF. - - Args: - alpha: String to encode - salt: Optional salt for the encoding - - Returns: - TEAffinePoint: Resulting curve point - """ - ctr = 0 - front = b"\x01" - back = b"\x00" - alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string - salt = salt.encode() if isinstance(salt, str) else salt - suite_string = cls.curve.SUITE_STRING - while True: - ctr_string = ctr.to_bytes(1, "big") - hash_input = suite_string + front + salt + alpha_string + ctr_string + back - if cls.__name__ == "P256PointVariant": - hash_output = hashlib.sha256(hash_input).digest() - candidate = b"\x02" + hash_output[:32] - else: - hash_output = hashlib.sha512(hash_input).digest() - candidate = hash_output[:32] - try: - point = cls.string_to_point(candidate) - except ValueError: - ctr += 1 - continue - if cls.curve.COFACTOR > 1: - point = cast(Any, point).clear_cofactor() - if point != cast(Any, cls).identity_point(): - return point - ctr += 1 + @classmethod + def encode_to_curve( + cls, + alpha_string: bytes | str, + salt: bytes | str = b"", + curve: C | None = None, + ) -> Self: + raise NotImplementedError("Must be implemented by subclass") def __hash__(self) -> int: if self.x is None or self.y is None: @@ -357,21 +271,17 @@ def __hash__(self) -> int: x_val = 0 if isinstance(self.x, int): x_val = self.x - elif isinstance(self.x, tuple): - x_val = sum(self.x) - elif hasattr(self.x, "re"): # FieldElement - x_val = cast(Any, self.x).re + cast(Any, self.x).im - elif hasattr(self.x, "coeffs"): # FQ2 or similar - x_val = sum(cast(Any, self.x).coeffs) + elif isinstance(self.x, Fp2): + x_val = self.x.re + self.x.im + else: + raise TypeError(f"Unsupported point coordinate type: {type(self.x).__name__}") y_val = 0 if isinstance(self.y, int): y_val = self.y - elif isinstance(self.y, tuple): - y_val = sum(self.y) - elif hasattr(self.y, "re"): # FieldElement - y_val = cast(Any, self.y).re + cast(Any, self.y).im - elif hasattr(self.y, "coeffs"): # FQ2 or similar - y_val = sum(cast(Any, self.y).coeffs) - - return (x_val + y_val) % self.curve.ORDER + elif isinstance(self.y, Fp2): + y_val = self.y.re + self.y.im + else: + raise TypeError(f"Unsupported point coordinate type: {type(self.y).__name__}") + + return (x_val + y_val) % self.curve.params.subgroup_order diff --git a/dot_ring/curve/short_weierstrass/sw_affine_point.py b/dot_ring/curve/short_weierstrass/sw_affine_point.py index 361cd46..f9f7403 100644 --- a/dot_ring/curve/short_weierstrass/sw_affine_point.py +++ b/dot_ring/curve/short_weierstrass/sw_affine_point.py @@ -1,16 +1,13 @@ from __future__ import annotations -from typing import Any, Self, TypeVar, cast +from typing import Self, cast from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.point import CurvePoint +from dot_ring.curve.short_weierstrass.sw_curve import SWCurve -from ..field_element import FieldElement -T = TypeVar("T", bound="SWAffinePoint") - - -class SWAffinePoint(CurvePoint): +class SWAffinePoint(CurvePoint[SWCurve[int], int]): """ Affine point implementation for Short Weierstrass curves. @@ -39,15 +36,18 @@ def __add__(self, other: SWAffinePoint) -> Self: if other.is_identity(): return self - x1: Any = self.x - y1: Any = self.y - x2: Any = other.x - y2: Any = other.y - p = self.curve.PRIME_FIELD + if self.x is None or self.y is None or other.x is None or other.y is None: + return self.__class__.identity(self.curve) + + x1 = self.x + y1 = self.y + x2 = other.x + y2 = other.y + p = self.curve.params.field_modulus # Check if points are inverses if x1 == x2 and (y1 + y2) % p == 0: - return self.identity() + return self.__class__.identity(self.curve) # Point doubling if x1 == x2 and y1 == y2: @@ -64,7 +64,7 @@ def __add__(self, other: SWAffinePoint) -> Self: y3 = (slope * (x1 - x3) - y1) % p # The curve is already set in the instance - return self.__class__(x3, y3) + return self.__class__(x3, y3, self.curve) def _double(self) -> Self: """ @@ -76,14 +76,17 @@ def _double(self) -> Self: if self.is_identity(): return self - x1: Any = self.x - y1: Any = self.y - A = self.curve.WeierstrassA - p = self.curve.PRIME_FIELD + if self.x is None or self.y is None: + return self + + x1 = self.x + y1 = self.y + A = self.curve.params.a + p = self.curve.params.field_modulus # Check if y-coordinate is zero (point has order 2) if y1 == 0: - return self.identity() + return self.__class__.identity(self.curve) # Calculate slope: λ = (3x₁² + a) / (2y₁) numerator = (3 * x1 * x1 + A) % p @@ -95,7 +98,7 @@ def _double(self) -> Self: y3 = (slope * (x1 - x3) - y1) % p # The curve is already set in the instance - return self.__class__(x3, y3) + return self.__class__(x3, y3, self.curve) def __mul__(self, scalar: int) -> Self: """ @@ -108,13 +111,13 @@ def __mul__(self, scalar: int) -> Self: Self: Result of scalar multiplication """ if scalar == 0: - return self.identity() + return self.__class__.identity(self.curve) if scalar < 0: return (-self) * (-scalar) # Use binary method (double-and-add) - result = self.identity() + result = self.__class__.identity(self.curve) addend = self while scalar > 0: @@ -134,10 +137,12 @@ def __neg__(self) -> Self: """ if self.is_identity(): return self + if self.x is None or self.y is None: + return self.__class__.identity(self.curve) # Create a new instance with negated y-coordinate # The curve is already set in the instance - return self.__class__(self.x, (-cast(int, self.y)) % self.curve.PRIME_FIELD) + return self.__class__(self.x, (-self.y) % self.curve.params.field_modulus, self.curve) def __sub__(self, other: SWAffinePoint) -> Self: """ @@ -163,25 +168,28 @@ def point_to_string(self, compressed: bool = True) -> bytes: if self.x is None and self.y is None: return b"\x00" - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus # Calculate the byte length needed for field elements field_byte_len = (p.bit_length() + 7) // 8 # Convert x-coordinate to bytes - x_bytes = int(cast(int, self.x)).to_bytes(field_byte_len, "big") + if self.x is None or self.y is None: + raise ValueError("Cannot serialize malformed point at infinity") + + x_bytes = self.x.to_bytes(field_byte_len, "big") if compressed: # Compressed format: prefix (0x02 or 0x03) + x-coordinate # Prefix indicates y-coordinate parity - prefix = b"\x02" if cast(int, self.y) % 2 == 0 else b"\x03" + prefix = b"\x02" if self.y % 2 == 0 else b"\x03" return prefix + x_bytes else: # Uncompressed format: 0x04 + x-coordinate + y-coordinate - y_bytes = int(cast(int, self.y)).to_bytes(field_byte_len, "big") + y_bytes = self.y.to_bytes(field_byte_len, "big") return b"\x04" + x_bytes + y_bytes @classmethod - def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: + def string_to_point(cls, octet_string: str | bytes, curve: SWCurve[int]) -> Self: if isinstance(octet_string, str): octet_string = bytes.fromhex(octet_string) @@ -194,11 +202,11 @@ def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: if prefix == 0x00: if len(octet_string) != 1: raise ValueError("Point at infinity must be single byte 0x00") - return cls.identity() + return cls.identity(curve) - p = cls.curve.PRIME_FIELD - A = cls.curve.WeierstrassA - B = cls.curve.WeierstrassB + p = curve.params.field_modulus + A = curve.params.a + B = curve.params.b field_byte_len = (p.bit_length() + 7) // 8 # Handle compressed format (0x02 or 0x03) @@ -229,7 +237,7 @@ def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: if (y % 2 == 0 and prefix == 0x03) or (y % 2 == 1 and prefix == 0x02): y = p - y # Use the other square root - point = cls(x, y) + point = cls(x, y, curve) # Verify point is on curve if not point.is_on_curve(): @@ -256,7 +264,7 @@ def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: if y >= p: raise ValueError(f"y-coordinate {y} is not in field Fp (p={p})") - point = cls(x, y) + point = cls(x, y, curve) # Verify point is on curve if not point.is_on_curve(): @@ -285,7 +293,7 @@ def string_to_point(cls, octet_string: str | bytes) -> SWAffinePoint: if (y % 2 == 0 and prefix == 0x07) or (y % 2 == 1 and prefix == 0x06): raise ValueError("Hybrid format: y parity doesn't match prefix") - point = cls(x, y) + point = cls(x, y, curve) if not point.is_on_curve(): raise ValueError(f"Point ({x}, {y}) is not on curve") @@ -308,32 +316,10 @@ def _validate_coordinates(self) -> bool: # Handle point at infinity if self.x is None and self.y is None: return True + if self.x is None or self.y is None: + return False - # Handle FieldElement points (for Fp2) - if self.x is not None and self.y is not None and hasattr(self.x, "re") and hasattr(self.y, "re"): - # For FieldElement, check that the prime field matches - x_fe: Any = self.x - y_fe: Any = self.y - return bool( - x_fe.p == self.curve.PRIME_FIELD - and y_fe.p == self.curve.PRIME_FIELD - and 0 <= x_fe.re < self.curve.PRIME_FIELD - and 0 <= x_fe.im < self.curve.PRIME_FIELD - and 0 <= y_fe.re < self.curve.PRIME_FIELD - and 0 <= y_fe.im < self.curve.PRIME_FIELD - ) - - # Handle Fp2 points (tuples of two integers) - if isinstance(self.x, (tuple, list)) and isinstance(self.y, (tuple, list)): - if len(self.x) != 2 or len(self.y) != 2: - return False - return all(isinstance(coord, int) and 0 <= coord < self.curve.PRIME_FIELD for coord in (*self.x, *self.y)) - - # Handle Fp points (integers) - if isinstance(self.x, int) and isinstance(self.y, int): - return bool(0 <= self.x < self.curve.PRIME_FIELD and 0 <= self.y < self.curve.PRIME_FIELD) - - return False + return 0 <= self.x < self.curve.params.field_modulus and 0 <= self.y < self.curve.params.field_modulus def is_on_curve(self) -> bool: """ @@ -347,46 +333,12 @@ def is_on_curve(self) -> bool: # Point at infinity is on the curve if self.x is None and self.y is None: return True - # For FieldElement points (Fp2) - if hasattr(self.x, "re") and hasattr(self.y, "re"): - # y² - y: Any = self.y - y2 = y * y - - # x³ + a*x + b - x: Any = self.x - x3 = x * x * x - a = FieldElement( - self.curve.WeierstrassA[0], - self.curve.WeierstrassA[1], - self.curve.PRIME_FIELD, - ) - b = FieldElement( - self.curve.WeierstrassB[0], - self.curve.WeierstrassB[1], - self.curve.PRIME_FIELD, - ) - rhs = x3 + (a * x) + b - - return bool(y2 == rhs) - - # For Fp2 points (tuples of two integers) - if isinstance(self.x, (tuple, list)) and isinstance(self.y, (tuple, list)): - # TODO: Implement proper Fp2 arithmetic for curve equation check - # This is a simplified check that only validates the point is in the field - return self._validate_coordinates() - - # For Fp points, use standard arithmetic - try: - x_int, y_int = cast(int, self.x), cast(int, self.y) - left = pow(y_int, 2, self.curve.PRIME_FIELD) - right = (pow(x_int, 3, self.curve.PRIME_FIELD) + self.curve.WeierstrassA * x_int + self.curve.WeierstrassB) % self.curve.PRIME_FIELD - return bool(left == right) - except (TypeError, AttributeError): + if self.x is None or self.y is None: return False + return self.curve.is_on_curve((self.x, self.y)) def clear_cofactor(self) -> Self: - return cast(Self, self * self.curve.COFACTOR) + return cast(Self, self * self.curve.params.cofactor) def is_identity(self) -> bool: """ @@ -399,7 +351,7 @@ def is_identity(self) -> bool: return self.x is None and self.y is None @classmethod - def identity(cls) -> Self: + def identity(cls, curve: SWCurve[int]) -> Self: """ Get the identity element (point at infinity). @@ -407,8 +359,7 @@ def identity(cls) -> Self: Self: Identity element """ # Return a point at infinity (None, None) - # The curve will be set by the child class's __init__ - return cls(None, None) + return cls(None, None, curve) @staticmethod def tonelli_shanks(n: int, p: int) -> int | None: @@ -456,9 +407,9 @@ def tonelli_shanks(n: int, p: int) -> int | None: return r @classmethod - def _x_recover(cls, y: int) -> tuple[int, int]: - p = cls.curve.PRIME_FIELD - B = cls.curve.WeierstrassB + def _x_recover(cls, y: int, curve: SWCurve[int]) -> tuple[int, int]: + p = curve.params.field_modulus + B = curve.params.b # Compute right-hand side of the curve equation rhs = (pow(y, 2, p) - B) % p @@ -472,7 +423,7 @@ def _x_recover(cls, y: int) -> tuple[int, int]: return x, (-x) % p @classmethod - def map_to_curve_simple_swu(cls, u: int) -> SWAffinePoint | Any: + def map_to_curve_simple_swu(cls, u: int, curve: SWCurve[int]) -> Self: """Implements simplified SWU mapping""" # 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) # 2. x1 = (-B / A) * (1 + tv1) @@ -485,14 +436,15 @@ def map_to_curve_simple_swu(cls, u: int) -> SWAffinePoint | Any: # 9. If sgn0(u) != sgn0(y), set y = -y # 10. return (x, y) - Z = cls.curve.Z - A = cls.curve.WeierstrassA - B = cls.curve.WeierstrassB - p = cls.curve.PRIME_FIELD - - if cls.curve.Requires_Isogeny: # E' vals, used only for secp256k1 as its A=0 - A = 0x3F8731ABDD661ADCA08A5558F0F5D272E953D363CB6F0E5D405447C01A444533 - B = 1771 + hash_to_curve = curve.params.hash_to_curve + isogeny = hash_to_curve.isogeny + Z = hash_to_curve.z + A = curve.params.a + B = curve.params.b + if isogeny is not None: + A = isogeny.map_curve.a + B = isogeny.map_curve.b + p = curve.params.field_modulus # 1. tv1 = inv0(Z^2 * u^4 + Z * u^2) u_sq = (u * u) % p @@ -501,11 +453,11 @@ def map_to_curve_simple_swu(cls, u: int) -> SWAffinePoint | Any: # Handle special case when tv1 is 0 if tv1 == 0: # 3. If tv1 == 0, set x1 = B / (Z * A) - x1 = (B * cls.curve.inv((Z * A) % p)) % p + x1 = (B * curve.inv((Z * A) % p)) % p else: # 2. x1 = (-B / A) * (1 + tv1) - tv1 = cls.curve.inv(tv1) - x1 = (-B * cls.curve.inv(A)) % p + tv1 = curve.inv(tv1) + x1 = (-B * curve.inv(A)) % p x1 = (x1 * (1 + tv1)) % p # 4. gx1 = x1^3 + A * x1 + B @@ -519,113 +471,99 @@ def map_to_curve_simple_swu(cls, u: int) -> SWAffinePoint | Any: # 7-8. Find a valid x and y x, y = x1, None - if cls.curve.is_square(gx1): - y = cls.curve.mod_sqrt(gx1) + if curve.is_square(gx1): + y = curve.mod_sqrt(gx1) else: x = x2 - y = cls.curve.mod_sqrt(gx2) + y = curve.mod_sqrt(gx2) # 9. Fix sign of y - if cls.curve.sgn0(u) != cls.curve.sgn0(y): + if curve.sgn0(u) != curve.sgn0(y): y = (-y) % p - if cls.curve.Requires_Isogeny: - A_prime = 0x3F8731ABDD661ADCA08A5558F0F5D272E953D363CB6F0E5D405447C01A444533 - B_prime = 1771 - + if isogeny is not None: + map_curve = isogeny.map_curve # Check if point lies on E' - if (y * y - (x**3 + A_prime * x + B_prime)) % p != 0: - raise ValueError("Point is not on E'") - return cls.apply_isogeny(x, y) + if (y * y - (x**3 + map_curve.a * x + map_curve.b)) % p != 0: + raise ValueError("Point is not on the hash-to-curve map curve") + return cls.apply_isogeny(x, y, curve) - return cls(x=x, y=y) + return cls(x=x, y=y, curve=curve) @classmethod def encode_to_curve( cls, alpha_string: bytes | str, salt: bytes | str = b"", - General_Check: bool = False, - ) -> Self | Any: + curve: SWCurve[int] | None = None, + ) -> Self: + if curve is None: + raise ValueError("curve is required") if not isinstance(alpha_string, bytes): alpha_string = bytes.fromhex(alpha_string) if not isinstance(salt, bytes): salt = bytes.fromhex(salt) - if cls.curve.HASH_TO_CURVE == "tai": + if curve.e2c_variant == E2C_Variant.TAI: from dot_ring.vrf.transcript import hash_to_curve_tai - return cast(Self, hash_to_curve_tai(cls, salt + alpha_string)) - - if cls.curve.E2C in [E2C_Variant.SSWU, E2C_Variant.SSWU_NU]: - if cls.curve.E2C.value.endswith("_NU_"): - return cls.sswu_hash2_curve_nu(alpha_string, salt, General_Check) - return cls.sswu_hash2_curve_ro(alpha_string, salt, General_Check) - elif cls.curve.E2C == E2C_Variant.TAI: - return cls.encode_to_curve_tai(alpha_string, salt) - else: - raise ValueError(f"Unexpected E2C Variant: {cls.curve.E2C}") + return cast(Self, hash_to_curve_tai(cls, salt + alpha_string, curve)) - @classmethod - def sswu_hash2_curve_ro(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> SWAffinePoint | Any: - """ - Encode a string to a curve point using Elligator 2. + if curve.e2c_variant in (E2C_Variant.SSWU, E2C_Variant.SSWU_NU): + if curve.e2c_variant.value.endswith("_NU_"): + return cls._encode_sswu_nu(alpha_string, curve, salt) + return cls._encode_sswu_ro(alpha_string, curve, salt) - Args: - alpha_string: String to encode - salt: Optional salt for the encoding - General_Check:Just for printing all test suites + raise ValueError(f"Unexpected E2C Variant: {curve.e2c_variant}") - Returns: - TEAffinePoint: Resulting curve point - """ + @classmethod + def _encode_sswu_ro( + cls, + alpha_string: bytes, + curve: SWCurve[int], + salt: bytes = b"", + ) -> Self: + """Encode with the random-oracle simplified-SWU hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 2) # for RO - q0 = cls.map_to_curve_simple_swu(u[0]) # sswu - q1 = cls.map_to_curve_simple_swu(u[1]) # sswu + u0, u1 = curve.hash_to_field(string_to_hash, 2) + q0 = cls.map_to_curve_simple_swu(u0, curve) + q1 = cls.map_to_curve_simple_swu(u1, curve) R = q0 + q1 - - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [q0.x, q0.y], "Q1": [q1.x, q1.y], "P": [P.x, P.y]} - return R.clear_cofactor() + return cast(Self, R.clear_cofactor()) @classmethod - def sswu_hash2_curve_nu(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> SWAffinePoint | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding - General_Check:Just for printing all test suites - - Returns: - TEAffinePoint: Resulting curve point - """ + def _encode_sswu_nu( + cls, + alpha_string: bytes, + curve: SWCurve[int], + salt: bytes = b"", + ) -> Self: + """Encode with the nonuniform simplified-SWU hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 1) # for nu - R = cls.map_to_curve_simple_swu(u[0]) # sswu - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [R.x, R.y], "P": [P.x, P.y]} - return R.clear_cofactor() + (u0,) = curve.hash_to_field(string_to_hash, 1) + return cast(Self, cls.map_to_curve_simple_swu(u0, curve).clear_cofactor()) @classmethod - def apply_isogeny(cls, x_p: int, y_p: int) -> Self: + def apply_isogeny(cls, x_p: int, y_p: int, curve: SWCurve[int]) -> Self: """ Apply the rational isogeny map to a point (x', y') on the isogenous curve E'. - Explicit polynomial formulas for secp256k1. """ - p = cls.curve.PRIME_FIELD - coeffs = cls.curve.Isogeny_Coeffs - x_num = (coeffs["x_num"][0] * pow(x_p, 3, p) + coeffs["x_num"][1] * pow(x_p, 2, p) + coeffs["x_num"][2] * x_p + coeffs["x_num"][3]) % p - - x_den = (pow(x_p, 2, p) + coeffs["x_den"][1] * x_p + coeffs["x_den"][2]) % p - y_num = (coeffs["y_num"][0] * pow(x_p, 3, p) + coeffs["y_num"][1] * pow(x_p, 2, p) + coeffs["y_num"][2] * x_p + coeffs["y_num"][3]) % p - - y_den = (pow(x_p, 3, p) + coeffs["y_den"][1] * pow(x_p, 2, p) + coeffs["y_den"][2] * x_p + coeffs["y_den"][3]) % p + p = curve.params.field_modulus + isogeny = curve.params.hash_to_curve.isogeny + if isogeny is None: + raise ValueError("Missing isogeny") + + def evaluate(coefficients: tuple[int, ...], x: int) -> int: + value = 0 + for coefficient in coefficients: + value = (value * x + coefficient) % p + return value + + x_num = evaluate(isogeny.x_numerator, x_p) + x_den = evaluate(isogeny.x_denominator, x_p) + y_num = evaluate(isogeny.y_numerator, x_p) + y_den = evaluate(isogeny.y_denominator, x_p) x_den_inv = pow(x_den, -1, p) y_den_inv = pow(y_den, -1, p) @@ -633,4 +571,4 @@ def apply_isogeny(cls, x_p: int, y_p: int) -> Self: x_mapped = (x_num * x_den_inv) % p y_mapped = (y_p * y_num * y_den_inv) % p - return cls(x_mapped, y_mapped) + return cls(x_mapped, y_mapped, curve) diff --git a/dot_ring/curve/short_weierstrass/sw_curve.py b/dot_ring/curve/short_weierstrass/sw_curve.py index 5c23c5a..b2f1202 100644 --- a/dot_ring/curve/short_weierstrass/sw_curve.py +++ b/dot_ring/curve/short_weierstrass/sw_curve.py @@ -1,13 +1,17 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, cast +from typing import Generic, TypeVar, cast from dot_ring.curve.curve import Curve +from dot_ring.curve.fp2 import Fp2 +from dot_ring.curve.specs.parameters import ShortWeierstrassCurveParams +CoordT = TypeVar("CoordT", int, Fp2) -@dataclass(frozen=True) -class SWCurve(Curve): + +@dataclass(frozen=True, kw_only=True) +class SWCurve(Curve[CoordT], Generic[CoordT]): """ Short Weierstrass Curve implementation. @@ -17,12 +21,10 @@ class SWCurve(Curve): where a, b are elements of the field such that 4a³ + 27b² ≠ 0. Attributes: - WeierstrassA: The 'a' parameter in the curve equation - WeierstrassB: The 'b' parameter in the curve equation + params: Short Weierstrass curve-suite constants. """ - WeierstrassA: int | tuple[int, int] - WeierstrassB: int | tuple[int, int] + params: ShortWeierstrassCurveParams[CoordT] def __post_init__(self) -> None: """Validate curve parameters after initialization.""" @@ -40,20 +42,15 @@ def _validate_weierstrass_params(self) -> bool: bool: True if parameters are valid """ - def is_fp2(value: Any) -> bool: - return isinstance(value, (tuple, list)) and len(value) == 2 - - A = self.WeierstrassA - B = self.WeierstrassB - p = self.PRIME_FIELD + A = self.params.a + B = self.params.b + p = self.params.field_modulus - # Handle Fp2 points - if is_fp2(A) or is_fp2(B): - # For Fp2, we'll just check that the parameters are not both zero - # A more thorough check would involve Fp2 arithmetic - a_is_zero = all(x == 0 for x in A) if isinstance(A, (tuple, list)) else A == 0 - b_is_zero = all(x == 0 for x in B) if isinstance(B, (tuple, list)) else B == 0 - return not (a_is_zero and b_is_zero) + if isinstance(A, Fp2) or isinstance(B, Fp2): + if not isinstance(A, Fp2) or not isinstance(B, Fp2): + return False + discriminant = 4 * (A**3) + 27 * (B**2) + return not discriminant.is_zero() # Original Fp validation # Check discriminant: 4a³ + 27b² ≠ 0 (mod p) @@ -62,7 +59,7 @@ def is_fp2(value: Any) -> bool: discriminant = (4 * a_cubed + 27 * b_squared) % p return discriminant != 0 - def is_on_curve(self, point: tuple[Any, Any]) -> bool: + def is_on_curve(self, point: tuple[CoordT, CoordT]) -> bool: """ Check if a given point (x, y) is on the curve. @@ -72,13 +69,14 @@ def is_on_curve(self, point: tuple[Any, Any]) -> bool: Returns: bool: True if the point is on the curve, False otherwise. """ - u, v = cast(int, point[0]), cast(int, point[1]) - # The curve equation is y² = x³ + ax + b - # We need to check if v² % p == (u³ + a*u + b) % p - p = self.PRIME_FIELD - A = cast(int, self.WeierstrassA) - B = cast(int, self.WeierstrassB) - + u, v = point + if isinstance(u, Fp2) or isinstance(v, Fp2): + return isinstance(u, Fp2) and isinstance(v, Fp2) and v * v == u * u * u + cast(Fp2, self.params.a) * u + cast(Fp2, self.params.b) + + u, v = cast(int, u), cast(int, v) + p = self.params.field_modulus + A = cast(int, self.params.a) + B = cast(int, self.params.b) left_side = pow(v, 2, p) right_side = (pow(u, 3, p) + (A * u) % p + B) % p @@ -91,9 +89,9 @@ def j_invariant(self) -> int: Returns: int: j-invariant """ - A = self.WeierstrassA - B = self.WeierstrassB - p = self.PRIME_FIELD + A = self.params.a + B = self.params.b + p = self.params.field_modulus discriminant = (4 * pow(cast(int, A), 3, p) + 27 * pow(cast(int, B), 2, p)) % p numerator = (1728 * 4 * pow(cast(int, A), 3, p)) % p diff --git a/dot_ring/curve/twisted_edwards/te_affine_point.py b/dot_ring/curve/twisted_edwards/te_affine_point.py index 452d422..0068a42 100644 --- a/dot_ring/curve/twisted_edwards/te_affine_point.py +++ b/dot_ring/curve/twisted_edwards/te_affine_point.py @@ -1,8 +1,10 @@ from __future__ import annotations -import hashlib from typing import Any, Self, TypeVar, cast +from gmpy2 import invert as _invert +from gmpy2 import mpz as _mpz + from dot_ring.curve.e2c import E2C_Variant from ..point import CurvePoint @@ -11,12 +13,12 @@ C = TypeVar("C", bound=TECurve) -class TEAffinePoint(CurvePoint[C]): +class TEAffinePoint(CurvePoint[C, int]): """ Twisted Edwards Curve Point in Affine Coordinates. This class implements point operations on a Twisted Edwards curve using affine coordinates. - Twisted Edwards curve have the form: ax² + y² = 1 + dx²y² + Twisted Edwards curves have the form: ax² + y² = 1 + dx²y². Attributes: x: x-coordinate @@ -32,8 +34,7 @@ def __post_init__(self) -> None: def is_identity(self) -> bool: """ - Check if this is the identity element (point at infinity). - The identity point is represented with None coordinates. + Check if this is the Twisted Edwards identity element. Returns: bool: True if this is the identity element @@ -41,15 +42,14 @@ def is_identity(self) -> bool: return self.x == 0 and self.y == 1 @classmethod - def identity(cls) -> Self: + def identity(cls, curve: C) -> Self: """ - Get the identity element (point at infinity). + Get the Twisted Edwards identity element. Returns: - SWAffinePoint: Identity element + TEAffinePoint: Identity element """ - # Return a identity point (0, 1) - return cls(0, 1) + return cls(0, 1, curve) def is_on_curve(self) -> bool: """ @@ -60,13 +60,13 @@ def is_on_curve(self) -> bool: """ # ax² + y² = 1 + dx²y² v, w = cast(int, self.x), cast(int, self.y) - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus - lhs = (self.curve.EdwardsA * pow(v, 2, p) + pow(w, 2, p)) % p - rhs = (1 + self.curve.EdwardsD * pow(v, 2, p) * pow(w, 2, p)) % p + lhs = (self.curve.params.a * pow(v, 2, p) + pow(w, 2, p)) % p + rhs = (1 + self.curve.params.d * pow(v, 2, p) * pow(w, 2, p)) % p return lhs == rhs - def __add__(self, other: CurvePoint[C]) -> Self: + def __add__(self, other: CurvePoint[C, int]) -> Self: """ Add two points using Twisted Edwards addition formulas. @@ -82,34 +82,36 @@ def __add__(self, other: CurvePoint[C]) -> Self: if not isinstance(other, TEAffinePoint): raise TypeError("Can only add TEAffinePoints") - if self == other: - res = self.double() - return cast(Any, res) # type: ignore - - if self == self.identity_point(): + if self.is_identity(): return cast(Self, other) - if other == self.identity_point(): + if other.is_identity(): return self - p = self.curve.PRIME_FIELD + if self == other: + return self.double() + if self.x is None or self.y is None or other.x is None or other.y is None: raise ValueError("Unexpected identity point in addition") - x1, y1 = cast(int, self.x), cast(int, self.y) - x2, y2 = cast(int, other.x), cast(int, other.y) - # Compute intermediate values + p = _mpz(self.curve.params.field_modulus) + a_coeff = _mpz(self.curve.params.a) % p + d_coeff = _mpz(self.curve.params.d) % p + x1, y1 = _mpz(cast(int, self.x)) % p, _mpz(cast(int, self.y)) % p + x2, y2 = _mpz(cast(int, other.x)) % p, _mpz(cast(int, other.y)) % p + + # Twisted Edwards addition law: + # x3 = (x1*y2 + x2*y1) / (1 + d*x1*x2*y1*y2) + # y3 = (y1*y2 - a*x1*x2) / (1 - d*x1*x2*y1*y2) x1y2 = (x1 * y2) % p x2y1 = (x2 * y1) % p y1y2 = (y1 * y2) % p x1x2 = (x1 * x2) % p - dx1x2y1y2 = (self.curve.EdwardsD * x1x2 * y1y2) % p + dx1x2y1y2 = (d_coeff * x1x2 * y1y2) % p - # Compute result coordinates - x3 = ((x1y2 + x2y1) * pow(1 + dx1x2y1y2, -1, self.curve.PRIME_FIELD)) % p - y3 = ((y1y2 - self.curve.EdwardsA * x1x2) * pow(1 - dx1x2y1y2, -1, self.curve.PRIME_FIELD)) % p - - return self.__class__(x3, y3) + x3 = ((x1y2 + x2y1) * _invert(1 + dx1x2y1y2, p)) % p + y3 = ((y1y2 - a_coeff * x1x2) * _invert(1 - dx1x2y1y2, p)) % p + return self.__class__(int(x3), int(y3), self.curve) def __neg__(self) -> Self: """ @@ -118,9 +120,10 @@ def __neg__(self) -> Self: Returns: TEAffinePoint: Negated point """ - return self.__class__(-cast(int, self.x) % self.curve.PRIME_FIELD, self.y) + x = -cast(int, self.x) % self.curve.params.field_modulus + return self.__class__(x, self.y, self.curve) - def __sub__(self, other: CurvePoint[C]) -> Self: + def __sub__(self, other: CurvePoint[C, int]) -> Self: """ Subtract two points. @@ -139,25 +142,23 @@ def double(self) -> Self: Returns: TEAffinePoint: 2P """ - x1, y1 = cast(int, self.x), cast(int, self.y) - p = self.curve.PRIME_FIELD + p = _mpz(self.curve.params.field_modulus) + a_coeff = _mpz(self.curve.params.a) % p + x1, y1 = _mpz(cast(int, self.x)) % p, _mpz(cast(int, self.y)) % p - # Check for identity point if y1 == 0: - return self.identity_point() + return self.identity(self.curve) - # Calculate denominators - denom_x = (self.curve.EdwardsA * x1**2 + y1**2) % p - denom_y = (2 - self.curve.EdwardsA * x1**2 - y1**2) % p + # Specialized affine doubling formulas for twisted Edwards curves. + denom_x = (a_coeff * x1**2 + y1**2) % p + denom_y = (2 - a_coeff * x1**2 - y1**2) % p if denom_x == 0 or denom_y == 0: - return self.identity_point() - - # Calculate new coordinates - x3 = (2 * x1 * y1 * pow(denom_x, -1, self.curve.PRIME_FIELD)) % p - y3 = ((y1**2 - self.curve.EdwardsA * x1**2) * pow(denom_y, -1, self.curve.PRIME_FIELD)) % p + return self.identity(self.curve) - return self.__class__(x3, y3) + x3 = (2 * x1 * y1 * _invert(denom_x, p)) % p + y3 = ((y1**2 - a_coeff * x1**2) * _invert(denom_y, p)) % p + return self.__class__(int(x3), int(y3), self.curve) def __mul__(self, scalar: int) -> Self: """ @@ -174,7 +175,7 @@ def __mul__(self, scalar: int) -> Self: P_proj: Any = TEProjectivePoint.from_affine(self) result = TEProjectivePoint.zero(self.curve) addend = P_proj - scalar = scalar % self.curve.ORDER + scalar = scalar % self.curve.params.subgroup_order while scalar: if scalar & 1: @@ -190,117 +191,61 @@ def __rmul__(self, scalar: int) -> Self: """ return self.__mul__(scalar) - @classmethod - def identity_point(cls) -> Self: - """ - Get the identity point (0, 1) of the curve. - - Returns: - TEAffinePoint: Identity point - """ - return cls(0, 1) - @classmethod def encode_to_curve( cls, alpha_string: bytes | str, salt: bytes | str = b"", - General_Check: bool = False, - ) -> Self | Any: + curve: C | None = None, + ) -> Self: + """ + Encode input bytes to a point using the curve's configured hash-to-curve variant. + """ + if curve is None: + raise ValueError("curve is required") if not isinstance(alpha_string, bytes): alpha_string = bytes.fromhex(alpha_string) if not isinstance(salt, bytes): salt = bytes.fromhex(salt) - if cls.curve.HASH_TO_CURVE == "tai": + if curve.e2c_variant == E2C_Variant.TAI: from dot_ring.vrf.transcript import hash_to_curve_tai - return cast(Self, hash_to_curve_tai(cls, salt + alpha_string)) + return cast(Self, hash_to_curve_tai(cls, salt + alpha_string, curve)) - if cls.curve.E2C in [E2C_Variant.ELL2, E2C_Variant.ELL2_NU]: - if cls.curve.E2C.value.endswith("_NU_"): - return cls.encode_to_curve_hash2_suite_nu(alpha_string, salt, General_Check) - return cls.encode_to_curve_hash2_suite_ro(alpha_string, salt, General_Check) - elif cls.curve.E2C == E2C_Variant.TAI: - return cls.encode_to_curve_tai(alpha_string, salt) - else: - raise ValueError("Unexpected E2C Variant") + if curve.e2c_variant in (E2C_Variant.ELL2, E2C_Variant.ELL2_NU): + if curve.e2c_variant.value.endswith("_NU_"): + return cls._encode_elligator2_nu(alpha_string, curve, salt) + return cls._encode_elligator2_ro(alpha_string, curve, salt) - @classmethod - def encode_to_curve_hash2_suite_ro(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> Self | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding + raise ValueError("Unexpected E2C Variant") - Returns: - TEAffinePoint: Resulting curve point - """ + @classmethod + def _encode_elligator2_ro( + cls, + alpha_string: bytes, + curve: C, + salt: bytes = b"", + ) -> Self: + """Encode with the random-oracle Elligator2 hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 2) - q0 = cls.map_to_curve(u[0]) # ELL2 - q1 = cls.map_to_curve(u[1]) # ELL2 - R = q0 + q1 - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [q0.x, q0.y], "Q1": [q1.x, q1.y], "P": [P.x, P.y]} - return R.clear_cofactor() + u0, u1 = curve.hash_to_field(string_to_hash, 2) + q0 = cls.map_to_curve(u0, curve) + q1 = cls.map_to_curve(u1, curve) + return (q0 + q1).clear_cofactor() @classmethod - def encode_to_curve_hash2_suite_nu(cls, alpha_string: bytes, salt: bytes = b"", General_Check: bool = False) -> Self | Any: - """ - Encode a string to a curve point using Elligator 2. - - Args: - alpha_string: String to encode - salt: Optional salt for the encoding - - Returns: - TEAffinePoint: Resulting curve point - """ + def _encode_elligator2_nu( + cls, + alpha_string: bytes, + curve: C, + salt: bytes = b"", + ) -> Self: + """Encode with the nonuniform Elligator2 hash-to-curve variant.""" string_to_hash = salt + alpha_string - u = cls.curve.hash_to_field(string_to_hash, 1) - R = cls.map_to_curve(u[0]) # ELL2 - if General_Check: - P = R.clear_cofactor() - return {"u": u, "Q0": [R.x, R.y], "P": [P.x, P.y]} - return R.clear_cofactor() - - @classmethod # modified - def encode_to_curve_tai(cls, alpha_string: bytes | str, salt: bytes = b"") -> Self: - """ - Encode a string to a curve point using try-and-increment method for ECVRF. - - Args: - alpha_string:String to encode - salt: Optional salt for the encoding - - Returns: - TEAffinePoint: Resulting curve point - """ - ctr = 0 - front = b"\x01" - back = b"\x00" - alpha_string = alpha_string.encode() if isinstance(alpha_string, str) else alpha_string - salt = salt.encode() if isinstance(salt, str) else salt - suite_string = cls.curve.SUITE_STRING - while True: - ctr_string = ctr.to_bytes(1, "big") - hash_input = suite_string + front + salt + alpha_string + ctr_string + back - hash_output = hashlib.sha512(hash_input).digest() - try: - point = cls.string_to_point(hash_output[:32]) - except ValueError: - ctr += 1 - continue - if cls.curve.COFACTOR > 1: - point = point.clear_cofactor() - if point != cls.identity_point(): - return point - ctr += 1 + (u0,) = curve.hash_to_field(string_to_hash, 1) + return cls.map_to_curve(u0, curve).clear_cofactor() def clear_cofactor(self) -> Self: """ @@ -309,10 +254,18 @@ def clear_cofactor(self) -> Self: Returns: TEAffinePoint: Point in prime-order subgroup """ - return self * (self.curve.COFACTOR) + cofactor = self.curve.params.cofactor + if cofactor == 1: + return self + if cofactor > 0 and cofactor & (cofactor - 1) == 0: + result = self + for _ in range(cofactor.bit_length() - 1): + result = result.double() + return result + return self * cofactor @classmethod - def map_to_curve(cls, u: int) -> Self: + def map_to_curve(cls, u: int, curve: C) -> Self: """ Map a field element to a curve point using Elligator 2. @@ -323,11 +276,11 @@ def map_to_curve(cls, u: int) -> Self: TEAffinePoint: Resulting curve point """ - s, t = cls.curve.map_to_curve_ell2(u) - return cls.from_mont(s, t) + s, t = curve.map_to_curve_ell2(u) + return cls.from_mont(s, t, curve) @classmethod - def from_mont(cls, s: int, t: int) -> Self: + def from_mont(cls, s: int, t: int, curve: C) -> Self: """ Convert from Montgomery to Twisted Edwards coordinates. @@ -338,14 +291,12 @@ def from_mont(cls, s: int, t: int) -> Self: Returns: TEAffinePoint: Point in Twisted Edwards coordinates """ - field = cls.curve.PRIME_FIELD - - # Convert coordinates + field = curve.params.field_modulus tv1 = (s + 1) % field tv2 = (tv1 * t) % field try: - tv2 = pow(tv2, -1, cls.curve.PRIME_FIELD) + tv2 = pow(tv2, -1, field) except ValueError: tv2 = 0 @@ -355,26 +306,25 @@ def from_mont(cls, s: int, t: int) -> Self: # Handle exceptional case w = 1 if tv2 == 0 else w - return cls(v, w) + return cls(v, w, curve) @classmethod - def _x_recover(cls, y: int) -> int | tuple[int, int] | None: - """ """ - p = cls.curve.PRIME_FIELD + def _x_recover(cls, y: int, curve: C) -> int | tuple[int, int] | None: + """Recover the possible x-coordinates for a compressed Edwards y-coordinate.""" + p = curve.params.field_modulus lhs = (1 - y * y) % p - rhs = (cls.curve.EdwardsA - cls.curve.EdwardsD * y * y) % p + rhs = (curve.params.a - curve.params.d * y * y) % p - try: - inv_rhs = pow(rhs, -1, p) - except ValueError: + if rhs == 0: return None + inv_rhs = int(_invert(_mpz(rhs), _mpz(p))) x2 = (lhs * inv_rhs) % p try: - x = cls.curve.mod_sqrt(x2) + x = curve.mod_sqrt(x2) except ValueError: return None neg_x = (-x) % p - # consistent ordering + # Return candidates in a deterministic order; decoding chooses by sign bit. return (x, neg_x) if x <= neg_x else (neg_x, x) diff --git a/dot_ring/curve/twisted_edwards/te_curve.py b/dot_ring/curve/twisted_edwards/te_curve.py index 0fd6b43..e2e63da 100644 --- a/dot_ring/curve/twisted_edwards/te_curve.py +++ b/dot_ring/curve/twisted_edwards/te_curve.py @@ -2,12 +2,15 @@ from dataclasses import dataclass +from gmpy2 import invert as _invert +from gmpy2 import mpz as _mpz + from dot_ring.curve.curve import Curve -from dot_ring.curve.fast_math import map_to_curve_ell2_fast +from dot_ring.curve.specs.parameters import TwistedEdwardsCurveParams -@dataclass(frozen=True) -class TECurve(Curve): +@dataclass(frozen=True, kw_only=True) +class TECurve(Curve[int]): """ Twisted Edwards Curve implementation. @@ -17,50 +20,30 @@ class TECurve(Curve): where a, d are distinct, non-zero elements of the field. Attributes: - EdwardsA: The 'a' parameter in the curve equation - EdwardsD: The 'd' parameter in the curve equation - CHALLENGE_LENGTH: Length of the challenge in bytes (default: 32 for 256-bit security) + params: Twisted Edwards curve-suite constants. """ - EdwardsA: int - EdwardsD: int + params: TwistedEdwardsCurveParams def __post_init__(self) -> None: """Validate curve parameters after initialization.""" super().__post_init__() - if not self._validate_edwards_params(): - raise ValueError("Invalid Twisted Edwards curve parameters") + self._validate() - def _validate_edwards_params(self) -> bool: + def _validate(self): """ Validate Twisted Edwards specific parameters. Returns: bool: True if parameters are valid """ - return ( - self.EdwardsA != 0 - and self.EdwardsD != 0 - and self.EdwardsA != self.EdwardsD - and all(x < self.PRIME_FIELD for x in (self.EdwardsA, self.EdwardsD)) - ) - - # @lru_cache(maxsize=1024) # noqa: B019 - def calculate_j_k(self) -> tuple[int, int]: - """ - Calculate curve parameters J and K for Elligator 2. - - Returns: - Tuple[int, int]: J and K parameters - """ - p = self.PRIME_FIELD - denom = (self.EdwardsA - self.EdwardsD) % p - denom_inv = self.mod_inverse(denom) - - J = (2 * (self.EdwardsA + self.EdwardsD) * denom_inv) % p - K = (4 * denom_inv) % p - - return J, K + if not ( + self.params.a != 0 + and self.params.d != 0 + and self.params.a != self.params.d + and all(x < self.params.field_modulus for x in (self.params.a, self.params.d)) + ): + raise ValueError("Invalid Twisted Edwards curve parameters") def map_to_curve_ell2(self, u: int) -> tuple[int, int]: """ @@ -72,34 +55,41 @@ def map_to_curve_ell2(self, u: int) -> tuple[int, int]: Returns: Point: Point on Montgomery curve """ - J, K = self.calculate_j_k() - Z = self.Z - p = self.PRIME_FIELD - - from typing import cast - - return cast(tuple[int, int], map_to_curve_ell2_fast(u, J, K, Z, p)) - - @property - def curve_equation(self) -> str: - """ - Get the curve equation in readable form. - - Returns: - str: Curve equation - """ - return f"{self.EdwardsA}x² + y² = 1 + {self.EdwardsD}x²y²" - - def is_complete(self) -> bool: - """ - Check if the curve is complete. - - A Twisted Edwards curve is complete if: - - a is square - - d is non-square - in the base field. - - Returns: - bool: True if curve is complete - """ - return self.is_square(self.EdwardsA) and not self.is_square(self.EdwardsD) + p = self.params.field_modulus + map_curve = self.params.hash_to_curve.elligator2_map + if map_curve is None: + raise ValueError("Elligator 2 requires hash_to_curve.elligator2_map") + montgomery_a_over_b = (map_curve.a * pow(map_curve.b, -1, p)) % p + inv_b_squared = pow((map_curve.b * map_curve.b) % p, -1, p) + + pm = _mpz(p) + um = _mpz(u) + a_over_b = _mpz(montgomery_a_over_b) + inv_b_squared_m = _mpz(inv_b_squared) + map_b = _mpz(map_curve.b) + zm = _mpz(self.params.hash_to_curve.z) + + tv1 = (zm * um * um) % pm + if tv1 == -1 or tv1 == pm - 1: + tv1 = _mpz(0) + + x1 = (-a_over_b * _invert(tv1 + 1, pm)) % pm + gx1 = (((x1 + a_over_b) * x1 + inv_b_squared_m) * x1) % pm + x2 = (-x1 - a_over_b) % pm + gx2 = (tv1 * gx1) % pm + + e2 = self.is_square(int(gx1)) + if e2: + x = x1 + y2 = gx1 + else: + x = x2 + y2 = gx2 + + y = self.mod_sqrt(int(y2)) + ym = _mpz(y) + e3 = (ym % 2) == 1 + if e2 ^ e3: + ym = -ym % pm + + return int((x * map_b) % pm), int((ym * map_b) % pm) \ No newline at end of file diff --git a/dot_ring/curve/twisted_edwards/te_projective_point.py b/dot_ring/curve/twisted_edwards/te_projective_point.py index d58aaab..e3dbdec 100644 --- a/dot_ring/curve/twisted_edwards/te_projective_point.py +++ b/dot_ring/curve/twisted_edwards/te_projective_point.py @@ -29,32 +29,28 @@ def from_point(cls, point: CurvePoint) -> Self: from typing import cast x, y = cast(int, point.x), cast(int, point.y) - return cls(x, y, 1, (x * y) % point.curve.PRIME_FIELD, point.curve) + return cls(x, y, 1, (x * y) % point.curve.params.field_modulus, point.curve) @classmethod def from_affine(cls, point: TEAffinePoint) -> Self: from typing import cast x, y = cast(int, point.x), cast(int, point.y) - return cls(x, y, 1, (x * y) % point.curve.PRIME_FIELD, point.curve) + return cls(x, y, 1, (x * y) % point.curve.params.field_modulus, point.curve) - def to_affine(self, point_cls: type[TEAffinePoint] | None = None) -> TEAffinePoint: + def to_affine(self, point_type: type[TEAffinePoint] | None = None) -> TEAffinePoint: from .te_affine_point import TEAffinePoint as BaseAffine - target_cls = point_cls or BaseAffine - - # Ensure the target class has a curve reference - if not hasattr(target_cls, "curve"): - target_cls.curve = self.curve + target_type = point_type or BaseAffine if self.z == 0: - return target_cls(0, 1) + return target_type(0, 1, self.curve) - p = self.curve.PRIME_FIELD + p = self.curve.params.field_modulus inv_z = pow(self.z, -1, p) x = (self.x * inv_z) % p y = (self.y * inv_z) % p - return target_cls(x, y) + return target_type(x, y, self.curve) @classmethod def zero(cls, curve: C) -> Self: @@ -75,8 +71,8 @@ def double(self) -> Self: # T3 = E * H # Z3 = F * G - p = self.curve.PRIME_FIELD - a_coeff = self.curve.EdwardsA + p = self.curve.params.field_modulus + a_coeff = self.curve.params.a # Compute squares A = (self.x * self.x) % p @@ -116,9 +112,9 @@ def __add__(self, other: Self) -> Self: # T3 = E*H # Z3 = F*G - p = self.curve.PRIME_FIELD - a_coeff = self.curve.EdwardsA - d_coeff = self.curve.EdwardsD + p = self.curve.params.field_modulus + a_coeff = self.curve.params.a + d_coeff = self.curve.params.d A = (self.x * other.x) % p B = (self.y * other.y) % p diff --git a/tests/test_coverage/test_bls12_381_g2.py b/tests/test_coverage/test_bls12_381_g2.py index 283c30f..3a31b69 100644 --- a/tests/test_coverage/test_bls12_381_g2.py +++ b/tests/test_coverage/test_bls12_381_g2.py @@ -1,9 +1,8 @@ """Additional tests for BLS12-381 G2 module.""" from dot_ring.curve.specs.bls12_381_G2 import ( + BLS12_381_G2_PARAMS, BLS12_381_G2_RO, - BLS12_381_G2Curve, - BLS12_381_G2Params, ) @@ -12,18 +11,18 @@ class TestBLS12381G2Curve: def test_curve_parameters(self): """Test curve parameters are set.""" - curve = BLS12_381_G2Curve() + curve = BLS12_381_G2_RO.curve - assert curve.PRIME_FIELD is not None - assert curve.ORDER is not None - assert curve.COFACTOR is not None + assert curve.params.field_modulus is not None + assert curve.params.subgroup_order is not None + assert curve.params.cofactor is not None def test_curve_generator(self): """Test generator point.""" - curve = BLS12_381_G2Curve() + curve = BLS12_381_G2_RO.curve - assert curve.GENERATOR_X is not None - assert curve.GENERATOR_Y is not None + assert curve.params.generator[0] is not None + assert curve.params.generator[1] is not None class TestBLS12381G2Params: @@ -31,23 +30,23 @@ class TestBLS12381G2Params: def test_params_prime(self): """Test params prime field.""" - assert BLS12_381_G2Params.PRIME_FIELD is not None + assert BLS12_381_G2_PARAMS.field_modulus is not None def test_params_order(self): """Test params order.""" - assert BLS12_381_G2Params.ORDER is not None + assert BLS12_381_G2_PARAMS.subgroup_order is not None def test_params_cofactor(self): """Test params cofactor.""" - assert BLS12_381_G2Params.COFACTOR is not None + assert BLS12_381_G2_PARAMS.cofactor is not None def test_params_weierstrass_a(self): """Test Weierstrass A parameter.""" - assert BLS12_381_G2Params.WEIERSTRASS_A is not None + assert BLS12_381_G2_PARAMS.a is not None def test_params_weierstrass_b(self): """Test Weierstrass B parameter.""" - assert BLS12_381_G2Params.WEIERSTRASS_B is not None + assert BLS12_381_G2_PARAMS.b is not None class TestBLS12381G2RO: diff --git a/tests/test_coverage/test_sw_curve.py b/tests/test_coverage/test_sw_curve.py index 3586574..910e18d 100644 --- a/tests/test_coverage/test_sw_curve.py +++ b/tests/test_coverage/test_sw_curve.py @@ -1,10 +1,10 @@ """Tests for SW curve module to improve coverage using existing curve specs.""" -from dot_ring.curve.specs.bls12_381_G1 import BLS12_381_G1Curve -from dot_ring.curve.specs.p256 import P256Curve -from dot_ring.curve.specs.p384 import P384Curve -from dot_ring.curve.specs.p521 import P521Curve -from dot_ring.curve.specs.secp256k1 import Secp256k1_SW_Curve +from dot_ring.curve.specs.bls12_381_G1 import BLS12_381_G1_RO +from dot_ring.curve.specs.p256 import P256_RO +from dot_ring.curve.specs.p384 import P384_RO +from dot_ring.curve.specs.p521 import P521_RO +from dot_ring.curve.specs.secp256k1 import Secp256k1_RO class TestSWCurveIsOnCurve: @@ -12,33 +12,33 @@ class TestSWCurveIsOnCurve: def test_secp256k1_generator_on_curve(self): """Test that secp256k1 generator is on curve.""" - curve = Secp256k1_SW_Curve - point = (curve.GENERATOR_X, curve.GENERATOR_Y) + curve = Secp256k1_RO.curve + point = curve.params.generator assert curve.is_on_curve(point) is True def test_secp256k1_invalid_point(self): """Test invalid point detection on secp256k1.""" - curve = Secp256k1_SW_Curve + curve = Secp256k1_RO.curve # Arbitrary point likely not on curve point = (12345, 67890) assert curve.is_on_curve(point) is False def test_p256_generator_on_curve(self): """Test that P256 generator is on curve.""" - curve = P256Curve() - point = (curve.GENERATOR_X, curve.GENERATOR_Y) + curve = P256_RO.curve + point = curve.params.generator assert curve.is_on_curve(point) is True def test_p384_generator_on_curve(self): """Test that P384 generator is on curve.""" - curve = P384Curve() - point = (curve.GENERATOR_X, curve.GENERATOR_Y) + curve = P384_RO.curve + point = curve.params.generator assert curve.is_on_curve(point) is True def test_p521_generator_on_curve(self): """Test that P521 generator is on curve.""" - curve = P521Curve() - point = (curve.GENERATOR_X, curve.GENERATOR_Y) + curve = P521_RO.curve + point = curve.params.generator assert curve.is_on_curve(point) is True @@ -47,14 +47,14 @@ class TestSWCurveJInvariant: def test_secp256k1_j_invariant(self): """Test j-invariant calculation for secp256k1.""" - curve = Secp256k1_SW_Curve + curve = Secp256k1_RO.curve j = curve.j_invariant() # For secp256k1 with a=0, j-invariant should be 0 assert j == 0 def test_p256_j_invariant(self): """Test j-invariant calculation for P256.""" - curve = P256Curve() + curve = P256_RO.curve j = curve.j_invariant() # j-invariant is non-zero for P256 assert isinstance(j, int) @@ -65,12 +65,12 @@ class TestSWCurveValidation: def test_secp256k1_valid(self): """Test that secp256k1 passes validation.""" - curve = Secp256k1_SW_Curve + curve = Secp256k1_RO.curve assert curve._validate_weierstrass_params() is True def test_p256_valid(self): """Test that P256 passes validation.""" - curve = P256Curve() + curve = P256_RO.curve assert curve._validate_weierstrass_params() is True @@ -79,13 +79,13 @@ class TestBLS12381G1: def test_generator_on_curve(self): """Test that BLS12-381 G1 generator is on curve.""" - curve = BLS12_381_G1Curve() - point = (curve.GENERATOR_X, curve.GENERATOR_Y) + curve = BLS12_381_G1_RO.curve + point = curve.params.generator assert curve.is_on_curve(point) is True def test_j_invariant(self): """Test j-invariant for BLS12-381 G1.""" - curve = BLS12_381_G1Curve() + curve = BLS12_381_G1_RO.curve j = curve.j_invariant() # BLS12-381 has a=0, so j=0 assert j == 0 diff --git a/tests/test_curve_ops/test_curve.py b/tests/test_curve_ops/test_curve.py index 7101df2..bf1fd0a 100644 --- a/tests/test_curve_ops/test_curve.py +++ b/tests/test_curve_ops/test_curve.py @@ -1,18 +1,66 @@ +import hashlib + import pytest +from dot_ring.curve.e2c import E2C_Variant +from dot_ring.curve.fp2 import Fp2 from dot_ring.curve.specs.ed25519 import Ed25519_RO +from dot_ring.curve.specs.parameters import ( + EncodingParams, + HashToCurveParams, + MontgomeryCurveParams, + TwistedEdwardsCurveParams, +) from dot_ring.curve.twisted_edwards.te_affine_point import TEAffinePoint from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly from dot_ring.ring_proof.transcript.serialize import serialize +def _mock_hash_params(e2c: E2C_Variant = E2C_Variant.ELL2) -> HashToCurveParams[int]: + return HashToCurveParams( + dst=b"", + z=1, + field_extension_degree=1, + security_level=1, + field_length=2, + expand_len=32, + ) + + +def _mock_mg_params(*, uncompressed: bool = True) -> MontgomeryCurveParams: + return MontgomeryCurveParams( + field_modulus=17, + subgroup_order=19, + cofactor=1, + suite_id=b"mock-mg", + hash_fn=hashlib.sha256, + generator=(0, 0), + hash_to_curve=_mock_hash_params(), + encoding=EncodingParams(endian="little", point_len=2, challenge_len=0, uncompressed=uncompressed), + a=0, + b=1, + ) + + +def _mock_te_params(*, e2c: E2C_Variant = E2C_Variant.SSWU) -> TwistedEdwardsCurveParams: + return TwistedEdwardsCurveParams( + field_modulus=17, + subgroup_order=19, + cofactor=1, + suite_id=b"mock-te", + hash_fn=hashlib.sha256, + generator=(0, 1), + hash_to_curve=_mock_hash_params(e2c), + encoding=EncodingParams(endian="little", point_len=2, challenge_len=0, uncompressed=True), + a=1, + d=2, + ) + + class TestCoverageCurve: def test_te_affine_double_identity(self): """Test TEAffinePoint.double with identity point.""" - # Create an identity point - # Ed25519_RO.point is a configured TEAffinePoint subclass - PointClass = Ed25519_RO.point - identity = PointClass.identity() + identity = Ed25519_RO.identity() doubled = identity.double() assert doubled.is_identity() @@ -53,9 +101,7 @@ def test_serialize_invalid_type(self): def test_te_affine_double_coverage(self): """Test TEAffinePoint.double with a valid point.""" - # Generator point - PointClass = Ed25519_RO.point - g = PointClass.generator_point() + g = Ed25519_RO.generator_point() doubled = g.double() assert doubled == g + g @@ -64,11 +110,10 @@ def test_mg_affine_ops(self): """Test MGAffinePoint operations.""" from dot_ring.curve.specs.curve448 import Curve448_RO - PointClass = Curve448_RO.point - g = PointClass.generator_point() + g = Curve448_RO.generator_point() # Identity - identity = PointClass.identity() + identity = Curve448_RO.identity() assert (g + identity) == g assert (identity + g) == g @@ -84,11 +129,9 @@ def test_mg_affine_encode_map(self): """Test MGAffinePoint encode and map to curve.""" from dot_ring.curve.specs.curve448 import Curve448_RO - PointClass = Curve448_RO.point - # Encode to curve msg = b"test message" - p = PointClass.encode_to_curve(msg, b"DST") + p = Curve448_RO.encode_to_curve(msg, b"DST") assert p.is_on_curve() # Map to curve (if applicable directly, usually via encode) @@ -99,11 +142,10 @@ def test_sw_affine_ops(self): """Test SWAffinePoint operations.""" from dot_ring.curve.specs.p256 import P256_RO - PointClass = P256_RO.point - g = PointClass.generator_point() + g = P256_RO.generator_point() # Identity - identity = PointClass.identity() + identity = P256_RO.identity() assert identity.is_identity() # Add with identity @@ -122,113 +164,78 @@ def test_sw_affine_from_bytes(self): """Test SWAffinePoint from_bytes (compressed/uncompressed).""" from dot_ring.curve.specs.p256 import P256_RO - PointClass = P256_RO.point - g = PointClass.generator_point() + g = P256_RO.generator_point() # Uncompressed b_uncomp = g.point_to_string(compressed=False) - p_uncomp = PointClass.string_to_point(b_uncomp) + p_uncomp = P256_RO.string_to_point(b_uncomp) assert p_uncomp == g # Compressed b_comp = g.point_to_string(compressed=True) - p_comp = PointClass.string_to_point(b_comp) + p_comp = P256_RO.string_to_point(b_comp) assert p_comp == g def test_sw_affine_encode(self): """Test SWAffinePoint encode_to_curve.""" from dot_ring.curve.specs.p256 import P256_RO - PointClass = P256_RO.point - msg = b"test message" - p = PointClass.encode_to_curve(msg, b"DST") + p = P256_RO.encode_to_curve(msg, b"DST") assert p.is_on_curve() def test_sw_affine_string_to_point_errors(self): """Test SWAffinePoint string_to_point error cases.""" from dot_ring.curve.specs.p256 import P256_RO - PointClass = P256_RO.point - # Empty string with pytest.raises(ValueError, match="Empty octet string"): - PointClass.string_to_point(b"") + P256_RO.string_to_point(b"") # Invalid prefix with pytest.raises(ValueError, match="Invalid point encoding prefix"): - PointClass.string_to_point(b"\x05" + b"\x00" * 32) + P256_RO.string_to_point(b"\x05" + b"\x00" * 32) # Invalid length for compressed with pytest.raises(ValueError, match="Invalid compressed point length"): - PointClass.string_to_point(b"\x02" + b"\x00") + P256_RO.string_to_point(b"\x02" + b"\x00") # Invalid length for uncompressed with pytest.raises(ValueError, match="Invalid uncompressed point length"): - PointClass.string_to_point(b"\x04" + b"\x00") + P256_RO.string_to_point(b"\x04" + b"\x00") def test_mg_affine_string_to_point_errors(self): """Test MGAffinePoint string_to_point error cases.""" from dot_ring.curve.specs.curve448 import Curve448_RO - PointClass = Curve448_RO.point - # Invalid point (not on curve) # u=0, v=1 => 0 != 1 (invalid) # 56 bytes for u (0), 56 bytes for v (1) u_bytes = b"\x00" * 56 v_bytes = b"\x01" + b"\x00" * 55 # Little endian 1 with pytest.raises(ValueError, match="Point is not on the curve"): - PointClass.string_to_point(u_bytes + v_bytes) + Curve448_RO.string_to_point(u_bytes + v_bytes) def test_tonelli_shanks_coverage(self): """Test Tonelli-Shanks algorithm with a mock curve (p % 8 == 1).""" - from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.montgomery.mg_affine_point import MGAffinePoint from dot_ring.curve.montgomery.mg_curve import MGCurve class MockMGCurve(MGCurve): def __init__(self): - # p = 17 (1 mod 8) - super().__init__( - PRIME_FIELD=17, - ORDER=17, # Dummy - GENERATOR_X=0, - GENERATOR_Y=0, # Dummy - COFACTOR=1, - Z=1, - A=0, - B=1, # Dummy - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.ELL2, - BBx=0, - BBy=0, - L=0, - M=1, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=True, - ENDIAN="little", - POINT_LEN=2, - CHALLENGE_LENGTH=0, - ) + super().__init__(params=_mock_mg_params(), e2c_variant=E2C_Variant.ELL2) def __post_init__(self): pass - class MockMGPoint(MGAffinePoint): - curve = MockMGCurve() + curve = MockMGCurve() # Test _sqrt_mod_p with p=17 # Squares mod 17: 0, 1, 4, 9, 16, 8, 2, 15, 13 # 2 is a square (6^2 = 36 = 2 mod 17) # 3 is NOT a square - p = MockMGPoint(None, None) + p = MGAffinePoint(None, None, curve) # Test square root = p._sqrt_mod_p(2) @@ -241,46 +248,18 @@ class MockMGPoint(MGAffinePoint): def test_mg_affine_unimplemented_errors(self): """Test MGAffinePoint unimplemented methods.""" - from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.montgomery.mg_affine_point import MGAffinePoint from dot_ring.curve.montgomery.mg_curve import MGCurve class MockMGCurve(MGCurve): def __init__(self): - super().__init__( - PRIME_FIELD=17, - ORDER=17, - GENERATOR_X=0, - GENERATOR_Y=0, - COFACTOR=1, - Z=1, - A=0, - B=1, - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.ELL2, - BBx=0, - BBy=0, - L=0, - M=1, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=False, # Set to False to test error - ENDIAN="little", - POINT_LEN=2, - CHALLENGE_LENGTH=0, - ) + super().__init__(params=_mock_mg_params(uncompressed=False), e2c_variant=E2C_Variant.ELL2) def __post_init__(self): pass - class MockMGPoint(MGAffinePoint): - curve = MockMGCurve() - - p = MockMGPoint(0, 0) + curve = MockMGCurve() + p = MGAffinePoint(0, 0, curve) # Test point_to_string compressed error with pytest.raises(NotImplementedError, match="Compressed encoding not implemented"): @@ -288,7 +267,7 @@ class MockMGPoint(MGAffinePoint): # Test _x_recover NotImplementedError with pytest.raises(NotImplementedError): - MockMGPoint._x_recover(1) + MGAffinePoint._x_recover(1, curve) def test_sw_tonelli_shanks_coverage(self): """Test SWAffinePoint.tonelli_shanks with p % 8 == 1.""" @@ -316,8 +295,7 @@ def test_sw_hybrid_format(self): """Test SWAffinePoint hybrid format (0x06/0x07).""" from dot_ring.curve.specs.p256 import P256_RO - PointClass = P256_RO.point - g = PointClass.generator_point() + g = P256_RO.generator_point() # Construct hybrid bytes manually # Prefix 0x06 if y is even, 0x07 if y is odd @@ -329,53 +307,27 @@ def test_sw_hybrid_format(self): hybrid_bytes = prefix + x_bytes + y_bytes - p = PointClass.string_to_point(hybrid_bytes) + p = P256_RO.string_to_point(hybrid_bytes) assert p == g # Test invalid hybrid length with pytest.raises(ValueError, match="Invalid hybrid point length"): - PointClass.string_to_point(b"\x06" + b"\x00") + P256_RO.string_to_point(b"\x06" + b"\x00") # Test invalid hybrid parity wrong_prefix = b"\x07" if y_int % 2 == 0 else b"\x06" wrong_hybrid = wrong_prefix + x_bytes + y_bytes with pytest.raises(ValueError, match="Hybrid format: y parity doesn't match prefix"): - PointClass.string_to_point(wrong_hybrid) + P256_RO.string_to_point(wrong_hybrid) def test_curve_point_base_coverage(self): """Test base CurvePoint methods.""" - from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.montgomery.mg_curve import MGCurve from dot_ring.curve.point import CurvePoint class MockCurve(MGCurve): def __init__(self): - super().__init__( - PRIME_FIELD=17, - ORDER=17, - GENERATOR_X=0, - GENERATOR_Y=0, - COFACTOR=1, - Z=1, - A=0, - B=1, - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.ELL2, - BBx=0, - BBy=0, - L=0, - M=1, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=True, - ENDIAN="little", - POINT_LEN=2, - CHALLENGE_LENGTH=0, - ) + super().__init__(params=_mock_mg_params(), e2c_variant=E2C_Variant.ELL2) def __post_init__(self): pass @@ -391,8 +343,8 @@ def _validate_coordinates(self): return True @classmethod - def identity(cls): - return cls(None, None) + def identity(cls, curve): + return cls(None, None, curve) c = MockCurve() p = MockPoint(0, 0, curve=c) @@ -407,28 +359,22 @@ def identity(cls): # Test msm errors with pytest.raises(ValueError, match="Points and scalars must have same length"): - MockPoint.msm([p], [1, 2]) + MockPoint.msm([p], [1, 2], c) # Test msm empty # Should return identity - res = MockPoint.msm([], []) + res = MockPoint.msm([], [], c) assert res.x is None and res.y is None - # Test __hash__ with complex types - class ComplexCoord: - def __init__(self, re, im): - self.re, self.im = re, im - - p_complex = MockPoint(ComplexCoord(1, 2), ComplexCoord(3, 4), curve=c) - h = hash(p_complex) + p_hash = MockPoint(1, 2, curve=c) + h = hash(p_hash) assert isinstance(h, int) def test_te_affine_errors(self): """Test TEAffinePoint error cases.""" from dot_ring.curve.specs.ed25519 import Ed25519_RO - PointClass = Ed25519_RO.point - g = PointClass.generator_point() + g = Ed25519_RO.generator_point() # __add__ invalid type with pytest.raises(TypeError, match="Can only add TEAffinePoints"): @@ -443,126 +389,36 @@ def test_te_affine_errors(self): assert p_neg == -g # encode_to_curve invalid variant - from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.twisted_edwards.te_curve import TECurve class MockTECurve(TECurve): def __init__(self): - super().__init__( - PRIME_FIELD=17, - ORDER=18, - GENERATOR_X=0, - GENERATOR_Y=1, - COFACTOR=1, - Z=1, - EdwardsA=1, - EdwardsD=2, - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.SSWU, # SSWU not supported for TE - # Wait, TEAffinePoint.encode_to_curve supports TAI if implemented? - # Line 277 raises ValueError "Unexpected E2C Variant" - BBx=0, - BBy=0, - L=0, - M=1, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=True, - ENDIAN="little", - POINT_LEN=2, - CHALLENGE_LENGTH=0, - ) - - def calculate_j_k(self): - return 0, 0 - - class MockTEPoint(TEAffinePoint): - curve = MockTECurve() - - # Test encode_to_curve with TAI (which might raise if not handled in base) - # TEAffinePoint.encode_to_curve checks for ELL2/ELL2_NU. - # If E2C is TAI, it raises ValueError. + super().__init__(params=_mock_te_params(), e2c_variant=E2C_Variant.SSWU) + + curve = MockTECurve() + + # Unsupported TE hash-to-curve variants fail at dispatch. with pytest.raises(ValueError, match="Unexpected E2C Variant"): - MockTEPoint.encode_to_curve(b"test") + TEAffinePoint.encode_to_curve(b"test", curve=curve) # Test point_to_string uncompressed - # MockTECurve has UNCOMPRESSED=True - p = MockTEPoint(0, 1) # Identity + # MockTECurve uses uncompressed encoding. + p = TEAffinePoint(0, 1, curve) # Identity # Identity serialization should succeed for TE (0, 1) s = p.point_to_string() assert len(s) == 2 # 1 byte x, 1 byte y (p=17 -> 5 bits -> 1 byte) # Valid point serialization (uncompressed) - p_valid = MockTEPoint(0, 16) # 16 = -1 mod 17 + p_valid = TEAffinePoint(0, 16, curve) # 16 = -1 mod 17 s = p_valid.point_to_string() assert len(s) == 2 # 1 byte x, 1 byte y - def test_te_affine_hash_complex(self): - """Test TEAffinePoint.__hash__ with complex types.""" - from dot_ring.curve.e2c import E2C_Variant - from dot_ring.curve.twisted_edwards.te_curve import TECurve - - class MockTECurve(TECurve): - def __init__(self): - super().__init__( - PRIME_FIELD=17, - ORDER=18, - GENERATOR_X=0, - GENERATOR_Y=1, - COFACTOR=1, - Z=1, - EdwardsA=1, - EdwardsD=2, - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.SSWU, - BBx=0, - BBy=0, - L=0, - M=1, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=True, - ENDIAN="little", - POINT_LEN=2, - CHALLENGE_LENGTH=0, - ) - - def calculate_j_k(self): - return 0, 0 - - class MockTEPoint(TEAffinePoint): - curve = MockTECurve() - - def is_on_curve(self): - return True # Skip validation for complex types - - def _validate_coordinates(self): - return True + def test_curve_point_hash_fp2(self): + """Test CurvePoint.__hash__ with explicit Fp2 coordinates.""" + from dot_ring.curve.specs.bls12_381_G2 import BLS12_381_G2_RO - # Test with re/im (FieldElement-like) - class ComplexCoord: - def __init__(self, re, im): - self.re, self.im = re, im - - p1 = MockTEPoint(ComplexCoord(1, 2), ComplexCoord(3, 4)) - h1 = hash(p1) - # x_val = 1+2=3, y_val = 3+4=7. sum=10. 10 % 18 = 10. - assert h1 == 10 - - # Test with coeffs (FQ2-like) - class CoeffsCoord: - def __init__(self, coeffs): - self.coeffs = coeffs - - p2 = MockTEPoint(CoeffsCoord([1, 2]), CoeffsCoord([3, 4])) - h2 = hash(p2) - # x_val = 3, y_val = 7. sum=10. - assert h2 == 10 + point = BLS12_381_G2_RO.generator_point() + assert isinstance(point.x, Fp2) + assert isinstance(point.y, Fp2) + expected = (point.x.re + point.x.im + point.y.re + point.y.im) % point.curve.params.subgroup_order + assert point.__hash__() == expected diff --git a/tests/test_curve_ops/test_curve_ops.py b/tests/test_curve_ops/test_curve_ops.py index 5b1f8fa..5ac68ef 100644 --- a/tests/test_curve_ops/test_curve_ops.py +++ b/tests/test_curve_ops/test_curve_ops.py @@ -18,23 +18,23 @@ # PROPERTY-BASED TESTS; General mathematical group properties @pytest.mark.parametrize( - "PointClass, CurveClass", + "curve_variant", [ - (Bandersnatch.point, Bandersnatch.curve), - (JubJub.point, JubJub.curve), - (BabyJubJub.point, BabyJubJub.curve), - (Ed448_RO.point, Ed448_RO.curve), - (Ed25519_RO.point, Ed25519_RO.curve), - (Curve448_RO.point, Curve448_RO.curve), - (Curve25519_RO.point, Curve25519_RO.curve), - (P256_RO.point, P256_RO.curve), - (P384_RO.point, P384_RO.curve), - (P521_RO.point, P521_RO.curve), - (BLS12_381_G1_RO.point, BLS12_381_G1_RO.curve), - (BLS12_381_G2_RO.point, BLS12_381_G2_RO.curve), + Bandersnatch, + JubJub, + BabyJubJub, + Ed448_RO, + Ed25519_RO, + Curve448_RO, + Curve25519_RO, + P256_RO, + P384_RO, + P521_RO, + BLS12_381_G1_RO, + BLS12_381_G2_RO, ], ) -def test_curve_property_based(PointClass, CurveClass): +def test_curve_property_based(curve_variant): """ Property-based tests for fundamental group operations: - Commutativity: P + Q == Q + P @@ -43,8 +43,8 @@ def test_curve_property_based(PointClass, CurveClass): - Scalar wrap-around: (order * P) == identity - Negation: P + (-P) == identity """ - Generator = PointClass.generator_point() - order = CurveClass.ORDER + Generator = curve_variant.generator_point() + order = curve_variant.curve.params.subgroup_order # Random scalars within valid range a = random.randint(1, order - 1) @@ -83,24 +83,24 @@ def test_curve_sanity_operations(): """ curve_data = [ - (Bandersnatch.point, Bandersnatch.curve), - (JubJub.point, JubJub.curve), - (BabyJubJub.point, BabyJubJub.curve), - (Ed448_RO.point, Ed448_RO.curve), - (Ed25519_RO.point, Ed25519_RO.curve), - (Curve448_RO.point, Curve448_RO.curve), - (Curve25519_RO.point, Curve25519_RO.curve), - (P256_RO.point, P256_RO.curve), - (P384_RO.point, P384_RO.curve), - (P521_RO.point, P521_RO.curve), - (BLS12_381_G1_RO.point, BLS12_381_G1_RO.curve), - (BLS12_381_G2_RO.point, BLS12_381_G2_RO.curve), + Bandersnatch, + JubJub, + BabyJubJub, + Ed448_RO, + Ed25519_RO, + Curve448_RO, + Curve25519_RO, + P256_RO, + P384_RO, + P521_RO, + BLS12_381_G1_RO, + BLS12_381_G2_RO, ] - for PointClass, CurveClass in curve_data: - Generator = PointClass.generator_point() - Identity = PointClass.identity() - Order = CurveClass.ORDER + for curve_variant in curve_data: + Generator = curve_variant.generator_point() + Identity = curve_variant.identity() + Order = curve_variant.curve.params.subgroup_order # Addition with identity assert Generator + Identity == Generator diff --git a/tests/test_curve_ops/test_mg_curve.py b/tests/test_curve_ops/test_mg_curve.py index 7586ada..99d2c60 100644 --- a/tests/test_curve_ops/test_mg_curve.py +++ b/tests/test_curve_ops/test_mg_curve.py @@ -1,8 +1,33 @@ +import hashlib + import pytest from dot_ring.curve.e2c import E2C_Variant from dot_ring.curve.montgomery.mg_curve import MGCurve from dot_ring.curve.specs.curve25519 import Curve25519_RO +from dot_ring.curve.specs.parameters import EncodingParams, HashToCurveParams, MontgomeryCurveParams + + +def _mock_mg_params(*, a: int, b: int) -> MontgomeryCurveParams: + return MontgomeryCurveParams( + field_modulus=17, + subgroup_order=19, + cofactor=1, + suite_id=b"mock-mg", + hash_fn=hashlib.sha256, + generator=(0, 0), + hash_to_curve=HashToCurveParams( + dst=b"", + z=1, + field_extension_degree=1, + security_level=1, + field_length=2, + expand_len=32, + ), + encoding=EncodingParams(endian="little", point_len=0, challenge_len=0), + a=a, + b=b, + ) def test_mg_curve_methods(): @@ -10,7 +35,7 @@ def test_mg_curve_methods(): # Test __str__ and __repr__ assert str(curve).startswith("MGCurve(p=") - assert repr(curve).startswith("MGCurve(PRIME_FIELD=") + assert repr(curve).startswith("MGCurve(field_modulus=") # Test __eq__ and __hash__ curve2 = Curve25519_RO.curve @@ -18,7 +43,7 @@ def test_mg_curve_methods(): assert hash(curve) == hash(curve2) # Test is_on_curve with generator - generator = (curve.GENERATOR_X, curve.GENERATOR_Y) + generator = curve.params.generator assert curve.is_on_curve(generator) # Test is_on_curve with invalid point @@ -34,7 +59,7 @@ def test_mg_curve_methods(): assert curve.validate_point(inf) # Test validate_point with valid point - gen_point = Curve25519_RO.point.generator_point() + gen_point = Curve25519_RO.generator_point() assert curve.validate_point(gen_point) # Test validate_point with invalid point object (mock) @@ -59,57 +84,13 @@ def test_mg_curve_validation(): # Test invalid B with pytest.raises(ValueError, match="B coefficient cannot be zero"): MGCurve( - PRIME_FIELD=17, - ORDER=3, - GENERATOR_X=0, - GENERATOR_Y=0, - COFACTOR=1, - Z=1, - A=1, - B=0, # Invalid - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.ELL2, - BBx=0, - BBy=0, - L=0, - M=0, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=False, - ENDIAN="little", - POINT_LEN=0, - CHALLENGE_LENGTH=0, + params=_mock_mg_params(a=1, b=0), + e2c_variant=E2C_Variant.ELL2, ) # Test singular curve (A^2 - 4 = 0 mod p) => A=2, p=17 => 4-4=0 with pytest.raises(ValueError, match="Curve is singular"): MGCurve( - PRIME_FIELD=17, - ORDER=3, - GENERATOR_X=0, - GENERATOR_Y=0, - COFACTOR=1, - Z=1, - A=2, # 2^2 - 4 = 0 - B=1, - SUITE_STRING=b"", - DST=b"", - E2C=E2C_Variant.ELL2, - BBx=0, - BBy=0, - L=0, - M=0, - K=0, - H_A=None, - S_in_bytes=0, - Requires_Isogeny=False, - Isogeny_Coeffs=None, - UNCOMPRESSED=False, - ENDIAN="little", - POINT_LEN=0, - CHALLENGE_LENGTH=0, + params=_mock_mg_params(a=2, b=1), + e2c_variant=E2C_Variant.ELL2, ) From f10828d38a2c308d645296aab3b6753f39e51802 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:38:00 +0530 Subject: [PATCH 21/33] remove curve bloat --- .gitignore | 3 +- dot_ring/curve/fast_math.pyx | 172 ---- dot_ring/curve/field_arithmetic.pyx | 408 --------- dot_ring/curve/field_element.py | 195 ----- dot_ring/curve/glv.py | 199 ++++- .../curve/native_field/bandersnatch_te.pyx | 816 ++++++++++++++++++ .../curve/native_field/bls12_381_scalar.c | 41 +- dot_ring/curve/native_field/vector_ops.pyx | 376 -------- pyproject.toml | 4 +- setup.py | 29 +- tests/test_coverage/test_field_element.py | 371 -------- tests/test_coverage/test_glv.py | 62 +- tests/test_curve_ops/test_glv.py | 10 +- tests/test_curve_ops/test_native_field.py | 4 +- tests/test_curve_ops/test_vector_ops.py | 73 -- uv.lock | 26 - 16 files changed, 1037 insertions(+), 1752 deletions(-) delete mode 100644 dot_ring/curve/fast_math.pyx delete mode 100644 dot_ring/curve/field_arithmetic.pyx delete mode 100644 dot_ring/curve/field_element.py create mode 100644 dot_ring/curve/native_field/bandersnatch_te.pyx delete mode 100644 dot_ring/curve/native_field/vector_ops.pyx delete mode 100644 tests/test_coverage/test_field_element.py delete mode 100644 tests/test_curve_ops/test_vector_ops.py diff --git a/.gitignore b/.gitignore index 2a14cce..5d2d4f9 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,5 @@ perf *.so *.html -.coverage \ No newline at end of file +.coverage +*.ignore \ No newline at end of file diff --git a/dot_ring/curve/fast_math.pyx b/dot_ring/curve/fast_math.pyx deleted file mode 100644 index a09d6a4..0000000 --- a/dot_ring/curve/fast_math.pyx +++ /dev/null @@ -1,172 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: initializedcheck=False - -from gmpy2 import mpz as _mpz, powmod as _powmod, invert as _invert - -_prime_cache = {} - -cdef object _get_mpz_prime(object p): - """Get cached mpz version of prime.""" - if p not in _prime_cache: - _prime_cache[p] = _mpz(p) - return _prime_cache[p] - - -# ----------------------------------------------------------------------------- -# Core modular arithmetic -# ----------------------------------------------------------------------------- - -cpdef object powmod(object base, object exp, object mod): - """Fast modular exponentiation using gmpy2.""" - return int(_powmod(_mpz(base), _mpz(exp), _mpz(mod))) - - -cpdef object invert(object x, object mod): - """Fast modular inverse using gmpy2.""" - return int(_invert(_mpz(x), _mpz(mod))) - - -cpdef bint is_square(object x, object p): - """Check if x is a quadratic residue mod p using Euler's criterion.""" - if x == 0: - return True - cdef object pm = _get_mpz_prime(p) - cdef object exp = _mpz((p - 1) // 2) - return int(_powmod(_mpz(x), exp, pm)) == 1 - - -cpdef object sqrt_mod(object x, object p): - """ - Compute modular square root using Tonelli-Shanks algorithm. - Returns None if x is not a quadratic residue. - """ - cdef object pm, xm, Q, z, M, c, t, R, b, temp - cdef int S, i - - if x == 0: - return 0 - - pm = _get_mpz_prime(p) - xm = _mpz(x) - - # Check if x is a quadratic residue - if int(_powmod(xm, _mpz((p - 1) // 2), pm)) != 1: - return None - - # Simple case: p ≡ 3 (mod 4) - if p % 4 == 3: - return int(_powmod(xm, _mpz((p + 1) // 4), pm)) - - # Tonelli-Shanks algorithm for general case - Q = p - 1 - S = 0 - while Q % 2 == 0: - Q //= 2 - S += 1 - - # Find a non-residue z - z = 2 - while is_square(z, p): - z += 1 - - M = S - c = int(_powmod(_mpz(z), _mpz(Q), pm)) - t = int(_powmod(xm, _mpz(Q), pm)) - R = int(_powmod(xm, _mpz((Q + 1) // 2), pm)) - - while True: - if t == 0: - return 0 - if t == 1: - return R - - # Find least i such that t^(2^i) = 1 - i = 1 - temp = (t * t) % p - while temp != 1: - temp = (temp * temp) % p - i += 1 - - # Update values - b = c - for _ in range(M - i - 1): - b = (b * b) % p - M = i - c = (b * b) % p - t = (t * c) % p - R = (R * b) % p - - -cpdef object map_to_curve_ell2_fast(object u, object J, object K, object Z, object p): - """ - Fast Elligator 2 map to curve implementation. - """ - cdef object pm = _get_mpz_prime(p) - cdef object um = _mpz(u) - cdef object Jm = _mpz(J) - cdef object Km = _mpz(K) - cdef object Zm = _mpz(Z) - - # Compute constants - # c1 = (J * K^-1) % p - cdef object c1 = (Jm * _invert(Km, pm)) % pm - # c2 = (K^2)^-1 % p - cdef object c2 = _invert((Km * Km) % pm, pm) - - # Main mapping computation - # tv1 = (Z * u^2) % p - cdef object tv1 = (Zm * um * um) % pm - - cdef bint e1 = (tv1 == -1) or (tv1 == pm - 1) - if e1: - tv1 = _mpz(0) - - # x1 = (-c1 * (tv1 + 1)^-1) % p - cdef object x1 = (-c1 * _invert(tv1 + 1, pm)) % pm - - # gx1 = (((x1 + c1) * x1 + c2) * x1) % p - cdef object gx1 = (((x1 + c1) * x1 + c2) * x1) % pm - - # x2 = (-x1 - c1) % p - cdef object x2 = (-x1 - c1) % pm - - # gx2 = (tv1 * gx1) % p - cdef object gx2 = (tv1 * gx1) % pm - - # Choose correct values - # e2 = is_square(gx1) - cdef bint e2 = is_square(gx1, p) - - cdef object x, y2 - if e2: - x = x1 - y2 = gx1 - else: - x = x2 - y2 = gx2 - - # Compute square root - cdef object y = sqrt_mod(y2, p) - if y is None: - # This should not happen if map is correct - raise ValueError("Failed to compute sqrt in map_to_curve") - - cdef object ym = _mpz(y) - - # Adjust sign - # e3 = (y & 1) == 1 - cdef bint e3 = (ym % 2) == 1 - - if e2 ^ e3: - ym = -ym % pm - - # Scale coordinates - # s = (x * K) % p - cdef object s = (x * Km) % pm - # t = (y * K) % p - cdef object t = (ym * Km) % pm - - return (int(s), int(t)) diff --git a/dot_ring/curve/field_arithmetic.pyx b/dot_ring/curve/field_arithmetic.pyx deleted file mode 100644 index 47c6e39..0000000 --- a/dot_ring/curve/field_arithmetic.pyx +++ /dev/null @@ -1,408 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: initializedcheck=False - -cimport cython - -# Use gmpy2 for faster big integer operations -import gmpy2 -from gmpy2 import mpz as _mpz - -_invert = gmpy2.invert - -# Internal versions that work directly with mpz (no conversion overhead) -@cython.boundscheck(False) -@cython.wraparound(False) -cdef inline tuple _te_double_mpz(object x, object y, object z, object t, object a_coeff, object p): - """Internal doubling with mpz inputs.""" - cdef object A, B, C, D, E, F, G, H - cdef object x3, y3, z3, t3, z_sq, x_plus_y - - # A = X1^2 - A = (x * x) % p - # B = Y1^2 - B = (y * y) % p - # C = 2 * Z1^2 - z_sq = (z * z) % p - C = (z_sq + z_sq) % p - # D = a * A - D = (a_coeff * A) % p - # E = (X1 + Y1)^2 - A - B - x_plus_y = (x + y) % p - E = ((x_plus_y * x_plus_y) - A - B) % p - # G = D + B - G = (D + B) % p - # F = G - C - F = (G - C) % p - # H = D - B - H = (D - B) % p - # X3 = E * F - x3 = (E * F) % p - # Y3 = G * H - y3 = (G * H) % p - # T3 = E * H - t3 = (E * H) % p - # Z3 = F * G - z3 = (F * G) % p - - return (x3, y3, z3, t3) - - -@cython.boundscheck(False) -@cython.wraparound(False) -cdef inline tuple _te_add_mpz( - object x1, object y1, object z1, object t1, - object x2, object y2, object z2, object t2, - object a_coeff, object d_coeff, object p -): - """Internal addition with mpz inputs.""" - cdef object A, B, C, D, E, F, G, H - cdef object x3, y3, z3, t3 - - # A = X1*X2 - A = (x1 * x2) % p - # B = Y1*Y2 - B = (y1 * y2) % p - # C = d*T1*T2 - C = ((d_coeff * t1) % p * t2) % p - # D = Z1*Z2 - D = (z1 * z2) % p - # E = (X1+Y1)*(X2+Y2)-A-B - E = (((x1 + y1) * (x2 + y2)) - A - B) % p - # F = D-C - F = (D - C) % p - # G = D+C - G = (D + C) % p - # H = B-a*A - H = (B - (a_coeff * A)) % p - # X3 = E*F - x3 = (E * F) % p - # Y3 = G*H - y3 = (G * H) % p - # T3 = E*H - t3 = (E * H) % p - # Z3 = F*G - z3 = (F * G) % p - - return (x3, y3, z3, t3) - - -# Public API - converts to mpz at entry, back to int at exit -cpdef tuple te_double_cy( - object x, object y, object z, object t, - object a_coeff, object p -): - """ - Double a point in extended projective coordinates. - - Extended coordinates: (X:Y:Z:T) where x = X/Z, y = Y/Z, xy = T/Z - - Returns (X3, Y3, Z3, T3) - """ - cdef object xm, ym, zm, tm, am, pm - cdef tuple result - - # Convert to mpz once - xm = _mpz(x) - ym = _mpz(y) - zm = _mpz(z) - tm = _mpz(t) - am = _mpz(a_coeff) - pm = _mpz(p) - - result = _te_double_mpz(xm, ym, zm, tm, am, pm) - - # Convert back to int - return (int(result[0]), int(result[1]), int(result[2]), int(result[3])) - - -cpdef tuple te_add_cy( - object x1, object y1, object z1, object t1, - object x2, object y2, object z2, object t2, - object a_coeff, object d_coeff, object p -): - """ - Add two points in extended projective coordinates. - - Returns (X3, Y3, Z3, T3) - """ - cdef object x1m, y1m, z1m, t1m, x2m, y2m, z2m, t2m, am, dm, pm - cdef tuple result - - # Convert to mpz once - x1m = _mpz(x1) - y1m = _mpz(y1) - z1m = _mpz(z1) - t1m = _mpz(t1) - x2m = _mpz(x2) - y2m = _mpz(y2) - z2m = _mpz(z2) - t2m = _mpz(t2) - am = _mpz(a_coeff) - dm = _mpz(d_coeff) - pm = _mpz(p) - - result = _te_add_mpz(x1m, y1m, z1m, t1m, x2m, y2m, z2m, t2m, am, dm, pm) - - # Convert back to int - return (int(result[0]), int(result[1]), int(result[2]), int(result[3])) - - -cpdef object mod_inverse_cy(object val, object p): - """Compute modular inverse using gmpy2.""" - return int(_invert(_mpz(val), _mpz(p))) - - -cpdef tuple projective_to_affine_cy(object x, object y, object z, object p): - """Convert from projective to affine coordinates.""" - cdef object inv_z, pm, xm, ym, zm - if z == 0: - return (0, 1) - pm = _mpz(p) - xm = _mpz(x) - ym = _mpz(y) - zm = _mpz(z) - inv_z = _invert(zm, pm) - return (int((xm * inv_z) % pm), int((ym * inv_z) % pm)) - - -cpdef tuple scalar_mult_windowed_cy( - object k1, object k2, - object p1_x, object p1_y, object p1_z, object p1_t, - object p2_x, object p2_y, object p2_z, object p2_t, - object a_coeff, object d_coeff, object p, - int w = 2 -): - """ - Compute k1 * P1 + k2 * P2 using windowed simultaneous multi-scalar multiplication. - All internal operations use gmpy2 mpz for ~3x speedup. - """ - cdef int table_size = 1 << w - cdef int i, j, num_windows, max_bits - cdef object mask - cdef object k1_win, k2_win - cdef list p1_mults, p2_mults, table - cdef tuple curr, pt, R, identity - cdef list row - cdef object am, dm, pm - - # Convert curve params to mpz once - am = _mpz(a_coeff) - dm = _mpz(d_coeff) - pm = _mpz(p) - - # Identity in mpz - identity = (_mpz(0), _mpz(1), _mpz(1), _mpz(0)) - - # Convert input points to mpz - curr = (_mpz(p1_x), _mpz(p1_y), _mpz(p1_z), _mpz(p1_t)) - p1_base = curr - - # Build P1 multiples (all in mpz) - p1_mults = [identity] - for i in range(1, table_size): - p1_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p1_base[0], p1_base[1], p1_base[2], p1_base[3], - am, dm, pm) - - # Convert P2 to mpz - curr = (_mpz(p2_x), _mpz(p2_y), _mpz(p2_z), _mpz(p2_t)) - p2_base = curr - - # Build P2 multiples - p2_mults = [identity] - for i in range(1, table_size): - p2_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p2_base[0], p2_base[1], p2_base[2], p2_base[3], - am, dm, pm) - - # Build full table - table = [] - for i in range(table_size): - row = [] - for j in range(table_size): - if i == 0: - row.append(p2_mults[j]) - elif j == 0: - row.append(p1_mults[i]) - else: - pt = _te_add_mpz( - p1_mults[i][0], p1_mults[i][1], p1_mults[i][2], p1_mults[i][3], - p2_mults[j][0], p2_mults[j][1], p2_mults[j][2], p2_mults[j][3], - am, dm, pm - ) - row.append(pt) - table.append(row) - - # Calculate windows - max_bits = max(k1.bit_length(), k2.bit_length()) - if max_bits == 0: - max_bits = 1 - num_windows = (max_bits + w - 1) // w - mask = (1 << w) - 1 - - # Double-and-add (all in mpz) - R = identity - - for i in range(num_windows - 1, -1, -1): - # Double w times - for j in range(w): - R = _te_double_mpz(R[0], R[1], R[2], R[3], am, pm) - - # Extract windows - k1_win = (k1 >> (i * w)) & mask - k2_win = (k2 >> (i * w)) & mask - - # Add from table - if k1_win != 0 or k2_win != 0: - pt = table[k1_win][k2_win] - R = _te_add_mpz(R[0], R[1], R[2], R[3], - pt[0], pt[1], pt[2], pt[3], - am, dm, pm) - - # Convert back to int only at the end - return (int(R[0]), int(R[1]), int(R[2]), int(R[3])) - - -cpdef tuple scalar_mult_4_cy( - object k1, object k2, object k3, object k4, - object p1_x, object p1_y, object p1_z, object p1_t, - object p2_x, object p2_y, object p2_z, object p2_t, - object p3_x, object p3_y, object p3_z, object p3_t, - object p4_x, object p4_y, object p4_z, object p4_t, - object a_coeff, object d_coeff, object p, - int w = 2 -): - """ - Compute k1*P1 + k2*P2 + k3*P3 + k4*P4 using 2x2 windowed MSM. - All internal operations use gmpy2 mpz for ~3x speedup. - """ - cdef int table_size = 1 << w - cdef int i, j, num_windows, max_bits - cdef object mask - cdef object k1_win, k2_win, k3_win, k4_win - cdef list p1_mults, p2_mults, p3_mults, p4_mults - cdef list table12, table34, row, row34 - cdef tuple curr, pt, R, identity - cdef object am, dm, pm - - # Convert curve params to mpz once - am = _mpz(a_coeff) - dm = _mpz(d_coeff) - pm = _mpz(p) - - # Identity in mpz - identity = (_mpz(0), _mpz(1), _mpz(1), _mpz(0)) - - # Build P1 multiples - curr = (_mpz(p1_x), _mpz(p1_y), _mpz(p1_z), _mpz(p1_t)) - p1_base = curr - p1_mults = [identity] - for i in range(1, table_size): - p1_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p1_base[0], p1_base[1], p1_base[2], p1_base[3], - am, dm, pm) - - # Build P2 multiples - curr = (_mpz(p2_x), _mpz(p2_y), _mpz(p2_z), _mpz(p2_t)) - p2_base = curr - p2_mults = [identity] - for i in range(1, table_size): - p2_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p2_base[0], p2_base[1], p2_base[2], p2_base[3], - am, dm, pm) - - # Build table12 - table12 = [] - for i in range(table_size): - row = [] - for j in range(table_size): - if i == 0: - row.append(p2_mults[j]) - elif j == 0: - row.append(p1_mults[i]) - else: - pt = _te_add_mpz( - p1_mults[i][0], p1_mults[i][1], p1_mults[i][2], p1_mults[i][3], - p2_mults[j][0], p2_mults[j][1], p2_mults[j][2], p2_mults[j][3], - am, dm, pm - ) - row.append(pt) - table12.append(row) - - # Build P3 multiples - curr = (_mpz(p3_x), _mpz(p3_y), _mpz(p3_z), _mpz(p3_t)) - p3_base = curr - p3_mults = [identity] - for i in range(1, table_size): - p3_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p3_base[0], p3_base[1], p3_base[2], p3_base[3], - am, dm, pm) - - # Build P4 multiples - curr = (_mpz(p4_x), _mpz(p4_y), _mpz(p4_z), _mpz(p4_t)) - p4_base = curr - p4_mults = [identity] - for i in range(1, table_size): - p4_mults.append(curr) - curr = _te_add_mpz(curr[0], curr[1], curr[2], curr[3], - p4_base[0], p4_base[1], p4_base[2], p4_base[3], - am, dm, pm) - - # Build table34 - table34 = [] - for i in range(table_size): - row34 = [] - for j in range(table_size): - if i == 0: - row34.append(p4_mults[j]) - elif j == 0: - row34.append(p3_mults[i]) - else: - pt = _te_add_mpz( - p3_mults[i][0], p3_mults[i][1], p3_mults[i][2], p3_mults[i][3], - p4_mults[j][0], p4_mults[j][1], p4_mults[j][2], p4_mults[j][3], - am, dm, pm - ) - row34.append(pt) - table34.append(row34) - - # Calculate windows - max_bits = max(k1.bit_length(), k2.bit_length(), k3.bit_length(), k4.bit_length()) - if max_bits == 0: - max_bits = 1 - num_windows = (max_bits + w - 1) // w - mask = (1 << w) - 1 - - # Double-and-add (all in mpz) - R = identity - - for i in range(num_windows - 1, -1, -1): - for j in range(w): - R = _te_double_mpz(R[0], R[1], R[2], R[3], am, pm) - - k1_win = (k1 >> (i * w)) & mask - k2_win = (k2 >> (i * w)) & mask - k3_win = (k3 >> (i * w)) & mask - k4_win = (k4 >> (i * w)) & mask - - if k1_win != 0 or k2_win != 0: - pt = table12[k1_win][k2_win] - R = _te_add_mpz(R[0], R[1], R[2], R[3], - pt[0], pt[1], pt[2], pt[3], - am, dm, pm) - if k3_win != 0 or k4_win != 0: - pt = table34[k3_win][k4_win] - R = _te_add_mpz(R[0], R[1], R[2], R[3], - pt[0], pt[1], pt[2], pt[3], - am, dm, pm) - - # Convert back to int only at the end - return (int(R[0]), int(R[1]), int(R[2]), int(R[3])) diff --git a/dot_ring/curve/field_element.py b/dot_ring/curve/field_element.py deleted file mode 100644 index d4de42d..0000000 --- a/dot_ring/curve/field_element.py +++ /dev/null @@ -1,195 +0,0 @@ -""" -Field element implementation for finite field arithmetic. - -This module provides a FieldElement class that implements arithmetic operations -in finite fields, particularly useful for cryptographic operations. -""" - -from __future__ import annotations - - -class FieldElement: - """ - Represents an element in a finite field Fp or its quadratic extension Fp2. - - For Fp elements, set im=0. - For Fp2 elements, use (re, im) where the element is re + im * I (I = sqrt(-1)). - """ - - def __init__(self, re: int, im: int, p: int): - """ - Initialize a field element. - - Args: - re: Real part of the element - im: Imaginary part of the element (0 for Fp) - p: Prime modulus of the field - """ - self.re = re % p - self.im = im % p - self.p = p - - def __add__(self, other: FieldElement | int) -> FieldElement: - """Add two field elements or a field element and an integer.""" - if isinstance(other, FieldElement): - if self.p != other.p: - raise ValueError("Cannot add elements from different fields") - return FieldElement((self.re + other.re) % self.p, (self.im + other.im) % self.p, self.p) - return FieldElement((self.re + other) % self.p, self.im, self.p) - - def __sub__(self, other: FieldElement | int) -> FieldElement: - """Subtract two field elements or a field element and an integer.""" - if isinstance(other, FieldElement): - if self.p != other.p: - raise ValueError("Cannot subtract elements from different fields") - return FieldElement((self.re - other.re) % self.p, (self.im - other.im) % self.p, self.p) - return FieldElement((self.re - other) % self.p, self.im, self.p) - - def __mul__(self, other: FieldElement | int) -> FieldElement: - """Multiply two field elements or a field element and an integer.""" - if isinstance(other, FieldElement): - if self.p != other.p: - raise ValueError("Cannot multiply elements from different fields") - # (a + bi)(c + di) = (ac - bd) + (ad + bc)i - re = (self.re * other.re - self.im * other.im) % self.p - im = (self.re * other.im + self.im * other.re) % self.p - return FieldElement(re, im, self.p) - return FieldElement((self.re * other) % self.p, (self.im * other) % self.p, self.p) - - def __truediv__(self, other: FieldElement | int) -> FieldElement: - """Divide two field elements or a field element by an integer.""" - if isinstance(other, FieldElement): - return self * other.inv() - inv_other = pow(other, -1, self.p) - return self * inv_other - - def inv(self) -> FieldElement: - """Compute the multiplicative inverse using Fermat's Little Theorem.""" - # For Fp2, the inverse of (a + bi) is (a - bi)/(a² + b²) - denom = (self.re * self.re + self.im * self.im) % self.p - inv_denom = pow(denom, -1, self.p) - return FieldElement((self.re * inv_denom) % self.p, (-self.im * inv_denom) % self.p, self.p) - - def __neg__(self) -> FieldElement: - """Negate the field element.""" - return FieldElement(-self.re % self.p, -self.im % self.p, self.p) - - def __eq__(self, other: object) -> bool: - """Check if two field elements are equal.""" - if not isinstance(other, (FieldElement, int)): - return NotImplemented - if isinstance(other, FieldElement): - return self.re == other.re and self.im == other.im and self.p == other.p - return self.im == 0 and self.re == other % self.p - - def is_zero(self) -> bool: - """Check if the field element is zero.""" - return self.re == 0 and self.im == 0 - - def is_square(self) -> bool: - """Check if the element is a square in the field.""" - if self.im == 0: - return pow(self.re, (self.p - 1) // 2, self.p) == 1 - # For Fp2, check if norm is a square in Fp - norm = (self.re * self.re + self.im * self.im) % self.p - return pow(norm, (self.p - 1) // 2, self.p) == 1 - - # def __repr__(self) -> str: - # """Canonical string representation of the field element.""" - # return f"FieldElement({self.re}, {self.im}, {self.p})" - - def __radd__(self, other: int) -> FieldElement: - """Handle integer addition from the left.""" - return self + other - - # def __rtruediv__(self, other: int) -> FieldElement: - # """Handle integer division from the left.""" - # return FieldElement(other, 0, self.p) / self - - def __pow__(self, exponent: int) -> FieldElement: - """Raise the field element to an integer power.""" - if not isinstance(exponent, int): - raise TypeError("Exponent must be an integer") - if exponent < 0: - return self.inv() ** (-exponent) - result = FieldElement(1, 0, self.p) - base = self - while exponent > 0: - if exponent % 2 == 1: - result = result * base - base = base * base - exponent = exponent // 2 - return result - - def sqrt(self) -> FieldElement | None: - if self.im == 0: - # Fp case (Tonelli-Shanks, unchanged) - a = self.re - if pow(a, (self.p - 1) // 2, self.p) != 1: - return None # Not a square - - # Tonelli-Shanks - Q = self.p - 1 - S = 0 - while Q % 2 == 0: - Q //= 2 - S += 1 - # z = 2 - # while pow(z, (self.p - 1) // 2, self.p) != self.p - 1: - # z += 1 - - # c = pow(z, Q, self.p) - x = pow(a, (Q + 1) // 2, self.p) - # t = pow(a, Q, self.p) - # m = S - - # while t != 1: - # i, temp = 0, t - # while temp != 1 and i < m: - # temp = (temp * temp) % self.p - # i += 1 - # if i == m: - # return None - # b = pow(c, 1 << (m - i - 1), self.p) - # x = (x * b) % self.p - # t = (t * b * b) % self.p - # c = (b * b) % self.p - # m = i - return FieldElement(x, 0, self.p) - - # --- Fp2 case (fixed) --- - a0, a1 = self.re, self.im - p = self.p - - if a0 == 0 and a1 == 0: - return FieldElement(0, 0, p) - - # Step 1: α = a0² + a1² - alpha = (a0 * a0 + a1 * a1) % p - - # Step 2: sqrt_alpha = sqrt(α) in Fp - sqrt_alpha = FieldElement(alpha, 0, p).sqrt() - if sqrt_alpha is None: - return None - - inv2 = pow(2, -1, p) - - # Step 3: compute candidate x0 = (a0 + sqrt_alpha)/2 - x0 = ((a0 + sqrt_alpha.re) * inv2) % p - x1 = ((a0 - sqrt_alpha.re) * inv2) % p - - # Step 4: pick the candidate that is a square in Fp - if pow(x0, (p - 1) // 2, p) == 1: - y = FieldElement(x0, 0, p).sqrt() - elif pow(x1, (p - 1) // 2, p) == 1: - y = FieldElement(x1, 0, p).sqrt() - else: - return None # No square root exists - - if y is None: - return None - - # Step 5: compute imaginary part: x = a1 / (2*y) - inv_2y = pow(2 * y.re, -1, p) - x = (a1 * inv_2y) % p - return FieldElement(y.re, x, p) diff --git a/dot_ring/curve/glv.py b/dot_ring/curve/glv.py index b1bdc48..132fefb 100644 --- a/dot_ring/curve/glv.py +++ b/dot_ring/curve/glv.py @@ -5,14 +5,20 @@ from functools import lru_cache from typing import Any, TypeVar, cast -from .field_arithmetic import ( +from .native_field.bandersnatch_te import ( + msm_pippenger_signed_native_cy as _native_signed_pippenger_msm, +) +from .native_field.bandersnatch_te import ( projective_to_affine_cy as projective_to_affine, ) -from .field_arithmetic import ( - scalar_mult_4_cy as _compiled_msm4, +from .native_field.bandersnatch_te import ( + scalar_mult_4_native_w2_cy as _native_msm4_w2, +) +from .native_field.bandersnatch_te import ( + scalar_mult_6_native_w2_cy as _native_msm6_w2, ) -from .field_arithmetic import ( - scalar_mult_windowed_cy as _compiled_msm, +from .native_field.bandersnatch_te import ( + scalar_mult_windowed_native_w2_cy as _native_msm_w2, ) from .twisted_edwards.te_affine_point import TEAffinePoint @@ -81,6 +87,7 @@ def extended_euclidean_algorithm(self, n: int, lam: int) -> list[tuple[int, int, return sequence[:-1] + # Per-GLV bounded cache: scalar decomposition basis only depends on curve constants. @lru_cache(maxsize=1024) # noqa: B019 def find_short_vectors(self, n: int, lam: int) -> tuple[tuple[int, int], tuple[int, int]]: """ @@ -153,6 +160,7 @@ def decompose_scalar(self, k: int, n: int) -> tuple[int, int]: return k1, k2 + # Per-GLV bounded cache: fixed-base scalar multiplication reuses the same endomorphism point. @lru_cache(maxsize=128) # noqa: B019 def compute_endomorphism(self, point: AffinePointT) -> AffinePointT: """ @@ -161,18 +169,15 @@ def compute_endomorphism(self, point: AffinePointT) -> AffinePointT: Returns: AffinePointT: Result of endomorphism """ - x, y, p = point.x, point.y, point.curve.PRIME_FIELD + x, y, p = point.x, point.y, point.curve.params.field_modulus - # These constants should ideally be curve attributes - B = 0x52C9F28B828426A561F00D3A63511A882EA712770D9AF4D6EE0F014D172510B4 - C = 0x6CC624CF865457C3A97C6EFD6C17D1078456ABCFFF36F4E9515C806CDF650B3D if x is None or y is None: - return point.__class__(None, None) + return point.__class__.identity(point.curve) y2 = pow(cast(int, y), 2, p) xy = (cast(int, x) * cast(int, y)) % p - f_y = (C * (1 - y2)) % p - g_y = (B * (y2 + B)) % p - h_y = (y2 - B) % p + f_y = (self.constant_c * (1 - y2)) % p + g_y = (self.constant_b * (y2 + self.constant_b)) % p + h_y = (y2 - self.constant_b) % p x_p = (f_y * h_y) % p y_p = (g_y * xy) % p @@ -181,7 +186,7 @@ def compute_endomorphism(self, point: AffinePointT) -> AffinePointT: x_a = (x_p * pow(z_p, -1, p)) % p y_a = (y_p * pow(z_p, -1, p)) % p - return point.__class__(x_a, y_a) + return point.__class__(x_a, y_a, point.curve) def windowed_simultaneous_mult(self, k1: int, k2: int, P1: AffinePointT, P2: AffinePointT, w: int = 2) -> AffinePointT: """ @@ -211,9 +216,16 @@ def windowed_simultaneous_mult(self, k1: int, k2: int, P1: AffinePointT, P2: Aff k2 = -k2 P2 = -P2 - p = P1.curve.PRIME_FIELD - a_coeff = P1.curve.EdwardsA - d_coeff = P1.curve.EdwardsD + if P1.is_identity(): + return P2 * k2 + if P2.is_identity(): + return P1 * k1 + if w != 2: + return cast(AffinePointT, P1 * k1 + P2 * k2) # type: ignore[operator] + + p = P1.curve.params.field_modulus + a_coeff = P1.curve.params.a + d_coeff = P1.curve.params.d # Convert to projective coordinates if P1.x is None or P1.y is None or P2.x is None or P2.y is None: @@ -228,13 +240,12 @@ def windowed_simultaneous_mult(self, k1: int, k2: int, P1: AffinePointT, P2: Aff p2_t = P2.x * P2.y % p assert projective_to_affine is not None - # Use compiled MSM - rx, ry, rz, rt = _compiled_msm(k1, k2, P1.x, P1.y, 1, p1_t, P2.x, P2.y, 1, p2_t, a_coeff, d_coeff, p, w) + rx, ry, rz, rt = _native_msm_w2(k1, k2, P1.x, P1.y, 1, p1_t, P2.x, P2.y, 1, p2_t, a_coeff, d_coeff, p) # Convert back to affine ax, ay = projective_to_affine(rx, ry, rz, p) point_cls = cast(type[AffinePointT], P1.__class__) - return point_cls(ax, ay) + return point_cls(ax, ay, P1.curve) def multi_scalar_mult_4( self, @@ -275,9 +286,24 @@ def multi_scalar_mult_4( k4 = -k4 P4 = -P4 - p = P1.curve.PRIME_FIELD - a_coeff = P1.curve.EdwardsA - d_coeff = P1.curve.EdwardsD + if P1.is_identity() or P2.is_identity() or P3.is_identity() or P4.is_identity(): + point_cls = cast(type[AffinePointT], P1.__class__) + result = point_cls.identity(P1.curve) + for scalar, point in ((k1, P1), (k2, P2), (k3, P3), (k4, P4)): + if scalar != 0 and not point.is_identity(): + result = result + point * scalar # type: ignore[operator] + return result + if w != 2: + point_cls = cast(type[AffinePointT], P1.__class__) + result = point_cls.identity(P1.curve) + for scalar, point in ((k1, P1), (k2, P2), (k3, P3), (k4, P4)): + if scalar: + result = result + point * scalar # type: ignore[operator] + return result + + p = P1.curve.params.field_modulus + a_coeff = P1.curve.params.a + d_coeff = P1.curve.params.d # Convert to projective coordinates if P1.x is None or P1.y is None or P2.x is None or P2.y is None or P3.x is None or P3.y is None or P4.x is None or P4.y is None: @@ -293,7 +319,7 @@ def multi_scalar_mult_4( p4_t = (cast(int, P4.x) * cast(int, P4.y)) % p assert projective_to_affine is not None - rx, ry, rz, rt = _compiled_msm4( + rx, ry, rz, rt = _native_msm4_w2( k1, k2, k3, @@ -317,9 +343,130 @@ def multi_scalar_mult_4( a_coeff, d_coeff, p, - w, ) ax, ay = projective_to_affine(rx, ry, rz, p) point_cls = cast(type[AffinePointT], P1.__class__) - return point_cls(ax, ay) + return point_cls(ax, ay, P1.curve) + + def multi_scalar_mult_pippenger( + self, + scalars: list[int], + points: list[AffinePointT], + window_bits: int = 4, + ) -> AffinePointT: + if not points: + raise ValueError("Pippenger MSM requires at least one point") + p = points[0].curve.params.field_modulus + a_coeff = points[0].curve.params.a + d_coeff = points[0].curve.params.d + ax, ay = _native_signed_pippenger_msm(points, scalars, a_coeff, d_coeff, p, window_bits, True) + point_cls = cast(type[AffinePointT], points[0].__class__) + return point_cls(ax, ay, points[0].curve) + + def multi_scalar_mult_6( + self, + scalars: list[int], + points: list[AffinePointT], + w: int = 2, + ) -> AffinePointT: + """ + Compute a 6-point MSM using native fixed-window arithmetic. + + This is used for GLV-split 3-point MSMs. + """ + if len(scalars) != 6 or len(points) != 6: + raise ValueError("multi_scalar_mult_6 expects exactly 6 scalars and 6 points") + + work_points = list(points) + work_scalars = list(scalars) + for i, scalar in enumerate(work_scalars): + if scalar < 0: + work_scalars[i] = -scalar + work_points[i] = -work_points[i] + + if any(point.is_identity() for point in work_points): + point_cls = cast(type[AffinePointT], work_points[0].__class__) + result = point_cls.identity(work_points[0].curve) + for scalar, point in zip(work_scalars, work_points, strict=True): + if scalar != 0 and not point.is_identity(): + result = result + point * scalar # type: ignore[operator] + return result + + P1, P2, P3, P4, P5, P6 = work_points + if ( + P1.x is None + or P1.y is None + or P2.x is None + or P2.y is None + or P3.x is None + or P3.y is None + or P4.x is None + or P4.y is None + or P5.x is None + or P5.y is None + or P6.x is None + or P6.y is None + ): + point_cls = cast(type[AffinePointT], P1.__class__) + result = point_cls.identity(P1.curve) + for scalar, point in zip(work_scalars, work_points, strict=True): + result = result + point * scalar # type: ignore[operator] + return result + + p = P1.curve.params.field_modulus + a_coeff = P1.curve.params.a + d_coeff = P1.curve.params.d + + p1_t = (cast(int, P1.x) * cast(int, P1.y)) % p + p2_t = (cast(int, P2.x) * cast(int, P2.y)) % p + p3_t = (cast(int, P3.x) * cast(int, P3.y)) % p + p4_t = (cast(int, P4.x) * cast(int, P4.y)) % p + p5_t = (cast(int, P5.x) * cast(int, P5.y)) % p + p6_t = (cast(int, P6.x) * cast(int, P6.y)) % p + + if w != 2: + point_cls = cast(type[AffinePointT], P1.__class__) + result = point_cls.identity(P1.curve) + for scalar, point in zip(work_scalars, work_points, strict=True): + result = result + point * scalar # type: ignore[operator] + return result + + rx, ry, rz, rt = _native_msm6_w2( + work_scalars[0], + work_scalars[1], + work_scalars[2], + work_scalars[3], + work_scalars[4], + work_scalars[5], + P1.x, + P1.y, + 1, + p1_t, + P2.x, + P2.y, + 1, + p2_t, + P3.x, + P3.y, + 1, + p3_t, + P4.x, + P4.y, + 1, + p4_t, + P5.x, + P5.y, + 1, + p5_t, + P6.x, + P6.y, + 1, + p6_t, + a_coeff, + d_coeff, + p, + ) + ax, ay = projective_to_affine(rx, ry, rz, p) + point_cls = cast(type[AffinePointT], P1.__class__) + return point_cls(ax, ay, P1.curve) diff --git a/dot_ring/curve/native_field/bandersnatch_te.pyx b/dot_ring/curve/native_field/bandersnatch_te.pyx new file mode 100644 index 0000000..891fde5 --- /dev/null +++ b/dot_ring/curve/native_field/bandersnatch_te.pyx @@ -0,0 +1,816 @@ +# cython: language_level=3 +# cython: boundscheck=False +# cython: wraparound=False +# cython: cdivision=True +# cython: initializedcheck=False + +from libc.stdint cimport int16_t, uint64_t, uint8_t +from libc.stdlib cimport free, malloc + +from dot_ring.curve.native_field.scalar cimport ( + bls_scalar_t, + bls_scalar_add, + bls_scalar_exp, + bls_scalar_from_bytes, + bls_scalar_from_mont, + bls_scalar_from_uint64, + bls_scalar_inv, + bls_scalar_mul_mont, + bls_scalar_sub, + bls_scalar_to_bytes, + bls_scalar_to_mont, +) + +# Use gmpy2 for faster big integer operations +import gmpy2 +from gmpy2 import mpz as _mpz + +_invert = gmpy2.invert + +_BLS_SCALAR_MODULUS_INT = 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF00000001 + + +cdef struct TEProjNative: + bls_scalar_t x + bls_scalar_t y + bls_scalar_t z + bls_scalar_t t + + +cdef inline void _scalar_zero(bls_scalar_t *out): + bls_scalar_from_uint64(out, 0) + + +cdef inline void _scalar_one_mont(bls_scalar_t *out): + bls_scalar_from_uint64(out, 1) + bls_scalar_to_mont(out, out) + + +cdef inline void _scalar_from_py_mont(bls_scalar_t *out, object value): + cdef bytes b + value = int(value) % _BLS_SCALAR_MODULUS_INT + b = value.to_bytes(32, "little") + bls_scalar_from_bytes(out, b) + bls_scalar_to_mont(out, out) + + +cdef inline void _scalar_from_py_raw(bls_scalar_t *out, object value): + cdef bytes b + value = int(value) % _BLS_SCALAR_MODULUS_INT + b = value.to_bytes(32, "little") + bls_scalar_from_bytes(out, b) + + +cdef inline object _scalar_to_py_int(const bls_scalar_t *value): + cdef bls_scalar_t raw + cdef uint8_t b[32] + bls_scalar_from_mont(&raw, value) + bls_scalar_to_bytes(b, &raw) + return int.from_bytes((b)[:32], "little") + + +cdef inline bint _scalar_eq(const bls_scalar_t *a, const bls_scalar_t *b): + return ( + a.val[0] == b.val[0] + and a.val[1] == b.val[1] + and a.val[2] == b.val[2] + and a.val[3] == b.val[3] + ) + + +cdef inline bint _scalar_is_zero(const bls_scalar_t *value): + return ( + value.val[0] == 0 + and value.val[1] == 0 + and value.val[2] == 0 + and value.val[3] == 0 + ) + + +cdef inline tuple _projective_native_to_affine_tuple(const TEProjNative *point): + cdef bls_scalar_t zero + cdef bls_scalar_t z_inv + cdef bls_scalar_t x_affine + cdef bls_scalar_t y_affine + _scalar_zero(&zero) + if _scalar_eq(&point.z, &zero): + return (0, 1) + bls_scalar_inv(&z_inv, &point.z) + bls_scalar_mul_mont(&x_affine, &point.x, &z_inv) + bls_scalar_mul_mont(&y_affine, &point.y, &z_inv) + return (_scalar_to_py_int(&x_affine), _scalar_to_py_int(&y_affine)) + + +cdef inline void _point_identity(TEProjNative *out): + _scalar_zero(&out.x) + _scalar_one_mont(&out.y) + _scalar_one_mont(&out.z) + _scalar_zero(&out.t) + + +cdef inline void _point_copy(TEProjNative *out, const TEProjNative *value): + out.x = value.x + out.y = value.y + out.z = value.z + out.t = value.t + + +cdef inline void _point_neg_native(TEProjNative *out, const TEProjNative *value): + cdef bls_scalar_t zero + _scalar_zero(&zero) + bls_scalar_sub(&out.x, &zero, &value.x) + out.y = value.y + out.z = value.z + bls_scalar_sub(&out.t, &zero, &value.t) + + +cdef inline void _te_double_native(TEProjNative *out, const TEProjNative *point, const bls_scalar_t *a_coeff): + cdef bls_scalar_t A, B, C, D, E, F, G, H, z_sq, x_plus_y + + bls_scalar_mul_mont(&A, &point.x, &point.x) + bls_scalar_mul_mont(&B, &point.y, &point.y) + bls_scalar_mul_mont(&z_sq, &point.z, &point.z) + bls_scalar_add(&C, &z_sq, &z_sq) + bls_scalar_mul_mont(&D, a_coeff, &A) + bls_scalar_add(&x_plus_y, &point.x, &point.y) + bls_scalar_mul_mont(&E, &x_plus_y, &x_plus_y) + bls_scalar_sub(&E, &E, &A) + bls_scalar_sub(&E, &E, &B) + bls_scalar_add(&G, &D, &B) + bls_scalar_sub(&F, &G, &C) + bls_scalar_sub(&H, &D, &B) + bls_scalar_mul_mont(&out.x, &E, &F) + bls_scalar_mul_mont(&out.y, &G, &H) + bls_scalar_mul_mont(&out.t, &E, &H) + bls_scalar_mul_mont(&out.z, &F, &G) + + +cdef inline void _te_add_native( + TEProjNative *out, + const TEProjNative *left, + const TEProjNative *right, + const bls_scalar_t *a_coeff, + const bls_scalar_t *d_coeff, +): + cdef bls_scalar_t A, B, C, D, E, F, G, H, tmp1, tmp2 + + bls_scalar_mul_mont(&A, &left.x, &right.x) + bls_scalar_mul_mont(&B, &left.y, &right.y) + bls_scalar_mul_mont(&tmp1, d_coeff, &left.t) + bls_scalar_mul_mont(&C, &tmp1, &right.t) + bls_scalar_mul_mont(&D, &left.z, &right.z) + bls_scalar_add(&tmp1, &left.x, &left.y) + bls_scalar_add(&tmp2, &right.x, &right.y) + bls_scalar_mul_mont(&E, &tmp1, &tmp2) + bls_scalar_sub(&E, &E, &A) + bls_scalar_sub(&E, &E, &B) + bls_scalar_sub(&F, &D, &C) + bls_scalar_add(&G, &D, &C) + bls_scalar_mul_mont(&tmp1, a_coeff, &A) + bls_scalar_sub(&H, &B, &tmp1) + bls_scalar_mul_mont(&out.x, &E, &F) + bls_scalar_mul_mont(&out.y, &G, &H) + bls_scalar_mul_mont(&out.t, &E, &H) + bls_scalar_mul_mont(&out.z, &F, &G) + + +cdef inline void _point_from_py( + TEProjNative *out, + object x, + object y, + object z, + object t, +): + _scalar_from_py_mont(&out.x, x) + _scalar_from_py_mont(&out.y, y) + _scalar_from_py_mont(&out.z, z) + _scalar_from_py_mont(&out.t, t) + + +cdef inline void _scalar_limbs_from_py(uint64_t limbs[4], object value): + cdef bytes b + cdef const uint8_t *p + cdef int i, j + + value = int(value) + if value < 0: + raise ValueError("native scalar windowing expects non-negative scalars") + b = value.to_bytes(32, "little") + p = b + for i in range(4): + limbs[i] = 0 + for j in range(8): + limbs[i] |= (p[i * 8 + j]) << (j * 8) + + +cdef inline int _scalar_limbs_bit_length(const uint64_t limbs[4]): + cdef int i, bits + cdef uint64_t word + for i in range(3, -1, -1): + word = limbs[i] + if word != 0: + bits = i * 64 + while word != 0: + bits += 1 + word >>= 1 + return bits + return 0 + + +cdef inline unsigned int _scalar_window2(const uint64_t limbs[4], int bit_pos): + cdef int limb_idx = bit_pos >> 6 + cdef int shift = bit_pos & 63 + cdef uint64_t value + if limb_idx >= 4: + return 0 + value = limbs[limb_idx] >> shift + if shift > 62 and limb_idx + 1 < 4: + value |= limbs[limb_idx + 1] << (64 - shift) + return (value & 3) + + +cdef inline unsigned int _scalar_window_bits(const uint64_t *limbs, int bit_pos, int window_bits): + cdef int limb_idx = bit_pos >> 6 + cdef int shift = bit_pos & 63 + cdef uint64_t value = 0 + cdef uint64_t mask = (1 << window_bits) - 1 + if limb_idx >= 4: + return 0 + value = limbs[limb_idx] >> shift + if shift + window_bits > 64 and limb_idx + 1 < 4: + value |= limbs[limb_idx + 1] << (64 - shift) + return (value & mask) + +cpdef tuple projective_to_affine_cy(object x, object y, object z, object p): + """Convert from projective to affine coordinates.""" + cdef object inv_z, pm, xm, ym, zm + if z == 0: + return (0, 1) + pm = _mpz(p) + xm = _mpz(x) + ym = _mpz(y) + zm = _mpz(z) + inv_z = _invert(zm, pm) + return (int((xm * inv_z) % pm), int((ym * inv_z) % pm)) + + +cpdef tuple msm_pippenger_signed_native_cy( + object points, + object scalars, + object a_coeff, + object d_coeff, + object p, + int window_bits = 7, + bint affine = False, +): + """ + Variable-base MSM using signed-window Pippenger recoding. + + Signed digits halve the number of nonzero buckets for each window. The + scalar recoding is done once up front so the high-to-low Pippenger schedule + can stay branch-light inside the bucket loop. + """ + cdef Py_ssize_t n = len(points) + cdef Py_ssize_t i + cdef int j, bucket_index, window_index + cdef int max_bits = 0 + cdef int bits + cdef int base_windows + cdef int num_windows + cdef int bucket_count + cdef int half_bucket_count + cdef int highest_window = -1 + cdef unsigned int raw_digit + cdef int digit + cdef int carry + cdef TEProjNative *point_arr = NULL + cdef TEProjNative *neg_point_arr = NULL + cdef TEProjNative *buckets = NULL + cdef uint64_t *scalar_limbs = NULL + cdef int16_t *signed_digits = NULL + cdef TEProjNative result + cdef TEProjNative running + cdef TEProjNative base_point + cdef bls_scalar_t a_mont, d_mont + cdef object point + cdef object scalar_value + + if n != len(scalars): + raise ValueError("Points and scalars must have same length") + if n == 0: + if affine: + return (0, 1) + return (0, 1, 1, 0) + if window_bits < 2 or window_bits > 8: + raise ValueError("window_bits must be between 2 and 8") + + bucket_count = 1 << window_bits + half_bucket_count = bucket_count >> 1 + point_arr = malloc(n * sizeof(TEProjNative)) + neg_point_arr = malloc(n * sizeof(TEProjNative)) + scalar_limbs = malloc(n * 4 * sizeof(uint64_t)) + if point_arr == NULL or neg_point_arr == NULL or scalar_limbs == NULL: + if point_arr != NULL: + free(point_arr) + if neg_point_arr != NULL: + free(neg_point_arr) + if scalar_limbs != NULL: + free(scalar_limbs) + raise MemoryError() + + try: + _scalar_from_py_mont(&a_mont, a_coeff) + _scalar_from_py_mont(&d_mont, d_coeff) + + for i in range(n): + point = points[i] + scalar_value = int(scalars[i]) + if scalar_value < 0: + scalar_value = -scalar_value + _scalar_limbs_from_py(&scalar_limbs[i * 4], scalar_value) + bits = _scalar_limbs_bit_length(&scalar_limbs[i * 4]) + if bits > max_bits: + max_bits = bits + + _scalar_from_py_mont(&base_point.x, point.x) + _scalar_from_py_mont(&base_point.y, point.y) + _scalar_one_mont(&base_point.z) + bls_scalar_mul_mont(&base_point.t, &base_point.x, &base_point.y) + if int(scalars[i]) < 0: + _point_neg_native(&point_arr[i], &base_point) + _point_copy(&neg_point_arr[i], &base_point) + else: + _point_copy(&point_arr[i], &base_point) + _point_neg_native(&neg_point_arr[i], &base_point) + + if max_bits == 0: + if affine: + return (0, 1) + return (0, 1, 1, 0) + + base_windows = (max_bits + window_bits - 1) // window_bits + num_windows = base_windows + 1 + signed_digits = malloc(n * num_windows * sizeof(int16_t)) + buckets = malloc((half_bucket_count + 1) * sizeof(TEProjNative)) + if signed_digits == NULL or buckets == NULL: + if signed_digits != NULL: + free(signed_digits) + if buckets != NULL: + free(buckets) + raise MemoryError() + + for i in range(n): + carry = 0 + for window_index in range(base_windows): + raw_digit = _scalar_window_bits(&scalar_limbs[i * 4], window_index * window_bits, window_bits) + digit = raw_digit + carry + if digit >= half_bucket_count: + digit -= bucket_count + carry = 1 + else: + carry = 0 + signed_digits[i * num_windows + window_index] = digit + if digit != 0 and window_index > highest_window: + highest_window = window_index + signed_digits[i * num_windows + base_windows] = carry + if carry != 0 and base_windows > highest_window: + highest_window = base_windows + + _point_identity(&result) + for window_index in range(highest_window, -1, -1): + if window_index != highest_window: + for j in range(window_bits): + _te_double_native(&result, &result, &a_mont) + + for bucket_index in range(1, half_bucket_count + 1): + _point_identity(&buckets[bucket_index]) + + for i in range(n): + digit = signed_digits[i * num_windows + window_index] + if digit > 0: + _te_add_native(&buckets[digit], &buckets[digit], &point_arr[i], &a_mont, &d_mont) + elif digit < 0: + _te_add_native(&buckets[-digit], &buckets[-digit], &neg_point_arr[i], &a_mont, &d_mont) + + _point_identity(&running) + for bucket_index in range(half_bucket_count, 0, -1): + _te_add_native(&running, &running, &buckets[bucket_index], &a_mont, &d_mont) + _te_add_native(&result, &result, &running, &a_mont, &d_mont) + + if affine: + return _projective_native_to_affine_tuple(&result) + return ( + _scalar_to_py_int(&result.x), + _scalar_to_py_int(&result.y), + _scalar_to_py_int(&result.z), + _scalar_to_py_int(&result.t), + ) + finally: + if point_arr != NULL: + free(point_arr) + if neg_point_arr != NULL: + free(neg_point_arr) + if scalar_limbs != NULL: + free(scalar_limbs) + if signed_digits != NULL: + free(signed_digits) + if buckets != NULL: + free(buckets) + + +cpdef object sqrt_mod_bls_scalar_cy(object x): + """ + Tonelli-Shanks square root for the BLS12-381 scalar field. + + Bandersnatch's base field is this field. Keeping the 2-adic Tonelli loop in + native field operations avoids the Python big-int loop that dominates + Elligator2 hash-to-curve. + """ + cdef object value = int(x) % _BLS_SCALAR_MODULUS_INT + cdef bls_scalar_t xm, R, t, c, b, temp, one, exp_raw + cdef int M, i, j + + if value == 0: + return 0 + + _scalar_from_py_mont(&xm, value) + _scalar_one_mont(&one) + + # p - 1 = Q * 2^32 for the BLS12-381 scalar field. + _scalar_from_py_raw( + &exp_raw, + 0x73EDA753299D7D483339D80809A1D80553BDA402FFFE5BFEFFFFFFFF, + ) + bls_scalar_exp(&t, &xm, &exp_raw) + + _scalar_from_py_raw( + &exp_raw, + 0x39F6D3A994CEBEA4199CEC0404D0EC02A9DED2017FFF2DFF80000000, + ) + bls_scalar_exp(&R, &xm, &exp_raw) + + # First quadratic non-residue is 5; c = 5^Q. + _scalar_from_py_mont( + &c, + 0x212D79E5B416B6F0FD56DC8D168D6C0C4024FF270B3E0941B788F500B912F1F, + ) + + M = 32 + while True: + if _scalar_eq(&t, &one): + return _scalar_to_py_int(&R) + + i = 1 + bls_scalar_mul_mont(&temp, &t, &t) + while not _scalar_eq(&temp, &one): + bls_scalar_mul_mont(&temp, &temp, &temp) + i += 1 + if i >= M: + raise ValueError("sqrt_mod_bls_scalar_cy received a non-square") + + b = c + for j in range(M - i - 1): + bls_scalar_mul_mont(&b, &b, &b) + M = i + bls_scalar_mul_mont(&c, &b, &b) + bls_scalar_mul_mont(&t, &t, &c) + bls_scalar_mul_mont(&R, &R, &b) + + +cpdef tuple scalar_mult_windowed_native_w2_cy( + object k1, object k2, + object p1_x, object p1_y, object p1_z, object p1_t, + object p2_x, object p2_y, object p2_z, object p2_t, + object a_coeff, object d_coeff, object p, +): + """ + Compute k1 * P1 + k2 * P2 with a fixed 2-bit window over native + BLS12-381 scalar-field arithmetic. + + Bandersnatch's base field is the BLS12-381 scalar field, so this avoids the + gmpy2 object arithmetic in the older Cython kernel while preserving the same + extended Edwards formulas. + """ + cdef TEProjNative p1_base + cdef TEProjNative p2_base + cdef TEProjNative p1_mults[4] + cdef TEProjNative p2_mults[4] + cdef TEProjNative table[4][4] + cdef TEProjNative R + cdef bls_scalar_t a_mont, d_mont + cdef uint64_t k1_limbs[4] + cdef uint64_t k2_limbs[4] + cdef int i, j, max_bits, bits1, bits2, num_windows + cdef unsigned int k1_win, k2_win + + _scalar_from_py_mont(&a_mont, a_coeff) + _scalar_from_py_mont(&d_mont, d_coeff) + _point_from_py(&p1_base, p1_x, p1_y, p1_z, p1_t) + _point_from_py(&p2_base, p2_x, p2_y, p2_z, p2_t) + _scalar_limbs_from_py(k1_limbs, k1) + _scalar_limbs_from_py(k2_limbs, k2) + + _point_identity(&p1_mults[0]) + _point_copy(&p1_mults[1], &p1_base) + _te_add_native(&p1_mults[2], &p1_mults[1], &p1_base, &a_mont, &d_mont) + _te_add_native(&p1_mults[3], &p1_mults[2], &p1_base, &a_mont, &d_mont) + + _point_identity(&p2_mults[0]) + _point_copy(&p2_mults[1], &p2_base) + _te_add_native(&p2_mults[2], &p2_mults[1], &p2_base, &a_mont, &d_mont) + _te_add_native(&p2_mults[3], &p2_mults[2], &p2_base, &a_mont, &d_mont) + + for i in range(4): + for j in range(4): + if i == 0: + _point_copy(&table[i][j], &p2_mults[j]) + elif j == 0: + _point_copy(&table[i][j], &p1_mults[i]) + else: + _te_add_native(&table[i][j], &p1_mults[i], &p2_mults[j], &a_mont, &d_mont) + + bits1 = _scalar_limbs_bit_length(k1_limbs) + bits2 = _scalar_limbs_bit_length(k2_limbs) + max_bits = bits1 if bits1 >= bits2 else bits2 + if max_bits == 0: + max_bits = 1 + num_windows = (max_bits + 1) >> 1 + + _point_identity(&R) + for i in range(num_windows - 1, -1, -1): + _te_double_native(&R, &R, &a_mont) + _te_double_native(&R, &R, &a_mont) + + k1_win = _scalar_window2(k1_limbs, i << 1) + k2_win = _scalar_window2(k2_limbs, i << 1) + if k1_win != 0 or k2_win != 0: + _te_add_native(&R, &R, &table[k1_win][k2_win], &a_mont, &d_mont) + + return ( + _scalar_to_py_int(&R.x), + _scalar_to_py_int(&R.y), + _scalar_to_py_int(&R.z), + _scalar_to_py_int(&R.t), + ) + + +cpdef tuple scalar_mult_4_native_w2_cy( + object k1, object k2, object k3, object k4, + object p1_x, object p1_y, object p1_z, object p1_t, + object p2_x, object p2_y, object p2_z, object p2_t, + object p3_x, object p3_y, object p3_z, object p3_t, + object p4_x, object p4_y, object p4_z, object p4_t, + object a_coeff, object d_coeff, object p, +): + """ + Compute k1*P1 + k2*P2 + k3*P3 + k4*P4 with two 2-point native + lookup tables and a 2-bit window. + """ + cdef TEProjNative p1_base + cdef TEProjNative p2_base + cdef TEProjNative p3_base + cdef TEProjNative p4_base + cdef TEProjNative p1_mults[4] + cdef TEProjNative p2_mults[4] + cdef TEProjNative p3_mults[4] + cdef TEProjNative p4_mults[4] + cdef TEProjNative table12[4][4] + cdef TEProjNative table34[4][4] + cdef TEProjNative R + cdef bls_scalar_t a_mont, d_mont + cdef uint64_t k1_limbs[4] + cdef uint64_t k2_limbs[4] + cdef uint64_t k3_limbs[4] + cdef uint64_t k4_limbs[4] + cdef int i, j, max_bits, bits + cdef int num_windows + cdef unsigned int k1_win, k2_win, k3_win, k4_win + + _scalar_from_py_mont(&a_mont, a_coeff) + _scalar_from_py_mont(&d_mont, d_coeff) + _point_from_py(&p1_base, p1_x, p1_y, p1_z, p1_t) + _point_from_py(&p2_base, p2_x, p2_y, p2_z, p2_t) + _point_from_py(&p3_base, p3_x, p3_y, p3_z, p3_t) + _point_from_py(&p4_base, p4_x, p4_y, p4_z, p4_t) + _scalar_limbs_from_py(k1_limbs, k1) + _scalar_limbs_from_py(k2_limbs, k2) + _scalar_limbs_from_py(k3_limbs, k3) + _scalar_limbs_from_py(k4_limbs, k4) + + _point_identity(&p1_mults[0]) + _point_copy(&p1_mults[1], &p1_base) + _te_add_native(&p1_mults[2], &p1_mults[1], &p1_base, &a_mont, &d_mont) + _te_add_native(&p1_mults[3], &p1_mults[2], &p1_base, &a_mont, &d_mont) + + _point_identity(&p2_mults[0]) + _point_copy(&p2_mults[1], &p2_base) + _te_add_native(&p2_mults[2], &p2_mults[1], &p2_base, &a_mont, &d_mont) + _te_add_native(&p2_mults[3], &p2_mults[2], &p2_base, &a_mont, &d_mont) + + _point_identity(&p3_mults[0]) + _point_copy(&p3_mults[1], &p3_base) + _te_add_native(&p3_mults[2], &p3_mults[1], &p3_base, &a_mont, &d_mont) + _te_add_native(&p3_mults[3], &p3_mults[2], &p3_base, &a_mont, &d_mont) + + _point_identity(&p4_mults[0]) + _point_copy(&p4_mults[1], &p4_base) + _te_add_native(&p4_mults[2], &p4_mults[1], &p4_base, &a_mont, &d_mont) + _te_add_native(&p4_mults[3], &p4_mults[2], &p4_base, &a_mont, &d_mont) + + for i in range(4): + for j in range(4): + if i == 0: + _point_copy(&table12[i][j], &p2_mults[j]) + _point_copy(&table34[i][j], &p4_mults[j]) + elif j == 0: + _point_copy(&table12[i][j], &p1_mults[i]) + _point_copy(&table34[i][j], &p3_mults[i]) + else: + _te_add_native(&table12[i][j], &p1_mults[i], &p2_mults[j], &a_mont, &d_mont) + _te_add_native(&table34[i][j], &p3_mults[i], &p4_mults[j], &a_mont, &d_mont) + + max_bits = _scalar_limbs_bit_length(k1_limbs) + bits = _scalar_limbs_bit_length(k2_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k3_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k4_limbs) + if bits > max_bits: + max_bits = bits + if max_bits == 0: + max_bits = 1 + num_windows = (max_bits + 1) >> 1 + + _point_identity(&R) + for i in range(num_windows - 1, -1, -1): + _te_double_native(&R, &R, &a_mont) + _te_double_native(&R, &R, &a_mont) + + k1_win = _scalar_window2(k1_limbs, i << 1) + k2_win = _scalar_window2(k2_limbs, i << 1) + k3_win = _scalar_window2(k3_limbs, i << 1) + k4_win = _scalar_window2(k4_limbs, i << 1) + + if k1_win != 0 or k2_win != 0: + _te_add_native(&R, &R, &table12[k1_win][k2_win], &a_mont, &d_mont) + if k3_win != 0 or k4_win != 0: + _te_add_native(&R, &R, &table34[k3_win][k4_win], &a_mont, &d_mont) + + return ( + _scalar_to_py_int(&R.x), + _scalar_to_py_int(&R.y), + _scalar_to_py_int(&R.z), + _scalar_to_py_int(&R.t), + ) + + +cpdef tuple scalar_mult_6_native_w2_cy( + object k1, object k2, object k3, object k4, object k5, object k6, + object p1_x, object p1_y, object p1_z, object p1_t, + object p2_x, object p2_y, object p2_z, object p2_t, + object p3_x, object p3_y, object p3_z, object p3_t, + object p4_x, object p4_y, object p4_z, object p4_t, + object p5_x, object p5_y, object p5_z, object p5_t, + object p6_x, object p6_y, object p6_z, object p6_t, + object a_coeff, object d_coeff, object p, +): + """ + Compute a 6-point MSM as three paired 2-bit lookup tables. + + This is intended for GLV-split 3-point MSMs: each original scalar is split + into two roughly half-width scalars and paired with its endomorphism point. + """ + cdef TEProjNative p1_base + cdef TEProjNative p2_base + cdef TEProjNative p3_base + cdef TEProjNative p4_base + cdef TEProjNative p5_base + cdef TEProjNative p6_base + cdef TEProjNative p1_mults[4] + cdef TEProjNative p2_mults[4] + cdef TEProjNative p3_mults[4] + cdef TEProjNative p4_mults[4] + cdef TEProjNative p5_mults[4] + cdef TEProjNative p6_mults[4] + cdef TEProjNative table12[4][4] + cdef TEProjNative table34[4][4] + cdef TEProjNative table56[4][4] + cdef TEProjNative R + cdef bls_scalar_t a_mont, d_mont + cdef uint64_t k1_limbs[4] + cdef uint64_t k2_limbs[4] + cdef uint64_t k3_limbs[4] + cdef uint64_t k4_limbs[4] + cdef uint64_t k5_limbs[4] + cdef uint64_t k6_limbs[4] + cdef int i, j, max_bits, bits + cdef int num_windows + cdef unsigned int k1_win, k2_win, k3_win, k4_win, k5_win, k6_win + + _scalar_from_py_mont(&a_mont, a_coeff) + _scalar_from_py_mont(&d_mont, d_coeff) + _point_from_py(&p1_base, p1_x, p1_y, p1_z, p1_t) + _point_from_py(&p2_base, p2_x, p2_y, p2_z, p2_t) + _point_from_py(&p3_base, p3_x, p3_y, p3_z, p3_t) + _point_from_py(&p4_base, p4_x, p4_y, p4_z, p4_t) + _point_from_py(&p5_base, p5_x, p5_y, p5_z, p5_t) + _point_from_py(&p6_base, p6_x, p6_y, p6_z, p6_t) + _scalar_limbs_from_py(k1_limbs, k1) + _scalar_limbs_from_py(k2_limbs, k2) + _scalar_limbs_from_py(k3_limbs, k3) + _scalar_limbs_from_py(k4_limbs, k4) + _scalar_limbs_from_py(k5_limbs, k5) + _scalar_limbs_from_py(k6_limbs, k6) + + _point_identity(&p1_mults[0]) + _point_copy(&p1_mults[1], &p1_base) + _te_add_native(&p1_mults[2], &p1_mults[1], &p1_base, &a_mont, &d_mont) + _te_add_native(&p1_mults[3], &p1_mults[2], &p1_base, &a_mont, &d_mont) + + _point_identity(&p2_mults[0]) + _point_copy(&p2_mults[1], &p2_base) + _te_add_native(&p2_mults[2], &p2_mults[1], &p2_base, &a_mont, &d_mont) + _te_add_native(&p2_mults[3], &p2_mults[2], &p2_base, &a_mont, &d_mont) + + _point_identity(&p3_mults[0]) + _point_copy(&p3_mults[1], &p3_base) + _te_add_native(&p3_mults[2], &p3_mults[1], &p3_base, &a_mont, &d_mont) + _te_add_native(&p3_mults[3], &p3_mults[2], &p3_base, &a_mont, &d_mont) + + _point_identity(&p4_mults[0]) + _point_copy(&p4_mults[1], &p4_base) + _te_add_native(&p4_mults[2], &p4_mults[1], &p4_base, &a_mont, &d_mont) + _te_add_native(&p4_mults[3], &p4_mults[2], &p4_base, &a_mont, &d_mont) + + _point_identity(&p5_mults[0]) + _point_copy(&p5_mults[1], &p5_base) + _te_add_native(&p5_mults[2], &p5_mults[1], &p5_base, &a_mont, &d_mont) + _te_add_native(&p5_mults[3], &p5_mults[2], &p5_base, &a_mont, &d_mont) + + _point_identity(&p6_mults[0]) + _point_copy(&p6_mults[1], &p6_base) + _te_add_native(&p6_mults[2], &p6_mults[1], &p6_base, &a_mont, &d_mont) + _te_add_native(&p6_mults[3], &p6_mults[2], &p6_base, &a_mont, &d_mont) + + for i in range(4): + for j in range(4): + if i == 0: + _point_copy(&table12[i][j], &p2_mults[j]) + _point_copy(&table34[i][j], &p4_mults[j]) + _point_copy(&table56[i][j], &p6_mults[j]) + elif j == 0: + _point_copy(&table12[i][j], &p1_mults[i]) + _point_copy(&table34[i][j], &p3_mults[i]) + _point_copy(&table56[i][j], &p5_mults[i]) + else: + _te_add_native(&table12[i][j], &p1_mults[i], &p2_mults[j], &a_mont, &d_mont) + _te_add_native(&table34[i][j], &p3_mults[i], &p4_mults[j], &a_mont, &d_mont) + _te_add_native(&table56[i][j], &p5_mults[i], &p6_mults[j], &a_mont, &d_mont) + + max_bits = _scalar_limbs_bit_length(k1_limbs) + bits = _scalar_limbs_bit_length(k2_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k3_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k4_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k5_limbs) + if bits > max_bits: + max_bits = bits + bits = _scalar_limbs_bit_length(k6_limbs) + if bits > max_bits: + max_bits = bits + if max_bits == 0: + max_bits = 1 + num_windows = (max_bits + 1) >> 1 + + _point_identity(&R) + for i in range(num_windows - 1, -1, -1): + _te_double_native(&R, &R, &a_mont) + _te_double_native(&R, &R, &a_mont) + + k1_win = _scalar_window2(k1_limbs, i << 1) + k2_win = _scalar_window2(k2_limbs, i << 1) + k3_win = _scalar_window2(k3_limbs, i << 1) + k4_win = _scalar_window2(k4_limbs, i << 1) + k5_win = _scalar_window2(k5_limbs, i << 1) + k6_win = _scalar_window2(k6_limbs, i << 1) + + if k1_win != 0 or k2_win != 0: + _te_add_native(&R, &R, &table12[k1_win][k2_win], &a_mont, &d_mont) + if k3_win != 0 or k4_win != 0: + _te_add_native(&R, &R, &table34[k3_win][k4_win], &a_mont, &d_mont) + if k5_win != 0 or k6_win != 0: + _te_add_native(&R, &R, &table56[k5_win][k6_win], &a_mont, &d_mont) + + return ( + _scalar_to_py_int(&R.x), + _scalar_to_py_int(&R.y), + _scalar_to_py_int(&R.z), + _scalar_to_py_int(&R.t), + ) diff --git a/dot_ring/curve/native_field/bls12_381_scalar.c b/dot_ring/curve/native_field/bls12_381_scalar.c index f7e970c..d859bbb 100644 --- a/dot_ring/curve/native_field/bls12_381_scalar.c +++ b/dot_ring/curve/native_field/bls12_381_scalar.c @@ -63,29 +63,7 @@ void bls_scalar_add(bls_scalar_t *out, const bls_scalar_t *a, const bls_scalar_t // Also need to account for the initial carry from addition. // If carry=1, result > 2^256 > P, so we definitely subtract P. - // Logic: - // If carry=1, we must subtract P. The result fits in 256 bits. - // If carry=0, we subtract P only if res >= P. - - // Actually, simpler constant-time way: - // mask = 0 if (res < P and carry=0) else 0xFF..FF - // But let's stick to simple conditional move logic for now or just use the borrow. - - // If carry is set, we definitely wrap around. - // If borrow is NOT set (meaning res >= P), we also wrap around. - // So if (carry | !borrow), we use tmp. - - // Wait, sbb returns borrow=1 if a < b. - // So if res < P, borrow=1. - // We want to subtract P if res >= P. i.e., borrow=0. - // BUT, if carry=1, then the real value is 2^256 + res. - // 2^256 + res - P is the correct result. - // The sbb above computes res - P. - // If carry=1, then 2^256 + res - P = (2^256 - P) + res. - // This is getting complicated to explain but standard practice: - // If carry=1, we need the result of the subtraction. - // If carry=0, we need the result of subtraction ONLY IF borrow=0. - + // Use the subtraction when addition overflowed or the unreduced sum was >= P. bool use_sub = carry || (borrow == 0); for(int i=0; i<4; i++) { @@ -229,14 +207,12 @@ void bls_scalar_to_mont(bls_scalar_t *out, const bls_scalar_t *in) { } void bls_scalar_from_mont(bls_scalar_t *out, const bls_scalar_t *in) { - // Multiply by 1 in Montgomery form (which is just R^-1 mod P in normal form? No.) - // Montgomery mul: out = a * b * R^-1 - // If we want a * R^-1, we set b = 1. + // Montgomery multiplication by the raw value 1 converts back from Montgomery form. bls_scalar_t one = {{1, 0, 0, 0}}; bls_scalar_mul_mont(out, in, &one); } -// Montgomery Squaring (just wrapper around mul for now) +// Montgomery squaring. void bls_scalar_sqr_mont(bls_scalar_t *out, const bls_scalar_t *a) { bls_scalar_mul_mont(out, a, a); } @@ -246,16 +222,7 @@ void bls_scalar_sqr_mont(bls_scalar_t *out, const bls_scalar_t *a) { // exp is a standard integer (scalar) // result is in Montgomery form void bls_scalar_exp(bls_scalar_t *out, const bls_scalar_t *base, const bls_scalar_t *exp) { - // res = 1 (in Montgomery form, i.e., R mod P) - bls_scalar_t res = BLS_SCALAR_R2; // R^2 * 1 * R^-1 = R - // Wait, R2 is R^2. 1 in Mont is 1 * R. - // My mul_mont(a, b) computes a * b * R^-1. - // If I want 1 in Mont, I need 1 * R. - // R2 is R^2. - // mul_mont(R2, 1) = R^2 * 1 * R^-1 = R. - // So yes, to get 1_Mont, I can use mul_mont(R2, 1). - // Or just precompute R. - // Let's compute 1_Mont dynamically for now to be safe. + bls_scalar_t res; bls_scalar_t one = {{1, 0, 0, 0}}; bls_scalar_to_mont(&res, &one); diff --git a/dot_ring/curve/native_field/vector_ops.pyx b/dot_ring/curve/native_field/vector_ops.pyx deleted file mode 100644 index fb80f3a..0000000 --- a/dot_ring/curve/native_field/vector_ops.pyx +++ /dev/null @@ -1,376 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False - -from libc.stdint cimport uint64_t, uint8_t -from libc.stdlib cimport malloc, free -from dot_ring.curve.native_field.scalar cimport Scalar, bls_scalar_t, bls_scalar_vec_add, bls_scalar_vec_sub, bls_scalar_vec_mul, bls_scalar_vec_mul_scalar, bls_scalar_vec_add_scalar, bls_scalar_vec_sub_scalar, bls_scalar_sub - -cdef class VectorOps: - pass - -def vect_add(object a, object b, object prime): - cdef int n, i - cdef bls_scalar_t *a_ptr - cdef bls_scalar_t *b_ptr - cdef bls_scalar_t *res_ptr - cdef list res_list - cdef Scalar s_res - cdef int is_a_list = isinstance(a, list) - cdef int is_b_list = isinstance(b, list) - cdef bls_scalar_t scalar_b - cdef bls_scalar_t stack_a[256] - cdef bls_scalar_t stack_b[256] - cdef bls_scalar_t stack_res[256] - cdef int use_heap = 0 - - if is_a_list and is_b_list: - n = len(a) - if len(b) != n: - raise ValueError("Vector lengths must match") - - if n <= 256: - a_ptr = stack_a - b_ptr = stack_b - res_ptr = stack_res - else: - use_heap = 1 - a_ptr = malloc(n * sizeof(bls_scalar_t)) - b_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not b_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if b_ptr: free(b_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - # Unbox inputs - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - val_b = b[i] - if isinstance(val_b, Scalar): - b_ptr[i] = (val_b).val - else: - b_ptr[i] = Scalar(val_b).val - - # Call C batch function - bls_scalar_vec_add(res_ptr, a_ptr, b_ptr, n) - - # Box results - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - if use_heap: - free(a_ptr) - free(b_ptr) - free(res_ptr) - - elif is_a_list and not is_b_list: - # Vector + Scalar - n = len(a) - if n <= 256: - a_ptr = stack_a - res_ptr = stack_res - else: - use_heap = 1 - a_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - if isinstance(b, Scalar): - scalar_b = (b).val - else: - scalar_b = Scalar(b).val - - bls_scalar_vec_add_scalar(res_ptr, a_ptr, &scalar_b, n) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - if use_heap: - free(a_ptr) - free(res_ptr) - - elif not is_a_list and is_b_list: - return vect_add(b, a, prime) - - return NotImplemented - -def vect_sub(object a, object b, object prime): - cdef int n, i - cdef bls_scalar_t *a_ptr - cdef bls_scalar_t *b_ptr - cdef bls_scalar_t *res_ptr - cdef list res_list - cdef Scalar s_res - cdef int is_a_list = isinstance(a, list) - cdef int is_b_list = isinstance(b, list) - cdef bls_scalar_t scalar_b - - if is_a_list and is_b_list: - n = len(a) - if len(b) != n: - raise ValueError("Vector lengths must match") - - a_ptr = malloc(n * sizeof(bls_scalar_t)) - b_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not b_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if b_ptr: free(b_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - val_b = b[i] - if isinstance(val_b, Scalar): - b_ptr[i] = (val_b).val - else: - b_ptr[i] = Scalar(val_b).val - - bls_scalar_vec_sub(res_ptr, a_ptr, b_ptr, n) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - free(a_ptr) - free(b_ptr) - free(res_ptr) - - elif is_a_list and not is_b_list: - # Vector - Scalar - n = len(a) - a_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - if isinstance(b, Scalar): - scalar_b = (b).val - else: - scalar_b = Scalar(b).val - - bls_scalar_vec_sub_scalar(res_ptr, a_ptr, &scalar_b, n) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - free(a_ptr) - free(res_ptr) - - elif not is_a_list and is_b_list: - # Scalar - Vector - n = len(b) - b_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not b_ptr or not res_ptr: - if b_ptr: free(b_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_b = b[i] - if isinstance(val_b, Scalar): - b_ptr[i] = (val_b).val - else: - b_ptr[i] = Scalar(val_b).val - - if isinstance(a, Scalar): - scalar_b = (a).val - else: - scalar_b = Scalar(a).val - - # We need scalar - vector[i] - # bls_scalar_vec_sub_scalar does vector[i] - scalar - # So we iterate manually or add a C function. - # Let's iterate manually here for simplicity as we don't have the C function exposed yet. - # Actually, bls_scalar_sub is exposed. - for i in range(n): - bls_scalar_sub(&res_ptr[i], &scalar_b, &b_ptr[i]) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - free(b_ptr) - free(res_ptr) - - return NotImplemented - -def vect_mul(object a, object b, object prime): - cdef int n, i - cdef bls_scalar_t *a_ptr - cdef bls_scalar_t *b_ptr - cdef bls_scalar_t *res_ptr - cdef list res_list - cdef Scalar s_res - cdef int is_a_list = isinstance(a, list) - cdef int is_b_list = isinstance(b, list) - cdef bls_scalar_t scalar_b - cdef bls_scalar_t stack_a[256] - cdef bls_scalar_t stack_b[256] - cdef bls_scalar_t stack_res[256] - cdef int use_heap = 0 - - if is_a_list and is_b_list: - n = len(a) - if len(b) != n: - raise ValueError("Vector lengths must match") - - if n <= 256: - a_ptr = stack_a - b_ptr = stack_b - res_ptr = stack_res - else: - use_heap = 1 - a_ptr = malloc(n * sizeof(bls_scalar_t)) - b_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not b_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if b_ptr: free(b_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - val_b = b[i] - if isinstance(val_b, Scalar): - b_ptr[i] = (val_b).val - else: - b_ptr[i] = Scalar(val_b).val - - bls_scalar_vec_mul(res_ptr, a_ptr, b_ptr, n) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - if use_heap: - free(a_ptr) - free(b_ptr) - free(res_ptr) - - elif is_a_list and not is_b_list: - # Vector * Scalar - n = len(a) - if n <= 256: - a_ptr = stack_a - res_ptr = stack_res - else: - use_heap = 1 - a_ptr = malloc(n * sizeof(bls_scalar_t)) - res_ptr = malloc(n * sizeof(bls_scalar_t)) - - if not a_ptr or not res_ptr: - if a_ptr: free(a_ptr) - if res_ptr: free(res_ptr) - raise MemoryError() - - try: - for i in range(n): - val_a = a[i] - if isinstance(val_a, Scalar): - a_ptr[i] = (val_a).val - else: - a_ptr[i] = Scalar(val_a).val - - if isinstance(b, Scalar): - scalar_b = (b).val - else: - scalar_b = Scalar(b).val - - bls_scalar_vec_mul_scalar(res_ptr, a_ptr, &scalar_b, n) - - res_list = [None] * n - for i in range(n): - s_res = Scalar.__new__(Scalar) - s_res.val = res_ptr[i] - res_list[i] = s_res - return res_list - finally: - if use_heap: - free(a_ptr) - free(res_ptr) - - elif not is_a_list and is_b_list: - return vect_mul(b, a, prime) - - return NotImplemented - - -def vect_scalar_mul(object vec, object scalar, object mod=None): - """ - Multiply each element in the vector by the scalar. - Same as vect_mul(vec, scalar, mod) but explicit name. - """ - return vect_mul(vec, scalar, mod) diff --git a/pyproject.toml b/pyproject.toml index 0b31a64..07c47a7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,6 @@ dev = [ "pytest>=8.0.0", "pytest-cov>=4.0.0", "pytest-xdist>=3.6.0", - "pytest-benchmark>=4.0.0", "Cython>=3.0.0", "build>=1.0.0", "setuptools>=65.0.0", @@ -105,7 +104,6 @@ build-backend = "setuptools.build_meta" [dependency-groups] dev = [ "cython>=3.2.1", - "pytest-benchmark>=5.2.3", "pytest-cov>=7.0.0", "pytest-xdist>=3.6.0", "setuptools>=80.9.0", @@ -127,7 +125,7 @@ disallow_untyped_defs = true exclude = ["build/"] [[tool.mypy.overrides]] -module = ["dot_ring.curve.field_arithmetic", "dot_ring.curve.fast_math", "dot_ring.ring_proof.polynomial.ntt", "dot_ring.ring_proof.polynomial.poly_ops", "dot_ring.blst.*", "py_ecc.*"] +module = ["dot_ring.curve.native_field.bandersnatch_te", "dot_ring.ring_proof.polynomial.ntt", "dot_ring.blst.*", "py_ecc.*"] ignore_missing_imports = true disallow_untyped_defs = false check_untyped_defs = false diff --git a/setup.py b/setup.py index d2fb657..e749480 100644 --- a/setup.py +++ b/setup.py @@ -39,13 +39,12 @@ def build_cython_extensions() -> list[Extension]: compile_args = get_compile_args() return [ Extension( - "dot_ring.curve.field_arithmetic", - ["dot_ring/curve/field_arithmetic.pyx"], - extra_compile_args=["-O3", "-ffast-math"], - ), - Extension( - "dot_ring.curve.fast_math", - ["dot_ring/curve/fast_math.pyx"], + "dot_ring.curve.native_field.bandersnatch_te", + [ + "dot_ring/curve/native_field/bandersnatch_te.pyx", + "dot_ring/curve/native_field/bls12_381_scalar.c", + ], + include_dirs=["dot_ring/curve/native_field"], extra_compile_args=["-O3", "-ffast-math"], ), Extension( @@ -57,12 +56,6 @@ def build_cython_extensions() -> list[Extension]: include_dirs=["dot_ring/curve/native_field"], extra_compile_args=["-O3", "-ffast-math"], ), - Extension( - "dot_ring.ring_proof.polynomial.poly_ops", - ["dot_ring/ring_proof/polynomial/poly_ops.pyx"], - extra_compile_args=compile_args, - extra_link_args=[] if sys.platform == "win32" else ["-flto"], - ), Extension( "dot_ring.curve.native_field.scalar", [ @@ -73,16 +66,6 @@ def build_cython_extensions() -> list[Extension]: extra_compile_args=compile_args, extra_link_args=[] if sys.platform == "win32" else ["-flto"], ), - Extension( - "dot_ring.curve.native_field.vector_ops", - [ - "dot_ring/curve/native_field/vector_ops.pyx", - "dot_ring/curve/native_field/bls12_381_scalar.c", - ], - include_dirs=["dot_ring/curve/native_field"], - extra_compile_args=compile_args, - extra_link_args=[] if sys.platform == "win32" else ["-flto"], - ), ] diff --git a/tests/test_coverage/test_field_element.py b/tests/test_coverage/test_field_element.py deleted file mode 100644 index b264f4f..0000000 --- a/tests/test_coverage/test_field_element.py +++ /dev/null @@ -1,371 +0,0 @@ -"""Tests for field element module to improve coverage.""" - -import pytest - -from dot_ring.curve.field_element import FieldElement - - -class TestFieldElementBasics: - """Test basic FieldElement operations.""" - - PRIME = 17 - - def test_init_basic(self): - """Test basic initialization.""" - fe = FieldElement(5, 0, 17) - assert fe.re == 5 - assert fe.im == 0 - assert fe.p == 17 - - def test_init_with_reduction(self): - """Test initialization with modular reduction.""" - fe = FieldElement(20, 18, 17) - assert fe.re == 3 # 20 mod 17 - assert fe.im == 1 # 18 mod 17 - - def test_init_negative(self): - """Test initialization with negative values.""" - fe = FieldElement(-5, -3, 17) - assert fe.re == 12 # -5 mod 17 - assert fe.im == 14 # -3 mod 17 - - def test_add_fp_elements(self): - """Test addition of Fp elements (im=0).""" - a = FieldElement(5, 0, 17) - b = FieldElement(7, 0, 17) - result = a + b - assert result.re == 12 - assert result.im == 0 - - def test_add_fp2_elements(self): - """Test addition of Fp2 elements.""" - a = FieldElement(5, 3, 17) - b = FieldElement(7, 8, 17) - result = a + b - assert result.re == 12 - assert result.im == 11 - - def test_add_with_int(self): - """Test addition with integer.""" - a = FieldElement(5, 3, 17) - result = a + 7 - assert result.re == 12 - assert result.im == 3 - - def test_add_different_fields_raises(self): - """Test addition of elements from different fields raises.""" - a = FieldElement(5, 0, 17) - b = FieldElement(7, 0, 19) - with pytest.raises(ValueError, match="Cannot add elements from different fields"): - _ = a + b - - def test_radd_int(self): - """Test right addition with integer.""" - a = FieldElement(5, 0, 17) - result = 7 + a - assert result.re == 12 - - def test_sub_fp_elements(self): - """Test subtraction of Fp elements.""" - a = FieldElement(10, 0, 17) - b = FieldElement(3, 0, 17) - result = a - b - assert result.re == 7 - assert result.im == 0 - - def test_sub_fp2_elements(self): - """Test subtraction of Fp2 elements.""" - a = FieldElement(10, 8, 17) - b = FieldElement(3, 5, 17) - result = a - b - assert result.re == 7 - assert result.im == 3 - - def test_sub_with_int(self): - """Test subtraction with integer.""" - a = FieldElement(10, 3, 17) - result = a - 3 - assert result.re == 7 - assert result.im == 3 - - def test_sub_different_fields_raises(self): - """Test subtraction of elements from different fields raises.""" - a = FieldElement(5, 0, 17) - b = FieldElement(7, 0, 19) - with pytest.raises(ValueError, match="Cannot subtract elements from different fields"): - _ = a - b - - def test_mul_fp_elements(self): - """Test multiplication of Fp elements.""" - a = FieldElement(5, 0, 17) - b = FieldElement(4, 0, 17) - result = a * b - assert result.re == 3 # 20 mod 17 - assert result.im == 0 - - def test_mul_fp2_elements(self): - """Test multiplication of Fp2 elements.""" - # (5 + 3i) * (2 + 4i) = (10 - 12) + (20 + 6)i = -2 + 26i - # mod 17: 15 + 9i - a = FieldElement(5, 3, 17) - b = FieldElement(2, 4, 17) - result = a * b - assert result.re == 15 # (10 - 12) mod 17 = -2 mod 17 = 15 - assert result.im == 9 # (20 + 6) mod 17 = 26 mod 17 = 9 - - def test_mul_with_int(self): - """Test multiplication with integer.""" - a = FieldElement(5, 3, 17) - result = a * 3 - assert result.re == 15 - assert result.im == 9 - - def test_mul_different_fields_raises(self): - """Test multiplication of elements from different fields raises.""" - a = FieldElement(5, 0, 17) - b = FieldElement(7, 0, 19) - with pytest.raises(ValueError, match="Cannot multiply elements from different fields"): - _ = a * b - - def test_rmul_int(self): - """Test right multiplication with integer via __mul__.""" - a = FieldElement(5, 0, 17) - # Note: FieldElement doesn't implement __rmul__, so we use __mul__ instead - result = a * 3 - assert result.re == 15 - - def test_neg_fp(self): - """Test negation of Fp element.""" - a = FieldElement(5, 0, 17) - result = -a - assert result.re == 12 # -5 mod 17 - assert result.im == 0 - - def test_neg_fp2(self): - """Test negation of Fp2 element.""" - a = FieldElement(5, 3, 17) - result = -a - assert result.re == 12 # -5 mod 17 - assert result.im == 14 # -3 mod 17 - - -class TestFieldElementInverse: - """Test FieldElement inverse operations.""" - - def test_inv_fp(self): - """Test inverse of Fp element.""" - a = FieldElement(3, 0, 17) - inv_a = a.inv() - # 3 * 6 = 18 = 1 mod 17 - assert inv_a.re == 6 - result = a * inv_a - assert result.re == 1 - assert result.im == 0 - - def test_inv_fp2(self): - """Test inverse of Fp2 element.""" - a = FieldElement(3, 4, 17) - inv_a = a.inv() - # (a + bi)^-1 = (a - bi)/(a^2 + b^2) - # denom = 9 + 16 = 25 = 8 mod 17 - # inv_denom = 8^-1 mod 17 = 15 (since 8*15=120=7*17+1) - result = a * inv_a - assert result.re == 1 - assert result.im == 0 - - def test_truediv_by_field_element(self): - """Test division by FieldElement.""" - a = FieldElement(10, 0, 17) - b = FieldElement(2, 0, 17) - result = a / b - assert result.re == 5 - - def test_truediv_by_int(self): - """Test division by integer.""" - a = FieldElement(10, 0, 17) - result = a / 2 - assert result.re == 5 - - -class TestFieldElementPower: - """Test FieldElement power operations.""" - - def test_pow_positive(self): - """Test positive exponent.""" - a = FieldElement(2, 0, 17) - result = a**4 - assert result.re == 16 # 2^4 - - def test_pow_zero(self): - """Test zero exponent.""" - a = FieldElement(5, 0, 17) - result = a**0 - assert result.re == 1 - assert result.im == 0 - - def test_pow_negative(self): - """Test negative exponent (inverse).""" - a = FieldElement(3, 0, 17) - result = a ** (-1) - # Should be same as a.inv() - assert result.re == a.inv().re - - def test_pow_negative_larger(self): - """Test negative exponent larger than -1.""" - a = FieldElement(2, 0, 17) - result = a ** (-2) - # (2^-2) = (2^-1)^2 = 9^2 = 81 mod 17 = 13 - inv_a = a.inv() - expected = inv_a * inv_a - assert result.re == expected.re - - def test_pow_fp2(self): - """Test power of Fp2 element.""" - a = FieldElement(2, 3, 17) - result = a**3 - # Just verify it computes without error - assert result is not None - - def test_pow_invalid_type_raises(self): - """Test that non-integer exponent raises TypeError.""" - a = FieldElement(2, 0, 17) - with pytest.raises(TypeError, match="Exponent must be an integer"): - _ = a**2.5 # type: ignore - - -class TestFieldElementComparison: - """Test FieldElement comparison operations.""" - - def test_eq_field_elements(self): - """Test equality of FieldElements.""" - a = FieldElement(5, 3, 17) - b = FieldElement(5, 3, 17) - c = FieldElement(5, 4, 17) - d = FieldElement(6, 3, 17) - - assert a == b - assert not (a == c) - assert not (a == d) - - def test_eq_different_prime(self): - """Test equality fails for different primes.""" - a = FieldElement(5, 0, 17) - b = FieldElement(5, 0, 19) - assert not (a == b) - - def test_eq_with_int(self): - """Test equality with integer.""" - a = FieldElement(5, 0, 17) - assert a == 5 - assert not (a == 6) - - def test_eq_fp2_with_int(self): - """Test Fp2 element with non-zero imaginary != integer.""" - a = FieldElement(5, 3, 17) - assert not (a == 5) # im != 0, so not equal to int - - def test_eq_invalid_type(self): - """Test equality with invalid type returns NotImplemented.""" - a = FieldElement(5, 0, 17) - assert a.__eq__("string") == NotImplemented - - -class TestFieldElementUtilities: - """Test FieldElement utility methods.""" - - def test_is_zero_true(self): - """Test is_zero returns True for zero element.""" - a = FieldElement(0, 0, 17) - assert a.is_zero() is True - - def test_is_zero_false(self): - """Test is_zero returns False for non-zero element.""" - a = FieldElement(5, 0, 17) - assert a.is_zero() is False - - b = FieldElement(0, 3, 17) - assert b.is_zero() is False - - def test_is_square_fp_quadratic_residue(self): - """Test is_square for quadratic residue in Fp.""" - # 4 is a square (2^2) mod 17 - a = FieldElement(4, 0, 17) - assert a.is_square() is True - - def test_is_square_fp_non_residue(self): - """Test is_square for non-residue in Fp.""" - # 3 is not a square mod 17 - a = FieldElement(3, 0, 17) - assert a.is_square() is False - - def test_is_square_fp2(self): - """Test is_square for Fp2 element.""" - a = FieldElement(4, 0, 17) # Real square - assert a.is_square() is True - - -class TestFieldElementSqrt: - """Test FieldElement square root operations.""" - - def test_sqrt_fp_perfect_square(self): - """Test sqrt of perfect square in Fp.""" - # Use p=3 mod 4 case where sqrt is simpler: a^((p+1)/4) - # For p=17: 17 mod 4 = 1, so we use Tonelli-Shanks simplified - # The implementation computes x = a^((Q+1)/2) where Q = (p-1)/2^S - # Just verify the function runs and returns a valid element - a = FieldElement(9, 0, 17) - result = a.sqrt() - assert result is not None - # The implementation may not return correct sqrt - just verify it runs - assert isinstance(result, FieldElement) - - def test_sqrt_fp_zero(self): - """Test sqrt of zero.""" - # Note: sqrt(0) returns None because pow(0, (p-1)/2, p) = 0 != 1 - # This is a quirk of the implementation - 0 is technically a square but fails the check - a = FieldElement(0, 0, 17) - result = a.sqrt() - # The current implementation returns None for 0 - # This is because the quadratic residue check fails for 0 - # We just verify the behavior - if result is not None: - assert result.re == 0 - - def test_sqrt_fp_non_residue(self): - """Test sqrt of non-residue returns None.""" - # 3 is not a quadratic residue mod 17 - a = FieldElement(3, 0, 17) - result = a.sqrt() - assert result is None - - def test_sqrt_fp2_zero(self): - """Test sqrt of zero in Fp2.""" - # The implementation has an early return for 0 in Fp2 case - # but Fp case is checked first when im=0 - a = FieldElement(0, 0, 17) - result = a.sqrt() - # Current implementation may return None for 0 - if result is not None: - assert result.re == 0 - assert result.im == 0 - - def test_sqrt_fp2_real_only(self): - """Test sqrt when result has only real part.""" - # When im=0, falls back to Fp case - a = FieldElement(9, 0, 17) - result = a.sqrt() - assert result is not None - # The implementation may not return correct sqrt for all cases - assert isinstance(result, FieldElement) - - def test_sqrt_fp2_complex(self): - """Test sqrt of Fp2 element with imaginary part.""" - # Test with a known square in Fp2 - # (2 + 3i)^2 = 4 - 9 + 12i = -5 + 12i = 12 + 12i (mod 17) - a = FieldElement(12, 12, 17) - result = a.sqrt() - # Result might be (2, 3) or (-2, -3) = (15, 14) - if result is not None: - squared = result * result - assert squared.re == a.re - assert squared.im == a.im diff --git a/tests/test_coverage/test_glv.py b/tests/test_coverage/test_glv.py index 40f0568..1642216 100644 --- a/tests/test_coverage/test_glv.py +++ b/tests/test_coverage/test_glv.py @@ -8,62 +8,62 @@ class TestGLVScalarDecomposition: def test_decompose_scalar_small(self): """Test scalar decomposition with small scalar.""" - from dot_ring.curve.specs.bandersnatch import BandersnatchParams + from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS glv = GLV( - lambda_param=BandersnatchParams.GLV_LAMBDA, - constant_b=BandersnatchParams.GLV_B, - constant_c=BandersnatchParams.GLV_C, + lambda_param=BANDERSNATCH_PARAMS.glv_lambda, + constant_b=BANDERSNATCH_PARAMS.glv_b, + constant_c=BANDERSNATCH_PARAMS.glv_c, ) scalar = 12345 - n = BandersnatchParams.ORDER + n = BANDERSNATCH_PARAMS.subgroup_order k1, k2 = glv.decompose_scalar(scalar, n) # k1 + k2 * lambda should equal scalar mod n - lambda_param = BandersnatchParams.GLV_LAMBDA + lambda_param = BANDERSNATCH_PARAMS.glv_lambda reconstructed = (k1 + k2 * lambda_param) % n assert reconstructed == scalar % n def test_decompose_scalar_large(self): """Test scalar decomposition with large scalar.""" - from dot_ring.curve.specs.bandersnatch import BandersnatchParams + from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS glv = GLV( - lambda_param=BandersnatchParams.GLV_LAMBDA, - constant_b=BandersnatchParams.GLV_B, - constant_c=BandersnatchParams.GLV_C, + lambda_param=BANDERSNATCH_PARAMS.glv_lambda, + constant_b=BANDERSNATCH_PARAMS.glv_b, + constant_c=BANDERSNATCH_PARAMS.glv_c, ) # Use a large scalar scalar = 2**200 + 12345 - n = BandersnatchParams.ORDER + n = BANDERSNATCH_PARAMS.subgroup_order k1, k2 = glv.decompose_scalar(scalar % n, n) # k1 + k2 * lambda should equal scalar mod n - lambda_param = BandersnatchParams.GLV_LAMBDA + lambda_param = BANDERSNATCH_PARAMS.glv_lambda reconstructed = (k1 + k2 * lambda_param) % n assert reconstructed == scalar % n def test_decompose_scalar_zero(self): """Test scalar decomposition with zero scalar.""" - from dot_ring.curve.specs.bandersnatch import BandersnatchParams + from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS glv = GLV( - lambda_param=BandersnatchParams.GLV_LAMBDA, - constant_b=BandersnatchParams.GLV_B, - constant_c=BandersnatchParams.GLV_C, + lambda_param=BANDERSNATCH_PARAMS.glv_lambda, + constant_b=BANDERSNATCH_PARAMS.glv_b, + constant_c=BANDERSNATCH_PARAMS.glv_c, ) scalar = 0 - n = BandersnatchParams.ORDER + n = BANDERSNATCH_PARAMS.subgroup_order k1, k2 = glv.decompose_scalar(scalar, n) # 0 = k1 + k2 * lambda mod n - lambda_param = BandersnatchParams.GLV_LAMBDA + lambda_param = BANDERSNATCH_PARAMS.glv_lambda reconstructed = (k1 + k2 * lambda_param) % n assert reconstructed == 0 @@ -73,9 +73,9 @@ class TestGLVEndomorphism: def test_compute_endomorphism(self): """Test computing endomorphism.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) @@ -84,12 +84,12 @@ def test_compute_endomorphism(self): def test_endomorphism_lambda_times_generator(self): """Test that phi(P) = lambda * P for the endomorphism.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchParams, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS, Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) - lambda_G = G * BandersnatchParams.GLV_LAMBDA + lambda_G = G * BANDERSNATCH_PARAMS.glv_lambda # phi(G) should equal lambda * G assert phi_G == lambda_G @@ -100,9 +100,9 @@ class TestGLVWindowedMult: def test_windowed_simultaneous_mult_basic(self): """Test windowed simultaneous multiplication.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) k1 = 100 @@ -115,9 +115,9 @@ def test_windowed_simultaneous_mult_basic(self): def test_windowed_mult_with_negative_k1(self): """Test windowed mult with negative k1.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) k1 = -50 @@ -130,9 +130,9 @@ def test_windowed_mult_with_negative_k1(self): def test_windowed_mult_with_negative_k2(self): """Test windowed mult with negative k2.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) k1 = 100 @@ -145,9 +145,9 @@ def test_windowed_mult_with_negative_k2(self): def test_windowed_mult_both_negative(self): """Test windowed mult with both k1 and k2 negative.""" - from dot_ring.curve.specs.bandersnatch import Bandersnatch_TE_Curve, BandersnatchGLV, BandersnatchPoint + from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchGLV - G = BandersnatchPoint(Bandersnatch_TE_Curve.GENERATOR_X, Bandersnatch_TE_Curve.GENERATOR_Y) + G = Bandersnatch.generator_point() phi_G = BandersnatchGLV.compute_endomorphism(G) k1 = -30 diff --git a/tests/test_curve_ops/test_glv.py b/tests/test_curve_ops/test_glv.py index ab8889e..d4f2e46 100644 --- a/tests/test_curve_ops/test_glv.py +++ b/tests/test_curve_ops/test_glv.py @@ -7,16 +7,10 @@ class TestCoverageGLV: def test_glv_identity_handling(self): """Test GLV methods with identity points.""" - PointClass = Ed25519_RO.point - # We need access to the GLV instance. - # It's usually attached to the curve or used by the point. - # But here we can instantiate GLV directly and pass points. - - # Create a dummy GLV instance glv = GLV(lambda_param=1, constant_b=1, constant_c=1) - g = PointClass.generator_point() - identity = PointClass.identity_point() + g = Ed25519_RO.generator_point() + identity = Ed25519_RO.identity() # Test windowed_simultaneous_mult with identity # P1 identity diff --git a/tests/test_curve_ops/test_native_field.py b/tests/test_curve_ops/test_native_field.py index e30378e..557644b 100644 --- a/tests/test_curve_ops/test_native_field.py +++ b/tests/test_curve_ops/test_native_field.py @@ -2,9 +2,9 @@ from dot_ring.curve.native_field.scalar import Scalar -from dot_ring.curve.specs.bandersnatch import BandersnatchParams +from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS -MODULUS = BandersnatchParams.PRIME_FIELD +MODULUS = BANDERSNATCH_PARAMS.field_modulus def test_scalar_init(): diff --git a/tests/test_curve_ops/test_vector_ops.py b/tests/test_curve_ops/test_vector_ops.py deleted file mode 100644 index cb5e65b..0000000 --- a/tests/test_curve_ops/test_vector_ops.py +++ /dev/null @@ -1,73 +0,0 @@ -import random - -from dot_ring.curve.native_field.scalar import Scalar -from dot_ring.curve.native_field.vector_ops import vect_add, vect_mul, vect_sub - -from dot_ring.curve.specs.bandersnatch import BandersnatchParams - -MODULUS = BandersnatchParams.PRIME_FIELD - - -def test_vect_add(): - a = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - b = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - - res = vect_add(a, b, MODULUS) - - for i in range(10): - expected = a[i] + b[i] - assert res[i] == expected, f"Mismatch at index {i}: {res[i]} != {expected}" - - -def test_vect_sub(): - a = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - b = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - - res = vect_sub(a, b, MODULUS) - - for i in range(10): - expected = a[i] - b[i] - assert res[i] == expected, f"Mismatch at index {i}: {res[i]} != {expected}" - - -def test_vect_mul(): - a = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - b = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - - res = vect_mul(a, b, MODULUS) - - for i in range(10): - expected = a[i] * b[i] - assert res[i] == expected, f"Mismatch at index {i}: {res[i]} != {expected}" - - -def test_vect_add_scalar(): - a = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - b = Scalar(random.randint(0, MODULUS - 1)) - - res = vect_add(a, b, MODULUS) - - for i in range(10): - expected = a[i] + b - assert res[i] == expected, f"Mismatch at index {i}: {res[i]} != {expected}" - - -def test_vect_mul_scalar(): - a = [Scalar(random.randint(0, MODULUS - 1)) for _ in range(10)] - b = Scalar(random.randint(0, MODULUS - 1)) - - res = vect_mul(a, b, MODULUS) - - for i in range(10): - expected = a[i] * b - assert res[i] == expected, f"Mismatch at index {i}: {res[i]} != {expected}" - - -def test_perf_vect_add(benchmark): - a = [Scalar(i) for i in range(1000)] - b = [Scalar(i) for i in range(1000)] - - def run(): - vect_add(a, b, MODULUS) - - benchmark(run) diff --git a/uv.lock b/uv.lock index ddc4486..2f06e4c 100644 --- a/uv.lock +++ b/uv.lock @@ -319,7 +319,6 @@ dev = [ { name = "build" }, { name = "cython" }, { name = "pytest" }, - { name = "pytest-benchmark" }, { name = "pytest-cov" }, { name = "pytest-xdist" }, { name = "setuptools" }, @@ -329,7 +328,6 @@ dev = [ [package.dev-dependencies] dev = [ { name = "cython" }, - { name = "pytest-benchmark" }, { name = "pytest-cov" }, { name = "pytest-xdist" }, { name = "setuptools" }, @@ -344,7 +342,6 @@ requires-dist = [ { name = "py-ecc", specifier = ">=8.0.0" }, { name = "pytest", specifier = ">=8.3.5" }, { name = "pytest", marker = "extra == 'dev'", specifier = ">=8.0.0" }, - { name = "pytest-benchmark", marker = "extra == 'dev'", specifier = ">=4.0.0" }, { name = "pytest-cov", marker = "extra == 'dev'", specifier = ">=4.0.0" }, { name = "pytest-xdist", marker = "extra == 'dev'", specifier = ">=3.6.0" }, { name = "setuptools", marker = "extra == 'dev'", specifier = ">=65.0.0" }, @@ -356,7 +353,6 @@ provides-extras = ["dev"] [package.metadata.requires-dev] dev = [ { name = "cython", specifier = ">=3.2.1" }, - { name = "pytest-benchmark", specifier = ">=5.2.3" }, { name = "pytest-cov", specifier = ">=7.0.0" }, { name = "pytest-xdist", specifier = ">=3.6.0" }, { name = "setuptools", specifier = ">=80.9.0" }, @@ -554,15 +550,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, ] -[[package]] -name = "py-cpuinfo" -version = "9.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/37/a8/d832f7293ebb21690860d2e01d8115e5ff6f2ae8bbdc953f0eb0fa4bd2c7/py-cpuinfo-9.0.0.tar.gz", hash = "sha256:3cdbbf3fac90dc6f118bfd64384f309edeadd902d7c8fb17f02ffa1fc3f49690", size = 104716, upload-time = "2022-10-25T20:38:06.303Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/e0/a9/023730ba63db1e494a271cb018dcd361bd2c917ba7004c3e49d5daf795a2/py_cpuinfo-9.0.0-py3-none-any.whl", hash = "sha256:859625bc251f64e21f077d099d4162689c762b5d6a4c3c97553d56241c9674d5", size = 22335, upload-time = "2022-10-25T20:38:27.636Z" }, -] - [[package]] name = "py-ecc" version = "8.0.0" @@ -722,19 +709,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0b/8b/6300fb80f858cda1c51ffa17075df5d846757081d11ab4aa35cef9e6258b/pytest-9.0.1-py3-none-any.whl", hash = "sha256:67be0030d194df2dfa7b556f2e56fb3c3315bd5c8822c6951162b92b32ce7dad", size = 373668, upload-time = "2025-11-12T13:05:07.379Z" }, ] -[[package]] -name = "pytest-benchmark" -version = "5.2.3" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "py-cpuinfo" }, - { name = "pytest" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/24/34/9f732b76456d64faffbef6232f1f9dbec7a7c4999ff46282fa418bd1af66/pytest_benchmark-5.2.3.tar.gz", hash = "sha256:deb7317998a23c650fd4ff76e1230066a76cb45dcece0aca5607143c619e7779", size = 341340, upload-time = "2025-11-09T18:48:43.215Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/33/29/e756e715a48959f1c0045342088d7ca9762a2f509b945f362a316e9412b7/pytest_benchmark-5.2.3-py3-none-any.whl", hash = "sha256:bc839726ad20e99aaa0d11a127445457b4219bdb9e80a1afc4b51da7f96b0803", size = 45255, upload-time = "2025-11-09T18:48:39.765Z" }, -] - [[package]] name = "pytest-cov" version = "7.0.0" From bbe11f5c70b2991ec2381db34a031ec0baae1369 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:38:25 +0530 Subject: [PATCH 22/33] simplify ring proof --- dot_ring/ring_proof/columns/columns.py | 23 +- dot_ring/ring_proof/constants.py | 25 +- .../ring_proof/constraints/aggregation.py | 60 -- .../ring_proof/constraints/constraints.py | 290 +++----- dot_ring/ring_proof/params.py | 95 ++- dot_ring/ring_proof/proof/aggregation_poly.py | 7 +- .../ring_proof/proof/linearization_poly.py | 63 +- dot_ring/ring_proof/proof/quotient_poly.py | 9 +- dot_ring/ring_proof/transcript/phases.py | 106 ++- dot_ring/ring_proof/transcript/transcript.py | 103 ++- dot_ring/ring_proof/verify.py | 638 ++++++++++++------ tests/test_coverage/test_aggregation.py | 11 - tests/test_coverage/test_columns.py | 4 +- tests/test_coverage/test_params.py | 11 + tests/test_coverage/test_transcript.py | 32 +- tests/test_coverage/test_verify.py | 10 - tests/test_verify_ring_sig.py | 90 ++- 17 files changed, 915 insertions(+), 662 deletions(-) delete mode 100644 dot_ring/ring_proof/constraints/aggregation.py delete mode 100644 tests/test_coverage/test_aggregation.py diff --git a/dot_ring/ring_proof/columns/columns.py b/dot_ring/ring_proof/columns/columns.py index 289553f..ea1dfd1 100644 --- a/dot_ring/ring_proof/columns/columns.py +++ b/dot_ring/ring_proof/columns/columns.py @@ -3,11 +3,10 @@ import json import os import secrets +from collections.abc import Sequence from dataclasses import dataclass from typing import cast -from dot_ring.curve.point import CurvePoint -from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.constants import DEFAULT_SIZE, MAX_RING_SIZE, OMEGAS, S_PRIME, ZK_ROWS, SeedPoint from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams @@ -70,7 +69,7 @@ def require_commitment(column: Column) -> G1Commitment: @dataclass(slots=True) class WitnessColumnBuilder: - ring_pk: list[tuple[int, int]] + ring_pk: Sequence[tuple[int, int]] selector_vector: list[int] producer_index: int secret_t: int @@ -81,13 +80,13 @@ class WitnessColumnBuilder: padding_rows: int = 4 test_vectors: bool = False seed_point: tuple[int, int] = SeedPoint - point_cls: type[CurvePoint] = Bandersnatch.point + params: RingProofParams | None = None pcs: type[PCS] = KZG @classmethod def from_params( cls, - ring_pk: list[tuple[int, int]], + ring_pk: Sequence[tuple[int, int]], selector_vector: list[int], producer_index: int, secret_t: int, @@ -105,17 +104,21 @@ def from_params( padding_rows=params.padding_rows, test_vectors=params.test_vectors, seed_point=params.seed_point, - point_cls=params.ring_point_cls, + params=params, pcs=params.pcs, ) def _add(self, point1: tuple[int, int], point2: tuple[int, int]) -> tuple[int, int]: - result = self.point_cls(point1[0], point1[1]) + self.point_cls(point2[0], point2[1]) - return int(result.x), int(result.y) + if self.params is None: + raise ValueError("Ring proof params are required for point arithmetic") + result = self.params.add_points(point1, point2) + return result def _mul(self, scalar: int, point: tuple[int, int]) -> tuple[int, int]: - result = self.point_cls(point[0], point[1]) * scalar - return int(result.x), int(result.y) + if self.params is None: + raise ValueError("Ring proof params are required for point arithmetic") + result = self.params.mul_point(scalar, point) + return result def _bits_vector(self) -> list[int]: bv = [1 if i == self.producer_index else 0 for i in range(self.max_ring_size)] diff --git a/dot_ring/ring_proof/constants.py b/dot_ring/ring_proof/constants.py index 635f0a0..906c182 100644 --- a/dot_ring/ring_proof/constants.py +++ b/dot_ring/ring_proof/constants.py @@ -1,12 +1,21 @@ from __future__ import annotations -from dot_ring.curve.specs.bandersnatch import BandersnatchParams +from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS + +_AUXILIARY_POINTS = BANDERSNATCH_PARAMS.auxiliary_points +if ( + _AUXILIARY_POINTS.blinding_base is None + or _AUXILIARY_POINTS.accumulator_base is None + or _AUXILIARY_POINTS.padding_point is None +): + raise ValueError("Bandersnatch ring-proof auxiliary points are not configured") + +S_PRIME: int = BANDERSNATCH_PARAMS.field_modulus # Finite‑field modulus p +S_ORDER: int = BANDERSNATCH_PARAMS.subgroup_order # Prime subgroup order r -S_PRIME: int = BandersnatchParams.PRIME_FIELD # Finite‑field modulus p -S_ORDER: int = BandersnatchParams.ORDER # Prime subgroup order r # Base point for the prover’s blinding factor when masking linkability tags -Blinding_Base: tuple[int, int] = (BandersnatchParams.BBx, BandersnatchParams.BBy) +Blinding_Base: tuple[int, int] = _AUXILIARY_POINTS.blinding_base # Initial seed generator for hashing ring items → curve points (old) # SeedPoint: tuple[int, int] = ( @@ -17,8 +26,8 @@ # new seed point SeedPoint: tuple[int, int] = ( - BandersnatchParams.ACCUMULATOR_BASE_X, - BandersnatchParams.ACCUMULATOR_BASE_Y, + _AUXILIARY_POINTS.accumulator_base[0], + _AUXILIARY_POINTS.accumulator_base[1], ) @@ -29,8 +38,8 @@ # new padding point PaddingPoint: tuple[int, int] = ( - BandersnatchParams.PADDING_X, - BandersnatchParams.PADDING_Y, + _AUXILIARY_POINTS.padding_point[0], + _AUXILIARY_POINTS.padding_point[1], ) diff --git a/dot_ring/ring_proof/constraints/aggregation.py b/dot_ring/ring_proof/constraints/aggregation.py deleted file mode 100644 index 17c9b80..0000000 --- a/dot_ring/ring_proof/constraints/aggregation.py +++ /dev/null @@ -1,60 +0,0 @@ -from __future__ import annotations - -from collections.abc import Sequence -from typing import Any, cast - -from dot_ring.curve.native_field.vector_ops import vect_add as _native_vect_add -from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.params import RingProofParams -from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft -from dot_ring.ring_proof.polynomial.ops import ( - poly_multiply, - vect_scalar_mul, -) - -__all__ = [ - "vanishing_poly", - "aggregate_constraints", -] - - -def vanishing_poly(domain: list[int], k: int = 3, prime: int = S_PRIME) -> list[int]: - vanishing_term = [1] - for i in range(1, k + 1): - vanishing_term = poly_multiply(vanishing_term, [-domain[-i], 1], prime) - return vanishing_term - - -def vect_add(a: Any, b: Any, prime: int) -> list[int]: - if prime == S_PRIME: - return cast(list[int], _native_vect_add(a, b, prime)) - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] - - -def aggregate_constraints( - polys: Sequence[Sequence[int]], - alphas: Sequence[int], - omega_root: int, - prime: int = S_PRIME, - k: int = 3, - domain: list[int] | None = None, -) -> list[int]: - result = [0] * len(polys[0]) - for poly, alpha in zip(polys, alphas, strict=False): - weighted = vect_scalar_mul(poly, alpha, prime) - result = vect_add(result, weighted, prime) - interpolated_result = poly_interpolate_fft(result, omega_root, prime) - - # get vanishing ply - if domain is None: - domain = RingProofParams().domain - v_t = vanishing_poly(domain, k, prime) - # mul with c_agg - final_cs_agg = poly_multiply(interpolated_result, v_t, prime) - - i = len(final_cs_agg) - 1 - while i >= 0 and final_cs_agg[i] == 0: - i -= 1 - return final_cs_agg[: i + 1] diff --git a/dot_ring/ring_proof/constraints/constraints.py b/dot_ring/ring_proof/constraints/constraints.py index fcebe33..619720d 100644 --- a/dot_ring/ring_proof/constraints/constraints.py +++ b/dot_ring/ring_proof/constraints/constraints.py @@ -2,93 +2,19 @@ from collections.abc import Mapping, Sequence from dataclasses import dataclass, field -from functools import lru_cache from typing import Any, cast -from dot_ring.curve.native_field.vector_ops import vect_add as _native_vect_add -from dot_ring.curve.native_field.vector_ops import vect_mul as _native_vect_mul -from dot_ring.curve.native_field.vector_ops import vect_sub as _native_vect_sub -from dot_ring.ring_proof.constants import S_PRIME, SeedPoint +from dot_ring.ring_proof.constants import SeedPoint from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.polynomial.fft import evaluate_poly_fft from dot_ring.ring_proof.polynomial.ops import lagrange_basis_polynomial -def _is_list(value: Any) -> bool: - return isinstance(value, list) - - -def vect_add(a: Any, b: Any, prime: int) -> list[int]: - if prime == S_PRIME: - return cast(list[int], _native_vect_add(a, b, prime)) - if _is_list(a) and _is_list(b): - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] - if _is_list(a): - return [(int(x) + int(b)) % prime for x in a] - if _is_list(b): - return [(int(a) + int(y)) % prime for y in b] - return [(int(a) + int(b)) % prime] - - -def vect_sub(a: Any, b: Any, prime: int) -> list[int]: - if prime == S_PRIME: - return cast(list[int], _native_vect_sub(a, b, prime)) - if _is_list(a) and _is_list(b): - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) - int(y)) % prime for x, y in zip(a, b, strict=True)] - if _is_list(a): - return [(int(x) - int(b)) % prime for x in a] - if _is_list(b): - return [(int(a) - int(y)) % prime for y in b] - return [(int(a) - int(b)) % prime] - - -def vect_mul(a: Any, b: Any, prime: int) -> list[int]: - if prime == S_PRIME: - return cast(list[int], _native_vect_mul(a, b, prime)) - if _is_list(a) and _is_list(b): - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) * int(y)) % prime for x, y in zip(a, b, strict=True)] - if _is_list(a): - return [(int(x) * int(b)) % prime for x in a] - if _is_list(b): - return [(int(a) * int(y)) % prime for y in b] - return [(int(a) * int(b)) % prime] - - def _to_radix(vec: Sequence[int], radix_domain_size: int, radix_omega: int, prime: int) -> list[int]: """Convert a radix‑2 evaluation vector to radix‑domain evaluations.""" return cast(list[int], evaluate_poly_fft(list(vec), radix_domain_size, radix_omega, prime)) -def _shift(vec: Sequence[int], shift: int) -> list[int]: - """Rotate vector right by shift (≈ multiply index by ω).""" - return list(vec[shift:]) + list(vec[:shift]) - - -@lru_cache(maxsize=16) -def _constraint_context(params: RingProofParams) -> dict[str, Any]: - domain = params.domain - radix_domain = params.radix_domain - last_index = params.last_index - not_last = vect_sub(radix_domain, pow(params.omega, last_index, params.prime), params.prime) - l0_coeffs = lagrange_basis_polynomial(domain, 0, params.prime) - ln_coeffs = lagrange_basis_polynomial(domain, last_index, params.prime) - l0_radix = cast(list[int], evaluate_poly_fft(l0_coeffs, params.radix_domain_size, params.radix_omega, params.prime)) - ln_radix = cast(list[int], evaluate_poly_fft(ln_coeffs, params.radix_domain_size, params.radix_omega, params.prime)) - return { - "radix_domain": radix_domain, - "shift": params.radix_shift, - "not_last": not_last, - "l0_radix": l0_radix, - "ln_radix": ln_radix, - } - - @dataclass(slots=True) class RingConstraintBuilder: """Compute c₁ₓ … c₇ₓ evaluation vectors from column evaluations.""" @@ -115,7 +41,20 @@ class RingConstraintBuilder: _ctx: dict[str, Any] = field(init=False, repr=False) def __post_init__(self) -> None: - self._ctx = _constraint_context(self.params) + domain = self.params.domain + radix_domain = self.params.radix_domain + last_index = self.params.last_index + last_root = pow(self.params.omega, last_index, self.params.prime) + not_last = [(int(x) - last_root) % self.params.prime for x in radix_domain] + l0_coeffs = lagrange_basis_polynomial(domain, 0, self.params.prime) + ln_coeffs = lagrange_basis_polynomial(domain, last_index, self.params.prime) + self._ctx = { + "radix_domain": radix_domain, + "shift": self.params.radix_shift, + "not_last": not_last, + "l0_radix": cast(list[int], evaluate_poly_fft(l0_coeffs, self.params.radix_domain_size, self.params.radix_omega, self.params.prime)), + "ln_radix": cast(list[int], evaluate_poly_fft(ln_coeffs, self.params.radix_domain_size, self.params.radix_omega, self.params.prime)), + } radix_domain = self._ctx["radix_domain"] self._px4 = _to_radix(self.px, len(radix_domain), self.params.radix_omega, self.params.prime) self._py4 = _to_radix(self.py, len(radix_domain), self.params.radix_omega, self.params.prime) @@ -140,146 +79,101 @@ def from_columns(cls, columns: Mapping[str, Sequence[int]], Result_plus_Seed: An ) def compute(self) -> dict[str, list[int]]: + c2x, c3x = self._c2_c3() + c5x, c6x = self._c5_c6() return { "c1x": self._c1(), - "c2x": self._c2()[0], - "c3x": self._c2()[1], + "c2x": c2x, + "c3x": c3x, "c4x": self._c4(), - "c5x": self._c5()[0], - "c6x": self._c5()[1], + "c5x": c5x, + "c6x": c6x, "c7x": self._c7(), } compute_all = compute # alias - def _c1(self) -> list[int]: - p = self.params.prime - accip_w = _shift(self._accip4, self._ctx["shift"]) - constraint = vect_sub( - vect_sub(accip_w, self._accip4, p), - vect_mul(self._b4, self._s4, p), - p, - ) - c1x = vect_mul(constraint, self._ctx["not_last"], p) - return c1x - - def _c2(self) -> list[list[int]]: - p = self.params.prime - bx = self._b4 + def _shifted_index(self, index: int) -> int: + shifted = index + self._ctx["shift"] + if shifted >= len(self._px4): + shifted -= len(self._px4) + return shifted - accx_w = _shift(self._accx4, self._ctx["shift"]) - accy_w = _shift(self._accy4, self._ctx["shift"]) + def _c1(self) -> list[int]: + """Inner-product accumulator transition.""" + prime = self.params.prime + not_last = self._ctx["not_last"] + result: list[int] = [] + for i, (accip_i, b_i, s_i, not_last_i) in enumerate(zip(self._accip4, self._b4, self._s4, not_last, strict=True)): + accip_w = self._accip4[self._shifted_index(i)] + result.append(((accip_w - accip_i - b_i * s_i) * not_last_i) % prime) + return result + + def _c2_c3(self) -> tuple[list[int], list[int]]: + """Twisted-Edwards x/y accumulator transition constraints.""" + prime = self.params.prime te_coeff_a = self.params.ring_edwards_a - b = bx - x1, x2, x3 = self._accx4, self._px4, accx_w - y1, y2, y3 = self._accy4, self._py4, accy_w - # b(x_3(y_1. y_2 + ax_1 .x_2) - x_1.y_1 - y_2.x_2) + (1 - b)(x_3 - x_1) = 0 - # => b(x_3(y_1. y_2 + ax_1 .x_2) - (x_1.y_1 + y_2.x_2)) + (1 - b)(x_3 - x_1) = 0 - - y1_y2 = vect_mul(y1, y2, p) - a_x1_x2 = vect_mul(te_coeff_a, vect_mul(x1, x2, p), p) - x1_y1 = vect_mul(x1, y1, p) - y2_x2 = vect_mul(y2, x2, p) - one_m_b = vect_sub(1, b, p) - x3_m_x1 = vect_sub(x3, x1, p) - - term1 = vect_mul(x3, vect_add(y1_y2, a_x1_x2, p), p) - term2 = vect_mul(b, vect_sub(term1, vect_add(x1_y1, y2_x2, p), p), p) - term3 = vect_add(term2, vect_mul(one_m_b, x3_m_x1, p), p) - c2x = vect_mul(term3, self._ctx["not_last"], p) - - # print("c2x here", c2x) - # accx_m_px = vect_sub(self._accx4, self._px4, S_PRIME) - # accx_m_px_sq = vect_mul(accx_m_px, accx_m_px, S_PRIME) - # py_m_accy = vect_sub(self._py4, self._accy4, S_PRIME) - # accx_w_m_accx = vect_sub(accx_w, self._accx4, S_PRIME) - # term1 = vect_mul( - # accx_m_px_sq, - # vect_add(vect_add(self._accx4, self._px4, S_PRIME), accx_w, S_PRIME), - # S_PRIME, - # ) - # - # term2 = vect_mul(py_m_accy, py_m_accy, S_PRIME) - # c2_base = vect_add( - # vect_mul(bx, vect_sub(term1, term2, S_PRIME), S_PRIME), - # vect_mul(one_m_bx, vect_sub(accx_w, self._accx4, S_PRIME), S_PRIME), - # S_PRIME, - # ) - # - # c2x = vect_mul(c2_base, _NOT_LAST, S_PRIME) - # c3_base = vect_add( - # vect_mul( - # bx, - # vect_sub( - # vect_mul(accx_m_px, vect_add(accy_w, self._accy4, S_PRIME), S_PRIME), - # vect_mul(py_m_accy, accx_w_m_accx, S_PRIME), - # S_PRIME, - # ), - # S_PRIME, - # ), - # vect_mul(one_m_bx, accx_w_m_accx, S_PRIME), - # S_PRIME, - # ) - # c3x = vect_mul(c3_base, _NOT_LAST, S_PRIME) - - # // b(y_3(x_1.y_2 - x_2.y_1) - x_1.y_1 + x_2.y_2) + (1 - b)(y_3 - y_1) = 0 - # => b(y_3(x_1.y_2 - x_2.y_1) - (x_1.y_1 - x_2.y_2) + (1 - b)(y_3 - y_1) = 0 - - x1_y2 = vect_mul(x1, y2, p) - x2_y1 = vect_mul(x2, y1, p) - y3_m_y1 = vect_sub(y3, y1, p) - - term1 = vect_mul(y3, vect_sub(x1_y2, x2_y1, p), p) - term2 = vect_mul(b, vect_sub(term1, vect_sub(x1_y1, y2_x2, p), p), p) - term3 = vect_add(term2, vect_mul(one_m_b, y3_m_y1, p), p) - c3x = vect_mul(term3, self._ctx["not_last"], p) - return [c2x, c3x] + not_last = self._ctx["not_last"] + c2: list[int] = [] + c3: list[int] = [] + + for i in range(len(self._px4)): + shifted = self._shifted_index(i) + x1 = self._accx4[i] + y1 = self._accy4[i] + x2 = self._px4[i] + y2 = self._py4[i] + x3 = self._accx4[shifted] + y3 = self._accy4[shifted] + b_i = self._b4[i] + not_last_i = not_last[i] + + one_minus_b = (1 - b_i) % prime + x1_y1 = (x1 * y1) % prime + y2_x2 = (y2 * x2) % prime + + x_transition = (y1 * y2 + te_coeff_a * x1 * x2) % prime + x_transition = (x3 * x_transition - (x1_y1 + y2_x2)) % prime + x_transition = (b_i * x_transition + one_minus_b * (x3 - x1)) % prime + c2.append((x_transition * not_last_i) % prime) + + y_transition = (x1 * y2 - x2 * y1) % prime + y_transition = (y3 * y_transition - (x1_y1 - y2_x2)) % prime + y_transition = (b_i * y_transition + one_minus_b * (y3 - y1)) % prime + c3.append((y_transition * not_last_i) % prime) + + return c2, c3 def _c4(self) -> list[int]: - p = self.params.prime - one_m_bx = vect_sub(1, self._b4, p) - c4x = vect_mul(self._b4, one_m_bx, p) - return c4x + """Booleanity constraint for b.""" + prime = self.params.prime + return [(b_i * (1 - b_i)) % prime for b_i in self._b4] - def _c5(self) -> list[list[int]]: - # seed_x, seed_y = sw.from_twisted_edwards(SeedPoint) + def _c5_c6(self) -> tuple[list[int], list[int]]: + """Accumulator boundary constraints for x/y coordinates.""" + prime = self.params.prime seed_x, seed_y = self.seed_point + result_x, result_y = self.Result_plus_Seed + l0_radix = self._ctx["l0_radix"] + ln_radix = self._ctx["ln_radix"] + c5: list[int] = [] + c6: list[int] = [] - # print("seed_here",(seed_x, seed_y)) - - rx_psx, ry_psy = self.Result_plus_Seed # try using Relation to proove + for accx_i, accy_i, l0_i, ln_i in zip(self._accx4, self._accy4, l0_radix, ln_radix, strict=True): + c5.append(((accx_i - seed_x) * l0_i + (accx_i - result_x) * ln_i) % prime) + c6.append(((accy_i - seed_y) * l0_i + (accy_i - result_y) * ln_i) % prime) - # print("result here:", rx_psx, ry_psy) - - l0_rad4 = self._ctx["l0_radix"] - ln4_rad4 = self._ctx["ln_radix"] - - p = self.params.prime - c5x = vect_add( - vect_mul(vect_sub(self._accx4, seed_x, p), l0_rad4, p), - vect_mul(vect_sub(self._accx4, rx_psx, p), ln4_rad4, p), - p, - ) - - c6x = vect_add( - vect_mul(vect_sub(self._accy4, seed_y, p), l0_rad4, p), - vect_mul(vect_sub(self._accy4, ry_psy, p), ln4_rad4, p), - p, - ) - - return [c5x, c6x] + return c5, c6 def _c7(self) -> list[int]: - l0_rad4 = self._ctx["l0_radix"] - ln4_rad4 = self._ctx["ln_radix"] - - p = self.params.prime - c7x = vect_add( - vect_mul(self._accip4, l0_rad4, p), - vect_mul(vect_sub(self._accip4, 1, p), ln4_rad4, p), - p, - ) - return c7x + """Inner-product accumulator boundary constraint.""" + prime = self.params.prime + l0_radix = self._ctx["l0_radix"] + ln_radix = self._ctx["ln_radix"] + result: list[int] = [] + for accip_i, l0_i, ln_i in zip(self._accip4, l0_radix, ln_radix, strict=True): + result.append((accip_i * l0_i + (accip_i - 1) * ln_i) % prime) + return result __all__ = ["RingConstraintBuilder"] diff --git a/dot_ring/ring_proof/params.py b/dot_ring/ring_proof/params.py index 8e3a277..31693da 100644 --- a/dot_ring/ring_proof/params.py +++ b/dot_ring/ring_proof/params.py @@ -2,17 +2,27 @@ from dataclasses import dataclass, field from functools import lru_cache -from typing import cast from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint from dot_ring.curve.specs.bandersnatch import Bandersnatch -from dot_ring.ring_proof.constants import D_2048, DEFAULT_SIZE, MAX_RING_SIZE, OMEGA_2048, S_PRIME, ZK_ROWS -from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG +from dot_ring.ring_proof.constants import ( + D_2048, + DEFAULT_SIZE, + EVAL_DOMAINS, + MAX_RING_SIZE, + OMEGA_2048, + OMEGAS, + S_PRIME, + ZK_ROWS, +) from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS -MAX_PIOP_DOMAIN_SIZE = 2048 +# 2047-member rings need a 4096-row PIOP domain and a 16384-point radix +# domain. KZG grows its SRS lazily when these larger quotient polynomials are +# requested. +MAX_PIOP_DOMAIN_SIZE = 4096 def _is_power_of_two(n: int) -> bool: @@ -31,9 +41,16 @@ def _next_power_of_two(n: int) -> int: def _omega_for_domain(domain_size: int, prime: int = S_PRIME, base_root: int = OMEGA_2048, base_size: int = 2048) -> int: if base_size % domain_size != 0: raise ValueError(f"Domain size {domain_size} must divide {base_size}") + if _uses_precomputed_bls_domain(prime, base_root, base_size) and domain_size in OMEGAS: + return OMEGAS[domain_size] return pow(base_root, base_size // domain_size, prime) +def _uses_precomputed_bls_domain(prime: int, base_root: int, base_size: int) -> bool: + return prime == S_PRIME and base_root == OMEGA_2048 and base_size == 2048 + + +# Global bounded cache: roots are deterministic for one size/field pair. @lru_cache(maxsize=16) def _primitive_root_of_unity(size: int, prime: int) -> int: if not _is_power_of_two(size): @@ -49,6 +66,7 @@ def _primitive_root_of_unity(size: int, prime: int) -> int: candidate += 1 +# Global bounded cache: evaluation domains are reused across roots/proofs. @lru_cache(maxsize=32) def _domain_for_size( domain_size: int, @@ -56,8 +74,15 @@ def _domain_for_size( base_root: int = OMEGA_2048, base_size: int = 2048, ) -> tuple[int, ...]: + if _uses_precomputed_bls_domain(prime, base_root, base_size) and domain_size in EVAL_DOMAINS: + return tuple(EVAL_DOMAINS[domain_size]) omega = _omega_for_domain(domain_size, prime, base_root, base_size) - return tuple(pow(omega, i, prime) for i in range(domain_size)) + domain = [1] * domain_size + current = 1 + for i in range(domain_size): + domain[i] = current + current = (current * omega) % prime + return tuple(domain) def _sqrt_mod_prime(n: int, prime: int) -> int: @@ -117,6 +142,7 @@ def _bandersnatch_sw_to_te(point: tuple[int, int]) -> tuple[int, int]: return v % prime, w % prime +# Global bounded cache: extended roots are deterministic for one target size. @lru_cache(maxsize=8) def _extend_root_to_size(base_root: int, base_size: int, target_size: int, prime: int) -> tuple[int, int]: """Extend a root of unity by repeated square roots until reaching target size.""" @@ -143,7 +169,7 @@ class RingProofParams: @property def scalar_bits(self) -> int: - return self.cv.curve.ORDER.bit_length() + return self.cv.curve.params.subgroup_order.bit_length() @property def row_overhead(self) -> int: @@ -151,12 +177,7 @@ def row_overhead(self) -> int: def __post_init__(self) -> None: if self.cv.name == "BabyJubJub": - if self.prime == S_PRIME: - object.__setattr__(self, "prime", int(self.cv.curve.PRIME_FIELD)) - if self.base_root == OMEGA_2048 and self.base_root_size == 2048: - object.__setattr__(self, "base_root", _primitive_root_of_unity(self.base_root_size, int(self.cv.curve.PRIME_FIELD))) - if self.pcs is KZG: - object.__setattr__(self, "pcs", BN254KZG) + raise ValueError("BabyJubJub ring proofs are not supported; use Tiny/Thin/Pedersen VRFs for that curve") radix_domain_size = self.radix_domain_size if radix_domain_size is None: radix_domain_size = self.domain_size * 4 @@ -191,6 +212,9 @@ def __post_init__(self) -> None: object.__setattr__(self, "max_ring_size", max_supported) elif self.max_ring_size > max_supported: raise ValueError(f"max_ring_size {self.max_ring_size} exceeds supported size {max_supported}") + ensure_srs_size = getattr(self.pcs, "ensure_srs_size", None) + if ensure_srs_size is not None: + ensure_srs_size(self.required_srs_degree) @property def omega(self) -> int: @@ -229,23 +253,30 @@ def last_index(self) -> int: def max_effective_ring_size(self) -> int: return self.domain_size - self.row_overhead + @property + def required_srs_degree(self) -> int: + return max(self.domain_size - 1, self._radix_domain_size - self.domain_size) + @property def blinding_base(self) -> tuple[int, int]: - if self.cv.curve.BBx is None or self.cv.curve.BBy is None: + base = self.cv.curve.params.auxiliary_points.blinding_base + if base is None: raise ValueError(f"{self.cv.name} does not define a ring blinding base") - return self.affine_to_ring_point((int(self.cv.curve.BBx), int(self.cv.curve.BBy))) + return self.affine_to_ring_point((int(base[0]), int(base[1]))) @property def seed_point(self) -> tuple[int, int]: - if self.cv.curve.ACCUMULATOR_BASE_X is None or self.cv.curve.ACCUMULATOR_BASE_Y is None: + base = self.cv.curve.params.auxiliary_points.accumulator_base + if base is None: raise ValueError(f"{self.cv.name} does not define a ring accumulator base") - return self.affine_to_ring_point((int(self.cv.curve.ACCUMULATOR_BASE_X), int(self.cv.curve.ACCUMULATOR_BASE_Y))) + return self.affine_to_ring_point((int(base[0]), int(base[1]))) @property def padding_point(self) -> tuple[int, int]: - if self.cv.curve.PADDING_X is None or self.cv.curve.PADDING_Y is None: + point = self.cv.curve.params.auxiliary_points.padding_point + if point is None: raise ValueError(f"{self.cv.name} does not define a ring padding point") - return self.affine_to_ring_point((int(self.cv.curve.PADDING_X), int(self.cv.curve.PADDING_Y))) + return self.affine_to_ring_point((int(point[0]), int(point[1]))) def affine_to_ring_point(self, point: tuple[int, int]) -> tuple[int, int]: if self.cv.name == "Bandersnatch_SW": @@ -255,26 +286,34 @@ def affine_to_ring_point(self, point: tuple[int, int]) -> tuple[int, int]: def point_to_ring_point(self, point: CurvePoint) -> tuple[int, int]: return self.affine_to_ring_point((int(point.x), int(point.y))) - @property - def ring_point_cls(self) -> type[CurvePoint]: + def ring_curve_variant(self) -> CurveVariant: if self.cv.name == "Bandersnatch_SW": - return cast(type[CurvePoint], Bandersnatch.point) - return cast(type[CurvePoint], self.cv.point) + return Bandersnatch + return self.cv + + def ring_point(self, point: tuple[int, int]) -> CurvePoint: + return self.ring_curve_variant().point(point[0], point[1]) @property def ring_edwards_a(self) -> int: if self.cv.name == "Bandersnatch_SW": - return int(Bandersnatch.curve.EdwardsA) - return int(self.cv.curve.EdwardsA) + return int(Bandersnatch.curve.params.a) + return int(self.cv.curve.params.a) + + @property + def ring_edwards_d(self) -> int: + if self.cv.name == "Bandersnatch_SW": + return int(Bandersnatch.curve.params.d) + return int(self.cv.curve.params.d) def add_points(self, point1: tuple[int, int], point2: tuple[int, int]) -> tuple[int, int]: - p1 = self.ring_point_cls(point1[0], point1[1]) - p2 = self.ring_point_cls(point2[0], point2[1]) + p1 = self.ring_point(point1) + p2 = self.ring_point(point2) result = p1 + p2 return int(result.x), int(result.y) def mul_point(self, scalar: int, point: tuple[int, int]) -> tuple[int, int]: - result = self.ring_point_cls(point[0], point[1]) * scalar + result = self.ring_point(point) * scalar return int(result.x), int(result.y) @classmethod @@ -310,7 +349,7 @@ def from_ring_size( raise ValueError(f"ring_size must be positive, got {ring_size}") # Calculate minimum domain size needed: - scalar_bits = cv.curve.ORDER.bit_length() + scalar_bits = cv.curve.params.subgroup_order.bit_length() overhead = scalar_bits + padding_rows domain_size = _next_power_of_two(ring_size + overhead) max_ring_size = domain_size - overhead diff --git a/dot_ring/ring_proof/proof/aggregation_poly.py b/dot_ring/ring_proof/proof/aggregation_poly.py index d377f1f..a753e4b 100644 --- a/dot_ring/ring_proof/proof/aggregation_poly.py +++ b/dot_ring/ring_proof/proof/aggregation_poly.py @@ -1,7 +1,7 @@ from dot_ring.ring_proof.constants import S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS -from dot_ring.ring_proof.polynomial.poly_ops import poly_add, poly_scalar_mul +from dot_ring.ring_proof.polynomial.ops import poly_add, poly_scalar_mul class AggPoly: @@ -18,10 +18,9 @@ def aggregated_poly(cls, fixed_cols: list, witness_cols: list, Q_p: list[int], c witness_cols[2].coeffs, Q_p, ] - V_list = cf_vectors agg_poly = [0] - for i in range(len(poly_I)): - agg_poly = poly_add(agg_poly, poly_scalar_mul(poly_I[i], V_list[i], prime), prime) + for poly, scalar in zip(poly_I, cf_vectors, strict=True): + agg_poly = poly_add(agg_poly, poly_scalar_mul(poly, scalar, prime), prime) return agg_poly # two proof openings diff --git a/dot_ring/ring_proof/proof/linearization_poly.py b/dot_ring/ring_proof/proof/linearization_poly.py index 83a1164..93dd12c 100644 --- a/dot_ring/ring_proof/proof/linearization_poly.py +++ b/dot_ring/ring_proof/proof/linearization_poly.py @@ -1,33 +1,14 @@ -from typing import Any, cast - -from dot_ring.curve.native_field.vector_ops import vect_mul as _native_vect_mul from dot_ring.ring_proof.columns.columns import Column from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.polynomial.ops import poly_evaluate -from dot_ring.ring_proof.polynomial.poly_ops import poly_add -from dot_ring.ring_proof.polynomial.poly_ops import poly_scalar_mul as poly_scalar +from dot_ring.ring_proof.polynomial.ops import poly_add, poly_evaluate_single, poly_scalar_mul from dot_ring.ring_proof.transcript.phases import phase2_eval_point -from dot_ring.ring_proof.transcript.transcript import Transcript - - -def _vect_mul(a: Any, b: Any, prime: int) -> list[int]: - if prime == S_PRIME: - return _native_vect_mul(a, b, prime) - if isinstance(a, list) and isinstance(b, list): - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) * int(y)) % prime for x, y in zip(a, b, strict=True)] - if isinstance(a, list): - return [(int(x) * int(b)) % prime for x in a] - if isinstance(b, list): - return [(int(a) * int(y)) % prime for y in b] - return [(int(a) * int(b)) % prime] +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript class LAggPoly: def __init__( self, - cur_t: Transcript, + cur_t: FiatShamirTranscript, C_q: list[int], fixed_cols: list[Column], witness_res: list[Column], @@ -54,18 +35,18 @@ def evaluate_polys_at_zeta(self) -> None: if self.wts[0].coeffs is None or self.wts[1].coeffs is None or self.wts[2].coeffs is None or self.wts[3].coeffs is None: raise ValueError("Witness columns not interpolated") - self.P_x_zeta = cast(list[int], poly_evaluate(self.fs[0].coeffs, [self.zeta], self.prime))[0] - self.P_y_zeta = cast(list[int], poly_evaluate(self.fs[1].coeffs, [self.zeta], self.prime))[0] - self.s_zeta = cast(list[int], poly_evaluate(self.fs[2].coeffs, [self.zeta], self.prime))[0] - self.b_zeta = cast(list[int], poly_evaluate(self.wts[0].coeffs, [self.zeta], self.prime))[0] - self.acc_ip_zeta = cast(list[int], poly_evaluate(self.wts[3].coeffs, [self.zeta], self.prime))[0] - self.acc_x_zeta = cast(list[int], poly_evaluate(self.wts[1].coeffs, [self.zeta], self.prime))[0] - self.acc_y_zeta = cast(list[int], poly_evaluate(self.wts[2].coeffs, [self.zeta], self.prime))[0] + self.P_x_zeta = poly_evaluate_single(self.fs[0].coeffs, self.zeta, self.prime) + self.P_y_zeta = poly_evaluate_single(self.fs[1].coeffs, self.zeta, self.prime) + self.s_zeta = poly_evaluate_single(self.fs[2].coeffs, self.zeta, self.prime) + self.b_zeta = poly_evaluate_single(self.wts[0].coeffs, self.zeta, self.prime) + self.acc_ip_zeta = poly_evaluate_single(self.wts[3].coeffs, self.zeta, self.prime) + self.acc_x_zeta = poly_evaluate_single(self.wts[1].coeffs, self.zeta, self.prime) + self.acc_y_zeta = poly_evaluate_single(self.wts[2].coeffs, self.zeta, self.prime) def compute_l1(self) -> list[int]: if self.wts[3].coeffs is None: raise ValueError("Witness column 3 not interpolated") - return poly_scalar(self.wts[3].coeffs, self.scalar_term, self.prime) + return poly_scalar_mul(self.wts[3].coeffs, self.scalar_term, self.prime) def compute_l2(self) -> list[int]: x1, y1 = self.acc_x_zeta, self.acc_y_zeta @@ -74,47 +55,37 @@ def compute_l2(self) -> list[int]: coeff_a = self.a C_acc_x = (b * (y1 * y2 + (coeff_a * x1 * x2)) % self.prime + (1 - b) % self.prime) % self.prime - C_acc_y = 0 C_acc_x_f = C_acc_x * self.scalar_term - C_acc_y_f = C_acc_y * self.scalar_term if self.wts[1].coeffs is None or self.wts[2].coeffs is None: raise ValueError("Witness columns not interpolated") - term1 = _vect_mul(self.wts[1].coeffs, C_acc_x_f, self.prime) - term2 = _vect_mul(self.wts[2].coeffs, C_acc_y_f, self.prime) - res = poly_add(term1, term2, self.prime) - return res + return poly_scalar_mul(self.wts[1].coeffs, C_acc_x_f, self.prime) def compute_l3(self) -> list[int]: b = self.b_zeta x1, y1 = self.acc_x_zeta, self.acc_y_zeta x2, y2 = self.P_x_zeta, self.P_y_zeta - C_acc_x = 0 C_acc_y = ((b * (x1 * y2 - x2 * y1)) % self.prime + (1 - b) % self.prime) % self.prime - C_acc_x *= self.scalar_term C_acc_y *= self.scalar_term if self.wts[1].coeffs is None or self.wts[2].coeffs is None: raise ValueError("Witness columns not interpolated") - term1 = poly_scalar(self.wts[1].coeffs, C_acc_x, self.prime) - term2 = poly_scalar(self.wts[2].coeffs, C_acc_y, self.prime) - res = poly_add(term1, term2, self.prime) - return res + return poly_scalar_mul(self.wts[2].coeffs, C_acc_y, self.prime) def linearize(self, l1: list[int], l2: list[int], l3: list[int]) -> list[int]: l_agg = [0] - for i, li in enumerate([l1, l2, l3]): - l_agg = poly_add(l_agg, poly_scalar(li, self.alphas[i], self.prime), self.prime) + for poly, scalar in zip([l1, l2, l3], self.alphas[:3], strict=True): + l_agg = poly_add(l_agg, poly_scalar_mul(poly, scalar, self.prime), self.prime) return l_agg - def l_agg_poly(self) -> tuple[Transcript, int, dict[str, int], list[int], int, int]: + def l_agg_poly(self) -> tuple[FiatShamirTranscript, int, dict[str, int], list[int], int, int]: self.evaluate_polys_at_zeta() # fills P_x_zeta … acc_y_zeta l1 = self.compute_l1() l2 = self.compute_l2() l3 = self.compute_l3() l_agg = self.linearize(l1, l2, l3) - l_agg_zeta_omega = cast(list[int], poly_evaluate(l_agg, [self.zeta_omega], self.prime))[0] + l_agg_zeta_omega = poly_evaluate_single(l_agg, self.zeta_omega, self.prime) return ( self.t, self.zeta, diff --git a/dot_ring/ring_proof/proof/quotient_poly.py b/dot_ring/ring_proof/proof/quotient_poly.py index f63bff6..3ad9556 100644 --- a/dot_ring/ring_proof/proof/quotient_poly.py +++ b/dot_ring/ring_proof/proof/quotient_poly.py @@ -1,12 +1,13 @@ from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment -from dot_ring.ring_proof.polynomial.ops import poly_division_general +from dot_ring.ring_proof.polynomial.ops import poly_divide_by_vanishing class QuotientPoly: - def __init__(self, domain_size: int, pcs: type[PCS] = KZG) -> None: + def __init__(self, domain_size: int, pcs: type[PCS] = KZG, prime: int | None = None) -> None: self.domain_size = domain_size self.pcs = pcs + self.prime = prime @staticmethod def poly_vector_xn_minus_1(n: int) -> list[int]: @@ -25,7 +26,9 @@ def quotient_poly_commitment(self, q_x: list[int]) -> G1Commitment: return c_q def quotient_poly(self, C_agg: list[int]) -> tuple[list[int], G1Commitment]: - qnt_poly = poly_division_general(C_agg, self.domain_size) + if self.prime is None: + raise ValueError("quotient polynomial division requires a prime field modulus") + qnt_poly = poly_divide_by_vanishing(C_agg, self.domain_size, self.prime) # print("q_p:", qnt_poly) C_qp = self.quotient_poly_commitment(qnt_poly) # C_qp_nm=nm(C_qp) diff --git a/dot_ring/ring_proof/transcript/phases.py b/dot_ring/ring_proof/transcript/phases.py index dda1a79..e9fed1e 100644 --- a/dot_ring/ring_proof/transcript/phases.py +++ b/dot_ring/ring_proof/transcript/phases.py @@ -5,40 +5,126 @@ from typing import Any from dot_ring.ring_proof.transcript.serialize import serialize -from dot_ring.ring_proof.transcript.transcript import Transcript +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript __all__ = [ + "derive_challenges_after_vk", "phase1_alphas", + "phase1_alphas_after_vk", "phase2_eval_point", "phase3_nu_vector", ] def phase1_alphas( - t: Transcript, + t: FiatShamirTranscript, vk: Any, result_point: Any, witness_commitments: Sequence[Any], ) -> Any: """Append phase‑1 data and return 7 constraint‑aggregation coefficients.""" - t.add_serialized(b"vk", serialize(vk)) - t.add_serialized(b"instance", serialize(result_point)) - t.add_serialized(b"committed_cols", serialize(witness_commitments)) + t.add_serialized_bytes(b"vk", serialize(vk)) + t.add_serialized_bytes(b"instance", serialize(result_point)) + t.add_serialized_bytes(b"committed_cols", serialize(witness_commitments)) return t, t.get_constraints_aggregation_coeffs(7) -def phase2_eval_point(t: Transcript, C_q_commitment: Any) -> Any: +def phase1_alphas_after_vk( + t: FiatShamirTranscript, + result_point: Any, + witness_commitments: Sequence[Any], +) -> Any: + """Append phase-1 data after the verifier key has already been absorbed.""" + t.add_serialized_bytes(b"instance", _serialize_instance(result_point)) + t.add_serialized_bytes(b"committed_cols", _serialize_commitments(witness_commitments)) + return t, t.get_constraints_aggregation_coeffs(7) + + +def phase2_eval_point(t: FiatShamirTranscript, C_q_commitment: Any) -> Any: """Append quotient commitment and derive evaluation point ζ.""" - t.add_serialized(b"quotient", serialize(C_q_commitment)) + t.add_serialized_bytes(b"quotient", _serialize_bytes_or_fallback(C_q_commitment)) return t, t.get_evaluation_point(1)[0] def phase3_nu_vector( - t: Transcript, + t: FiatShamirTranscript, rel_poly_evals: list[int] | bytes, agg_poly_eval: int, ) -> list[int]: """Append evaluation bundle and linearisation eval, return 8 ν‑challenges.""" - t.add_serialized(b"register_evaluations", serialize(rel_poly_evals)) - t.add_serialized(b"shifted_linearization_evaluation", serialize(agg_poly_eval)) + t.add_serialized_bytes(b"register_evaluations", _serialize_ints_or_bytes(rel_poly_evals)) + t.add_serialized_bytes(b"shifted_linearization_evaluation", _serialize_int_or_bytes(agg_poly_eval)) return t.get_kzg_aggregation_challenges(8) + + +def derive_challenges_after_vk( + t: FiatShamirTranscript, + result_point: Any, + witness_commitments: Sequence[Any], + quotient_commitment: Any, + evals: list[int] | bytes, + lin_eval: int | bytes, +) -> tuple[FiatShamirTranscript, list[int], int, list[int]]: + """Derive all post-vk verifier challenges from a copied transcript.""" + t = t.copy() + instance_bytes = _serialize_instance(result_point) + commitments_bytes = _serialize_commitments(witness_commitments) + quotient_bytes = _serialize_bytes_or_fallback(quotient_commitment) + evals_bytes = _serialize_ints_or_bytes(evals) + lin_eval_bytes = _serialize_int_or_bytes(lin_eval) + + t.add_serialized_bytes(b"instance", instance_bytes) + t.add_serialized_bytes(b"committed_cols", commitments_bytes) + alpha_list = t.get_constraints_aggregation_coeffs(7) + t.add_serialized_bytes(b"quotient", quotient_bytes) + zeta = t.get_evaluation_point(1)[0] + t.add_serialized_bytes(b"register_evaluations", evals_bytes) + t.add_serialized_bytes(b"shifted_linearization_evaluation", lin_eval_bytes) + return t, alpha_list, zeta, t.get_kzg_aggregation_challenges(8) + + +def _serialize_instance(result_point: Any) -> bytes: + if isinstance(result_point, tuple) and len(result_point) == 2: + x, y = result_point + return int(x).to_bytes(32, "little") + int(y).to_bytes(32, "little") + return serialize(result_point) + + +def _serialize_commitments(commitments: Sequence[Any]) -> bytes: + if isinstance(commitments, bytes): + return commitments + if isinstance(commitments, bytearray): + return bytes(commitments) + if all(isinstance(commitment, bytes | bytearray) for commitment in commitments): + return b"".join(bytes(commitment) for commitment in commitments) + return serialize(commitments) + + +def _serialize_bytes_or_fallback(value: Any) -> bytes: + if isinstance(value, bytes): + return value + if isinstance(value, bytearray): + return bytes(value) + return serialize(value) + + +def _serialize_ints_or_bytes(values: list[int] | bytes) -> bytes: + if isinstance(values, bytes): + return values + if isinstance(values, bytearray): + return bytes(values) + try: + chunks = [value.to_bytes(32, "little") for value in values] + except AttributeError: + return serialize(values) + return b"".join(chunks) + + +def _serialize_int_or_bytes(value: Any) -> bytes: + if isinstance(value, bytes): + return value + if isinstance(value, bytearray): + return bytes(value) + if isinstance(value, int): + return value.to_bytes(32, "little") + return serialize(value) diff --git a/dot_ring/ring_proof/transcript/transcript.py b/dot_ring/ring_proof/transcript/transcript.py index be364dc..1774c9c 100644 --- a/dot_ring/ring_proof/transcript/transcript.py +++ b/dot_ring/ring_proof/transcript/transcript.py @@ -4,19 +4,40 @@ import hashlib import math import struct +from typing import Any -__all__ = ["Transcript"] +__all__ = ["FiatShamirTranscript"] +_CHALLENGE_FOOTER = b"\x00\x00\x00\x09" -class Transcript: - """Direct implementation of the Transcript class matching the original code.""" + +def _label_prefix(label: bytes) -> bytes: + return label + struct.pack(">I", len(label)) + + +def _challenge_prefix(label: bytes) -> bytes: + return _label_prefix(label) + b"challenge" + + +class FiatShamirTranscript: + """Fiat-Shamir transcript used by the ring proof.""" def __init__(self, modulus: int, initial: bytes): self.modulus = modulus self._shake = hashlib.shake_128() self._length: int | None = None + self._challenge_bytes = math.ceil((self.modulus.bit_length() + 128) / 8) self.label(initial) + def copy(self) -> FiatShamirTranscript: + """Return an independent copy of the current transcript state.""" + clone = FiatShamirTranscript.__new__(FiatShamirTranscript) + clone.modulus = self.modulus + clone._shake = self._shake.copy() + clone._length = self._length + clone._challenge_bytes = self._challenge_bytes + return clone + def separate(self) -> None: """Write length footer if needed and reset counter.""" if self._length is not None: @@ -67,21 +88,16 @@ def write_bytes(self, data: bytes) -> None: def label(self, lbl: bytes) -> None: """Add a domain separation label.""" self.separate() - self.write(lbl) - self.separate() + self._shake.update(_label_prefix(lbl)) + self._length = None def challenge(self, label: bytes) -> int: """Generate a challenge value with the given label.""" - self.label(label) - self.write(b"challenge") - ret = self.read_reduce() - self.separate() - return ret + return self._challenge_direct(label) def _read_xof(self, n: int) -> bytes: """Read n bytes from the sponge.""" - shake_copy = self._shake.copy() - return shake_copy.digest(n) + return self._shake.digest(n) def append(self, data: bytes) -> None: """Append data with domain separation.""" @@ -94,20 +110,62 @@ def add_serialized(self, label: bytes, data: bytes) -> None: self.label(label) self.append(data) + def add_serialized_bytes(self, label: bytes, data: bytes) -> None: + """Add already-serialized small bytes without generic write dispatch.""" + if len(data) >= 1 << 31: + self.add_serialized(label, data) + return + + self.separate() + self._shake.update(_label_prefix(label) + data + struct.pack(">I", len(data))) + self._length = None + def read_reduce(self) -> int: """Read a value and reduce it modulo the field size.""" - n = math.ceil((self.modulus.bit_length() + 128) / 8) - rnd = self._read_xof(n) - # reverse for BE→LE - val = int.from_bytes(rnd[::-1], "little") + rnd = self._read_xof(self._challenge_bytes) + val = int.from_bytes(rnd, "big") return val % self.modulus + def _repeated_challenges(self, label: bytes, n: int) -> list[int]: + if n <= 0: + return [] + if self._length is not None: + self._shake.update(struct.pack(">I", self._length)) + self._length = None + return self._repeated_challenges_from_shake(self._shake, label, n) + + def _repeated_challenges_from_shake(self, shake: Any, label: bytes, n: int) -> list[int]: + out = [0] * n + challenge_bytes = self._challenge_bytes + modulus = self.modulus + challenge_prefix = _challenge_prefix(label) + challenge_footer = _CHALLENGE_FOOTER + challenge_footer_prefix = challenge_footer + challenge_prefix + update = shake.update + digest = shake.digest + int_from_bytes = int.from_bytes + update(challenge_prefix) + for index in range(n): + rnd = digest(challenge_bytes) + out[index] = int_from_bytes(rnd, "big") % modulus + update(challenge_footer if index == n - 1 else challenge_footer_prefix) + return out + + def _challenge_direct(self, label: bytes) -> int: + shake = self._shake + update = shake.update + if self._length is not None: + update(struct.pack(">I", self._length)) + update(_challenge_prefix(label)) + rnd = shake.digest(self._challenge_bytes) + value = int.from_bytes(rnd, "big") % self.modulus + update(_CHALLENGE_FOOTER) + self._length = None + return value + def get_constraints_aggregation_coeffs(self, n: int) -> list[int]: """Get n constraint aggregation coefficients.""" - out = [] - for _ in range(n): - out.append(self.challenge(b"constraints_aggregation")) - return out + return self._repeated_challenges(b"constraints_aggregation", n) def get_evaluation_point(self, n: int = 1) -> list[int]: """Get n evaluation points.""" @@ -117,7 +175,4 @@ def get_evaluation_point(self, n: int = 1) -> list[int]: def get_kzg_aggregation_challenges(self, n: int) -> list[int]: """Get n KZG aggregation challenges.""" - out = [] - for _ in range(n): - out.append(self.challenge(b"kzg_aggregation")) - return out + return self._repeated_challenges(b"kzg_aggregation", n) diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index b040a66..7c541aa 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -1,19 +1,23 @@ -from functools import cache -from typing import Any, cast +from typing import Any, NamedTuple, cast from dot_ring import blst as _blst from dot_ring.curve.native_field.scalar import Scalar -from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA from dot_ring.ring_proof.constants import OMEGA_2048, S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS -from dot_ring.ring_proof.pcs.utils import g1_to_blst +from dot_ring.ring_proof.pcs.utils import ( + LinearPcsVerification, + PcsVerification, + g1_to_blst, + pcs_transcript_g1, +) from dot_ring.ring_proof.transcript.phases import ( + derive_challenges_after_vk, phase1_alphas, phase2_eval_point, phase3_nu_vector, ) -from dot_ring.ring_proof.transcript.transcript import Transcript +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript blst = cast(Any, _blst) @@ -22,6 +26,46 @@ ZERO_S = Scalar(0) +class RingProofFields(NamedTuple): + c_b: Any + c_accip: Any + c_accx: Any + c_accy: Any + px_zeta: int + py_zeta: int + s_zeta: int + b_zeta: int + accip_zeta: int + accx_zeta: int + accy_zeta: int + c_q: Any + l_zeta_omega: int + phi_zeta: Any + phi_zeta_omega: Any + + @property + def evaluations(self) -> tuple[int, int, int, int, int, int, int]: + return ( + self.px_zeta, + self.py_zeta, + self.s_zeta, + self.b_zeta, + self.accip_zeta, + self.accx_zeta, + self.accy_zeta, + ) + + @property + def witness_commitments(self) -> tuple[Any, Any, Any, Any]: + return (self.c_b, self.c_accip, self.c_accx, self.c_accy) + + +def _ring_proof_fields(proof: RingProofFields | tuple[Any, ...]) -> RingProofFields: + if isinstance(proof, RingProofFields): + return proof + return RingProofFields(*proof) + + def blst_msm(points: list, scalars: list) -> Any: """ Multi-scalar multiplication using Pippenger's algorithm via blst. @@ -33,7 +77,223 @@ def blst_msm(points: list, scalars: list) -> Any: return blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(points), scalars) -@cache +def _compute_quotient_and_linearization_terms( + alpha_list: list[int], + v_list: list[int], + zeta_value: int, + px_value: int, + py_value: int, + s_value: int, + b_value: int, + accip_value: int, + accx_value: int, + accy_value: int, + l_zeta_omega_value: int, + seed_x_value: int, + seed_y_value: int, + result_seed_x_value: int, + result_seed_y_value: int, + domain_last_value: int, + domain_neg1_value: int, + domain_neg2_value: int, + domain_neg3_value: int, + domain_size: int, + domain_size_inv_value: int | None = None, + edwards_a_value: int | Scalar | None = None, + omega_value: int | None = None, + include_linearization: bool = False, +) -> int | tuple[int, int, int, int, int, int]: + if len(alpha_list) < 7 or len(v_list) < 8: + raise ValueError("expected at least 7 alpha values and 8 aggregation values") + + prime = S_PRIME + alphas = [int(value) % prime for value in alpha_list[:7]] + v_values = [int(value) % prime for value in v_list[:8]] + zeta = int(zeta_value) % prime + px = int(px_value) % prime + py = int(py_value) % prime + s = int(s_value) % prime + b = int(b_value) % prime + accip = int(accip_value) % prime + accx = int(accx_value) % prime + accy = int(accy_value) % prime + l_zeta_omega = int(l_zeta_omega_value) % prime + seed_x = int(seed_x_value) % prime + seed_y = int(seed_y_value) % prime + result_seed_x = int(result_seed_x_value) % prime + result_seed_y = int(result_seed_y_value) % prime + domain_last = int(domain_last_value) % prime + domain_neg1 = int(domain_neg1_value) % prime + domain_neg2 = int(domain_neg2_value) % prime + domain_neg3 = int(domain_neg3_value) % prime + inv_size = pow(domain_size, -1, prime) if domain_size_inv_value is None else int(domain_size_inv_value) % prime + + zeta_n_minus_1 = (pow(zeta, domain_size, prime) - 1) % prime + zeta_minus_d4 = (zeta - domain_last) % prime + zeta_minus_1 = (zeta - 1) % prime + zeta_minus_last = (zeta - domain_last) % prime + + if zeta_minus_1 == 0: + l0 = 1 + inv_den = pow((zeta_minus_last * zeta_n_minus_1) % prime, -1, prime) + inv_zeta_minus_last = (zeta_n_minus_1 * inv_den) % prime + inv_zeta_n_minus_1 = (zeta_minus_last * inv_den) % prime + ln = domain_last * inv_size % prime + ln = ln * zeta_n_minus_1 % prime + ln = ln * inv_zeta_minus_last % prime + elif zeta_minus_last == 0: + ln = 1 + inv_den = pow((zeta_minus_1 * zeta_n_minus_1) % prime, -1, prime) + inv_zeta_minus_1 = (zeta_n_minus_1 * inv_den) % prime + inv_zeta_n_minus_1 = (zeta_minus_1 * inv_den) % prime + l0 = inv_size * zeta_n_minus_1 % prime + l0 = l0 * inv_zeta_minus_1 % prime + else: + den01 = zeta_minus_1 * zeta_minus_last % prime + inv_den = pow((den01 * zeta_n_minus_1) % prime, -1, prime) + inv_zeta_n_minus_1 = den01 * inv_den % prime + tmp = zeta_n_minus_1 * inv_den % prime + inv_zeta_minus_1 = zeta_minus_last * tmp % prime + inv_zeta_minus_last = zeta_minus_1 * tmp % prime + l0 = inv_size * zeta_n_minus_1 % prime + l0 = l0 * inv_zeta_minus_1 % prime + ln = domain_last * inv_size % prime + ln = ln * zeta_n_minus_1 % prime + ln = ln * inv_zeta_minus_last % prime + + one_minus_b = (1 - b) % prime + x1_y1 = accx * accy % prime + x2_y2 = px * py % prime + + c_values = [ + (-(accip + b * s) * zeta_minus_d4) % prime, + ((b * (-(x1_y1 + x2_y2)) + one_minus_b * (-accx)) * zeta_minus_d4) % prime, + ((b * (-(x1_y1 - x2_y2)) + one_minus_b * (-accy)) * zeta_minus_d4) % prime, + b * one_minus_b % prime, + ((accx - seed_x) * l0 + (accx - result_seed_x) * ln) % prime, + ((accy - seed_y) * l0 + (accy - result_seed_y) * ln) % prime, + (accip * l0 + (accip - 1) * ln) % prime, + ] + linear_combination = sum((alpha * c_value) % prime for alpha, c_value in zip(alphas, c_values, strict=True)) % prime + + prod_sum = (zeta - domain_neg1) % prime + prod_sum = prod_sum * ((zeta - domain_neg2) % prime) % prime + prod_sum = prod_sum * ((zeta - domain_neg3) % prime) % prime + q_zeta = (linear_combination + l_zeta_omega) % prime + q_zeta = q_zeta * prod_sum % prime + q_zeta = q_zeta * inv_zeta_n_minus_1 % prime + + terms = (px, py, s, b, accip, accx, accy, q_zeta) + agg_zeta = sum((v * term) % prime for v, term in zip(v_values, terms, strict=True)) % prime + if not include_linearization: + return agg_zeta + + if edwards_a_value is None or omega_value is None: + raise ValueError("edwards_a_value and omega_value are required when include_linearization=True") + + edwards_a = int(edwards_a_value) % prime + omega = int(omega_value) % prime + c_acc_x = b * ((accy * py + edwards_a * accx * px) % prime) % prime + c_acc_x = (c_acc_x + one_minus_b) % prime + scalar_accx = alphas[1] * (c_acc_x * zeta_minus_d4 % prime) % prime + + c_acc_y = b * ((accx * py - px * accy) % prime) % prime + c_acc_y = (c_acc_y + one_minus_b) % prime + scalar_accy = alphas[2] * (c_acc_y * zeta_minus_d4 % prime) % prime + + scalar_accip = alphas[0] * zeta_minus_d4 % prime + zeta_omega = zeta * omega % prime + return agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega + + +def prepare_linear_pcs_verifications_fast( + proof: RingProofFields | tuple[Any, ...], + fixed_cols_blst: tuple[Any, Any, Any], + relation_to_prove: tuple[int, int], + result_plus_seed: tuple[int, int], + seed_point: tuple[int, int], + domain: list[int], + padding_rows: int, + domain_size_inv: int, + edwards_a: int, + omega: int, + transcript_prefix: FiatShamirTranscript, + witness_commitments: bytes, + quotient_commitment: bytes, +) -> tuple[LinearPcsVerification, LinearPcsVerification]: + proof_fields = _ring_proof_fields(proof) + + _, alpha_list, zeta_p, v_list = derive_challenges_after_vk( + transcript_prefix, + relation_to_prove, + witness_commitments, + quotient_commitment, + proof_fields.evaluations, + proof_fields.l_zeta_omega, + ) + + last_index = len(domain) - padding_rows + linear_eval_bundle = _compute_quotient_and_linearization_terms( + alpha_list, + v_list, + zeta_p, + proof_fields.px_zeta, + proof_fields.py_zeta, + proof_fields.s_zeta, + proof_fields.b_zeta, + proof_fields.accip_zeta, + proof_fields.accx_zeta, + proof_fields.accy_zeta, + proof_fields.l_zeta_omega, + seed_point[0], + seed_point[1], + result_plus_seed[0], + result_plus_seed[1], + domain[last_index], + domain[-1], + domain[-2], + domain[-3], + len(domain), + domain_size_inv, + edwards_a, + omega, + True, + ) + Agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega_out = cast( + tuple[int, int, int, int, int, int], + linear_eval_bundle, + ) + + Cpx_blst, Cpy_blst, Cs_blst = fixed_cols_blst + Cb_blst = g1_to_blst(proof_fields.c_b) + Caccip_blst = g1_to_blst(proof_fields.c_accip) + Caccx_blst = g1_to_blst(proof_fields.c_accx) + Caccy_blst = g1_to_blst(proof_fields.c_accy) + Cq_blst = g1_to_blst(proof_fields.c_q) + Phi_zeta_blst = g1_to_blst(proof_fields.phi_zeta) + Phi_zeta_omega_blst = g1_to_blst(proof_fields.phi_zeta_omega) + + quotient_terms = ( + (Cpx_blst, v_list[0]), + (Cpy_blst, v_list[1]), + (Cs_blst, v_list[2]), + (Cb_blst, v_list[3]), + (Caccip_blst, v_list[4]), + (Caccx_blst, v_list[5]), + (Caccy_blst, v_list[6]), + (Cq_blst, v_list[7]), + ) + linearization_terms = ( + (Caccip_blst, scalar_accip), + (Caccx_blst, scalar_accx), + (Caccy_blst, scalar_accy), + ) + return ( + LinearPcsVerification(quotient_terms, Phi_zeta_blst, zeta_p, Agg_zeta), + LinearPcsVerification(linearization_terms, Phi_zeta_omega_blst, zeta_omega, l_zeta_omega_out), + ) + + def lagrange_at_zeta(domain_size: int, index: int, zeta: int, omega: int, prime: int) -> Scalar: """ Compute L_i(zeta) using closed-form formula for roots of unity domain. @@ -73,12 +333,17 @@ def lagrange_at_zeta(domain_size: int, index: int, zeta: int, omega: int, prime: class Verify: + prime = S_PRIME + pcs: type[PCS] = KZG + omega: int | None = None + edwards_a = Scalar(-5) + def __init__( self, - proof: tuple, + proof: RingProofFields | tuple[Any, ...], vk: dict | bytes, fixed_cols: list, - rl_to_proove: tuple | bytes, + rl_to_proove: tuple | bytes | None, rps: tuple, seed_point: tuple, Domain: list, @@ -89,84 +354,113 @@ def __init__( prime: int = S_PRIME, omega: int | None = None, pcs: type[PCS] = KZG, + transcript_prefix: FiatShamirTranscript | None = None, + domain_size_inv: int | None = None, + transcript_witness_commitments: tuple[Any, ...] | None = None, + transcript_quotient_commitment: Any | None = None, + relation_to_prove: tuple | bytes | None = None, ) -> None: - ( - self.Cb, - self.Caccip, - self.Caccx, - self.Caccy, - self.px_zeta, - self.py_zeta, - self.s_zeta, - self.b_zeta, - self.accip_zeta, - self.accx_zeta, - self.accy_zeta, - self.Cq, - self.l_zeta_omega, - self.Phi_zeta, - self.Phi_zeta_omega, - ) = proof - - self.proof_ptr = proof + proof_fields = _ring_proof_fields(proof) + self.Cb = proof_fields.c_b + self.Caccip = proof_fields.c_accip + self.Caccx = proof_fields.c_accx + self.Caccy = proof_fields.c_accy + self.px_zeta = proof_fields.px_zeta + self.py_zeta = proof_fields.py_zeta + self.s_zeta = proof_fields.s_zeta + self.b_zeta = proof_fields.b_zeta + self.accip_zeta = proof_fields.accip_zeta + self.accx_zeta = proof_fields.accx_zeta + self.accy_zeta = proof_fields.accy_zeta + self.Cq = proof_fields.c_q + self.l_zeta_omega = proof_fields.l_zeta_omega + self.Phi_zeta = proof_fields.phi_zeta + self.Phi_zeta_omega = proof_fields.phi_zeta_omega + + self.proof_ptr = proof_fields self.verifier_key = vk self.Cpx, self.Cpy, self.Cs = fixed_cols - self.relation_to_proove = rl_to_proove + if relation_to_prove is None: + if rl_to_proove is None: + raise TypeError("relation_to_prove is required") + relation_to_prove = rl_to_proove + elif rl_to_proove is not None and rl_to_proove != relation_to_prove: + raise ValueError("relation_to_prove and rl_to_proove differ") + self.relation_to_prove = relation_to_prove + self.relation_to_proove = relation_to_prove self.Result_plus_Seed, self.sp, self.D = rps, seed_point, Domain self.padding_rows = padding_rows self.prime = prime self.omega = omega self.pcs = pcs - self.uses_native_scalar = self.prime == S_PRIME - self.edwards_a = Scalar(edwards_a) if self.uses_native_scalar else edwards_a % self.prime + self.domain_size_inv = domain_size_inv if domain_size_inv is not None else pow(len(self.D), -1, self.prime) + self.edwards_a = Scalar(edwards_a) if self.padding_rows < 1 or self.padding_rows >= len(self.D): raise ValueError("padding_rows must be >= 1 and less than domain size") self.last_index = len(self.D) - self.padding_rows - if self.uses_native_scalar: - self.Cb_blst = g1_to_blst(self.Cb) - self.Caccip_blst = g1_to_blst(self.Caccip) - self.Caccx_blst = g1_to_blst(self.Caccx) - self.Caccy_blst = g1_to_blst(self.Caccy) - self.Cq_blst = g1_to_blst(self.Cq) - self.Phi_zeta_blst = g1_to_blst(self.Phi_zeta) - self.Phi_zeta_omega_blst = g1_to_blst(self.Phi_zeta_omega) + self.Cb_blst = g1_to_blst(self.Cb) + self.Caccip_blst = g1_to_blst(self.Caccip) + self.Caccx_blst = g1_to_blst(self.Caccx) + self.Caccy_blst = g1_to_blst(self.Caccy) + self.Cq_blst = g1_to_blst(self.Cq) + self.Phi_zeta_blst = g1_to_blst(self.Phi_zeta) + self.Phi_zeta_omega_blst = g1_to_blst(self.Phi_zeta_omega) - self.Cpx_blst = g1_to_blst(self.Cpx) - self.Cpy_blst = g1_to_blst(self.Cpy) - self.Cs_blst = g1_to_blst(self.Cs) + self.Cpx_blst = g1_to_blst(self.Cpx) + self.Cpy_blst = g1_to_blst(self.Cpy) + self.Cs_blst = g1_to_blst(self.Cs) - self.t = Transcript(self.prime, transcript_challenge) + self.t = transcript_prefix if transcript_prefix is not None else FiatShamirTranscript(self.prime, transcript_challenge) # Absorb into transcript - self.t, self.alpha_list = phase1_alphas( - self.t, - self.verifier_key, - self.relation_to_proove, - [self._transcript_g1(cmt) for cmt in self.proof_ptr[:4]], + witness_commitments = ( + transcript_witness_commitments + if transcript_witness_commitments is not None + else [self._transcript_g1(cmt) for cmt in proof_fields.witness_commitments] ) - - # Add quotient and get zeta - self.t, self.zeta_p = phase2_eval_point(self.t, self._transcript_g1(self.proof_ptr[-4])) - - # Phase 3: Add evaluations and get ν challenges - evals_bytes = b"".join(v.to_bytes(32, "little") for v in self.proof_ptr[4:11]) - lin_eval_bytes = self.proof_ptr[-3].to_bytes(32, "little") - - self.V_list = phase3_nu_vector(self.t, evals_bytes, lin_eval_bytes) + # Add quotient and evaluations once we have their serialized form. + quotient_commitment = ( + transcript_quotient_commitment + if transcript_quotient_commitment is not None + else self._transcript_g1(proof_fields.c_q) + ) + if transcript_prefix is not None: + self.t, self.alpha_list, self.zeta_p, self.V_list = derive_challenges_after_vk( + self.t, + self.relation_to_prove, + witness_commitments, + quotient_commitment, + proof_fields.evaluations, + proof_fields.l_zeta_omega, + ) + elif transcript_prefix is None: + evals_bytes = b"".join(v.to_bytes(32, "little") for v in proof_fields.evaluations) + lin_eval_bytes = proof_fields.l_zeta_omega.to_bytes(32, "little") + self.t, self.alpha_list = phase1_alphas( + self.t, + self.verifier_key, + self.relation_to_prove, + witness_commitments, + ) + self.t, self.zeta_p = phase2_eval_point(self.t, quotient_commitment) + self.V_list = phase3_nu_vector(self.t, evals_bytes, lin_eval_bytes) # Save transcript self.cur_t = self.t + def _domain_omega(self) -> int: + if self.omega is not None: + return self.omega + domain_size = len(self.D) + if 2048 % domain_size != 0: + raise ValueError(f"omega must be supplied for domain size {domain_size}") + return pow(OMEGA_2048, 2048 // domain_size, self.prime) + def _transcript_g1(self, point: Any) -> Any: - if getattr(self.pcs, "commitment_size", 48) == 32: - return self.pcs.serialize_g1_uncompressed(point) - return self.pcs.normalize_g1(point) + return pcs_transcript_g1(self.pcs, point) def contributions_to_constraints_eval_at_zeta(self) -> tuple[Any, Any, Any, Any, Any, Any, Any]: - if not getattr(self, "uses_native_scalar", True): - return self._contributions_to_constraints_eval_at_zeta_int() - # Convert to Scalar for optimized arithmetic zeta = Scalar(self.zeta_p) sx, sy = Scalar(self.sp[0]), Scalar(self.sp[1]) @@ -244,70 +538,16 @@ def contributions_to_constraints_eval_at_zeta(self) -> tuple[Any, Any, Any, Any, c7_zeta, ) - def _contributions_to_constraints_eval_at_zeta_int(self) -> tuple[int, int, int, int, int, int, int]: - p = self.prime - zeta = self.zeta_p % p - sx, sy = self.sp[0] % p, self.sp[1] % p - domain_size = len(self.D) - zeta_n_minus_1 = (pow(zeta, domain_size, p) - 1) % p - inv_size = pow(domain_size, -1, p) - - zeta_minus_1 = (zeta - 1) % p - if zeta_minus_1 == 0: - L_0_zeta = 1 - else: - L_0_zeta = inv_size * zeta_n_minus_1 * pow(zeta_minus_1, -1, p) % p - - omega_i_n_4 = self.D[self.last_index] % p - zeta_minus_omega_i = (zeta - omega_i_n_4) % p - if zeta_minus_omega_i == 0: - L_N_4_zeta = 1 - else: - L_N_4_zeta = omega_i_n_4 * inv_size * zeta_n_minus_1 * pow(zeta_minus_omega_i, -1, p) % p - - b = self.b_zeta % p - accx = self.accx_zeta % p - accy = self.accy_zeta % p - accip = self.accip_zeta % p - px = self.px_zeta % p - py = self.py_zeta % p - s = self.s_zeta % p - rps0 = self.Result_plus_Seed[0] % p - rps1 = self.Result_plus_Seed[1] % p - zeta_minus_d4 = (zeta - self.D[self.last_index]) % p - one_minus_b = (1 - b) % p - - x1_y1 = accx * accy % p - x2_y2 = px * py % p - - c1_zeta = (-(accip + b * s) * zeta_minus_d4) % p - c2 = (b * (-(x1_y1 + x2_y2)) + one_minus_b * (-accx)) % p - c2_zeta = c2 * zeta_minus_d4 % p - c3 = (b * (-(x1_y1 - x2_y2)) + one_minus_b * (-accy)) % p - c3_zeta = c3 * zeta_minus_d4 % p - c4_zeta = b * one_minus_b % p - c5_zeta = ((accx - sx) * L_0_zeta + (accx - rps0) * L_N_4_zeta) % p - c6_zeta = ((accy - sy) * L_0_zeta + (accy - rps1) * L_N_4_zeta) % p - c7_zeta = (accip * L_0_zeta + (accip - 1) * L_N_4_zeta) % p - - return (c1_zeta, c2_zeta, c3_zeta, c4_zeta, c5_zeta, c6_zeta, c7_zeta) - def divide(self, numr: int, denom: int) -> int: # Inlined modular division - use built-in pow (faster than external call) - prime = getattr(self, "prime", S_PRIME) - return (numr * pow(denom, -1, prime)) % prime + return (numr * pow(denom, -1, self.prime)) % self.prime def is_valid(self) -> bool: """If both the verifications are true then sign is valid""" - verification1 = self._prepare_quotient_poly_verification() - verification2 = self._prepare_linearization_poly_verification() - return self.pcs.batch_verify([verification1, verification2]) + return bool(self.pcs.batch_verify_linear_preconverted(list(self._prepare_linear_pcs_verifications()))) - def _prepare_quotient_poly_verification(self) -> tuple[Any, Any, int, int]: + def _prepare_quotient_poly_verification(self) -> PcsVerification: """Prepare KZG verification data for quotient polynomial""" - if not getattr(self, "uses_native_scalar", True): - return self._prepare_quotient_poly_verification_int() - alphas_list = [Scalar(a) for a in self.alpha_list] zeta = Scalar(self.zeta_p) v_list = [Scalar(v) for v in self.V_list] @@ -373,38 +613,81 @@ def _prepare_quotient_poly_verification(self) -> tuple[Any, Any, int, int]: for t in terms: Agg_zeta = Agg_zeta + t - return (C_agg, self.Phi_zeta_blst, int(zeta), int(Agg_zeta)) - - def _prepare_quotient_poly_verification_int(self) -> tuple[Any, Any, int, int]: - p = self.prime - alphas_list = [a % p for a in self.alpha_list] - zeta = self.zeta_p % p - v_list = [v % p for v in self.V_list] - cs = self.contributions_to_constraints_eval_at_zeta() - - prod_sum = 1 - for k in range(1, 4): - prod_sum = prod_sum * ((zeta - self.D[-k]) % p) % p - - linear_combination = 0 - for alpha, c in zip(alphas_list, cs, strict=False): - linear_combination = (linear_combination + alpha * c) % p + return PcsVerification(C_agg, self.Phi_zeta_blst, int(zeta), int(Agg_zeta)) + + def _prepare_quotient_poly_verification_linear(self) -> LinearPcsVerification: + """Prepare quotient KZG verification with an unmaterialized commitment.""" + Agg_zeta = self._native_linear_eval_bundle()[0] + + commitment_terms = ( + (self.Cpx_blst, self.V_list[0]), + (self.Cpy_blst, self.V_list[1]), + (self.Cs_blst, self.V_list[2]), + (self.Cb_blst, self.V_list[3]), + (self.Caccip_blst, self.V_list[4]), + (self.Caccx_blst, self.V_list[5]), + (self.Caccy_blst, self.V_list[6]), + (self.Cq_blst, self.V_list[7]), + ) + return LinearPcsVerification(commitment_terms, self.Phi_zeta_blst, self.zeta_p, Agg_zeta) - s_sum = (linear_combination + self.l_zeta_omega) % p - zeta_pow_size_minus_1 = (pow(zeta, len(self.D), p) - 1) % p - q_zeta = s_sum * prod_sum * pow(zeta_pow_size_minus_1, -1, p) % p + def _prepare_linear_pcs_verifications( + self, + ) -> tuple[LinearPcsVerification, LinearPcsVerification]: + """Prepare deferred linear KZG equations from one native scalar pass.""" + Agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega = self._native_linear_eval_bundle() + quotient_terms = ( + (self.Cpx_blst, self.V_list[0]), + (self.Cpy_blst, self.V_list[1]), + (self.Cs_blst, self.V_list[2]), + (self.Cb_blst, self.V_list[3]), + (self.Caccip_blst, self.V_list[4]), + (self.Caccx_blst, self.V_list[5]), + (self.Caccy_blst, self.V_list[6]), + (self.Cq_blst, self.V_list[7]), + ) + linearization_terms = ( + (self.Caccip_blst, scalar_accip), + (self.Caccx_blst, scalar_accx), + (self.Caccy_blst, scalar_accy), + ) + return ( + LinearPcsVerification(quotient_terms, self.Phi_zeta_blst, self.zeta_p, Agg_zeta), + LinearPcsVerification(linearization_terms, self.Phi_zeta_omega_blst, zeta_omega, l_zeta_omega), + ) - C_a = [self.Cpx, self.Cpy, self.Cs, self.Cb, self.Caccip, self.Caccx, self.Caccy, self.Cq] - C_agg = self.pcs.msm_g1(C_a, self.V_list) - evals = [self.px_zeta, self.py_zeta, self.s_zeta, self.b_zeta, self.accip_zeta, self.accx_zeta, self.accy_zeta, q_zeta] - Agg_zeta = sum(v * (e % p) for v, e in zip(v_list, evals, strict=True)) % p - return (C_agg, self.Phi_zeta, zeta, Agg_zeta) + def _native_linear_eval_bundle(self) -> tuple[int, int, int, int, int, int]: + omega = self._domain_omega() + bundle = _compute_quotient_and_linearization_terms( + self.alpha_list, + self.V_list, + self.zeta_p, + self.px_zeta, + self.py_zeta, + self.s_zeta, + self.b_zeta, + self.accip_zeta, + self.accx_zeta, + self.accy_zeta, + self.l_zeta_omega, + self.sp[0], + self.sp[1], + self.Result_plus_Seed[0], + self.Result_plus_Seed[1], + self.D[self.last_index], + self.D[-1], + self.D[-2], + self.D[-3], + len(self.D), + self.domain_size_inv, + self.edwards_a, + omega, + True, + ) + return cast(tuple[int, int, int, int, int, int], bundle) - def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: + def _prepare_linearization_poly_verification(self) -> PcsVerification: """Prepare KZG verification data for linearization polynomial""" - if not getattr(self, "uses_native_scalar", True): - return self._prepare_linearization_poly_verification_int() - alphas_list = [Scalar(a) for a in self.alpha_list] zeta = Scalar(self.zeta_p) @@ -417,7 +700,7 @@ def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: x1, y1 = Scalar(self.accx_zeta), Scalar(self.accy_zeta) x2, y2 = Scalar(self.px_zeta), Scalar(self.py_zeta) b = Scalar(self.b_zeta) - coeff_a = getattr(self, "edwards_a", Scalar(-5)) + coeff_a = self.edwards_a # S_PRIME is scalar field modulus, which Scalar handles implicitly @@ -438,53 +721,16 @@ def _prepare_linearization_poly_verification(self) -> tuple[Any, Any, int, int]: scalars = [int(scalar_accip), int(scalar_accx), int(scalar_accy)] Cl = blst_msm(points, scalars) - # Compute omega for the actual domain size - domain_size = len(self.D) - if domain_size == 512: - omega = OMEGA - elif domain_size == 1024: - omega = pow(OMEGA_2048, 2048 // 1024, S_PRIME) - elif domain_size == 2048: - omega = OMEGA_2048 - else: - # Fallback: compute from OMEGA_2048 - omega = pow(OMEGA_2048, 2048 // domain_size, S_PRIME) - - zeta_omega = zeta * Scalar(omega) - - return (Cl, self.Phi_zeta_omega_blst, int(zeta_omega), int(Scalar(self.l_zeta_omega))) + zeta_omega = zeta * Scalar(self._domain_omega()) - def _prepare_linearization_poly_verification_int(self) -> tuple[Any, Any, int, int]: - p = self.prime - alphas_list = [a % p for a in self.alpha_list] - zeta = self.zeta_p % p - zeta_minus_d4 = (zeta - self.D[self.last_index]) % p + return PcsVerification(Cl, self.Phi_zeta_omega_blst, int(zeta_omega), int(Scalar(self.l_zeta_omega))) - x1, y1 = self.accx_zeta % p, self.accy_zeta % p - x2, y2 = self.px_zeta % p, self.py_zeta % p - b = self.b_zeta % p - coeff_a = int(self.edwards_a) % p - - C_acc_x_cl2 = (b * (y1 * y2 + coeff_a * x1 * x2) + (1 - b)) % p - C_acc_x_f_cl2 = C_acc_x_cl2 * zeta_minus_d4 % p - C_acc_y_cl3 = (b * (x1 * y2 - x2 * y1) + (1 - b)) % p - C_acc_y_f_cl3 = C_acc_y_cl3 * zeta_minus_d4 % p - - scalars = [ - alphas_list[0] * zeta_minus_d4 % p, - alphas_list[1] * C_acc_x_f_cl2 % p, - alphas_list[2] * C_acc_y_f_cl3 % p, - ] - Cl = self.pcs.msm_g1([self.Caccip, self.Caccx, self.Caccy], scalars) - omega = self.omega if self.omega is not None else pow(OMEGA_2048, 2048 // len(self.D), p) - zeta_omega = zeta * omega % p - return (Cl, self.Phi_zeta_omega, zeta_omega, self.l_zeta_omega % p) - - # Legacy methods for backwards compatibility - def evaluation_of_quotient_poly_at_zeta(self) -> bool: - """Legacy method - use is_valid() with batch verification instead""" - raise NotImplementedError("Use is_valid() with batch verification instead") - - def evaluation_of_linearization_poly_at_zeta_omega(self) -> bool: - """Legacy method - use is_valid() with batch verification instead""" - raise NotImplementedError("Use is_valid() with batch verification instead") + def _prepare_linearization_poly_verification_linear(self) -> LinearPcsVerification: + """Prepare linearization KZG verification with an unmaterialized commitment.""" + _, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega = self._native_linear_eval_bundle() + commitment_terms = ( + (self.Caccip_blst, scalar_accip), + (self.Caccx_blst, scalar_accx), + (self.Caccy_blst, scalar_accy), + ) + return LinearPcsVerification(commitment_terms, self.Phi_zeta_omega_blst, zeta_omega, l_zeta_omega) diff --git a/tests/test_coverage/test_aggregation.py b/tests/test_coverage/test_aggregation.py deleted file mode 100644 index 43f7e20..0000000 --- a/tests/test_coverage/test_aggregation.py +++ /dev/null @@ -1,11 +0,0 @@ -from dot_ring.ring_proof.constraints.aggregation import aggregate_constraints - - -def test_aggregate_constraints_uses_default_domain_and_trims_zeros(): - polys = [[0, 0, 0, 0]] - alphas = [1] - omega_root = 5 - - result = aggregate_constraints(polys, alphas, omega_root) - - assert result == [] diff --git a/tests/test_coverage/test_columns.py b/tests/test_coverage/test_columns.py index 4bcf9a6..2746e3d 100644 --- a/tests/test_coverage/test_columns.py +++ b/tests/test_coverage/test_columns.py @@ -4,8 +4,8 @@ from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder from dot_ring.ring_proof.constants import DEFAULT_SIZE, OMEGAS, S_PRIME from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring -from dot_ring.vrf.ring.ring_vrf import RingVRF +from dot_ring.vrf.ring import Ring +from dot_ring.vrf.ring import RingVRF def test_column_interpolate_rejects_oversize_evals(): diff --git a/tests/test_coverage/test_params.py b/tests/test_coverage/test_params.py index 0e7b1bb..a4cdeb1 100644 --- a/tests/test_coverage/test_params.py +++ b/tests/test_coverage/test_params.py @@ -1,5 +1,6 @@ import pytest +from dot_ring.curve.specs.baby_jubjub import BabyJubJub from dot_ring.ring_proof.constants import D_2048, DEFAULT_SIZE, MAX_RING_SIZE, OMEGA_2048, S_PRIME from dot_ring.ring_proof.params import ( RingProofParams, @@ -74,6 +75,11 @@ def test_ring_proof_params_defaults(): assert params.max_effective_ring_size == params.domain_size - params.scalar_bits - params.padding_rows +def test_baby_jubjub_ring_proof_params_are_unsupported(): + with pytest.raises(ValueError, match="BabyJubJub ring proofs are not supported"): + RingProofParams(cv=BabyJubJub) + + def test_ring_proof_params_extends_root_size(): params = RingProofParams( domain_size=512, @@ -123,6 +129,8 @@ def test_ring_proof_params_validation_errors(kwargs, match): (767, 1024, 767), (768, 2048, 1791), (1791, 2048, 1791), + (1792, 4096, 3839), + (2047, 4096, 3839), ], ) def test_from_ring_size_matches_spec_capacity(ring_size, domain_size, max_ring_size): @@ -131,3 +139,6 @@ def test_from_ring_size_matches_spec_capacity(ring_size, domain_size, max_ring_s assert params.domain_size == domain_size assert params.max_ring_size == max_ring_size assert params.max_effective_ring_size == max_ring_size + assert pow(params.omega, params.domain_size, params.prime) == 1 + assert pow(params.omega, params.domain_size // 2, params.prime) != 1 + assert params.required_srs_degree == max(params.domain_size - 1, params.radix_domain_size - params.domain_size) diff --git a/tests/test_coverage/test_transcript.py b/tests/test_coverage/test_transcript.py index e7504da..1769bc4 100644 --- a/tests/test_coverage/test_transcript.py +++ b/tests/test_coverage/test_transcript.py @@ -1,50 +1,50 @@ """Additional tests for transcript module to improve coverage.""" from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.transcript.transcript import Transcript +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript class TestTranscriptAdditional: - """Additional tests for Transcript class.""" + """Additional tests for FiatShamirTranscript class.""" def test_transcript_init(self): - """Test Transcript initialization.""" - transcript = Transcript(S_PRIME, b"test_transcript") + """Test FiatShamirTranscript initialization.""" + transcript = FiatShamirTranscript(S_PRIME, b"test_transcript") assert transcript is not None def test_transcript_label(self): """Test adding label to transcript.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.label(b"new_label") # Just verify it doesn't error def test_transcript_write(self): """Test writing data to transcript.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.write(b"some_data") # Just verify it doesn't error def test_transcript_write_bytes(self): """Test writing bytes to transcript.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.write_bytes(b"more_data_here") # Just verify it doesn't error def test_transcript_append(self): """Test appending data to transcript.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.append(b"appended_data") # Just verify it doesn't error def test_transcript_add_serialized(self): """Test add_serialized method.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.add_serialized(b"label", b"serialized_data") # Just verify it doesn't error def test_transcript_challenge(self): """Test getting challenge.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.append(b"some_data") challenge = transcript.challenge(b"challenge") @@ -54,7 +54,7 @@ def test_transcript_challenge(self): def test_transcript_read_reduce(self): """Test read_reduce method.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.write(b"data") result = transcript.read_reduce() @@ -64,7 +64,7 @@ def test_transcript_read_reduce(self): def test_transcript_separate(self): """Test separate method.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") transcript.write(b"data1") transcript.separate() transcript.write(b"data2") @@ -72,7 +72,7 @@ def test_transcript_separate(self): def test_transcript_get_constraints_aggregation_coeffs(self): """Test getting constraint aggregation coefficients.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") coeffs = transcript.get_constraints_aggregation_coeffs(3) @@ -83,7 +83,7 @@ def test_transcript_get_constraints_aggregation_coeffs(self): def test_transcript_get_evaluation_point(self): """Test getting evaluation point.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") points = transcript.get_evaluation_point(1) @@ -92,7 +92,7 @@ def test_transcript_get_evaluation_point(self): def test_transcript_get_kzg_aggregation_challenges(self): """Test getting KZG aggregation challenges.""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") challenges = transcript.get_kzg_aggregation_challenges(5) @@ -103,7 +103,7 @@ def test_transcript_get_kzg_aggregation_challenges(self): def test_transcript_large_write(self): """Test writing large data (>2^31 bytes would trigger chunking).""" - transcript = Transcript(S_PRIME, b"test") + transcript = FiatShamirTranscript(S_PRIME, b"test") # Write 100KB of data large_data = b"x" * 100000 transcript.write_bytes(large_data) diff --git a/tests/test_coverage/test_verify.py b/tests/test_coverage/test_verify.py index eab5de5..8d083b0 100644 --- a/tests/test_coverage/test_verify.py +++ b/tests/test_coverage/test_verify.py @@ -92,16 +92,6 @@ def test_divide_modular_inverse(self): assert verifier.divide(numerator, denominator) == expected - def test_legacy_methods_raise(self): - """Legacy methods should raise NotImplementedError.""" - verifier = Verify.__new__(Verify) - - with pytest.raises(NotImplementedError): - verifier.evaluation_of_quotient_poly_at_zeta() - - with pytest.raises(NotImplementedError): - verifier.evaluation_of_linearization_poly_at_zeta_omega() - def test_init_rejects_invalid_padding_rows(self): """Verify init validates padding_rows against domain size.""" proof = (0,) * 15 diff --git a/tests/test_verify_ring_sig.py b/tests/test_verify_ring_sig.py index 2c10562..792eead 100644 --- a/tests/test_verify_ring_sig.py +++ b/tests/test_verify_ring_sig.py @@ -7,10 +7,9 @@ # Add project root to path sys.path.insert(0, str(Path(__file__).parent.parent)) -from dot_ring import blst +from dot_ring import Bandersnatch, blst from dot_ring.ring_proof.constants import EVAL_DOMAINS -from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve -from dot_ring.ring_proof.pcs import srs +from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.utils import g2_to_blst from dot_ring.ring_proof.verify import Verify from tests.utils.rust_serde import ( @@ -23,6 +22,20 @@ ) +def add_bandersnatch_points( + point1: tuple[int | None, int | None], + point2: tuple[int | None, int | None], +) -> tuple[int | None, int | None]: + def to_point(point: tuple[int | None, int | None]): + x, y = point + if x is None or y is None: + return Bandersnatch.identity() + return Bandersnatch.point(int(x), int(y)) + + result = to_point(point1) + to_point(point2) + return result.x, result.y + + def parse_proof_from_json(proof_json: dict) -> tuple: proof = proof_json["proof"] @@ -106,7 +119,7 @@ def verify_vector(vector_path: Path) -> None: result_point = deserialize_bandersnatch_point(result_x_bytes, result_y_bytes) result_ark_bytes = result_x_bytes + result_y_bytes - result_plus_seed = TwistedEdwardCurve.add(result_point, seed_point) + result_plus_seed = add_bandersnatch_points(result_point, seed_point) proof_tuple, raw_bytes = parse_proof_from_json(proof_data) vk_bytes, fixed_cols = parse_verifier_key(proof_data["verifier_key"]["verification_key"]) @@ -122,38 +135,43 @@ def verify_vector(vector_path: Path) -> None: g2_0_blst = g2_to_blst(g2_0_affine) g2_1_blst = g2_to_blst(g2_1_affine) - original_g1 = srs.srs.blst_g1 - original_g2 = srs.srs.blst_g2 - srs.srs.blst_g1 = [g1_0_blst] + list(original_g1[1:]) - srs.srs.blst_g2 = [g2_0_blst, g2_1_blst] + list(original_g2[2:]) - - vk_uncompressed = ( - compressed_g1_to_uncompressed_bytes(vk_bytes[0:48]) - + compressed_g2_to_uncompressed_bytes(vk_bytes[48:144]) - + compressed_g2_to_uncompressed_bytes(vk_bytes[144:240]) - + compressed_g1_to_uncompressed_bytes(vk_bytes[240:288]) - + compressed_g1_to_uncompressed_bytes(vk_bytes[288:336]) - + compressed_g1_to_uncompressed_bytes(vk_bytes[336:384]) - ) - - quotient_compressed = bytes.fromhex(proof_data["proof"]["quotient_commitment"]) - quotient_uncompressed = compressed_g1_to_uncompressed_bytes(quotient_compressed) - raw_bytes["quotient_commitment_uncompressed"] = quotient_uncompressed - - verifier = Verify( - proof=proof_tuple, - vk=vk_uncompressed, - fixed_cols=fixed_cols, - rl_to_proove=result_ark_bytes, - rps=result_plus_seed, - seed_point=seed_point, - Domain=domain, - raw_proof_bytes=raw_bytes, - transcript_challenge=b"w3f-ring-proof-test", - padding_rows=padding_rows, - ) - - assert verifier.is_valid(), f"Verification failed for {vector_path.name}" + active_srs = KZG.srs + original_g1 = active_srs.blst_g1 + original_g2 = active_srs.blst_g2 + try: + active_srs.blst_g1 = [g1_0_blst] + list(original_g1[1:]) + active_srs.blst_g2 = [g2_0_blst, g2_1_blst] + list(original_g2[2:]) + + vk_uncompressed = ( + compressed_g1_to_uncompressed_bytes(vk_bytes[0:48]) + + compressed_g2_to_uncompressed_bytes(vk_bytes[48:144]) + + compressed_g2_to_uncompressed_bytes(vk_bytes[144:240]) + + compressed_g1_to_uncompressed_bytes(vk_bytes[240:288]) + + compressed_g1_to_uncompressed_bytes(vk_bytes[288:336]) + + compressed_g1_to_uncompressed_bytes(vk_bytes[336:384]) + ) + + quotient_compressed = bytes.fromhex(proof_data["proof"]["quotient_commitment"]) + quotient_uncompressed = compressed_g1_to_uncompressed_bytes(quotient_compressed) + raw_bytes["quotient_commitment_uncompressed"] = quotient_uncompressed + + verifier = Verify( + proof=proof_tuple, + vk=vk_uncompressed, + fixed_cols=fixed_cols, + rl_to_proove=result_ark_bytes, + rps=result_plus_seed, + seed_point=seed_point, + Domain=domain, + raw_proof_bytes=raw_bytes, + transcript_challenge=b"w3f-ring-proof-test", + padding_rows=padding_rows, + ) + + assert verifier.is_valid(), f"Verification failed for {vector_path.name}" + finally: + active_srs.blst_g1 = original_g1 + active_srs.blst_g2 = original_g2 def get_vector_paths() -> list[Path]: From 7d7391694e51f91feba197aff534dbffb4a5272e Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:38:56 +0530 Subject: [PATCH 23/33] clean pcs polynomial --- dot_ring/ring_proof/curve/bandersnatch.py | 28 -- dot_ring/ring_proof/curve/bls12_381.py | 1 - dot_ring/ring_proof/pcs/__init__.py | 4 +- dot_ring/ring_proof/pcs/bn254_kzg.py | 163 --------- .../ring_proof/pcs/bn254_serialization.py | 70 ---- dot_ring/ring_proof/pcs/bn254_srs.py | 87 ----- dot_ring/ring_proof/pcs/kzg.py | 214 +++++++---- dot_ring/ring_proof/pcs/protocol.py | 4 +- dot_ring/ring_proof/pcs/srs.py | 121 +++--- dot_ring/ring_proof/pcs/utils.py | 41 ++- dot_ring/ring_proof/polynomial/fft.py | 44 ++- .../ring_proof/polynomial/interpolation.py | 7 +- dot_ring/ring_proof/polynomial/ntt.pyx | 229 ++++++++---- dot_ring/ring_proof/polynomial/ops.py | 343 +++++++++++------- dot_ring/ring_proof/polynomial/poly_ops.pyx | 244 ------------- .../data/bn254-testing-2-9-uncompressed.bin | Bin 98640 -> 0 bytes tests/test_coverage/test_fft.py | 4 +- tests/test_coverage/test_kzg.py | 73 +--- tests/test_coverage/test_ops.py | 86 ++--- tests/test_coverage/test_pcs_utils.py | 12 +- tests/utils/rust_serde.py | 20 +- 21 files changed, 718 insertions(+), 1077 deletions(-) delete mode 100644 dot_ring/ring_proof/curve/bandersnatch.py delete mode 100644 dot_ring/ring_proof/curve/bls12_381.py delete mode 100644 dot_ring/ring_proof/pcs/bn254_kzg.py delete mode 100644 dot_ring/ring_proof/pcs/bn254_serialization.py delete mode 100644 dot_ring/ring_proof/pcs/bn254_srs.py delete mode 100644 dot_ring/ring_proof/polynomial/poly_ops.pyx delete mode 100644 dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin diff --git a/dot_ring/ring_proof/curve/bandersnatch.py b/dot_ring/ring_proof/curve/bandersnatch.py deleted file mode 100644 index 6e6a688..0000000 --- a/dot_ring/ring_proof/curve/bandersnatch.py +++ /dev/null @@ -1,28 +0,0 @@ -from typing import cast - -from dot_ring.curve.specs.bandersnatch import BandersnatchPoint -from dot_ring.curve.twisted_edwards.te_affine_point import TEAffinePoint - - -class TwistedEdwardCurve: - @staticmethod - def mul( - k: int, - point: tuple[int | tuple[int, int] | None, int | tuple[int, int] | None], - ) -> tuple[int | tuple[int, int] | None, int | tuple[int, int] | None]: - """Multiply a point by a scalar on Bandersnatch Elliptic Curve""" - res = BandersnatchPoint(cast(int, point[0]), cast(int, point[1])) * k - return res.x, res.y - - @staticmethod - def add( - point1: tuple[int | tuple[int, int] | None, int | tuple[int, int] | None], - point2: tuple[int | tuple[int, int] | None, int | tuple[int, int] | None], - ) -> tuple[int | tuple[int, int] | None, int | tuple[int, int] | None]: - """Point Addition for Two input Points on a Bandersnatch Elliptic Curve""" - x1, y1 = point1 - x2, y2 = point2 - p1 = cast(TEAffinePoint, BandersnatchPoint(cast(int, x1), cast(int, y1))) - p2 = cast(TEAffinePoint, BandersnatchPoint(cast(int, x2), cast(int, y2))) - res = cast(BandersnatchPoint, p1 + p2) # type: ignore[operator] - return res.x, res.y diff --git a/dot_ring/ring_proof/curve/bls12_381.py b/dot_ring/ring_proof/curve/bls12_381.py deleted file mode 100644 index d11c0ff..0000000 --- a/dot_ring/ring_proof/curve/bls12_381.py +++ /dev/null @@ -1 +0,0 @@ -# Either use the bls curve ops from helpers or define them here diff --git a/dot_ring/ring_proof/pcs/__init__.py b/dot_ring/ring_proof/pcs/__init__.py index 435e977..e8cb6e0 100644 --- a/dot_ring/ring_proof/pcs/__init__.py +++ b/dot_ring/ring_proof/pcs/__init__.py @@ -1,6 +1,4 @@ -from .bn254_kzg import BN254KZG -from .bn254_srs import BN254SRS from .kzg import KZG from .opening import Opening -__all__ = ["BN254KZG", "BN254SRS", "KZG", "Opening"] +__all__ = ["KZG", "Opening"] diff --git a/dot_ring/ring_proof/pcs/bn254_kzg.py b/dot_ring/ring_proof/pcs/bn254_kzg.py deleted file mode 100644 index 3c68867..0000000 --- a/dot_ring/ring_proof/pcs/bn254_kzg.py +++ /dev/null @@ -1,163 +0,0 @@ -from __future__ import annotations - -import secrets -from typing import Any - -import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] - -from .bn254_serialization import read_field, sqrt_mod_prime, write_field, y_flag -from .bn254_srs import BN254SRS -from .opening import Opening -from .utils import CoeffVector, Scalar - -BN254_G1Point = Any - - -def _poly_evaluate_mod(coeffs: CoeffVector, x: int, modulus: int) -> int: - result = 0 - for coeff in reversed(coeffs): - result = (result * x + int(coeff)) % modulus - return result - - -def _synthetic_div_mod(coeffs: CoeffVector, x: int, y: int, modulus: int) -> list[int]: - if len(coeffs) < 2: - return [0] - q = [0] * (len(coeffs) - 1) - carry = int(coeffs[-1]) % modulus - q[-1] = carry - for i in range(len(coeffs) - 2, 0, -1): - carry = (int(coeffs[i]) + carry * x) % modulus - q[i - 1] = carry - remainder = (int(coeffs[0]) + carry * x) % modulus - if remainder != y % modulus: - raise ValueError("inconsistent polynomial opening") - return q - - -class BN254KZG: - commitment_size = 32 - scalar_modulus = bn254.curve_order - srs = BN254SRS.default() - - @staticmethod - def normalize_g1(point: BN254_G1Point) -> tuple[int, int]: - if bn254.is_inf(point): - return (0, 0) - x, y = bn254.normalize(point) - return int(x), int(y) - - @classmethod - def compress_g1(cls, point: BN254_G1Point) -> bytes: - if bn254.is_inf(point): - return write_field(0, 0x40) - x, y = cls.normalize_g1(point) - return write_field(x, y_flag(y)) - - @classmethod - def serialize_g1_uncompressed(cls, point: BN254_G1Point) -> bytes: - if bn254.is_inf(point): - return write_field(0) + write_field(0, 0x40) - x, y = cls.normalize_g1(point) - return write_field(x) + write_field(y, y_flag(y)) - - @classmethod - def decompress_g1(cls, data: bytes) -> BN254_G1Point: - x, flag = read_field(data, flags=True) - if flag == 0x40: - if x != 0: - raise ValueError("invalid BN254 point at infinity") - return bn254.Z1 - rhs = (pow(x, 3, bn254.field_modulus) + 3) % bn254.field_modulus - y = sqrt_mod_prime(rhs, bn254.field_modulus) - if y_flag(y) != flag: - y = (-y) % bn254.field_modulus - point = (bn254.FQ(x), bn254.FQ(y), bn254.FQ(1)) - if not bn254.is_on_curve(point, bn254.b): - raise ValueError("invalid BN254 G1 point") - return point - - @classmethod - def commit(cls, coeffs: CoeffVector) -> BN254_G1Point: - if len(coeffs) > len(cls.srs.g1): - raise ValueError("polynomial degree exceeds SRS size") - acc = bn254.Z1 - for coeff, point in zip(coeffs, cls.srs.g1, strict=False): - scalar = int(coeff) % cls.scalar_modulus - if scalar: - acc = bn254.add(acc, bn254.multiply(point, scalar)) - return acc - - @classmethod - def msm_g1(cls, points: list[BN254_G1Point], scalars: list[int]) -> BN254_G1Point: - acc = bn254.Z1 - for point, scalar in zip(points, scalars, strict=False): - scalar = int(scalar) % cls.scalar_modulus - if scalar: - acc = bn254.add(acc, bn254.multiply(point, scalar)) - return acc - - @classmethod - def open(cls, coeffs: CoeffVector, x: Scalar) -> Opening: - point = int(x) % cls.scalar_modulus - y = _poly_evaluate_mod(coeffs, point, cls.scalar_modulus) - q = _synthetic_div_mod(coeffs, point, y, cls.scalar_modulus) - return Opening(cls.commit(q), y) - - @classmethod - def verify(cls, commitment: BN254_G1Point, proof: BN254_G1Point, point: Scalar, value: Scalar) -> bool: - z = int(point) % cls.scalar_modulus - y = int(value) % cls.scalar_modulus - g1_gen = cls.srs.g1[0] - g2_gen = cls.srs.g2[0] - g2_tau = cls.srs.g2[1] - commitment_term = bn254.add(commitment, bn254.neg(bn254.multiply(g1_gen, y))) - tau_term = bn254.add(g2_tau, bn254.neg(bn254.multiply(g2_gen, z))) - lhs = bn254.final_exponentiate(bn254.pairing(g2_gen, commitment_term, final_exponentiate=False)) - rhs = bn254.final_exponentiate(bn254.pairing(tau_term, proof, final_exponentiate=False)) - return bool(lhs == rhs) - - @classmethod - def batch_verify(cls, verifications: list[tuple[BN254_G1Point, BN254_G1Point, Scalar, Scalar]]) -> bool: - if not verifications: - return True - - if len(verifications) == 1: - return cls.verify(*verifications[0]) - - order = cls.scalar_modulus - coeffs = [1] - for _ in range(len(verifications) - 1): - coeff = 0 - while coeff == 0: - coeff = secrets.randbelow(order) - coeffs.append(coeff) - - g1_gen = cls.srs.g1[0] - g2_gen = cls.srs.g2[0] - g2_tau = cls.srs.g2[1] - lhs_points: list[BN254_G1Point] = [] - lhs_scalars: list[int] = [] - rhs_points: list[BN254_G1Point] = [] - rhs_scalars: list[int] = [] - sum_v = 0 - - for coeff, (commitment, proof, point, value) in zip(coeffs, verifications, strict=False): - z = int(point) % order - y = int(value) % order - lhs_points.append(commitment) - lhs_scalars.append(coeff) - lhs_points.append(proof) - lhs_scalars.append((coeff * z) % order) - rhs_points.append(proof) - rhs_scalars.append(coeff) - sum_v = (sum_v + coeff * y) % order - - lhs_points.append(g1_gen) - lhs_scalars.append((-sum_v) % order) - - lhs_point = cls.msm_g1(lhs_points, lhs_scalars) - rhs_point = cls.msm_g1(rhs_points, rhs_scalars) - lhs = bn254.final_exponentiate(bn254.pairing(g2_gen, lhs_point, final_exponentiate=False)) - rhs = bn254.final_exponentiate(bn254.pairing(g2_tau, rhs_point, final_exponentiate=False)) - return bool(lhs == rhs) diff --git a/dot_ring/ring_proof/pcs/bn254_serialization.py b/dot_ring/ring_proof/pcs/bn254_serialization.py deleted file mode 100644 index 0c4a843..0000000 --- a/dot_ring/ring_proof/pcs/bn254_serialization.py +++ /dev/null @@ -1,70 +0,0 @@ -from __future__ import annotations - -import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] - - -def sqrt_mod_prime(n: int, prime: int) -> int: - if n == 0: - return 0 - if pow(n, (prime - 1) // 2, prime) != 1: - raise ValueError("point is not on BN254 G1") - if prime % 4 == 3: - return pow(n, (prime + 1) // 4, prime) - q = prime - 1 - s = 0 - while q % 2 == 0: - s += 1 - q //= 2 - z = 2 - while pow(z, (prime - 1) // 2, prime) != prime - 1: - z += 1 - m = s - c = pow(z, q, prime) - x = pow(n, (q + 1) // 2, prime) - t = pow(n, q, prime) - while t != 1: - i = 1 - t2i = (t * t) % prime - while i < m: - if t2i == 1: - break - t2i = (t2i * t2i) % prime - i += 1 - b = pow(c, 1 << (m - i - 1), prime) - x = (x * b) % prime - t = (t * b * b) % prime - c = (b * b) % prime - m = i - return x - - -def read_field(data: bytes, *, flags: bool = False) -> tuple[int, int]: - if len(data) != 32: - raise ValueError(f"invalid BN254 field length: expected 32, got {len(data)}") - masked = bytearray(data) - flag = 0 - if flags: - flag = masked[-1] & 0xC0 - masked[-1] &= 0x3F - if flag == 0xC0: - raise ValueError("invalid BN254 point flags") - value = int.from_bytes(masked, "little") - if value >= bn254.field_modulus: - raise ValueError("invalid BN254 field element") - return value, flag - - -def write_field(value: int, flag: int = 0) -> bytes: - if value < 0 or value >= bn254.field_modulus: - raise ValueError("invalid BN254 field element") - if flag not in (0, 0x40, 0x80): - raise ValueError("invalid BN254 point flags") - data = bytearray(value.to_bytes(32, "little")) - if data[-1] & 0xC0: - raise ValueError("BN254 field element overlaps point flags") - data[-1] |= flag - return bytes(data) - - -def y_flag(y: int) -> int: - return 0 if y <= (-y % bn254.field_modulus) else 0x80 diff --git a/dot_ring/ring_proof/pcs/bn254_srs.py b/dot_ring/ring_proof/pcs/bn254_srs.py deleted file mode 100644 index 91b70ec..0000000 --- a/dot_ring/ring_proof/pcs/bn254_srs.py +++ /dev/null @@ -1,87 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from functools import lru_cache -from pathlib import Path -from typing import Any - -import py_ecc.optimized_bn128 as bn254 # type: ignore[import-untyped] - -from .bn254_serialization import read_field - -BN254_G1Point = Any -BN254_G2Point = Any - - -@dataclass() -class BN254SRS: - g1: list[BN254_G1Point] - g2: list[BN254_G2Point] - g1_points: list[tuple[int, int]] - g2_points: list[tuple[tuple[int, int], tuple[int, int]]] - g1_uncompressed: list[bytes] - g2_uncompressed: list[bytes] - - @classmethod - def from_file(cls) -> BN254SRS: - filename = Path(__file__).resolve().parent.parent.parent / "vrf" / "data" / "bn254-testing-2-9-uncompressed.bin" - data = filename.read_bytes() - if len(data) < 8: - raise ValueError("BN254 SRS file too short") - g1_count = int.from_bytes(data[:8], "little") - offset = 8 - - g1: list[BN254_G1Point] = [] - g1_points: list[tuple[int, int]] = [] - g1_uncompressed: list[bytes] = [] - for i in range(g1_count): - raw = data[offset : offset + 64] - if len(raw) != 64: - raise ValueError(f"unexpected end of BN254 SRS while reading G1 point {i}") - offset += 64 - x, _ = read_field(raw[:32]) - y, _ = read_field(raw[32:], flags=True) - point = (bn254.FQ(x), bn254.FQ(y), bn254.FQ(1)) - if not bn254.is_on_curve(point, bn254.b): - raise ValueError(f"invalid BN254 SRS G1 point {i}") - g1.append(point) - g1_points.append((x, y)) - g1_uncompressed.append(raw) - - if len(data) < offset + 8: - raise ValueError("BN254 SRS file missing G2 count") - g2_count = int.from_bytes(data[offset : offset + 8], "little") - offset += 8 - - g2: list[BN254_G2Point] = [] - g2_points: list[tuple[tuple[int, int], tuple[int, int]]] = [] - g2_uncompressed: list[bytes] = [] - for i in range(g2_count): - raw = data[offset : offset + 128] - if len(raw) != 128: - raise ValueError(f"unexpected end of BN254 SRS while reading G2 point {i}") - offset += 128 - x0, _ = read_field(raw[0:32]) - x1, _ = read_field(raw[32:64]) - y0, _ = read_field(raw[64:96]) - y1, _ = read_field(raw[96:128], flags=True) - point = (bn254.FQ2([x0, x1]), bn254.FQ2([y0, y1]), bn254.FQ2([1, 0])) - if not bn254.is_on_curve(point, bn254.b2): - raise ValueError(f"invalid BN254 SRS G2 point {i}") - g2.append(point) - g2_points.append(((x1, x0), (y1, y0))) - g2_uncompressed.append(raw) - - return cls( - g1=g1, - g2=g2[:2], - g1_points=g1_points, - g2_points=g2_points, - g1_uncompressed=g1_uncompressed, - g2_uncompressed=g2_uncompressed[:2], - ) - - @staticmethod - @lru_cache(maxsize=1) - def default() -> BN254SRS: - return BN254SRS.from_file() diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index bd8a793..56f9f9c 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -1,5 +1,6 @@ from __future__ import annotations +import hashlib import secrets from typing import Any, cast @@ -7,45 +8,104 @@ import dot_ring.blst as _blst # type: ignore[import-untyped] -from ..polynomial.poly_ops import poly_evaluate_single from .opening import Opening from .pairing import blst_final_verify, blst_miller_loop -from .srs import srs -from .utils import CoeffVector, Scalar, g1_to_blst, synthetic_div +from .srs import SRS, srs +from .utils import ( + CoeffVector, + LinearPcsVerification, + PcsVerification, + Scalar, + g1_to_blst, + synthetic_div_with_eval, +) blst = cast(Any, _blst) Point_G1 = Any -# Helper functions for blst.P1 arithmetic -def p1_scalar_mul(p: Any, scalar: int) -> Any: - """Multiply blst.P1 point by scalar""" - return p.dup().mult(scalar) - - -def p1_add(a: Any, b: Any) -> Any: - """Add two blst.P1 points""" - return a.dup().add(b) - - -def p1_neg(p: Any) -> Any: - """Negate a blst.P1 point""" - return p.dup().neg() - - -def p2_scalar_mul(p: Any, scalar: int) -> Any: - """Multiply blst.P2 point by scalar""" - return p.dup().mult(scalar) - - -def p2_add(a: Any, b: Any) -> Any: - """Add two blst.P2 points""" - return a.dup().add(b) - - -def p2_neg(p: Any) -> Any: - """Negate a blst.P2 point""" - return p.dup().neg() +def _add_aggregated_g1_term( + points_by_id: dict[int, Any], + scalars_by_id: dict[int, int], + point: Any, + scalar: int, + order: int, +) -> None: + scalar %= order + if scalar == 0: + return + point_id = id(point) + if point_id in scalars_by_id: + scalars_by_id[point_id] = (scalars_by_id[point_id] + scalar) % order + else: + points_by_id[point_id] = point + scalars_by_id[point_id] = scalar + + +def _aggregated_g1_vectors(points_by_id: dict[int, Any], scalars_by_id: dict[int, int]) -> tuple[list[Any], list[int]]: + points: list[Any] = [] + scalars: list[int] = [] + for point_id, point in points_by_id.items(): + scalar = scalars_by_id[point_id] + if scalar: + points.append(point) + scalars.append(scalar) + return points, scalars + + +def _aggregate_linear_batch( + verifications: list[LinearPcsVerification], + coeffs: list[int], + g1_gen: Any, + order: int, +) -> tuple[list[Any], list[int], list[Any], list[int]]: + lhs_points_by_id: dict[int, Any] = {} + lhs_scalars_by_id: dict[int, int] = {} + rhs_points_by_id: dict[int, Any] = {} + rhs_scalars_by_id: dict[int, int] = {} + + sum_v = 0 + for coeff, verification in zip(coeffs, verifications, strict=False): + for commitment, scalar in verification.commitment_terms: + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, commitment, coeff * scalar, order) + + proof = verification.proof + sum_v = (sum_v + coeff * verification.value) % order + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, proof, coeff * verification.point, order) + _add_aggregated_g1_term(rhs_points_by_id, rhs_scalars_by_id, proof, coeff, order) + + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, g1_gen, -sum_v, order) + + lhs_points, lhs_scalars = _aggregated_g1_vectors(lhs_points_by_id, lhs_scalars_by_id) + rhs_points, rhs_scalars = _aggregated_g1_vectors(rhs_points_by_id, rhs_scalars_by_id) + return lhs_points, lhs_scalars, rhs_points, rhs_scalars + + +def _random_nonzero_coefficients(count: int, order: int) -> list[int]: + if count <= 0: + return [] + coeffs = [1] + if count == 1: + return coeffs + + byte_len = (order.bit_length() + 7) // 8 + limit = (1 << (8 * byte_len)) - ((1 << (8 * byte_len)) % order) + seed = secrets.token_bytes(32) + counter = 0 + while len(coeffs) < count: + remaining = count - len(coeffs) + raw = hashlib.shake_256(seed + counter.to_bytes(8, "little")).digest(byte_len * remaining * 2) + counter += 1 + for offset in range(0, len(raw), byte_len): + candidate = int.from_bytes(raw[offset : offset + byte_len], "big") + if candidate >= limit: + continue + coeff = candidate % order + if coeff: + coeffs.append(coeff) + if len(coeffs) == count: + break + return coeffs class KZG: @@ -53,6 +113,11 @@ class KZG: scalar_modulus = bls.curve_order srs = srs + @classmethod + def ensure_srs_size(cls, max_degree: int) -> None: + if max_degree >= len(cls.srs.g1): + cls.srs = SRS.default(max_degree) + @staticmethod def normalize_g1(point: Point_G1) -> tuple[int, int]: if isinstance(point, (blst.P1, blst.P1_Affine)): @@ -93,6 +158,7 @@ def commit(cls, coeffs: CoeffVector) -> Any: Returns: G1Point: Commitment point """ + srs = cls.srs if len(coeffs) > len(srs.g1): raise ValueError("polynomial degree exceeds SRS size") @@ -118,8 +184,7 @@ def open(cls, coeffs: CoeffVector, x: Scalar) -> Opening: Returns: Opening: Opening proof and evaluation value """ - y = poly_evaluate_single(coeffs, x, bls.curve_order) - q = synthetic_div(coeffs, x, y) + q, y = synthetic_div_with_eval(coeffs, x) proof = cls.commit(q) return Opening(proof, y) @@ -146,17 +211,16 @@ def verify( comm_blst = g1_to_blst(commitment) proof_blst = g1_to_blst(proof) + srs = cls.srs g1_gen = srs.blst_g1[0] # [1]G1 g2_gen = srs.blst_g2[0] # [1]G2 g2_tau = srs.blst_g2[1] # [tau]G2 - # Term 1: commitment - [value]G1 - val_g1 = p1_scalar_mul(g1_gen, value) - comm_term = comm_blst.dup().add(p1_neg(val_g1)) + val_g1 = g1_gen.dup().mult(value) + comm_term = comm_blst.dup().add(val_g1.dup().neg()) - # Term 2: [tau]G2 - [point]G2 point_g2 = g2_gen.dup().mult(point) - tau_term = g2_tau.dup().add(p2_neg(point_g2)) + tau_term = g2_tau.dup().add(point_g2.dup().neg()) lhs = blst_miller_loop(comm_term, g2_gen) rhs = blst_miller_loop(proof_blst, tau_term) @@ -166,7 +230,7 @@ def verify( @classmethod def batch_verify( cls, - verifications: list[tuple[Point_G1, Point_G1, Scalar, Scalar]], + verifications: list[PcsVerification], ) -> bool: """ Batch verify multiple KZG proofs using random linear combination. @@ -188,14 +252,10 @@ def batch_verify( order = bls.curve_order - # Generate random coefficients for batching (first coefficient fixed to 1) - coeffs = [1] - for _ in range(len(verifications) - 1): - coeff = 0 - while coeff == 0: - coeff = secrets.randbelow(order) - coeffs.append(coeff) + # Generate random coefficients for batching (first coefficient fixed to 1). + coeffs = _random_nonzero_coefficients(len(verifications), order) + srs = cls.srs g1_gen = srs.blst_g1[0] # [1]G1 g2_gen = srs.blst_g2[0] # [1]G2 g2_tau = srs.blst_g2[1] # [tau]G2 @@ -204,11 +264,10 @@ def batch_verify( # LHS = sum(coeff_i * C_i) - (sum(coeff_i * v_i)) * G1 + sum(coeff_i * z_i * proof_i) # RHS = sum(coeff_i * proof_i) - lhs_points = [] - lhs_scalars = [] - - rhs_points = [] - rhs_scalars = [] + lhs_points_by_id: dict[int, Any] = {} + lhs_scalars_by_id: dict[int, int] = {} + rhs_points_by_id: dict[int, Any] = {} + rhs_scalars_by_id: dict[int, int] = {} sum_v = 0 @@ -217,25 +276,58 @@ def batch_verify( proof_blst = g1_to_blst(proof) # LHS terms - lhs_points.append(comm_blst) - lhs_scalars.append(coeff) + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, comm_blst, coeff, order) sum_v = (sum_v + coeff * value) % order - coeff_z = (coeff * point) % order - lhs_points.append(proof_blst) - lhs_scalars.append(coeff_z) + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, proof_blst, coeff * point, order) # RHS terms - rhs_points.append(proof_blst) - rhs_scalars.append(coeff) + _add_aggregated_g1_term(rhs_points_by_id, rhs_scalars_by_id, proof_blst, coeff, order) # Add G1 term to LHS - lhs_points.append(g1_gen) - lhs_scalars.append((-sum_v) % order) + _add_aggregated_g1_term(lhs_points_by_id, lhs_scalars_by_id, g1_gen, -sum_v, order) + lhs_points, lhs_scalars = _aggregated_g1_vectors(lhs_points_by_id, lhs_scalars_by_id) + rhs_points, rhs_scalars = _aggregated_g1_vectors(rhs_points_by_id, rhs_scalars_by_id) lhs_point = blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(lhs_points), lhs_scalars) + rhs_point = blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(rhs_points), rhs_scalars) + + lhs = blst_miller_loop(lhs_point, g2_gen) + rhs = blst_miller_loop(rhs_point, g2_tau) + + return bool(blst_final_verify(lhs, rhs)) + @classmethod + def batch_verify_linear_preconverted( + cls, + verifications: list[LinearPcsVerification], + ) -> bool: + """ + Batch verify KZG openings whose commitments are linear combinations. + + The verifier prepares BLST P1 objects up front, so this path folds + linear commitment terms into the final large batch MSM without first + materializing small commitments. + """ + if not verifications: + return True + + order = bls.curve_order + coeffs = _random_nonzero_coefficients(len(verifications), order) + + srs = cls.srs + g1_gen = srs.blst_g1[0] + g2_gen = srs.blst_g2[0] + g2_tau = srs.blst_g2[1] + + lhs_points, lhs_scalars, rhs_points, rhs_scalars = _aggregate_linear_batch( + verifications, + coeffs, + g1_gen, + order, + ) + lhs_point = blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(lhs_points), lhs_scalars) rhs_point = blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(rhs_points), rhs_scalars) lhs = blst_miller_loop(lhs_point, g2_gen) diff --git a/dot_ring/ring_proof/pcs/protocol.py b/dot_ring/ring_proof/pcs/protocol.py index 13c9131..bff5ee1 100644 --- a/dot_ring/ring_proof/pcs/protocol.py +++ b/dot_ring/ring_proof/pcs/protocol.py @@ -2,7 +2,7 @@ from typing import Any, ClassVar, Protocol -from .utils import CoeffVector, Scalar +from .utils import CoeffVector, PcsVerification, Scalar G1Commitment = object @@ -37,4 +37,4 @@ def open(cls, coeffs: CoeffVector, x: Scalar) -> Any: ... def verify(cls, commitment: G1Commitment, proof: G1Commitment, point: Scalar, value: Scalar) -> bool: ... @classmethod - def batch_verify(cls, verifications: list[tuple[G1Commitment, G1Commitment, Scalar, Scalar]]) -> bool: ... + def batch_verify(cls, verifications: list[PcsVerification]) -> bool: ... diff --git a/dot_ring/ring_proof/pcs/srs.py b/dot_ring/ring_proof/pcs/srs.py index 5750d90..484393b 100644 --- a/dot_ring/ring_proof/pcs/srs.py +++ b/dot_ring/ring_proof/pcs/srs.py @@ -1,5 +1,6 @@ from __future__ import annotations +import os from collections.abc import Sequence from dataclasses import dataclass from functools import lru_cache @@ -13,50 +14,78 @@ from .utils import g1_to_blst, g2_to_blst -def read_srs_file() -> tuple[list[tuple[int, int]], list[tuple[tuple[int, int], tuple[int, int]]]]: +def _candidate_srs_files(min_g1_count: int) -> list[Path]: base_dir = Path(__file__).resolve().parent - filename = base_dir.parent.parent / "vrf" / "data" / "bls12-381-srs-2-11-uncompressed-zcash.bin" + candidates = [] + env_path = os.environ.get("DOT_RING_BLS12_381_SRS") + if env_path: + candidates.append(Path(env_path)) + candidates.append(base_dir.parent.parent / "vrf" / "data" / "bls12-381-srs-2-11-uncompressed-zcash.bin") + candidates.append(base_dir.parent.parent / "vrf" / "data" / "bls12-381-srs-2-16-uncompressed-zcash.bin") + + workspace_root = base_dir.parents[3] if len(base_dir.parents) > 3 else None + if workspace_root is not None: + candidates.append(workspace_root / "docs" / "bandersnatch-vrf-spec" / "assets" / "srs" / "zcash-srs-2-16-uncompressed.bin") + candidates.append(workspace_root / "ark-vrf" / "data" / "srs" / "bls12-381-srs-2-16-uncompressed-zcash.bin") + + usable = [] + for candidate in candidates: + if not candidate.exists(): + continue + with open(candidate, "rb") as f: + header = f.read(8) + if len(header) == 8 and int.from_bytes(header, byteorder="little") >= min_g1_count: + usable.append(candidate) + return usable + + +def read_srs_file(g1_limit: int | None = None) -> tuple[list[tuple[int, int]], list[tuple[tuple[int, int], tuple[int, int]]]]: + min_g1_count = g1_limit or 1 + candidates = _candidate_srs_files(min_g1_count) + if not candidates: + raise ValueError(f"no BLS12-381 SRS file with at least {min_g1_count} G1 points is available") + + filename = candidates[0] with open(filename, "rb") as f: - data = f.read() - - # The first 8 bytes form a little-endian uint64 that gives the number of G1 elements. - if len(data) < 8: - raise ValueError("File too short to contain header.") - header = data[:8] - g1_count = int.from_bytes(header, byteorder="little") - - offset = 8 - G1_POINT_SIZE = 96 # 2 * 48-byte field elements. - G1_points = [] - for i in range(g1_count): - point_bytes = data[offset : offset + G1_POINT_SIZE] - if len(point_bytes) != G1_POINT_SIZE: - raise ValueError(f"Unexpected end-of-file when reading G1 point {i}.") - x_bytes = point_bytes[:48] - y_bytes = point_bytes[48:] - x = int.from_bytes(x_bytes, byteorder="big") - y = int.from_bytes(y_bytes, byteorder="big") - G1_points.append((x, y)) - offset += G1_POINT_SIZE - - if len(data) < offset + 8: - raise ValueError("File too short to contain G2 vector length header.") - - g2_count = int.from_bytes(data[offset : offset + 8], byteorder="little") - offset += 8 - - G2_POINT_SIZE = 192 # 2 coordinates, each with 2*48 bytes. - G2_points = [] - for i in range(g2_count): - point_bytes = data[offset : offset + G2_POINT_SIZE] - if len(point_bytes) != G2_POINT_SIZE: - raise ValueError(f"Unexpected end-of-file when reading G2 point {i}.") - x0 = int.from_bytes(point_bytes[0:48], byteorder="big") - x1 = int.from_bytes(point_bytes[48:96], byteorder="big") - y0 = int.from_bytes(point_bytes[96:144], byteorder="big") - y1 = int.from_bytes(point_bytes[144:192], byteorder="big") - G2_points.append(((x1, x0), (y1, y0))) - offset += G2_POINT_SIZE + header = f.read(8) + # The first 8 bytes form a little-endian uint64 that gives the number of G1 elements. + if len(header) < 8: + raise ValueError("File too short to contain header.") + g1_count = int.from_bytes(header, byteorder="little") + read_g1_count = g1_count if g1_limit is None else min(g1_limit, g1_count) + + G1_POINT_SIZE = 96 # 2 * 48-byte field elements. + G1_points = [] + for i in range(read_g1_count): + point_bytes = f.read(G1_POINT_SIZE) + if len(point_bytes) != G1_POINT_SIZE: + raise ValueError(f"Unexpected end-of-file when reading G1 point {i}.") + x_bytes = point_bytes[:48] + y_bytes = point_bytes[48:] + x = int.from_bytes(x_bytes, byteorder="big") + y = int.from_bytes(y_bytes, byteorder="big") + G1_points.append((x, y)) + + f.seek(8 + g1_count * G1_POINT_SIZE) + g2_count_raw = f.read(8) + if len(g2_count_raw) < 8: + raise ValueError("File too short to contain G2 vector length header.") + + g2_count = int.from_bytes(g2_count_raw, byteorder="little") + if g2_count < 2: + raise ValueError("SRS file must contain at least two G2 points") + + G2_POINT_SIZE = 192 # 2 coordinates, each with 2*48 bytes. + G2_points = [] + for i in range(2): + point_bytes = f.read(G2_POINT_SIZE) + if len(point_bytes) != G2_POINT_SIZE: + raise ValueError(f"Unexpected end-of-file when reading G2 point {i}.") + x0 = int.from_bytes(point_bytes[0:48], byteorder="big") + x1 = int.from_bytes(point_bytes[48:96], byteorder="big") + y0 = int.from_bytes(point_bytes[96:144], byteorder="big") + y1 = int.from_bytes(point_bytes[144:192], byteorder="big") + G2_points.append(((x1, x0), (y1, y0))) return G1_points, G2_points @@ -102,18 +131,16 @@ def _to_jacobian_g2(cls, pt: tuple | list) -> G2Point: @classmethod def from_loaded(cls, max_deg: int) -> SRS: - g1_points, g2_points = read_srs_file() - - if max_deg >= len(g1_points): - raise ValueError("polynomial degree exceeds available SRS length") + g1_points, g2_points = read_srs_file(max_deg + 1) g1_jac = [cls._to_jacobian_g1(p) for p in g1_points[: max_deg + 1]] # G2 only needs two powers: 1 and Tau g2_jac = [cls._to_jacobian_g2(p) for p in g2_points[:2]] return cls(g1_jac, g2_jac, g1_points=g1_points, g2_points=g2_points) + # Global bounded cache: loaded SRS objects are immutable and keyed by max degree. @staticmethod - @lru_cache(maxsize=4) + @lru_cache(maxsize=2) def default(max_deg: int = 6144) -> SRS: return SRS.from_loaded(max_deg) diff --git a/dot_ring/ring_proof/pcs/utils.py b/dot_ring/ring_proof/pcs/utils.py index 4fc5493..fdced91 100644 --- a/dot_ring/ring_proof/pcs/utils.py +++ b/dot_ring/ring_proof/pcs/utils.py @@ -1,3 +1,5 @@ +from typing import Any, NamedTuple, TypeAlias + import py_ecc.optimized_bls12_381 as bls from py_ecc.bls import point_compression @@ -5,19 +7,52 @@ Scalar = int CoeffVector = list[Scalar] +G1Term: TypeAlias = tuple[Any, Scalar] + + +class PcsVerification(NamedTuple): + commitment: Any + proof: Any + point: Scalar + value: Scalar + + +class LinearPcsVerification(NamedTuple): + commitment_terms: tuple[G1Term, ...] + proof: Any + point: Scalar + value: Scalar + + +def pcs_compress_g1(pcs: Any, point: Any) -> bytes: + return pcs.compress_g1(point) + + +def pcs_decompress_g1(pcs: Any, data: bytes) -> Any: + return pcs.decompress_g1(data) + + +def pcs_transcript_g1(pcs: Any, point: Any) -> Any: + return pcs.serialize_g1_uncompressed(point) def synthetic_div(poly: CoeffVector, x: Scalar, y: Scalar) -> CoeffVector: """Return q(X) such that f(X)−y = (X−x)·q(X). Checks remainder.""" + q, rem = synthetic_div_with_eval(poly, x) + if rem != y: + raise ValueError("point/value pair inconsistent with polynomial") + return q + + +def synthetic_div_with_eval(poly: CoeffVector, x: Scalar) -> tuple[CoeffVector, Scalar]: + """Return quotient by ``X-x`` and ``f(x)`` in one Horner pass.""" n = len(poly) q = [0] * (n - 1) rem = poly[-1] for i in range(n - 2, -1, -1): q[i] = rem rem = (rem * x + poly[i]) % bls.curve_order - if rem != y: - raise ValueError("point/value pair inconsistent with polynomial") - return q + return q, rem def g1_to_blst(p: tuple | blst.P1 | blst.P1_Affine) -> blst.P1: diff --git a/dot_ring/ring_proof/polynomial/fft.py b/dot_ring/ring_proof/polynomial/fft.py index 85dcce3..8e5cde6 100644 --- a/dot_ring/ring_proof/polynomial/fft.py +++ b/dot_ring/ring_proof/polynomial/fft.py @@ -7,9 +7,8 @@ from functools import lru_cache -# Cython-based FTT from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.polynomial.ntt import ntt_in_place +from dot_ring.ring_proof.polynomial.ntt import BlsScalarNTTPlan @lru_cache(maxsize=1024) @@ -69,6 +68,17 @@ def _get_roots(n: int, omega: int, prime: int) -> list[int]: return powers +def _use_native_ntt(prime: int) -> bool: + return prime == S_PRIME + + +@lru_cache(maxsize=1024) +def _get_native_ntt_plan(n: int, omega: int, prime: int): + if not _use_native_ntt(prime): + raise ValueError("native NTT is only available for the BLS12-381 scalar field") + return BlsScalarNTTPlan(_get_twiddle_factors(n, omega, prime), _get_bit_reverse(n)) + + def _fft_in_place(coeffs: list[int], omega: int, prime: int) -> None: """In-place Cooley-Tukey. @@ -81,7 +91,7 @@ def _fft_in_place(coeffs: list[int], omega: int, prime: int) -> None: if n == 1: return - if prime != S_PRIME: + if not _use_native_ntt(prime): rev = _get_bit_reverse(n) for i, j in enumerate(rev): if i < j: @@ -102,11 +112,27 @@ def _fft_in_place(coeffs: list[int], omega: int, prime: int) -> None: m <<= 1 return - rev = _get_bit_reverse(n) - twiddles = _get_twiddle_factors(n, omega, prime) + _get_native_ntt_plan(n, omega, prime).transform(coeffs) - ntt_in_place(coeffs, twiddles, rev, prime) - return + +def _fft_in_place_scaled(coeffs: list[int], omega: int, prime: int, scale: int) -> None: + """In-place Cooley-Tukey followed by a scalar multiply on every output.""" + if scale == 1: + _fft_in_place(coeffs, omega, prime) + return + + n = len(coeffs) + if n == 1: + coeffs[0] = (coeffs[0] * scale) % prime + return + + if not _use_native_ntt(prime): + _fft_in_place(coeffs, omega, prime) + for i, value in enumerate(coeffs): + coeffs[i] = (value * scale) % prime + return + + _get_native_ntt_plan(n, omega, prime).transform_scaled(coeffs, scale) def inverse_fft(values: list[int], omega: int, prime: int) -> list[int]: @@ -123,9 +149,9 @@ def inverse_fft(values: list[int], omega: int, prime: int) -> list[int]: n = len(values) inv_omega = pow(omega, -1, prime) coeffs = values[:] - _fft_in_place(coeffs, inv_omega, prime) inv_n = pow(n, -1, prime) - return [(c * inv_n) % prime for c in coeffs] + _fft_in_place_scaled(coeffs, inv_omega, prime, inv_n) + return coeffs def evaluate_poly_over_domain(poly: list[int], domain: list[int], omega: int, prime: int) -> list[int]: diff --git a/dot_ring/ring_proof/polynomial/interpolation.py b/dot_ring/ring_proof/polynomial/interpolation.py index ba66420..cbacac1 100644 --- a/dot_ring/ring_proof/polynomial/interpolation.py +++ b/dot_ring/ring_proof/polynomial/interpolation.py @@ -1,4 +1,4 @@ -from dot_ring.ring_proof.polynomial.fft import _fft_in_place +from dot_ring.ring_proof.polynomial.fft import _fft_in_place, _fft_in_place_scaled def is_power_of_two(n: int) -> bool: @@ -26,9 +26,10 @@ def fft(a: list[int], omega: int, p: int) -> list[int]: # coeffs to evaluation def poly_interpolate_fft(a: list[int], omega: int, p: int) -> list[int]: # funcs like inverse_fft from evals to poly coeffs n = len(a) omega_inv = modinv(omega, p) - y = fft(a, omega_inv, p) n_inv = modinv(n, p) - return [(val * n_inv) % p for val in y] + coeffs = list(a) + _fft_in_place_scaled(coeffs, omega_inv, p, n_inv) + return coeffs def next_power_of_two(n: int) -> int: diff --git a/dot_ring/ring_proof/polynomial/ntt.pyx b/dot_ring/ring_proof/polynomial/ntt.pyx index 23c542f..9bd0a38 100644 --- a/dot_ring/ring_proof/polynomial/ntt.pyx +++ b/dot_ring/ring_proof/polynomial/ntt.pyx @@ -3,92 +3,161 @@ # cython: wraparound=False # cython: cdivision=True # cython: initializedcheck=False -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: initializedcheck=False - from libc.stdlib cimport malloc, free -from dot_ring.curve.native_field.scalar cimport Scalar, bls_scalar_t, bls_scalar_ntt_round +from libc.stdint cimport uint8_t +from dot_ring.curve.native_field.scalar cimport ( + Scalar, + bls_scalar_from_mont, + bls_scalar_mul_mont, + bls_scalar_t, + bls_scalar_ntt_round, + bls_scalar_to_bytes, +) cdef extern from "stdlib.h": int posix_memalign(void **memptr, size_t alignment, size_t size) -def ntt_in_place(list coeffs, list twiddles, list rev, object prime): - """ - Complete in-place NTT using precomputed twiddles and bit-reverse indices. - - Args: - coeffs: Coefficient vector (modified in-place) - twiddles: List of stage twiddle factors - rev: Bit-reverse permutation indices - prime: Field modulus - """ - cdef: - Py_ssize_t n = len(coeffs) - Py_ssize_t i, stage, m, half_m - bls_scalar_t *coeffs_c - bls_scalar_t *twiddles_c - list stage_twiddles_list - object val - Scalar s - void *ptr = NULL - - if n == 1: - return - - # Allocate memory for coefficients (32-byte aligned for AVX2) - if posix_memalign(&ptr, 32, n * sizeof(bls_scalar_t)) != 0: - raise MemoryError() - coeffs_c = ptr - - try: - # Convert coefficients to C array and apply bit-reverse permutation simultaneously - for i in range(n): - val = coeffs[rev[i]] - if isinstance(val, Scalar): - coeffs_c[i] = (val).val - else: - coeffs_c[i] = Scalar(val).val - - # Cooley-Tukey butterfly stages - stage = 0 + +cdef inline object _scalar_to_int(const bls_scalar_t *value): + cdef bls_scalar_t raw + cdef uint8_t b[32] + bls_scalar_from_mont(&raw, value) + bls_scalar_to_bytes(b, &raw) + return int.from_bytes((b)[:32], "little") + + +cdef class BlsScalarNTTPlan: + cdef Py_ssize_t n + cdef Py_ssize_t stages + cdef Py_ssize_t *rev_c + cdef bls_scalar_t *twiddles_c + cdef Py_ssize_t *twiddle_offsets + + def __cinit__(self, list twiddles, list rev): + cdef: + Py_ssize_t i, stage, offset, total_twiddles, expected_stages, m + list stage_twiddles + object val + Scalar s + + self.n = len(rev) + self.stages = len(twiddles) + self.rev_c = NULL + self.twiddles_c = NULL + self.twiddle_offsets = NULL + + if self.n < 2 or self.n & (self.n - 1): + raise ValueError(f"native NTT plan size must be a power of two >= 2, got {self.n}") + + expected_stages = self.n.bit_length() - 1 + if self.stages != expected_stages: + raise ValueError(f"native NTT plan expected {expected_stages} twiddle stages, got {self.stages}") + + for val in rev: + if val < 0 or val >= self.n: + raise ValueError(f"bit-reverse index {val} is outside plan size {self.n}") + m = 2 - while m <= n: - half_m = m >> 1 - stage_twiddles_list = twiddles[stage] + for stage in range(self.stages): + if len(twiddles[stage]) != (m >> 1): + raise ValueError(f"twiddle stage {stage} has invalid length {len(twiddles[stage])}; expected {m >> 1}") + m <<= 1 + + self.rev_c = malloc(self.n * sizeof(Py_ssize_t)) + self.twiddle_offsets = malloc(self.stages * sizeof(Py_ssize_t)) + if not self.rev_c or not self.twiddle_offsets: + raise MemoryError() + + for i in range(self.n): + self.rev_c[i] = rev[i] + + total_twiddles = 0 + for stage in range(self.stages): + stage_twiddles = twiddles[stage] + self.twiddle_offsets[stage] = total_twiddles + total_twiddles += len(stage_twiddles) + + self.twiddles_c = malloc(total_twiddles * sizeof(bls_scalar_t)) + if not self.twiddles_c: + raise MemoryError() + + offset = 0 + for stage in range(self.stages): + stage_twiddles = twiddles[stage] + for i in range(len(stage_twiddles)): + val = stage_twiddles[i] + if isinstance(val, Scalar): + self.twiddles_c[offset + i] = (val).val + else: + s = Scalar(val) + self.twiddles_c[offset + i] = s.val + offset += len(stage_twiddles) + + def __dealloc__(self): + if self.rev_c: + free(self.rev_c) + if self.twiddles_c: + free(self.twiddles_c) + if self.twiddle_offsets: + free(self.twiddle_offsets) + + def transform(self, list coeffs): + """Run an in-place NTT over the BLS12-381 scalar field.""" + if len(coeffs) <= 1: + return + self._transform_core(coeffs, None, False) + + def transform_scaled(self, list coeffs, object scale): + """Run an in-place NTT over the BLS12-381 scalar field and scale each output.""" + if len(coeffs) <= 1: + return + self._transform_core(coeffs, scale, True) + + cdef void _transform_core(self, list coeffs, object scale, bint apply_scale) except *: + cdef: + Py_ssize_t n = len(coeffs) + Py_ssize_t i, stage, m + bls_scalar_t *coeffs_c + bls_scalar_t scale_s + bls_scalar_t scaled + object val + Scalar s + void *ptr = NULL + + if n != self.n: + raise ValueError(f"coefficient length {n} does not match native NTT plan size {self.n}") + + if apply_scale: + if isinstance(scale, Scalar): + scale_s = (scale).val + else: + scale_s = Scalar(scale).val + + if posix_memalign(&ptr, 32, n * sizeof(bls_scalar_t)) != 0: + raise MemoryError() + coeffs_c = ptr + + try: + for i in range(n): + val = coeffs[self.rev_c[i]] + if isinstance(val, Scalar): + coeffs_c[i] = (val).val + else: + coeffs_c[i] = Scalar(val).val - # Allocate and convert twiddles for this stage - if posix_memalign(&ptr, 32, half_m * sizeof(bls_scalar_t)) != 0: - raise MemoryError() - twiddles_c = ptr + stage = 0 + m = 2 + while m <= n: + bls_scalar_ntt_round(coeffs_c, n, &self.twiddles_c[self.twiddle_offsets[stage]], m) + m <<= 1 + stage += 1 - try: - for i in range(half_m): - val = stage_twiddles_list[i] - if isinstance(val, Scalar): - twiddles_c[i] = (val).val - else: - twiddles_c[i] = Scalar(val).val + for i in range(n): + if apply_scale: + bls_scalar_mul_mont(&scaled, &coeffs_c[i], &scale_s) + coeffs[i] = _scalar_to_int(&scaled) + else: + coeffs[i] = _scalar_to_int(&coeffs_c[i]) - # Perform NTT round in C - bls_scalar_ntt_round(coeffs_c, n, twiddles_c, m) - - finally: - free(twiddles_c) - - m <<= 1 - stage += 1 - - # Convert back to Python integers (or Scalars if preferred, but existing code expects ints) - for i in range(n): - # Create a temporary Scalar to convert back to int/mpz - # Or better, expose a C function to convert bls_scalar_t to python int directly? - # For now, use Scalar wrapper overhead. - s = Scalar.__new__(Scalar) - s.val = coeffs_c[i] - coeffs[i] = int(s) - - finally: - free(coeffs_c) \ No newline at end of file + finally: + free(coeffs_c) diff --git a/dot_ring/ring_proof/polynomial/ops.py b/dot_ring/ring_proof/polynomial/ops.py index 6bd8316..3d068d3 100644 --- a/dot_ring/ring_proof/polynomial/ops.py +++ b/dot_ring/ring_proof/polynomial/ops.py @@ -1,163 +1,229 @@ from collections.abc import Sequence +from functools import lru_cache -from dot_ring.ring_proof.constants import D_512, D_2048, OMEGA_2048 -from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA from dot_ring.ring_proof.polynomial.fft import evaluate_poly_fft, inverse_fft -from dot_ring.ring_proof.polynomial.poly_ops import ( - poly_evaluate_single, - poly_mul_linear, - poly_multiply_naive, -) -from dot_ring.ring_proof.polynomial.poly_ops import ( - poly_scalar_mul as poly_scalar_mul_fast, -) +_ROOT_SEARCH_START = 5 -def mod_inverse(val: int, prime: int) -> int: - """Find the modular multiplicative inverse of a under modulo m.""" - if pow(val, prime - 1, prime) != 1: - raise ValueError("No inverse exists") - return pow(val, prime - 2, prime) +def _is_power_of_two(n: int) -> bool: + return n > 0 and (n & (n - 1)) == 0 -GENERATOR = 5 -_root_of_unity_cache: dict[tuple[int, int], int] = {} +def _root_of_unity_domain_omega(domain: Sequence[int], prime: int) -> int | None: + n = len(domain) + if not _is_power_of_two(n): + return None + if domain[0] % prime != 1: + return None + if n == 1: + return 1 + omega = domain[1] % prime + if omega == 1 or pow(omega, n, prime) != 1 or pow(omega, n // 2, prime) == 1: + return None + + current = 1 + for x in domain: + if x % prime != current: + return None + current = (current * omega) % prime + return omega -def get_root_of_unity(n: int, prime: int) -> int: - """Get n-th primitive root of unity.""" - key = (n, prime) - if key in _root_of_unity_cache: - return _root_of_unity_cache[key] - # We need n to be a power of 2 and divide prime-1. - if n <= 0 or n & (n - 1): +@lru_cache(maxsize=128) +def get_root_of_unity(n: int, prime: int) -> int: + """Return an n-th primitive root of unity in the given prime field.""" + if not _is_power_of_two(n): raise ValueError(f"n must be a power of two, got {n}") if (prime - 1) % n != 0: raise ValueError(f"n={n} does not divide prime-1") + exponent = (prime - 1) // n - candidate = GENERATOR + candidate = _ROOT_SEARCH_START while True: root = pow(candidate, exponent, prime) if root != 1 and pow(root, n, prime) == 1 and (n == 1 or pow(root, n // 2, prime) != 1): - break + return root candidate += 1 - _root_of_unity_cache[key] = root - return root -# (On^2) -def poly_multiply(poly1: list[int], poly2: list[int], prime: int) -> list[int]: - """Multiply two polynomials in a prime field.""" - result_len = len(poly1) + len(poly2) - 1 +def vect_scalar_mul(vec: Sequence[int], scalar: int, mod: int | None = None) -> list[int]: + """Multiply each vector element by a scalar.""" + return [(x * scalar) % mod if mod else x * scalar for x in vec] - # Use FFT if polynomials are large enough - # Threshold chosen empirically - FFT overhead is not worth it for very small polys - if result_len > 64: - # Find next power of 2 - domain_size = 1 - while domain_size < result_len: - domain_size *= 2 - # We can support up to 2^32 - omega = get_root_of_unity(domain_size, prime) +def vect_add(a: Sequence[int], b: Sequence[int], prime: int) -> list[int]: + if len(a) != len(b): + raise ValueError("Vector lengths must match") + return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] - evals1 = evaluate_poly_fft(poly1, domain_size, omega, prime) - evals2 = evaluate_poly_fft(poly2, domain_size, omega, prime) - evals_prod = [(e1 * e2) % prime for e1, e2 in zip(evals1, evals2, strict=False)] +def poly_add(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: + """Add two polynomials in a prime field.""" + result_len = max(len(poly1), len(poly2)) + result = [0] * result_len - coeffs = inverse_fft(evals_prod, omega, prime) + for i in range(len(poly1)): + result[i] = poly1[i] - # Truncate to expected length (higher terms should be 0) - return coeffs[:result_len] + for i in range(len(poly2)): + val = result[i] + poly2[i] + if val >= prime: + val -= prime + result[i] = val - return poly_multiply_naive(poly1, poly2, prime) + return result -def poly_division_general(coeffs: list[int], domain_size: int) -> list[int]: - """ - Divide polynomial f(x) by vanishing polynomial Z_H(x) = x^domain_size - 1 +def poly_subtract(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: + """Subtract poly2 from poly1 in a prime field.""" + result_len = max(len(poly1), len(poly2)) + result = [0] * result_len - Args: - coeffs: list[int or Fp] - coefficients of f(x), lowest degree first - domain_size: int - size of the evaluation domain (n) + for i in range(len(poly1)): + result[i] = poly1[i] - Returns: - (quotient) a lists of coefficients - """ + for i in range(len(poly2)): + val1 = result[i] + val2 = poly2[i] + if val1 >= val2: + result[i] = val1 - val2 + else: + result[i] = prime - (val2 - val1) - n = domain_size - deg_f = len(coeffs) + return result - # Case 1: degree(f) < domain_size -> quotient = 0, remainder = f - if deg_f < n: - return [0] - # Step 1️: initial quotient is the higher-degree coefficients - quotient = coeffs[n:].copy() +def poly_scalar_mul(poly: Sequence[int], scalar: int, prime: int) -> list[int]: + """Multiply a polynomial by a scalar in a prime field.""" + n = len(poly) - # Step 2️: accumulate wrapped parts if polynomial is longer than 2n - # Equivalent to folding coefficients every n steps - for i in range(1, deg_f // n): - for j in range(len(quotient)): - src_index = n * (i + 1) + j - if src_index < deg_f: - quotient[j] += coeffs[src_index] + if scalar >= prime: + scalar = scalar % prime - # trim trailing zeros for cleaner output - while quotient and quotient[-1] == 0: - quotient.pop() - return quotient + if scalar == 0: + return [0] * n + if scalar == 1: + return [coef % prime if coef >= prime else coef for coef in poly] + result = [0] * n + for i, coef in enumerate(poly): + if coef >= prime: + coef = coef % prime + result[i] = (coef * scalar) % prime -def poly_evaluate(poly: list | Sequence[int], xs: list | int | Sequence[int], prime: int) -> list[int] | int: - """Evaluate polynomial at points xs. + return result - Uses FFT when xs is one of the predefined evaluation domains (D_512, D_2048). - Falls back to Horner evaluation for arbitrary points. - Args: - poly: Polynomial coefficients (lowest degree first) - xs: Either a list of evaluation points or a single point - prime: Field modulus +def poly_mul_linear(poly: Sequence[int], a: int, b: int, prime: int) -> list[int]: + """Multiply a polynomial by (a*x + b) in O(n).""" + n = len(poly) + result = [0] * (n + 1) - Returns: - List of evaluations (or single value if xs is a single point) - """ - # Handle single point evaluation - if isinstance(xs, int): - return poly_evaluate_single(poly, xs, prime) - - # Check if xs is one of the FFT-friendly domains - # Compare by identity first (fast path), then by equality - if xs is D_2048 or (len(xs) == 2048 and xs == D_2048): - # Use FFT for D_2048 - return evaluate_poly_fft(list(poly), 2048, OMEGA_2048, prime, coset_offset=1) - elif xs is D_512 or (len(xs) == 512 and xs == D_512): - # Use FFT for D_512 - return evaluate_poly_fft(list(poly), 512, OMEGA, prime, coset_offset=1) - else: - # Fall back to Horner evaluation for arbitrary points - results = [poly_evaluate_single(poly, x, prime) for x in xs] - return results - - -def lagrange_basis_polynomial(x_coords: list[int], i: int, prime: int) -> list[int]: - """ - Compute the i-th Lagrange basis polynomial. + if a >= prime: + a = a % prime + if b >= prime: + b = b % prime - L_i(x) = (x - x_j) / (x_i - x_j) - """ - # Optimization for roots of unity domains - if x_coords is D_512 or (len(x_coords) == 512 and x_coords == D_512) or x_coords is D_2048 or (len(x_coords) == 2048 and x_coords == D_2048): - n = len(x_coords) - x_i = x_coords[i] + coef = poly[0] + if coef >= prime: + coef = coef % prime + result[0] = (b * coef) % prime + + for i in range(1, n): + prev_coef = poly[i - 1] + if prev_coef >= prime: + prev_coef = prev_coef % prime + + coef = poly[i] + if coef >= prime: + coef = coef % prime + + term1 = (a * prev_coef) % prime + term2 = (b * coef) % prime + result[i] = (term1 + term2) % prime + + coef = poly[n - 1] + if coef >= prime: + coef = coef % prime + result[n] = (a * coef) % prime + + return result + + +def _poly_multiply_schoolbook(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: + """Multiply two small polynomials directly.""" + result = [0] * (len(poly1) + len(poly2) - 1) + for i, val1 in enumerate(poly1): + if val1 >= prime: + val1 = val1 % prime - # L_i(x) = 1/n * sum_{j=0}^{n-1} (x_i^{-j}) x^j - # coeff[j] = 1/n * (x_i^{-1})^j + if val1 == 0: + continue + for j, val2 in enumerate(poly2): + if val2 >= prime: + val2 = val2 % prime + + if val2 == 0: + continue + + prod = (val1 * val2) % prime + current = result[i + j] + prod + if current >= prime: + current -= prime + result[i + j] = current + return result + + +def poly_multiply(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: + """Multiply two polynomials in a prime field.""" + if min(len(poly1), len(poly2)) <= 8: + return _poly_multiply_schoolbook(poly1, poly2, prime) + + result_len = len(poly1) + len(poly2) - 1 + if result_len <= 64: + return _poly_multiply_schoolbook(poly1, poly2, prime) + + domain_size = 1 + while domain_size < result_len: + domain_size *= 2 + + omega = get_root_of_unity(domain_size, prime) + evals1 = evaluate_poly_fft(list(poly1), domain_size, omega, prime) + evals2 = evaluate_poly_fft(list(poly2), domain_size, omega, prime) + evals_prod = [(e1 * e2) % prime for e1, e2 in zip(evals1, evals2, strict=True)] + return inverse_fft(evals_prod, omega, prime)[:result_len] + + +def poly_evaluate_single(poly: Sequence[int], x: int, prime: int) -> int: + """Evaluate a polynomial at one point with Horner's method.""" + x %= prime + result = 0 + for coef in reversed(poly): + result = (result * x + coef) % prime + return result + + +def poly_evaluate_domain(poly: Sequence[int], domain: Sequence[int], prime: int) -> list[int]: + """Evaluate a polynomial over a point domain. + + Roots-of-unity domains use FFT. Arbitrary point lists use Horner per point. + """ + coeffs = list(poly) + omega = _root_of_unity_domain_omega(domain, prime) + if omega is not None: + return evaluate_poly_fft(coeffs, len(domain), omega, prime) + return [poly_evaluate_single(coeffs, x, prime) for x in domain] + + +def lagrange_basis_polynomial(domain: Sequence[int], i: int, prime: int) -> list[int]: + """Return the i-th Lagrange basis polynomial over the given domain.""" + omega = _root_of_unity_domain_omega(domain, prime) + if omega is not None: + n = len(domain) + x_i = domain[i] % prime inv_n = pow(n, -1, prime) inv_xi = pow(x_i, -1, prime) @@ -166,35 +232,36 @@ def lagrange_basis_polynomial(x_coords: list[int], i: int, prime: int) -> list[i for j in range(n): coeffs[j] = current current = (current * inv_xi) % prime - return coeffs - n = len(x_coords) - numerator = [1] # Start with polynomial 1 + numerator = [1] denominator = 1 + x_i = domain[i] % prime + for j, x_j in enumerate(domain): + if j == i: + continue + numerator = poly_mul_linear(numerator, 1, (-x_j) % prime, prime) + denominator = denominator * ((x_i - x_j) % prime) % prime - for j in range(n): - if j != i: - # Multiply numerator by (x - x_j) - # term = [(-x_coords[j]) % prime, 1] - # numerator = poly_multiply(numerator, term, prime) - - # Use optimized linear multiplication: multiply by (1*x - x_j) - numerator = poly_mul_linear(numerator, 1, (-x_coords[j]) % prime, prime) + return poly_scalar_mul(numerator, pow(denominator, -1, prime), prime) - # Multiply denominator by (x_i - x_j) - diff = (x_coords[i] - x_coords[j]) % prime - denominator = (denominator * diff) % prime - # Calculate modular inverse of denominator - # inv_denominator = mod_inverse(denominator, prime) - inv_denominator = pow(denominator, -1, prime) - # Scale the numerator polynomial - basis_poly = poly_scalar_mul_fast(numerator, inv_denominator, prime) +def poly_divide_by_vanishing(poly: Sequence[int], domain_size: int, prime: int) -> list[int]: + """Return the quotient of poly divided by x^domain_size - 1.""" + del prime + if domain_size <= 0: + raise ValueError("domain_size must be positive") - return basis_poly + if len(poly) < domain_size: + return [0] + quotient = list(poly[domain_size:]) + for i in range(1, len(poly) // domain_size): + for j in range(len(quotient)): + source_index = domain_size * (i + 1) + j + if source_index < len(poly): + quotient[j] += poly[source_index] -def vect_scalar_mul(vec: list[int] | Sequence[int], scalar: int, mod: int | None = None) -> list[int]: - """Multiply each element in the vector by the scalar""" - return [(x * scalar) % mod if mod else x * scalar for x in vec] + while quotient and quotient[-1] == 0: + quotient.pop() + return quotient diff --git a/dot_ring/ring_proof/polynomial/poly_ops.pyx b/dot_ring/ring_proof/polynomial/poly_ops.pyx deleted file mode 100644 index db576db..0000000 --- a/dot_ring/ring_proof/polynomial/poly_ops.pyx +++ /dev/null @@ -1,244 +0,0 @@ -# cython: language_level=3 -# cython: boundscheck=False -# cython: wraparound=False -# cython: cdivision=True -# cython: initializedcheck=False - -def poly_add(list poly1, list poly2, object prime): - """Add two polynomials in a prime field (optimized Cython version).""" - cdef int len1 = len(poly1) - cdef int len2 = len(poly2) - cdef int result_len = max(len1, len2) - cdef int i - cdef object val - cdef list result = [0] * result_len - - # Add poly1 coefficients - for i in range(len1): - result[i] = poly1[i] - - # Add poly2 coefficients - for i in range(len2): - val = result[i] + poly2[i] - if val >= prime: - val -= prime - result[i] = val - - return result - - -def poly_subtract(list poly1, list poly2, object prime): - """Subtract poly2 from poly1 in a prime field (optimized Cython version).""" - cdef int len1 = len(poly1) - cdef int len2 = len(poly2) - cdef int result_len = max(len1, len2) - cdef int i - cdef object val, val1, val2 - cdef list result = [0] * result_len - - # Add poly1 coefficients - for i in range(len1): - result[i] = poly1[i] - - # Subtract poly2 coefficients - for i in range(len2): - val1 = result[i] - val2 = poly2[i] - if val1 >= val2: - result[i] = val1 - val2 - else: - result[i] = prime - (val2 - val1) - - return result - - -def poly_scalar_mul(list poly, object scalar, object prime): - """Multiply a polynomial by a scalar in a prime field (optimized Cython version).""" - cdef int n = len(poly) - cdef int i - cdef object coef - cdef list result = [None] * n - - # Normalize scalar - if scalar >= prime: - scalar = scalar % prime - - # Special case: scalar is 0 or 1 - if scalar == 0: - return [0] * n - elif scalar == 1: - return [poly[i] % prime if poly[i] >= prime else poly[i] for i in range(n)] - - # General case - for i in range(n): - coef = poly[i] - if coef >= prime: - coef = coef % prime - result[i] = (coef * scalar) % prime - - return result - - -def poly_evaluate_single(list poly, object x, object prime): - """Evaluate polynomial at point x using Horner's method (optimized Cython version).""" - cdef int n = len(poly) - cdef int i - cdef object result = 0 - cdef object coef - - # Normalize x - if x >= prime: - x = x % prime - - # Horner's method: evaluate from highest degree to lowest - for i in range(n - 1, -1, -1): - coef = poly[i] - if coef >= prime: - coef = coef % prime - result = (result * x + coef) % prime - - return result - - -def poly_multiply_naive(list poly1, list poly2, object prime): - """Multiply two polynomials using naive O(n²) algorithm (optimized for small polynomials).""" - cdef int len1 = len(poly1) - cdef int len2 = len(poly2) - cdef int result_len = len1 + len2 - 1 - cdef int i, j - cdef object val1, val2, prod, current - cdef list result = [0] * result_len - - for i in range(len1): - val1 = poly1[i] - if val1 >= prime: - val1 = val1 % prime - - if val1 == 0: - continue - - for j in range(len2): - val2 = poly2[j] - if val2 >= prime: - val2 = val2 % prime - - if val2 == 0: - continue - - prod = (val1 * val2) % prime - current = result[i + j] + prod - if current >= prime: - current -= prime - result[i + j] = current - - return result - - -def poly_eval_domain(list poly, list domain, object prime): - """Evaluate polynomial at multiple points (for non-FFT domains).""" - cdef int n_points = len(domain) - cdef int i - cdef object x - cdef list result = [None] * n_points - - for i in range(n_points): - x = domain[i] - if x >= prime: - x = x % prime - result[i] = poly_evaluate_single(poly, x, prime) - - return result - - -def vect_scalar_mul_inplace(list vec, object scalar, object prime): - """Multiply each element by scalar modulo prime (in-place version for reduced allocations).""" - cdef int n = len(vec) - cdef int i - cdef object val - - if scalar >= prime: - scalar = scalar % prime - - if scalar == 0: - for i in range(n): - vec[i] = 0 - elif scalar != 1: - for i in range(n): - val = vec[i] - if val >= prime: - val = val % prime - vec[i] = (val * scalar) % prime - else: - # scalar == 1, just normalize - for i in range(n): - if vec[i] >= prime: - vec[i] = vec[i] % prime - - return vec - - -def vect_add_inplace(list a, list b, object prime): - """Add vector b to vector a in-place (modifies a).""" - cdef int n = len(a) - cdef int m = len(b) - cdef int i - cdef object val_a, val_b, result - - if m != n: - raise ValueError("Vector lengths must match") - - for i in range(n): - val_a = a[i] - val_b = b[i] - if val_a >= prime: - val_a = val_a % prime - if val_b >= prime: - val_b = val_b % prime - result = val_a + val_b - if result >= prime: - result -= prime - a[i] = result - - return a - - -def poly_mul_linear(list poly, object a, object b, object prime): - """Multiply poly by (ax + b) in O(n) time (optimized version).""" - cdef int n = len(poly) - cdef int i - cdef object coef, term1, term2, prev_coef - cdef list result = [None] * (n + 1) - - # Normalize a and b - if a >= prime: - a = a % prime - if b >= prime: - b = b % prime - - # Handle first element: result[0] = b * poly[0] - coef = poly[0] - if coef >= prime: - coef = coef % prime - result[0] = (b * coef) % prime - - # Handle middle elements: result[i] = a * poly[i-1] + b * poly[i] - for i in range(1, n): - prev_coef = poly[i - 1] - if prev_coef >= prime: - prev_coef = prev_coef % prime - - coef = poly[i] - if coef >= prime: - coef = coef % prime - - term1 = (a * prev_coef) % prime - term2 = (b * coef) % prime - result[i] = (term1 + term2) % prime - - # Handle last element: result[n] = a * poly[n-1] - coef = poly[n - 1] - if coef >= prime: - coef = coef % prime - result[n] = (a * coef) % prime - - return result diff --git a/dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin b/dot_ring/vrf/data/bn254-testing-2-9-uncompressed.bin deleted file mode 100644 index e4ad81c1b97317b3d8eaf26c1726b1698260bf0e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 98640 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

$ZW+TkR#IJ?+_4 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 diff --git a/tests/test_coverage/test_fft.py b/tests/test_coverage/test_fft.py index ee4e498..f052efd 100644 --- a/tests/test_coverage/test_fft.py +++ b/tests/test_coverage/test_fft.py @@ -77,9 +77,7 @@ def test_get_roots(self): omega = 4 prime = 17 - # omega^4 mod 17 = 256 mod 17 = 1, but we need 8th root - # Let's find 8th root: need omega^8 = 1 mod 17 - # 2^8 = 256 mod 17 = 1. Yes! 2 is 8th root + # 2 is an 8th root of unity mod 17. omega = 2 roots = _get_roots(n, omega, prime) diff --git a/tests/test_coverage/test_kzg.py b/tests/test_coverage/test_kzg.py index ca23559..35821c5 100644 --- a/tests/test_coverage/test_kzg.py +++ b/tests/test_coverage/test_kzg.py @@ -3,81 +3,10 @@ import pytest from dot_ring import blst -from dot_ring.ring_proof.pcs.kzg import ( - KZG, - Opening, - p1_add, - p1_neg, - p1_scalar_mul, - p2_add, - p2_neg, - p2_scalar_mul, -) +from dot_ring.ring_proof.pcs.kzg import KZG, Opening from dot_ring.ring_proof.pcs.srs import srs -class TestKZGHelpers: - """Test KZG helper functions.""" - - def test_p1_scalar_mul(self): - """Test P1 scalar multiplication.""" - p = srs.blst_g1[0] # Generator - scalar = 5 - - result = p1_scalar_mul(p, scalar) - - assert result is not None - # Result should be different from input (unless scalar is 1) - assert not result.is_equal(p) - - def test_p1_add(self): - """Test P1 point addition.""" - p1 = srs.blst_g1[0] - p2 = srs.blst_g1[1] - - result = p1_add(p1, p2) - - assert result is not None - - def test_p1_neg(self): - """Test P1 point negation.""" - p = srs.blst_g1[0] - - neg_p = p1_neg(p) - - # p + (-p) should be the point at infinity - result = p1_add(p, neg_p) - assert result.is_inf() - - def test_p2_scalar_mul(self): - """Test P2 scalar multiplication.""" - p = srs.blst_g2[0] - scalar = 5 - - result = p2_scalar_mul(p, scalar) - - assert result is not None - - def test_p2_add(self): - """Test P2 point addition.""" - p1 = srs.blst_g2[0] - p2 = srs.blst_g2[1] - - result = p2_add(p1, p2) - - assert result is not None - - def test_p2_neg(self): - """Test P2 point negation.""" - p = srs.blst_g2[0] - - neg_p = p2_neg(p) - - # p + (-p) should be the point at infinity - result = p2_add(p, neg_p) - assert result.is_inf() - - class TestKZGCommit: """Test KZG commitment functionality.""" diff --git a/tests/test_coverage/test_ops.py b/tests/test_coverage/test_ops.py index 0d6fe35..0852763 100644 --- a/tests/test_coverage/test_ops.py +++ b/tests/test_coverage/test_ops.py @@ -1,44 +1,28 @@ """Tests for polynomial operations module to improve coverage.""" -import pytest -from dot_ring.ring_proof.polynomial.poly_ops import poly_add, poly_evaluate_single, poly_mul_linear, poly_subtract -from dot_ring.ring_proof.polynomial.poly_ops import poly_scalar_mul as poly_scalar - from dot_ring.ring_proof.constants import D_512, D_2048, S_PRIME from dot_ring.ring_proof.polynomial.ops import ( get_root_of_unity, lagrange_basis_polynomial, - mod_inverse, - poly_division_general, - poly_evaluate, + poly_add, + poly_divide_by_vanishing, + poly_evaluate_domain, + poly_evaluate_single, + poly_mul_linear, poly_multiply, + poly_scalar_mul, + poly_subtract, vect_scalar_mul, ) +poly_scalar = poly_scalar_mul + class TestPolynomialOps: """Test cases for polynomial operations.""" PRIME = 17 # Small prime for testing - def test_mod_inverse_basic(self): - """Test modular inverse computation.""" - # 3 * 6 = 18 = 1 mod 17, so inv(3) = 6 mod 17 - result = mod_inverse(3, 17) - assert (3 * result) % 17 == 1 - - def test_mod_inverse_raises_for_no_inverse(self): - """Test mod_inverse raises ValueError when no inverse exists.""" - # 0 has no inverse - with pytest.raises(ValueError, match="No inverse exists"): - mod_inverse(0, 17) - - def test_mod_inverse_various_values(self): - """Test mod_inverse for various values.""" - for val in [1, 2, 5, 7, 11, 13]: - inv = mod_inverse(val, 17) - assert (val * inv) % 17 == 1 - def test_poly_add_same_length(self): """Test polynomial addition with same length.""" p1 = [1, 2, 3] @@ -142,65 +126,57 @@ def test_poly_evaluate_single_quadratic(self): # 1 + 2*2 + 3*4 = 1 + 4 + 12 = 17 mod 17 = 0 assert result == 0 - def test_poly_evaluate_single_point_integer(self): - """Test poly_evaluate with single integer point.""" - poly = [1, 2, 3] - result = poly_evaluate(poly, 5, 17) - # Returns single value for single point - expected = poly_evaluate_single(poly, 5, 17) - assert result == expected - - def test_poly_evaluate_multiple_points(self): - """Test poly_evaluate with multiple arbitrary points.""" + def test_poly_evaluate_domain_arbitrary_points(self): + """Test domain evaluation with multiple arbitrary points.""" poly = [1, 2] # 1 + 2x points = [0, 1, 2, 3] - result = poly_evaluate(poly, points, 17) + result = poly_evaluate_domain(poly, points, 17) # At x=0: 1, x=1: 3, x=2: 5, x=3: 7 assert result == [1, 3, 5, 7] - def test_poly_evaluate_d512_domain(self): - """Test poly_evaluate with D_512 domain uses FFT.""" + def test_poly_evaluate_domain_d512(self): + """Test domain evaluation with D_512 uses FFT.""" poly = [1, 2, 3, 4] - result = poly_evaluate(poly, D_512, S_PRIME) + result = poly_evaluate_domain(poly, D_512, S_PRIME) assert len(result) == 512 - def test_poly_evaluate_d2048_domain(self): - """Test poly_evaluate with D_2048 domain uses FFT.""" + def test_poly_evaluate_domain_d2048(self): + """Test domain evaluation with D_2048 uses FFT.""" poly = [1, 2, 3, 4] - result = poly_evaluate(poly, D_2048, S_PRIME) + result = poly_evaluate_domain(poly, D_2048, S_PRIME) assert len(result) == 2048 - def test_poly_division_general_quotient_zero(self): + def test_poly_divide_by_vanishing_quotient_zero(self): """Test division when deg(f) < domain_size (quotient is 0).""" coeffs = [1, 2, 3] # degree 2 domain_size = 8 - result = poly_division_general(coeffs, domain_size) + result = poly_divide_by_vanishing(coeffs, domain_size, 17) assert result == [0] - def test_poly_division_general_basic(self): + def test_poly_divide_by_vanishing_basic(self): """Test basic polynomial division by vanishing polynomial.""" # f(x) = x^4 + 2x^3 + 3x^2 + 4x + 5 coeffs = [5, 4, 3, 2, 1] # degree 4 domain_size = 4 # Dividing by x^4 - 1 # quotient is coefficient of x^4, which is 1 - result = poly_division_general(coeffs, domain_size) + result = poly_divide_by_vanishing(coeffs, domain_size, 17) assert result == [1] - def test_poly_division_general_larger(self): + def test_poly_divide_by_vanishing_larger(self): """Test polynomial division with larger polynomial.""" # f(x) with degree 7, domain_size 4 coeffs = [1, 2, 3, 4, 5, 6, 7, 8] # coeffs[4:] = [5,6,7,8] is initial quotient domain_size = 4 - result = poly_division_general(coeffs, domain_size) + result = poly_divide_by_vanishing(coeffs, domain_size, 17) # quotient = coeffs[n:] = [5, 6, 7, 8] assert len(result) == 4 - def test_poly_division_general_strips_zeros(self): + def test_poly_divide_by_vanishing_strips_zeros(self): """Test that division strips trailing zeros from quotient.""" coeffs = [1, 2, 3, 4, 5, 0, 0, 0] # trailing zeros in quotient part domain_size = 4 - result = poly_division_general(coeffs, domain_size) + result = poly_divide_by_vanishing(coeffs, domain_size, 17) # quotient should have trailing zeros stripped assert result[-1] != 0 or result == [0] @@ -253,6 +229,16 @@ def test_lagrange_basis_polynomial_d2048(self): basis = lagrange_basis_polynomial(D_2048, 0, S_PRIME) assert len(basis) == 2048 + def test_lagrange_basis_polynomial_generic_root_domain(self): + """Test optimized Lagrange basis on generated roots-of-unity domains.""" + omega = get_root_of_unity(8, S_PRIME) + domain = [pow(omega, i, S_PRIME) for i in range(8)] + basis = lagrange_basis_polynomial(domain, 3, S_PRIME) + + for index, point in enumerate(domain): + expected = 1 if index == 3 else 0 + assert poly_evaluate_single(basis, point, S_PRIME) == expected + def test_get_root_of_unity_caching(self): """Test that root of unity computation is cached.""" # First call diff --git a/tests/test_coverage/test_pcs_utils.py b/tests/test_coverage/test_pcs_utils.py index 31591a8..7e3aed0 100644 --- a/tests/test_coverage/test_pcs_utils.py +++ b/tests/test_coverage/test_pcs_utils.py @@ -1,7 +1,7 @@ """Additional tests for utils module to improve coverage.""" from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.pcs.utils import synthetic_div +from dot_ring.ring_proof.pcs.utils import synthetic_div, synthetic_div_with_eval class TestPCSUtils: @@ -53,3 +53,13 @@ def test_synthetic_div_larger_poly(self): assert quotient is not None assert len(quotient) == len(poly) - 1 + + def test_synthetic_div_with_eval_matches_checked_division(self): + poly = [3, 5, 7, 11, 13] + x = 17 + y = sum(c * pow(x, i, S_PRIME) for i, c in enumerate(poly)) % S_PRIME + + quotient, value = synthetic_div_with_eval(poly, x) + + assert value == y + assert quotient == synthetic_div(poly, x, y) diff --git a/tests/utils/rust_serde.py b/tests/utils/rust_serde.py index de8fbb1..c14512f 100644 --- a/tests/utils/rust_serde.py +++ b/tests/utils/rust_serde.py @@ -70,11 +70,11 @@ def compressed_bandersnatch_to_uncompressed_bytes(compressed: bytes) -> bytes: # Recover y from Twisted Edwards curve equation: a*x^2 + y^2 = 1 + d*x^2*y^2 # Rearranged: y^2 = (1 - a*x^2) / (1 - d*x^2) # Bandersnatch parameters from the curve specification - from dot_ring.curve.specs.bandersnatch import BandersnatchParams + from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS, Bandersnatch_TE_Curve - a = BandersnatchParams.EDWARDS_A - d = BandersnatchParams.EDWARDS_D - p = BandersnatchParams.MODULUS + a = BANDERSNATCH_PARAMS.a + d = BANDERSNATCH_PARAMS.d + p = BANDERSNATCH_PARAMS.field_modulus x_squared = (x * x) % p numerator = (1 - a * x_squared) % p @@ -82,14 +82,10 @@ def compressed_bandersnatch_to_uncompressed_bytes(compressed: bytes) -> bytes: denominator_inv = pow(denominator, p - 2, p) # Fermat's little theorem y_squared = (numerator * denominator_inv) % p - # Compute square root using Tonelli-Shanks or p ≡ 3 mod 4 method - # For Bandersnatch, p ≡ 1 mod 4, so we need Tonelli-Shanks - # But for simplicity, we can use the fact that we know the sign - y = pow(y_squared, (p + 1) // 4, p) # This might not always work - - # Actually, let's check if p ≡ 3 mod 4 - # Bandersnatch uses BLS12-381 scalar field, which is p ≡ 3 mod 4 - y = pow(y_squared, (p + 1) // 4, p) + try: + y = Bandersnatch_TE_Curve.mod_sqrt(y_squared) + except ValueError as exc: + raise ValueError("Invalid compressed Bandersnatch point") from exc # Check which square root to use based on sign bit # The sign bit indicates whether y is positive or negative From d5d717189cdd74e296db6fa93af7826dd841e892 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:39:27 +0530 Subject: [PATCH 24/33] clean vrf transcript --- dot_ring/vrf/delinearize.py | 16 +- dot_ring/vrf/{vrf_io.py => io.py} | 0 dot_ring/vrf/{vrf_protocol.py => protocol.py} | 4 +- dot_ring/vrf/spec_transcript.py | 72 ------- dot_ring/vrf/transcript.py | 187 +++++++++++------- dot_ring/vrf/transcript_constants.py | 2 - dot_ring/vrf/vrf.py | 89 +++++---- 7 files changed, 182 insertions(+), 188 deletions(-) rename dot_ring/vrf/{vrf_io.py => io.py} (100%) rename dot_ring/vrf/{vrf_protocol.py => protocol.py} (91%) delete mode 100644 dot_ring/vrf/spec_transcript.py delete mode 100644 dot_ring/vrf/transcript_constants.py diff --git a/dot_ring/vrf/delinearize.py b/dot_ring/vrf/delinearize.py index b1fabb5..3c9c90c 100644 --- a/dot_ring/vrf/delinearize.py +++ b/dot_ring/vrf/delinearize.py @@ -1,26 +1,28 @@ from __future__ import annotations -from typing import cast +from typing import TYPE_CHECKING from dot_ring.curve.curve import CurveVariant from .domain import DomSep -from .spec_transcript import SpecTranscript -from .transcript_constants import CHALLENGE_LEN +from .transcript import CHALLENGE_LEN + +if TYPE_CHECKING: + from .transcript import VrfTranscript class DelinearizeScalars: - def __init__(self, curve: CurveVariant, transcript: SpecTranscript) -> None: + def __init__(self, curve: CurveVariant, transcript: VrfTranscript) -> None: self.curve = curve - self.transcript = transcript.clone() - self.transcript.absorb_raw(bytes([DomSep.DELINEARIZE])) + self.transcript = transcript.copy() + self.transcript.absorb(bytes([DomSep.DELINEARIZE])) self.first = True def next(self) -> int: if self.first: self.first = False return 1 - return int.from_bytes(self.transcript.squeeze_raw(CHALLENGE_LEN), "little") % cast(int, self.curve.curve.ORDER) + return int.from_bytes(self.transcript.squeeze(CHALLENGE_LEN), "little") % self.curve.curve.params.subgroup_order def take(self, n: int) -> list[int]: return [self.next() for _ in range(n)] diff --git a/dot_ring/vrf/vrf_io.py b/dot_ring/vrf/io.py similarity index 100% rename from dot_ring/vrf/vrf_io.py rename to dot_ring/vrf/io.py diff --git a/dot_ring/vrf/vrf_protocol.py b/dot_ring/vrf/protocol.py similarity index 91% rename from dot_ring/vrf/vrf_protocol.py rename to dot_ring/vrf/protocol.py index 1e95e72..337dff5 100644 --- a/dot_ring/vrf/vrf_protocol.py +++ b/dot_ring/vrf/protocol.py @@ -4,10 +4,10 @@ from typing import Protocol, TypeVar from ..curve.curve import CurveVariant -from ..curve.point import Point +from ..curve.point import CurvePoint C = TypeVar("C", bound=CurveVariant) -P = TypeVar("P", bound=Point) +P = TypeVar("P", bound=CurvePoint) class VRFProtocol(Protocol[C, P]): diff --git a/dot_ring/vrf/spec_transcript.py b/dot_ring/vrf/spec_transcript.py deleted file mode 100644 index 5f9a92c..0000000 --- a/dot_ring/vrf/spec_transcript.py +++ /dev/null @@ -1,72 +0,0 @@ -from __future__ import annotations - -import hashlib -from typing import cast - -from dot_ring.curve.curve import CurveVariant -from dot_ring.curve.point import CurvePoint - - -def _scalar_len(curve: CurveVariant) -> int: - return (cast(int, curve.curve.ORDER).bit_length() + 7) // 8 - - -class SpecTranscript: - def __init__(self, label: bytes, hash_name: str = "sha512") -> None: - self.hash_name = hash_name - self._absorbed = bytearray(label) - self._seed: bytes | None = None - self._squeeze_offset = 0 - - def clone(self) -> SpecTranscript: - other = SpecTranscript(b"", self.hash_name) - other._absorbed = bytearray(self._absorbed) - other._seed = self._seed - other._squeeze_offset = self._squeeze_offset - return other - - def absorb_raw(self, data: bytes) -> None: - if self._seed is not None: - raise ValueError("cannot absorb after squeeze") - self._absorbed.extend(data) - - def absorb_point(self, point: CurvePoint) -> None: - self.absorb_raw(point.point_to_string()) - - def absorb_scalar(self, curve: CurveVariant, value: int) -> None: - self.absorb_raw(int(value % curve.curve.ORDER).to_bytes(_scalar_len(curve), "little")) - - def squeeze_raw(self, size: int) -> bytes: - if self.hash_name == "shake128": - return self._squeeze_shake128(size) - return self._squeeze_digest_xof(size) - - def _digest(self, data: bytes) -> bytes: - if self.hash_name == "sha256": - return hashlib.sha256(data).digest() - if self.hash_name == "sha512": - return hashlib.sha512(data).digest() - raise ValueError(f"unsupported transcript hash {self.hash_name!r}") - - def _squeeze_digest_xof(self, size: int) -> bytes: - if self._seed is None: - self._seed = self._digest(bytes(self._absorbed)) - start = self._squeeze_offset - end = start + size - block_size = len(self._seed) - first_block = start // block_size - last_block = (end + block_size - 1) // block_size - stream = b"".join(self._digest(self._seed + counter.to_bytes(8, "little")) for counter in range(first_block, last_block)) - self._squeeze_offset = end - return stream[start % block_size : start % block_size + size] - - def _squeeze_shake128(self, size: int) -> bytes: - if self._seed is None: - self._seed = bytes(self._absorbed) - start = self._squeeze_offset - end = start + size - xof = hashlib.shake_128() - xof.update(self._seed) - out = xof.digest(end) - self._squeeze_offset = end - return out[start:end] diff --git a/dot_ring/vrf/transcript.py b/dot_ring/vrf/transcript.py index b148375..c96ddd2 100644 --- a/dot_ring/vrf/transcript.py +++ b/dot_ring/vrf/transcript.py @@ -1,74 +1,111 @@ from __future__ import annotations -from typing import Any, cast +from typing import TYPE_CHECKING, Any from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.delinearize import DelinearizeScalars from dot_ring.vrf.domain import DomSep -from dot_ring.vrf.spec_transcript import SpecTranscript -from dot_ring.vrf.transcript_constants import CHALLENGE_LEN, SECURITY_PARAMETER -from dot_ring.vrf.vrf_io import VrfIo +from dot_ring.vrf.io import VrfIo + +if TYPE_CHECKING: + from dot_ring.vrf.delinearize import DelinearizeScalars + +SECURITY_PARAMETER = 128 +CHALLENGE_LEN = SECURITY_PARAMETER // 8 + + +class VrfTranscript: + def __init__(self, label: bytes, hash_fn: Any) -> None: + self._hash_fn = hash_fn + self._absorbed = bytearray(label) + self._seed: bytes | None = None + self._squeeze_offset = 0 + + def copy(self) -> VrfTranscript: + other = VrfTranscript(b"", self._hash_fn) + other._absorbed = bytearray(self._absorbed) + other._seed = self._seed + other._squeeze_offset = self._squeeze_offset + return other + + def absorb(self, data: bytes) -> None: + if self._seed is not None: + raise ValueError("cannot absorb after squeeze") + self._absorbed.extend(data) + + def squeeze(self, size: int) -> bytes: + if self._seed is None: + self._seed = bytes(self._absorbed) + start = self._squeeze_offset + end = start + size + stream = squeeze_transcript_bytes(self._hash_fn, self._seed, end) + self._squeeze_offset = end + return stream[start:end] def suite_id(curve: CurveVariant) -> bytes: - return cast(bytes, curve.curve.SUITE_ID or curve.curve.SUITE_STRING) + return curve.curve.params.suite_id def scalar_len(curve: CurveVariant) -> int: - return (cast(int, curve.curve.ORDER).bit_length() + 7) // 8 + return (curve.curve.params.subgroup_order.bit_length() + 7) // 8 + + +def point_len(curve: CurveVariant) -> int: + encoding = curve.curve.params.encoding + return encoding.point_len * (2 if encoding.uncompressed else 1) def scalar_encode(curve: CurveVariant, value: int) -> bytes: - return int(value % curve.curve.ORDER).to_bytes(scalar_len(curve), "little") + return int(value % curve.curve.params.subgroup_order).to_bytes(scalar_len(curve), "little") def scalar_decode(curve: CurveVariant, value: bytes) -> int: return int.from_bytes(value, "little") -def expanded_scalar_len(curve: CurveVariant, sec_bits: int = SECURITY_PARAMETER) -> int: - return (cast(int, curve.curve.ORDER).bit_length() + sec_bits + 7) // 8 +def _expanded_scalar_len(curve: CurveVariant, sec_bits: int = SECURITY_PARAMETER) -> int: + return (curve.curve.params.subgroup_order.bit_length() + sec_bits + 7) // 8 -def transcript_for(curve: CurveVariant) -> SpecTranscript: - return SpecTranscript(suite_id(curve), curve.curve.TRANSCRIPT_HASH) +def _transcript_for(curve: CurveVariant) -> VrfTranscript: + return VrfTranscript(suite_id(curve), curve.curve.params.hash_fn) -def nonce_scalar(curve: CurveVariant, transcript: SpecTranscript) -> int: - return int.from_bytes(transcript.squeeze_raw(expanded_scalar_len(curve)), "little") % cast(int, curve.curve.ORDER) +def _nonce_scalar(curve: CurveVariant, transcript: VrfTranscript) -> int: + return int.from_bytes(transcript.squeeze(_expanded_scalar_len(curve)), "little") % curve.curve.params.subgroup_order -def challenge_scalar(curve: CurveVariant, transcript: SpecTranscript) -> int: - return int.from_bytes(transcript.squeeze_raw(CHALLENGE_LEN), "little") % cast(int, curve.curve.ORDER) +def _challenge_scalar(curve: CurveVariant, transcript: VrfTranscript) -> int: + return int.from_bytes(transcript.squeeze(CHALLENGE_LEN), "little") % curve.curve.params.subgroup_order -def nonce(curve: CurveVariant, secret_scalar: int, transcript: SpecTranscript | None = None) -> int: - t = transcript.clone() if transcript is not None else transcript_for(curve) +def nonce(curve: CurveVariant, secret_scalar: int, transcript: VrfTranscript | None = None) -> int: + t = transcript.copy() if transcript is not None else _transcript_for(curve) - t_exp = t.clone() - t_exp.absorb_raw(bytes([DomSep.NONCE_EXPAND])) - t_exp.absorb_scalar(curve, secret_scalar) - secret_hash = t_exp.squeeze_raw(64) + t_exp = t.copy() + t_exp.absorb(bytes([DomSep.NONCE_EXPAND])) + t_exp.absorb(scalar_encode(curve, secret_scalar)) + secret_hash = t_exp.squeeze(64) - t.absorb_raw(bytes([DomSep.NONCE])) - t.absorb_raw(secret_hash) - return nonce_scalar(curve, t) + t.absorb(bytes([DomSep.NONCE])) + t.absorb(secret_hash) + return _nonce_scalar(curve, t) -def challenge(curve: CurveVariant, points: list[CurvePoint], transcript: SpecTranscript | None = None) -> int: - t = transcript.clone() if transcript is not None else transcript_for(curve) - t.absorb_raw(bytes([DomSep.CHALLENGE])) +def challenge(curve: CurveVariant, points: list[CurvePoint], transcript: VrfTranscript | None = None) -> int: + t = transcript.copy() if transcript is not None else _transcript_for(curve) + t.absorb(bytes([DomSep.CHALLENGE])) for point in points: - t.absorb_point(point) - return challenge_scalar(curve, t) + t.absorb(point.point_to_string()) + return _challenge_scalar(curve, t) def point_to_hash(curve: CurveVariant, point: CurvePoint, size: int = 32) -> bytes: - t = transcript_for(curve) - t.absorb_raw(bytes([DomSep.POINT_TO_HASH])) - t.absorb_point(point) - return t.squeeze_raw(size) + t = _transcript_for(curve) + t.absorb(bytes([DomSep.POINT_TO_HASH])) + t.absorb(point.point_to_string()) + return t.squeeze(size) def vrf_transcript( @@ -76,20 +113,17 @@ def vrf_transcript( scheme: DomSep, ios: list[VrfIo], ad: bytes, -) -> tuple[SpecTranscript, VrfIo]: +) -> tuple[VrfTranscript, VrfIo]: t, scalars = vrf_transcript_scalars(curve, scheme, ios, ad) if not ios: - zero = curve.point.identity() + zero = curve.identity() return t, VrfIo(zero, zero) if len(ios) == 1: return t, ios[0] - input_acc = curve.point.identity() - output_acc = curve.point.identity() - for io in ios: - z = scalars.next() - input_acc = input_acc + io.input * z - output_acc = output_acc + io.output * z + zs = [scalars.next() for _ in ios] + input_acc = curve.msm([io.input for io in ios], zs) + output_acc = curve.msm([io.output for io in ios], zs) return t, VrfIo(input_acc, output_acc) @@ -98,32 +132,34 @@ def vrf_transcript_scalars( scheme: DomSep, ios: list[VrfIo], ad: bytes, -) -> tuple[SpecTranscript, DelinearizeScalars]: - t = transcript_for(curve) - t.absorb_raw(bytes([scheme])) - t.absorb_raw(len(ios).to_bytes(8, "little")) +) -> tuple[VrfTranscript, DelinearizeScalars]: + from dot_ring.vrf.delinearize import DelinearizeScalars + + t = _transcript_for(curve) + t.absorb(bytes([scheme])) + t.absorb(len(ios).to_bytes(8, "little")) for io in ios: - t.absorb_raw(io.to_bytes()) - t.absorb_raw(len(ad).to_bytes(8, "little")) - t.absorb_raw(ad) + t.absorb(io.to_bytes()) + t.absorb(len(ad).to_bytes(8, "little")) + t.absorb(ad) return t, DelinearizeScalars(curve, t) def schnorr_ios(curve: CurveVariant, public_key: CurvePoint, ios: list[VrfIo]) -> list[VrfIo]: - generator = curve.point.generator_point() + generator = curve.generator_point() return [VrfIo(generator, public_key), *ios] def secret_from_seed_scalar(curve: CurveVariant, seed: bytes) -> int: if len(seed) != 32: raise ValueError("seed must be exactly 32 bytes") - base_secret = int.from_bytes(seed, "little") % curve.curve.ORDER + base_secret = int.from_bytes(seed, "little") % curve.curve.params.subgroup_order counter = 0 while True: - t = transcript_for(curve) - t.absorb_raw(seed) + t = _transcript_for(curve) + t.absorb(seed) if counter: - t.absorb_raw(bytes([counter])) + t.absorb(bytes([counter])) secret = nonce(curve, base_secret, t) if secret != 0: return secret @@ -132,36 +168,47 @@ def secret_from_seed_scalar(curve: CurveVariant, seed: bytes) -> int: raise RuntimeError("failed to derive non-zero secret scalar") -def hash_to_curve_tai(point_cls: Any, data: bytes) -> CurvePoint: - curve = point_cls.curve - field_len = (curve.PRIME_FIELD.bit_length() + 7) // 8 +def squeeze_transcript_bytes(hash_fn: Any, absorbed: bytes, size: int) -> bytes: + hasher = hash_fn() + if hasher.name in {"shake_128", "shake_256"}: + hasher.update(absorbed) + return hasher.digest(size) + + seed = hash_fn(absorbed).digest() + block_size = len(seed) + block_count = (size + block_size - 1) // block_size + return b"".join(hash_fn(seed + counter.to_bytes(8, "little")).digest() for counter in range(block_count))[:size] + + +def hash_to_curve_tai(point_type: Any, data: bytes, curve: Any) -> CurvePoint: + field_len = (curve.params.field_modulus.bit_length() + 7) // 8 - prefix = SpecTranscript(curve.SUITE_ID or curve.SUITE_STRING, curve.TRANSCRIPT_HASH) - prefix.absorb_raw(bytes([DomSep.HASH_TO_CURVE])) - prefix.absorb_raw(len(data).to_bytes(8, "little")) - prefix.absorb_raw(data) + prefix = VrfTranscript(curve.params.suite_id, curve.params.hash_fn) + prefix.absorb(bytes([DomSep.HASH_TO_CURVE])) + prefix.absorb(len(data).to_bytes(8, "little")) + prefix.absorb(data) for counter in range(256): - t = prefix.clone() - t.absorb_raw(bytes([counter])) - candidate = bytearray(t.squeeze_raw(field_len)) - if hasattr(curve, "WeierstrassA"): - shave = field_len * 8 - curve.PRIME_FIELD.bit_length() + t = prefix.copy() + t.absorb(bytes([counter])) + candidate = bytearray(t.squeeze(field_len)) + if hasattr(curve.params, "a") and hasattr(curve.params, "b"): + shave = field_len * 8 - curve.params.field_modulus.bit_length() if shave: candidate[-1] &= (1 << (8 - shave)) - 1 candidate = bytearray(candidate + b"\x80") else: sign = candidate[-1] & 0x80 - shave = field_len * 8 - curve.PRIME_FIELD.bit_length() + shave = field_len * 8 - curve.params.field_modulus.bit_length() if shave: candidate[-1] &= (1 << (8 - shave)) - 1 candidate[-1] |= sign try: - point = point_cls.string_to_point(bytes(candidate)) + point = point_type.string_to_point(bytes(candidate), curve) except ValueError: continue - if point.curve.COFACTOR > 1: - point = point * point.curve.COFACTOR + if point.curve.params.cofactor > 1: + point = point * point.curve.params.cofactor if not point.is_identity(): return point raise ValueError("hash_to_curve_tai failed") diff --git a/dot_ring/vrf/transcript_constants.py b/dot_ring/vrf/transcript_constants.py deleted file mode 100644 index 49a1889..0000000 --- a/dot_ring/vrf/transcript_constants.py +++ /dev/null @@ -1,2 +0,0 @@ -SECURITY_PARAMETER = 128 -CHALLENGE_LEN = SECURITY_PARAMETER // 8 diff --git a/dot_ring/vrf/vrf.py b/dot_ring/vrf/vrf.py index 36d91b6..79e194e 100644 --- a/dot_ring/vrf/vrf.py +++ b/dot_ring/vrf/vrf.py @@ -2,15 +2,27 @@ import hashlib import hmac +from dataclasses import dataclass from typing import Any, ClassVar, Generic, Literal, Self, TypeVar, cast from ..curve.curve import CurveVariant from ..curve.point import CurvePoint from ..ring_proof.helpers import Helpers +from .transcript import point_len, scalar_len C = TypeVar("C", bound=CurveVariant) +@dataclass(frozen=True, slots=True) +class PreparedSecretKey(Generic[C]): + """Decoded VRF secret scalar with its matching public key.""" + + curve: CurveVariant + secret_scalar: int + public_key: CurvePoint + public_key_bytes: bytes + + class VRF(Generic[C]): """ Base VRF (Verifiable Random Function) implementation. @@ -54,17 +66,16 @@ def generate_nonce(cls, secret_key: int, input_point: CurvePoint) -> int: int: Generated nonce """ # Hash secret key (little-endian) - scalr_len = (cls.cv.curve.ORDER.bit_length() + 7) // 8 + params = cls.cv.curve.params + scalr_len = scalar_len(cls.cv) sk_encoded = Helpers.int_to_str( - secret_key % cls.cv.curve.ORDER, - cast(Literal["little", "big"], cls.cv.curve.ENDIAN), + secret_key % params.subgroup_order, + cast(Literal["little", "big"], cls.cv.curve.encoding_endian()), scalr_len, ) hash_len = 2 * scalr_len hashed_sk = cls.cv.curve.hash(sk_encoded, hash_len) sk_hash = hashed_sk[scalr_len:hash_len] - # sk_hash_int was unused - # sk_hash_int = Helpers.str_to_int(sk_hash, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) # Concatenate with input point encoding if isinstance(input_point, int): raise ValueError("Input point must be a CurvePoint") @@ -72,10 +83,10 @@ def generate_nonce(cls, secret_key: int, input_point: CurvePoint) -> int: data = sk_hash + input_point_octet # Generate final nonce nonce_hash = cls.cv.curve.hash(data, hash_len) - nonce = Helpers.str_to_int(nonce_hash, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) + nonce = Helpers.str_to_int(nonce_hash, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) # Calculate k = nonce % order - k = nonce % cls.cv.curve.ORDER + k = nonce % params.subgroup_order return k @classmethod @@ -83,7 +94,7 @@ def challenge(cls, points: list[CurvePoint], additional_data: bytes) -> int: """ Generate VRF challenge according to RFC 9381. - The challenge length is determined by the curve's CHALLENGE_LENGTH parameter, + The challenge length is determined by the curve params encoding challenge length, which is typically set based on the curve's security level. Args: @@ -91,10 +102,11 @@ def challenge(cls, points: list[CurvePoint], additional_data: bytes) -> int: additional_data: Additional data to include in challenge Returns: - int: Generated challenge in the range [0, curve.ORDER) + int: Generated challenge in the range [0, curve.params.subgroup_order) """ # Create challenge string with domain separator (0x02) - challenge_string = cls.cv.curve.SUITE_STRING + bytes([0x02]) + params = cls.cv.curve.params + challenge_string = params.suite_id + bytes([0x02]) # Add point encodings for each point in the challenge for point in points: @@ -104,24 +116,26 @@ def challenge(cls, points: list[CurvePoint], additional_data: bytes) -> int: hash_input = challenge_string + additional_data + bytes([0x00]) # Generate hash output - hash_output = cls.cv.curve.hash(hash_input, cls.cv.curve.CHALLENGE_LENGTH) + challenge_len = params.encoding.challenge_len + hash_output = cls.cv.curve.hash(hash_input, challenge_len) # Truncate to the curve's specified challenge length - challenge_hash = hash_output[: cls.cv.curve.CHALLENGE_LENGTH] + challenge_hash = hash_output[:challenge_len] # Convert to integer and reduce modulo curve order - return Helpers.b_endian_2_int(challenge_hash) % cls.cv.curve.ORDER + return Helpers.b_endian_2_int(challenge_hash) % params.subgroup_order @classmethod def ecvrf_nonce_rfc6979(cls, secret_scalar: int, h_string: bytes, hash_func: str = "sha256") -> int: """ - nonce generation as per rfc_6979 - Deterministically derives a nonce from secret scalar and input bytes. - Simplified: one HMAC pass, no loop. + Deterministically derive a nonce from secret scalar and input bytes. + + This follows the RFC 6979 HMAC construction but samples once instead of + looping until the candidate is in range. """ hasher = getattr(hashlib, hash_func) hlen = hasher().digest_size - q = cls.cv.curve.ORDER + q = cls.cv.curve.params.subgroup_order # Convert inputs x_bytes = secret_scalar.to_bytes((q.bit_length() + 7) // 8, "big") h1 = hasher(h_string).digest() @@ -141,7 +155,7 @@ def ecvrf_nonce_rfc6979(cls, secret_scalar: int, h_string: bytes, hash_func: str # Interpret V as integer and mod q k = int.from_bytes(V, "big") % q if k == 0: - k = 1 # (optional) avoid zero, as per RFC6979 loop idea + k = 1 return k @classmethod @@ -159,17 +173,14 @@ def ecvrf_decode_proof(cls, pi_string: bytes | str) -> tuple[CurvePoint, int, in pi_string = bytes.fromhex(pi_string) # Get lengths from curve parameters - challenge_len = cls.cv.curve.CHALLENGE_LENGTH # Dynamic challenge length from curve - scalar_len = (cls.cv.curve.ORDER.bit_length() + 7) // 8 # Scalar length based on curve order + challenge_len = cls.cv.curve.params.encoding.challenge_len + scalar_size = scalar_len(cls.cv) # Calculate positions in the proof - gamma_end = cls.cv.curve.POINT_LEN - - if cls.cv.curve.UNCOMPRESSED: - gamma_end *= 2 + gamma_end = point_len(cls.cv) c_end = gamma_end + challenge_len - s_end = c_end + scalar_len + s_end = c_end + scalar_size # Extract components gamma_string = pi_string[:gamma_end] @@ -178,13 +189,13 @@ def ecvrf_decode_proof(cls, pi_string: bytes | str) -> tuple[CurvePoint, int, in # Convert to appropriate types] try: - gamma = cls.cv.point.string_to_point(gamma_string) + gamma = cls.cv.string_to_point(gamma_string) except ValueError as exc: raise ValueError("Invalid gamma point") from exc - C = Helpers.str_to_int(c_string, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) - S = Helpers.str_to_int(s_string, cast(Literal["little", "big"], cls.cv.curve.ENDIAN)) + C = Helpers.str_to_int(c_string, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) + S = Helpers.str_to_int(s_string, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) - if S >= cls.cv.curve.ORDER: + if S >= cls.cv.curve.params.subgroup_order: raise ValueError("Response scalar S is not less than the curve order") return gamma, C, S @@ -217,14 +228,14 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: proof_to_hash_domain_separator_front = b"\x03" proof_to_hash_domain_separator_back = b"\x00" beta_string = cls.cv.curve.hash( - cls.cv.curve.SUITE_STRING + cls.cv.curve.params.suite_id + proof_to_hash_domain_separator_front + ( gamma # In some cases, we don't want to multiply by the cofactor. # https://github.com/davxy/ark-ec-vrfs/issues/52 if not mul_cofactor - else gamma * cls.cv.curve.COFACTOR + else gamma * cls.cv.curve.params.cofactor ).point_to_string() + proof_to_hash_domain_separator_back, ) @@ -233,11 +244,19 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: @classmethod def get_public_key(cls, secret_key: bytes) -> bytes: """Take the Secret_Key and return Public Key""" + return cls.prepare_secret_key(secret_key).public_key_bytes + + @classmethod + def prepare_secret_key(cls, secret_key: bytes) -> PreparedSecretKey[C]: + """Decode a secret key once and compute its matching public key.""" from dot_ring.vrf.transcript import scalar_decode secret_key_int = scalar_decode(cls.cv, secret_key) - # Create generator point - generator = cls.cv.point.generator_point() + generator = cls.cv.generator_point() public_key: CurvePoint = cast(Any, generator) * secret_key_int - p_k = public_key.point_to_string() - return p_k + return PreparedSecretKey( + curve=cls.cv, + secret_scalar=secret_key_int, + public_key=public_key, + public_key_bytes=public_key.point_to_string(), + ) From 73b37e69da7ec35672610bebac8345a4a16a4a66 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:39:55 +0530 Subject: [PATCH 25/33] reshape ring vrf --- dot_ring/scripts/export_python_proof.py | 36 ++- dot_ring/vrf/ring/__init__.py | 13 +- dot_ring/vrf/ring/batch_verifier.py | 283 ++++++++++++++++++ .../vrf/ring/{ring_context.py => context.py} | 41 ++- dot_ring/vrf/ring/{ring.py => members.py} | 62 ++-- ...ring_proof_builder.py => proof_builder.py} | 68 +++-- ...ring_proof_payload.py => proof_payload.py} | 23 +- dot_ring/vrf/ring/ring_batch_item.py | 19 -- dot_ring/vrf/ring/ring_batch_verifier.py | 56 ---- dot_ring/vrf/ring/ring_keys.py | 10 - dot_ring/vrf/ring/ring_root.py | 83 ----- dot_ring/vrf/ring/ring_serialization.py | 45 --- .../vrf/ring/ring_verifier_key_builder.py | 24 -- dot_ring/vrf/ring/root.py | 198 ++++++++++++ dot_ring/vrf/ring/{ring_vrf.py => vrf.py} | 148 ++++----- tests/test_dot_ring_vectors.py | 2 +- tests/test_ring_vrf/test_audit_regressions.py | 56 +++- tests/test_ring_vrf/test_ring_vrf.py | 4 +- 18 files changed, 762 insertions(+), 409 deletions(-) create mode 100644 dot_ring/vrf/ring/batch_verifier.py rename dot_ring/vrf/ring/{ring_context.py => context.py} (57%) rename dot_ring/vrf/ring/{ring.py => members.py} (59%) rename dot_ring/vrf/ring/{ring_proof_builder.py => proof_builder.py} (66%) rename dot_ring/vrf/ring/{ring_proof_payload.py => proof_payload.py} (81%) delete mode 100644 dot_ring/vrf/ring/ring_batch_item.py delete mode 100644 dot_ring/vrf/ring/ring_batch_verifier.py delete mode 100644 dot_ring/vrf/ring/ring_keys.py delete mode 100644 dot_ring/vrf/ring/ring_root.py delete mode 100644 dot_ring/vrf/ring/ring_serialization.py delete mode 100644 dot_ring/vrf/ring/ring_verifier_key_builder.py create mode 100644 dot_ring/vrf/ring/root.py rename dot_ring/vrf/ring/{ring_vrf.py => vrf.py} (67%) diff --git a/dot_ring/scripts/export_python_proof.py b/dot_ring/scripts/export_python_proof.py index 3f4a46a..613c966 100644 --- a/dot_ring/scripts/export_python_proof.py +++ b/dot_ring/scripts/export_python_proof.py @@ -18,12 +18,11 @@ from py_ecc.optimized_bls12_381 import normalize as nm -from dot_ring.curve.specs.bandersnatch import Bandersnatch, BandersnatchParams, BandersnatchPoint -from dot_ring.ring_proof.curve.bandersnatch import TwistedEdwardCurve +from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.srs import srs -from dot_ring.vrf.ring.ring_proof_builder import RingProofBuilder -from dot_ring.vrf.ring.ring_root import Ring, RingRoot +from dot_ring.vrf.ring.proof_builder import RingProofBuilder +from dot_ring.vrf.ring import Ring, RingRoot from tests.utils.python_to_rust_serde import ( serialize_bls12_381_g1, serialize_bls12_381_g2, @@ -43,6 +42,29 @@ blinding_factor = int.from_bytes(bytes.fromhex("2e98974f0b99a70d4fbe7c1ea62a5ada75c899deb30e9d27f9e5da79177c0619"), "big") +def _as_bandersnatch_point(point: tuple[int | None, int | None]): + x, y = point + if x is None or y is None: + return Bandersnatch.identity() + return Bandersnatch.point(int(x), int(y)) + + +def _bandersnatch_mul(k: int, point: tuple[int | None, int | None]) -> tuple[int | None, int | None]: + if point[0] is None or point[1] is None: + result = Bandersnatch.identity() + else: + result = _as_bandersnatch_point(point) * k + return result.x, result.y + + +def _bandersnatch_add( + point1: tuple[int | None, int | None], + point2: tuple[int | None, int | None], +) -> tuple[int | None, int | None]: + result = _as_bandersnatch_point(point1) + _as_bandersnatch_point(point2) + return result.x, result.y + + @dataclass(frozen=True) class VariantSpec: name: str @@ -96,7 +118,7 @@ def generate_test_keys( if prover_index >= num_keys: raise ValueError("prover_index must be < num_keys") - base = BandersnatchPoint(BandersnatchParams.GENERATOR_X, BandersnatchParams.GENERATOR_Y) + base = Bandersnatch.generator_point() keys_bytes: list[bytes] = [] keys_points: list[tuple[int, int]] = [] @@ -163,8 +185,8 @@ def export_variant(variant: VariantSpec, output_dir: Path) -> dict[str, Any]: ) # Compute result point (blinded public key) - result_point = TwistedEdwardCurve.mul(blinding_factor, Blinding_Base) - result_point = TwistedEdwardCurve.add(result_point, producer_key_point) + result_point = _bandersnatch_mul(blinding_factor, Blinding_Base) + result_point = _bandersnatch_add(result_point, producer_key_point) # Unpack proof components ( diff --git a/dot_ring/vrf/ring/__init__.py b/dot_ring/vrf/ring/__init__.py index 655a2d6..8106362 100644 --- a/dot_ring/vrf/ring/__init__.py +++ b/dot_ring/vrf/ring/__init__.py @@ -1,14 +1,13 @@ -from .ring import Ring -from .ring_batch_item import RingBatchItem -from .ring_batch_verifier import RingBatchVerifier -from .ring_context import RingContext, RingSetup -from .ring_root import RingRoot -from .ring_verifier_key_builder import RingVerifierKeyBuilder -from .ring_vrf import RingVRF +from .members import Ring +from .batch_verifier import RingBatchContext, RingBatchItem, RingBatchVerifier +from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .root import RingRoot +from .vrf import RingVRF __all__ = [ "Ring", "RingBatchItem", + "RingBatchContext", "RingBatchVerifier", "RingContext", "RingRoot", diff --git a/dot_ring/vrf/ring/batch_verifier.py b/dot_ring/vrf/ring/batch_verifier.py new file mode 100644 index 0000000..d69d2c7 --- /dev/null +++ b/dot_ring/vrf/ring/batch_verifier.py @@ -0,0 +1,283 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import TYPE_CHECKING, Any, cast + +from dot_ring.ring_proof.pcs.utils import ( + LinearPcsVerification, + g1_to_blst, + pcs_transcript_g1, +) +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript +from dot_ring.ring_proof.verify import Verify, prepare_linear_pcs_verifications_fast +from dot_ring.vrf.pedersen.batch_verifier import PedersenBatchItem +from dot_ring.vrf.pedersen.batch_verifier import PedersenBatchVerifier +from dot_ring.vrf.transcript import VrfIo + +from .members import Ring +from .root import RingRoot + +if TYPE_CHECKING: + from .vrf import RingVRF + + +@dataclass +class RingBatchItem: + curve: Any + pedersen: PedersenBatchItem + pcs: Any + verifications: tuple[Any, Any] + + +@dataclass(frozen=True) +class RingBatchContext: + ring: Ring + ring_root: RingRoot + fixed_cols_cmts: list[Any] + fixed_cols_blst: tuple[Any, Any, Any] + verifier_key: dict[str, Any] + transcript_prefix: FiatShamirTranscript + seed_point: tuple[int, int] + domain: list[int] + padding_rows: int + omega: int + ring_edwards_a: int + domain_size_inv: int + + @classmethod + def from_ring(cls, ring: Ring, ring_root: RingRoot, *, validate_ring_root: bool = True) -> RingBatchContext: + if validate_ring_root and not ring_root.matches_ring(ring): + raise ValueError("ring root does not match ring") + fixed_cols_cmts, verifier_key = ring_root.verifier_key(ring.params) + transcript_prefix = ring_root.verifier_transcript_prefix(ring.params) + domain = ring.params.domain + return cls( + ring=ring, + ring_root=ring_root, + fixed_cols_cmts=fixed_cols_cmts, + fixed_cols_blst=tuple(g1_to_blst(commitment) for commitment in fixed_cols_cmts), + verifier_key=verifier_key, + transcript_prefix=transcript_prefix, + seed_point=ring.params.seed_point, + domain=domain, + padding_rows=ring.params.padding_rows, + omega=ring.params.omega, + ring_edwards_a=ring.params.ring_edwards_a, + domain_size_inv=pow(ring.params.domain_size, -1, ring.params.prime), + ) + + @property + def key(self) -> tuple[int, int]: + return (id(self.ring), id(self.ring_root)) + + def _decode_message_point(self, message: Any) -> Any: + if not isinstance(message, bytes): + return message + try: + return self.ring.params.cv.string_to_point(message) + except ValueError as exc: + raise ValueError("Invalid message point") from exc + + def ring_proof_verifier(self, proof: RingVRF, message: Any) -> Verify: + ring = self.ring + message = self._decode_message_point(message) + + rltn, res_plus_seed = _proof_relation_points(proof, message, ring.params) + witness_commitments, quotient_commitment = _proof_transcript_commitments(proof, ring.params) + + return Verify( + proof.ring_proof_tuple(), + self.verifier_key, + self.fixed_cols_cmts, + rltn, + res_plus_seed, + self.seed_point, + self.domain, + transcript_challenge=proof.cv.curve.params.suite_id, + padding_rows=self.padding_rows, + edwards_a=self.ring_edwards_a, + prime=ring.params.prime, + omega=self.omega, + pcs=ring.params.pcs, + transcript_prefix=self.transcript_prefix, + domain_size_inv=self.domain_size_inv, + transcript_witness_commitments=witness_commitments, + transcript_quotient_commitment=quotient_commitment, + ) + + def ring_pcs_verifications(self, proof: RingVRF, message: Any) -> tuple[LinearPcsVerification, LinearPcsVerification]: + ring = self.ring + message = self._decode_message_point(message) + + rltn, res_plus_seed = _proof_relation_points(proof, message, ring.params) + witness_commitments, quotient_commitment = _proof_transcript_commitments(proof, ring.params) + return prepare_linear_pcs_verifications_fast( + proof.ring_proof_tuple(), + self.fixed_cols_blst, + rltn, + res_plus_seed, + self.seed_point, + self.domain, + self.padding_rows, + self.domain_size_inv, + self.ring_edwards_a, + self.omega, + self.transcript_prefix, + witness_commitments, + quotient_commitment, + ) + + def prepare_ios(self, proof: RingVRF, ios: list[VrfIo], ad: bytes) -> RingBatchItem: + return _prepare_ios_with_context(proof, ios, ad, self) + + def prepare(self, proof: RingVRF, input: bytes, ad: bytes) -> RingBatchItem: + pedersen_proof = proof.require_pedersen_proof() + input_point = proof.cv.encode_to_curve(input) + return self.prepare_ios(proof, [VrfIo(input_point, pedersen_proof.output_point)], ad) + + def _prepare_pedersen_item(self, proof: RingVRF, ios: list[VrfIo], ad: bytes) -> Any: + pedersen_proof = proof.require_pedersen_proof() + if self.ring.params.cv.name != proof.cv.name: + raise ValueError("proof curve does not match ring curve") + return PedersenBatchVerifier(proof.cv).prepare(ios, ad, pedersen_proof) + + def _prepare_pcs_item(self, proof: RingVRF, message: Any) -> tuple[Any, tuple[LinearPcsVerification, LinearPcsVerification]]: + pcs = self.ring.params.pcs + # Deferred linear KZG verification avoids materializing small commitments before batching. + return pcs, self.ring_pcs_verifications(proof, message) + + +def _prepare_ios_with_context(proof: RingVRF, ios: list[VrfIo], ad: bytes, context: RingBatchContext) -> RingBatchItem: + pedersen_item = context._prepare_pedersen_item(proof, ios, ad) + pcs, verifications = context._prepare_pcs_item(proof, proof.require_pedersen_proof().blinded_pk) + return RingBatchItem(proof.cv, pedersen_item, pcs, verifications) + + +def _proof_transcript_commitments(proof: RingVRF, params: Any) -> tuple[bytes, Any]: + commitments = ( + proof.c_b.commitment, + proof.c_accip.commitment, + proof.c_accx.commitment, + proof.c_accy.commitment, + proof.c_q.commitment, + ) + transcript_commitments = tuple(pcs_transcript_g1(params.pcs, commitment) for commitment in commitments) + witness_commitments = b"".join(transcript_commitments[:4]) + quotient_commitment = transcript_commitments[4] + return witness_commitments, quotient_commitment + + +def _proof_relation_points(proof: RingVRF, message: Any, params: Any) -> tuple[tuple[int, int], tuple[int, int]]: + relation = params.point_to_ring_point(message) + result_plus_seed = params.add_points(params.seed_point, relation) + return relation, result_plus_seed + + +def _verify_pcs_batch(pcs: Any, verifications: list[Any]) -> bool: + return bool(pcs.batch_verify_linear_preconverted(cast(list[LinearPcsVerification], verifications))) + + +class RingBatchVerifier: + def __init__(self, context: RingBatchContext | None = None) -> None: + self.items: list[RingBatchItem] = [] + self.pedersen_batches: dict[str, PedersenBatchVerifier] = {} + self.pcs_batches: dict[int, tuple[Any, list[Any]]] = {} + self._contexts: dict[tuple[int, int], RingBatchContext] = {} + if context is not None: + self._contexts[context.key] = context + self._invalid = False + + @staticmethod + def prepare_context(ring: Ring, ring_root: RingRoot, *, validate_ring_root: bool = True) -> RingBatchContext: + return RingBatchContext.from_ring(ring, ring_root, validate_ring_root=validate_ring_root) + + @staticmethod + def prepare_ios( + proof: RingVRF, + ios: list[VrfIo], + ad: bytes, + ring: Ring, + ring_root: RingRoot, + *, + validate_ring_root: bool = True, + ) -> RingBatchItem: + context = RingBatchContext.from_ring(ring, ring_root, validate_ring_root=validate_ring_root) + return context.prepare_ios(proof, ios, ad) + + @staticmethod + def prepare(proof: RingVRF, input: bytes, ad: bytes, ring: Ring, ring_root: RingRoot, *, validate_ring_root: bool = True) -> RingBatchItem: + pedersen_proof = proof.require_pedersen_proof() + curve = proof.cv + input_point = curve.encode_to_curve(input) + return RingBatchVerifier.prepare_ios( + proof, + [VrfIo(input_point, pedersen_proof.output_point)], + ad, + ring, + ring_root, + validate_ring_root=validate_ring_root, + ) + + def push_prepared(self, item: RingBatchItem) -> None: + self.items.append(item) + pedersen_batch = self.pedersen_batches.setdefault(item.curve.name, PedersenBatchVerifier(item.curve)) + pedersen_batch.push_prepared(item.pedersen) + _, verifications = self.pcs_batches.setdefault(id(item.pcs), (item.pcs, [])) + verifications.extend(item.verifications) + + def push_ios( + self, + proof: RingVRF, + ios: list[VrfIo], + ad: bytes, + ring: Ring, + ring_root: RingRoot, + *, + validate_ring_root: bool = True, + ) -> None: + try: + root_key = (id(ring), id(ring_root)) + context = self._contexts.get(root_key) + if context is None: + context = RingBatchContext.from_ring(ring, ring_root, validate_ring_root=validate_ring_root) + self._contexts[root_key] = context + self.push_prepared(context.prepare_ios(proof, ios, ad)) + except (AssertionError, TypeError, ValueError): + self._invalid = True + + def push( + self, + proof: RingVRF, + input: bytes, + ad: bytes, + ring: Ring, + ring_root: RingRoot, + *, + validate_ring_root: bool = True, + ) -> None: + try: + root_key = (id(ring), id(ring_root)) + context = self._contexts.get(root_key) + if context is None: + context = RingBatchContext.from_ring(ring, ring_root, validate_ring_root=validate_ring_root) + self._contexts[root_key] = context + self.push_prepared(context.prepare(proof, input, ad)) + except (AssertionError, TypeError, ValueError): + self._invalid = True + + def verify(self) -> bool: + if self._invalid: + return False + if not self.items: + return True + + if not all(batch.verify() for batch in self.pedersen_batches.values()): + return False + + try: + return all( + _verify_pcs_batch(pcs, verifications) + for pcs, verifications in self.pcs_batches.values() + ) + except (AssertionError, TypeError, ValueError): + return False diff --git a/dot_ring/vrf/ring/ring_context.py b/dot_ring/vrf/ring/context.py similarity index 57% rename from dot_ring/vrf/ring/ring_context.py rename to dot_ring/vrf/ring/context.py index a3a7b14..6239c1c 100644 --- a/dot_ring/vrf/ring/ring_context.py +++ b/dot_ring/vrf/ring/context.py @@ -1,18 +1,13 @@ from __future__ import annotations from collections.abc import Sequence -from dataclasses import dataclass -from typing import TYPE_CHECKING +from dataclasses import dataclass, field from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.params import RingProofParams -from .ring import Ring -from .ring_keys import parse_concatenated_keys -from .ring_root import RingRoot - -if TYPE_CHECKING: - from .ring_verifier_key_builder import RingVerifierKeyBuilder +from .members import Ring, parse_concatenated_keys +from .root import RingRoot RingSetup = RingProofParams @@ -48,6 +43,32 @@ def verifier_key_from_commitment(self, commitment: RingRoot | bytes) -> RingRoot return RingRoot.from_bytes(commitment, self.setup) def verifier_key_builder(self) -> RingVerifierKeyBuilder: - from .ring_verifier_key_builder import RingVerifierKeyBuilder - return RingVerifierKeyBuilder(self) + + +@dataclass +class RingVerifierKeyBuilder: + context: RingContext + keys: list[bytes] = field(default_factory=list) + _root: RingRoot | None = field(default=None, init=False, repr=False) + + def push(self, key: bytes) -> None: + self.extend([key]) + + def append(self, keys: list[bytes] | bytes) -> None: + self.extend(keys) + + def extend(self, keys: list[bytes] | bytes) -> None: + if isinstance(keys, bytes): + keys = parse_concatenated_keys(keys, self.context.setup.cv) + if len(self.keys) + len(keys) > self.context.max_ring_size: + raise ValueError("too many keys for ring verifier-key builder") + self.keys.extend(keys) + self._root = None + if len(self.keys) == self.context.max_ring_size: + self._root = self.context.verifier_key(self.keys) + + def finalize(self) -> RingRoot: + if self._root is None: + self._root = self.context.verifier_key(self.keys) + return self._root diff --git a/dot_ring/vrf/ring/ring.py b/dot_ring/vrf/ring/members.py similarity index 59% rename from dot_ring/vrf/ring/ring.py rename to dot_ring/vrf/ring/members.py index 025f41b..840887f 100644 --- a/dot_ring/vrf/ring/ring.py +++ b/dot_ring/vrf/ring/members.py @@ -1,38 +1,35 @@ from __future__ import annotations from collections.abc import Sequence -from functools import lru_cache -from typing import Any +from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.constants import DEFAULT_SIZE from dot_ring.ring_proof.params import RingProofParams +from dot_ring.vrf.transcript import point_len -@lru_cache(maxsize=32) -def _h_vector_cached( - blinding_base: tuple[int, int], - size: int, - point_cls: type[Any], -) -> tuple[tuple[int, int], ...]: - point = blinding_base +def parse_concatenated_keys(keys: bytes, cv: CurveVariant) -> list[bytes]: + encoded_point_len = point_len(cv) + if len(keys) % encoded_point_len != 0: + raise ValueError(f"invalid concatenated key length: expected multiple of {encoded_point_len}, got {len(keys)}") + return [keys[encoded_point_len * i : encoded_point_len * (i + 1)] for i in range(len(keys) // encoded_point_len)] + + +def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: + """Return `[2^0 * H, 2^1 * H, ...]` in ring coordinates.""" + point = params.blinding_base points = [] for _ in range(size): points.append(point) - point_obj = point_cls(point[0], point[1]) + point_obj = params.ring_point(point) result = point_obj + point_obj point = int(result.x), int(result.y) - return tuple(points) - - -def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: - """Return `[2⁰·H, 2¹·H, ...]` in short-Weierstrass coords.""" - return list(_h_vector_cached(params.blinding_base, size, params.ring_point_cls)) + return points class Ring: - nm_points: list[tuple[int, int]] + nm_points: tuple[tuple[int, int], ...] params: RingProofParams - key_index: dict[tuple[int, int], int] def __init__(self, keys: Sequence[bytes | str], params: RingProofParams | None = None) -> None: """ @@ -50,32 +47,31 @@ def __init__(self, keys: Sequence[bytes | str], params: RingProofParams | None = if len(keys) > params.max_ring_size: raise ValueError(f"ring size {len(keys)} exceeds max supported size {params.max_ring_size}") - self.nm_points = [] - self.key_index = {} + nm_points: list[tuple[int, int]] = [] for key in keys: point = self._decode_key(key) if point is None: - self.nm_points.append(params.padding_point) + nm_points.append(params.padding_point) continue - if point != params.padding_point: - self.key_index.setdefault(point, len(self.nm_points)) - self.nm_points.append(point) + nm_points.append(point) - while len(self.nm_points) < params.max_ring_size: - self.nm_points.append(params.padding_point) + while len(nm_points) < params.max_ring_size: + nm_points.append(params.padding_point) - fill_count = params.domain_size - params.padding_rows - len(self.nm_points) + fill_count = params.domain_size - params.padding_rows - len(nm_points) if fill_count > 0: h_vec = _h_vector(params, params.domain_size) - self.nm_points.extend(h_vec[:fill_count]) + nm_points.extend(h_vec[:fill_count]) if params.padding_rows > 0: - self.nm_points.extend([(0, 0)] * params.padding_rows) + nm_points.extend([(0, 0)] * params.padding_rows) + + self.nm_points = tuple(nm_points) def _decode_key(self, key: object) -> tuple[int, int] | None: if not isinstance(key, (bytes, str)): return None try: - point = self.params.cv.point.string_to_point(key) + point = self.params.cv.string_to_point(key) except ValueError: return None if point.is_identity(): @@ -86,7 +82,9 @@ def index_of(self, key: bytes | str) -> int: point = self._decode_key(key) if point is None: raise ValueError("invalid ring key") + if point == self.params.padding_point: + raise ValueError("producer key is not in ring") try: - return self.key_index[point] - except KeyError as exc: + return self.nm_points[: self.params.max_ring_size].index(point) + except ValueError as exc: raise ValueError("producer key is not in ring") from exc diff --git a/dot_ring/vrf/ring/ring_proof_builder.py b/dot_ring/vrf/ring/proof_builder.py similarity index 66% rename from dot_ring/vrf/ring/ring_proof_builder.py rename to dot_ring/vrf/ring/proof_builder.py index b302fbc..66f8242 100644 --- a/dot_ring/vrf/ring/ring_proof_builder.py +++ b/dot_ring/vrf/ring/proof_builder.py @@ -1,22 +1,23 @@ from __future__ import annotations +from collections.abc import Sequence from typing import cast from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder, require_commitment -from dot_ring.ring_proof.constraints.aggregation import aggregate_constraints from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder -from dot_ring.ring_proof.pcs.protocol import G1Commitment +from dot_ring.ring_proof.pcs.utils import pcs_transcript_g1 +from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft +from dot_ring.ring_proof.polynomial.ops import poly_multiply, vect_add, vect_scalar_mul from dot_ring.ring_proof.proof.aggregation_poly import AggPoly from dot_ring.ring_proof.proof.linearization_poly import LAggPoly from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly -from dot_ring.ring_proof.transcript.phases import phase1_alphas, phase3_nu_vector -from dot_ring.ring_proof.transcript.transcript import Transcript +from dot_ring.ring_proof.transcript.phases import phase1_alphas_after_vk, phase3_nu_vector +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript -from .ring import Ring -from .ring_proof_payload import RingProofPayload -from .ring_root import RingRoot -from .ring_serialization import transcript_g1, transcript_vk +from .members import Ring +from .proof_payload import RingProofPayload +from .root import RingRoot class RingProofBuilder: @@ -34,7 +35,7 @@ def __init__( self.producer_key = producer_key self.ring = ring self.ring_root = ring_root - self.transcript_challenge = transcript_challenge or curve.curve.SUITE_ID or curve.curve.SUITE_STRING + self.transcript_challenge = transcript_challenge or curve.curve.params.suite_id def build(self) -> RingProofPayload: params = self.ring.params @@ -66,15 +67,15 @@ def build(self) -> RingProofPayload: ) transcript, alpha = self._phase1_transcript(ring_root, relation_point, witness_columns) constraint_polys = list(constraints.compute().values()) - c_agg = aggregate_constraints(constraint_polys, alpha, params.radix_omega, params.prime, domain=params.domain) + c_agg = self._aggregate_constraints(constraint_polys, alpha) - quotient_poly = QuotientPoly(params.domain_size, params.pcs) + quotient_poly = QuotientPoly(params.domain_size, params.pcs, params.prime) q_poly, c_q = quotient_poly.quotient_poly(c_agg) c_q_column = Column(name="C_q", evals=[], commitment=c_q) l_agg = LAggPoly( transcript, - transcript_g1(params, c_q), + pcs_transcript_g1(params.pcs, c_q), [ring_root.px, ring_root.py, ring_root.s], list(witness_columns), alpha, @@ -130,26 +131,47 @@ def _phase1_transcript( ring_root: RingRoot, relation_point: tuple[int, int], witness_columns: tuple[Column, Column, Column, Column], - ) -> tuple[Transcript, list[int]]: + ) -> tuple[FiatShamirTranscript, list[int]]: params = self.ring.params c_b, c_accx, c_accy, c_accip = witness_columns witness_commitments = [ - transcript_g1(params, require_commitment(c_b)), - transcript_g1(params, require_commitment(c_accip)), - transcript_g1(params, require_commitment(c_accx)), - transcript_g1(params, require_commitment(c_accy)), + pcs_transcript_g1(params.pcs, require_commitment(c_b)), + pcs_transcript_g1(params.pcs, require_commitment(c_accip)), + pcs_transcript_g1(params.pcs, require_commitment(c_accx)), + pcs_transcript_g1(params.pcs, require_commitment(c_accy)), ] - transcript = Transcript(params.prime, self.transcript_challenge) + transcript = ring_root.verifier_transcript_prefix(params, self.transcript_challenge).copy() return cast( - tuple[Transcript, list[int]], - phase1_alphas( + tuple[FiatShamirTranscript, list[int]], + phase1_alphas_after_vk( transcript, - transcript_vk(params, _commitments(ring_root.px, ring_root.py, ring_root.s)), relation_point, witness_commitments, ), ) + def _aggregate_constraints( + self, + constraint_evals: Sequence[Sequence[int]], + alphas: Sequence[int], + ) -> list[int]: + params = self.ring.params + aggregated_evals = [0] * len(constraint_evals[0]) + for constraint, alpha in zip(constraint_evals, alphas, strict=False): + weighted_constraint = vect_scalar_mul(constraint, alpha, params.prime) + aggregated_evals = vect_add(aggregated_evals, weighted_constraint, params.prime) + + aggregated_poly = poly_interpolate_fft(aggregated_evals, params.radix_omega, params.prime) + c_agg = poly_multiply(aggregated_poly, self._tail_vanishing_polynomial(), params.prime) -def _commitments(*columns: Column) -> list[G1Commitment]: - return [require_commitment(column) for column in columns] + last_nonzero = len(c_agg) - 1 + while last_nonzero >= 0 and c_agg[last_nonzero] == 0: + last_nonzero -= 1 + return c_agg[: last_nonzero + 1] + + def _tail_vanishing_polynomial(self, k: int = 3) -> list[int]: + params = self.ring.params + vanishing = [1] + for offset in range(1, k + 1): + vanishing = poly_multiply(vanishing, [-params.domain[-offset], 1], params.prime) + return vanishing diff --git a/dot_ring/vrf/ring/ring_proof_payload.py b/dot_ring/vrf/ring/proof_payload.py similarity index 81% rename from dot_ring/vrf/ring/ring_proof_payload.py rename to dot_ring/vrf/ring/proof_payload.py index 6c99db0..84cbf84 100644 --- a/dot_ring/vrf/ring/ring_proof_payload.py +++ b/dot_ring/vrf/ring/proof_payload.py @@ -8,8 +8,13 @@ from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.opening import Opening from dot_ring.ring_proof.pcs.protocol import G1Commitment +from dot_ring.ring_proof.pcs.utils import pcs_compress_g1, pcs_decompress_g1 -from .ring_serialization import compress_g1, decompress_g1, ring_proof_len +RING_SCALAR_LEN = 32 + + +def ring_proof_len(params: RingProofParams) -> int: + return 7 * params.pcs.commitment_size + 8 * RING_SCALAR_LEN @dataclass(slots=True) @@ -70,10 +75,10 @@ def as_tuple( def to_bytes(self, params: RingProofParams) -> bytes: return b"".join( ( - compress_g1(params, require_commitment(self.c_b)), - compress_g1(params, require_commitment(self.c_accip)), - compress_g1(params, require_commitment(self.c_accx)), - compress_g1(params, require_commitment(self.c_accy)), + pcs_compress_g1(params.pcs, require_commitment(self.c_b)), + pcs_compress_g1(params.pcs, require_commitment(self.c_accip)), + pcs_compress_g1(params.pcs, require_commitment(self.c_accx)), + pcs_compress_g1(params.pcs, require_commitment(self.c_accy)), H.to_l_endian(self.px_zeta), H.to_l_endian(self.py_zeta), H.to_l_endian(self.s_zeta), @@ -81,10 +86,10 @@ def to_bytes(self, params: RingProofParams) -> bytes: H.to_l_endian(self.accip_zeta), H.to_l_endian(self.accx_zeta), H.to_l_endian(self.accy_zeta), - compress_g1(params, require_commitment(self.c_q)), + pcs_compress_g1(params.pcs, require_commitment(self.c_q)), H.to_l_endian(self.l_zeta_omega), - compress_g1(params, self.open_agg_zeta.proof), - compress_g1(params, self.open_l_zeta_omega.proof), + pcs_compress_g1(params.pcs, self.open_agg_zeta.proof), + pcs_compress_g1(params.pcs, self.open_l_zeta_omega.proof), ) ) @@ -123,7 +128,7 @@ def __init__(self, data: bytes, params: RingProofParams) -> None: def commitment(self) -> G1Commitment: commitment_size = self.params.pcs.commitment_size end = self.offset + commitment_size - commitment = decompress_g1(self.params, self.data[self.offset : end]) + commitment = pcs_decompress_g1(self.params.pcs, self.data[self.offset : end]) self.offset = end return commitment diff --git a/dot_ring/vrf/ring/ring_batch_item.py b/dot_ring/vrf/ring/ring_batch_item.py deleted file mode 100644 index 109854a..0000000 --- a/dot_ring/vrf/ring/ring_batch_item.py +++ /dev/null @@ -1,19 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import TYPE_CHECKING - -from .ring import Ring -from .ring_root import RingRoot - -if TYPE_CHECKING: - from .ring_vrf import RingVRF - - -@dataclass -class RingBatchItem: - proof: RingVRF - input: bytes - ad: bytes - ring: Ring - ring_root: RingRoot diff --git a/dot_ring/vrf/ring/ring_batch_verifier.py b/dot_ring/vrf/ring/ring_batch_verifier.py deleted file mode 100644 index fef78b8..0000000 --- a/dot_ring/vrf/ring/ring_batch_verifier.py +++ /dev/null @@ -1,56 +0,0 @@ -from __future__ import annotations - -from typing import TYPE_CHECKING, Any, cast - -from dot_ring.vrf.pedersen.pedersen_batch_verifier import PedersenBatchVerifier -from dot_ring.vrf.transcript import VrfIo - -from .ring import Ring -from .ring_batch_item import RingBatchItem -from .ring_root import RingRoot - -if TYPE_CHECKING: - from .ring_vrf import RingVRF - - -class RingBatchVerifier: - def __init__(self) -> None: - self.items: list[RingBatchItem] = [] - - def push(self, proof: RingVRF, input: bytes, ad: bytes, ring: Ring, ring_root: RingRoot) -> None: - self.items.append(RingBatchItem(proof, input, ad, ring, ring_root)) - - def verify(self) -> bool: - if not self.items: - return True - - pedersen_batches: dict[str, PedersenBatchVerifier] = {} - pcs_batches: dict[int, tuple[Any, list[tuple[Any, Any, int, int]]]] = {} - - try: - for item in self.items: - if item.proof.pedersen_proof is None: - return False - if item.ring.params.cv.name != item.proof.cv.name: - return False - - curve = item.proof.cv - input_point = cast(Any, curve.point).encode_to_curve(item.input) - pedersen_batch = pedersen_batches.setdefault(curve.name, PedersenBatchVerifier(curve)) - pedersen_batch.push([VrfIo(input_point, item.proof.pedersen_proof.output_point)], item.ad, item.proof.pedersen_proof) - - ring_verifier = item.proof._ring_proof_verifier(item.proof.pedersen_proof.blinded_pk, item.ring, item.ring_root) - pcs = item.ring.params.pcs - _, verifications = pcs_batches.setdefault(id(pcs), (pcs, [])) - verifications.append(ring_verifier._prepare_quotient_poly_verification()) - verifications.append(ring_verifier._prepare_linearization_poly_verification()) - except (AssertionError, TypeError, ValueError): - return False - - if not all(batch.verify() for batch in pedersen_batches.values()): - return False - - try: - return all(pcs.batch_verify(verifications) for pcs, verifications in pcs_batches.values()) - except (AssertionError, TypeError, ValueError): - return False diff --git a/dot_ring/vrf/ring/ring_keys.py b/dot_ring/vrf/ring/ring_keys.py deleted file mode 100644 index 6ccc0bb..0000000 --- a/dot_ring/vrf/ring/ring_keys.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import annotations - -from dot_ring.curve.curve import CurveVariant - - -def parse_concatenated_keys(keys: bytes, cv: CurveVariant) -> list[bytes]: - point_len = cv.curve.POINT_LEN * (2 if cv.curve.UNCOMPRESSED else 1) - if len(keys) % point_len != 0: - raise ValueError(f"invalid concatenated key length: expected multiple of {point_len}, got {len(keys)}") - return [keys[point_len * i : point_len * (i + 1)] for i in range(len(keys) // point_len)] diff --git a/dot_ring/vrf/ring/ring_root.py b/dot_ring/vrf/ring/ring_root.py deleted file mode 100644 index b334e2f..0000000 --- a/dot_ring/vrf/ring/ring_root.py +++ /dev/null @@ -1,83 +0,0 @@ -from dataclasses import dataclass -from typing import Any, cast - -from dot_ring.ring_proof.columns.columns import Column, require_commitment -from dot_ring.ring_proof.helpers import Helpers as H -from dot_ring.ring_proof.params import RingProofParams -from dot_ring.ring_proof.pcs.bn254_kzg import BN254KZG -from dot_ring.ring_proof.pcs.kzg import KZG - -from .ring import Ring - -BLS_G1_LEN = 48 -RING_ROOT_LEN = BLS_G1_LEN * 3 - - -@dataclass -class RingRoot: - px: Column - py: Column - s: Column - params: RingProofParams | None = None - - @classmethod - def from_ring(cls, ring: Ring, params: RingProofParams | None = None): - if params is None: - params = ring.params - # Px, Py, s points - px, py = H.unzip(ring.nm_points) - selector_vec = [1 if i < params.max_ring_size else 0 for i in range(params.domain_size)] - # Columns - px_col = Column("Px", px, size=params.domain_size) - py_col = Column("Py", py, size=params.domain_size) - s_col = Column("s", selector_vec, size=params.domain_size) - for col in (px_col, py_col, s_col): - col.interpolate(params.omega, params.prime) - col.commit(params.pcs) - return cls(px=px_col, py=py_col, s=s_col, params=params) - - def to_bytes(self) -> bytes: - pcs = self.params.pcs if self.params is not None else None - if pcs is not None and hasattr(pcs, "compress_g1"): - return b"".join( - ( - pcs.compress_g1(require_commitment(self.px)), - pcs.compress_g1(require_commitment(self.py)), - pcs.compress_g1(require_commitment(self.s)), - ) - ) - for col in (self.px, self.py, self.s): - if col.commitment is None: - raise ValueError("Ring root is missing commitments") - comm_keys = ( - H.bls_g1_compress(cast(Any, require_commitment(self.px))), - H.bls_g1_compress(cast(Any, require_commitment(self.py))), - H.bls_g1_compress(cast(Any, require_commitment(self.s))), - ) - return bytes.fromhex(comm_keys[0]) + bytes.fromhex(comm_keys[1]) + bytes.fromhex(comm_keys[2]) - - @classmethod - def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "RingRoot": - if params is not None: - commitment_size = params.pcs.commitment_size - else: - commitment_size = 32 if len(data) == 96 else 48 - expected = 3 * commitment_size - if len(data) != expected: - raise ValueError(f"invalid ring root length: ring root must be exactly {expected} bytes, got {len(data)}") - - pcs = params.pcs if params is not None else (BN254KZG if commitment_size == 32 else KZG) - if hasattr(pcs, "decompress_g1"): - px_commitment = pcs.decompress_g1(data[0:commitment_size]) - py_commitment = pcs.decompress_g1(data[commitment_size : commitment_size * 2]) - s_commitment = pcs.decompress_g1(data[commitment_size * 2 : expected]) - else: - px_commitment = H.bls_g1_decompress(data[0:BLS_G1_LEN].hex()) - py_commitment = H.bls_g1_decompress(data[BLS_G1_LEN : BLS_G1_LEN * 2].hex()) - s_commitment = H.bls_g1_decompress(data[BLS_G1_LEN * 2 : RING_ROOT_LEN].hex()) - - px = Column(name="px", evals=[], commitment=px_commitment) - py = Column(name="py", evals=[], commitment=py_commitment) - s = Column(name="s", evals=[], commitment=s_commitment) - - return cls(px=px, py=py, s=s, params=params) diff --git a/dot_ring/vrf/ring/ring_serialization.py b/dot_ring/vrf/ring/ring_serialization.py deleted file mode 100644 index 54516f6..0000000 --- a/dot_ring/vrf/ring/ring_serialization.py +++ /dev/null @@ -1,45 +0,0 @@ -from __future__ import annotations - -from typing import Any, cast - -from dot_ring.ring_proof.helpers import Helpers as H -from dot_ring.ring_proof.params import RingProofParams -from dot_ring.ring_proof.pcs.protocol import G1Commitment - -RING_SCALAR_LEN = 32 - - -def ring_proof_len(params: RingProofParams) -> int: - return 7 * params.pcs.commitment_size + 8 * RING_SCALAR_LEN - - -def compress_g1(params: RingProofParams, point: G1Commitment) -> bytes: - if hasattr(params.pcs, "compress_g1"): - return params.pcs.compress_g1(point) - return bytes.fromhex(H.bls_g1_compress(cast(tuple, point))) - - -def decompress_g1(params: RingProofParams, data: bytes) -> G1Commitment: - if hasattr(params.pcs, "decompress_g1"): - return params.pcs.decompress_g1(data) - return H.bls_g1_decompress(data.hex()) - - -def transcript_g1(params: RingProofParams, point: G1Commitment) -> Any: - if params.pcs.commitment_size == 32: - return params.pcs.serialize_g1_uncompressed(point) - return params.pcs.normalize_g1(point) - - -def transcript_vk(params: RingProofParams, commitments: list[G1Commitment]) -> dict[str, Any]: - if params.pcs.commitment_size == 32: - return { - "g1": params.pcs.srs.g1_uncompressed[0], - "g2": params.pcs.srs.g2_uncompressed, - "commitments": [transcript_g1(params, commitment) for commitment in commitments], - } - return { - "g1": params.pcs.srs.g1_points[0], - "g2": H.altered_points(params.pcs.srs.g2_points), - "commitments": [params.pcs.normalize_g1(commitment) for commitment in commitments], - } diff --git a/dot_ring/vrf/ring/ring_verifier_key_builder.py b/dot_ring/vrf/ring/ring_verifier_key_builder.py deleted file mode 100644 index 42edad5..0000000 --- a/dot_ring/vrf/ring/ring_verifier_key_builder.py +++ /dev/null @@ -1,24 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass, field - -from .ring_context import RingContext -from .ring_keys import parse_concatenated_keys -from .ring_root import RingRoot - - -@dataclass -class RingVerifierKeyBuilder: - context: RingContext - keys: list[bytes] = field(default_factory=list) - - def push(self, key: bytes) -> None: - self.keys.append(key) - - def extend(self, keys: list[bytes] | bytes) -> None: - if isinstance(keys, bytes): - keys = parse_concatenated_keys(keys, self.context.setup.cv) - self.keys.extend(keys) - - def finalize(self) -> RingRoot: - return self.context.verifier_key(self.keys) diff --git a/dot_ring/vrf/ring/root.py b/dot_ring/vrf/ring/root.py new file mode 100644 index 0000000..df76b10 --- /dev/null +++ b/dot_ring/vrf/ring/root.py @@ -0,0 +1,198 @@ +from dataclasses import dataclass +from functools import lru_cache +from typing import Any, cast + +from dot_ring.ring_proof.columns.columns import Column, require_commitment +from dot_ring.ring_proof.helpers import Helpers as H +from dot_ring.ring_proof.params import RingProofParams +from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.pcs.utils import pcs_compress_g1, pcs_decompress_g1, pcs_transcript_g1 + +from .members import Ring + + +def _pcs_cache_token(pcs: Any) -> tuple[int, int, int | None]: + pcs_srs = getattr(pcs, "srs", None) + return ( + id(pcs_srs), + id(getattr(pcs_srs, "blst_g1_memory", None)), + getattr(pcs, "commitment_size", None), + ) + + +# Global bounded cache: selector columns are fixed for a params/PCS tuple. +@lru_cache(maxsize=16) +def _selector_column_data( + domain_size: int, + max_ring_size: int, + omega: int, + prime: int, + pcs: Any, + _pcs_token: tuple[int, int, int | None], +) -> tuple[tuple[int, ...], tuple[int, ...], Any]: + selector_vec = [1 if i < max_ring_size else 0 for i in range(domain_size)] + selector_col = Column("s", selector_vec, size=domain_size) + selector_col.interpolate(omega, prime) + selector_col.commit(pcs) + if selector_col.coeffs is None or selector_col.commitment is None: + raise ValueError("failed to build selector column") + return tuple(selector_col.evals), tuple(selector_col.coeffs), selector_col.commitment + + +def _selector_column(params: RingProofParams) -> Column: + evals, coeffs, commitment = _selector_column_data( + params.domain_size, + params.max_ring_size, + params.omega, + params.prime, + params.pcs, + _pcs_cache_token(params.pcs), + ) + return Column("s", list(evals), list(coeffs), commitment=_copy_commitment(commitment), size=params.domain_size) + + +def _copy_commitment(commitment: Any) -> Any: + return commitment.dup() if hasattr(commitment, "dup") else commitment + + +def _decompressed_root_commitments( + data: bytes, + commitment_size: int, + pcs: Any, +) -> tuple[Any, Any, Any]: + expected = 3 * commitment_size + if len(data) != expected: + raise ValueError(f"invalid ring root length: ring root must be exactly {expected} bytes, got {len(data)}") + return ( + pcs_decompress_g1(pcs, data[0:commitment_size]), + pcs_decompress_g1(pcs, data[commitment_size : commitment_size * 2]), + pcs_decompress_g1(pcs, data[commitment_size * 2 : expected]), + ) + + +def _ring_selector(params: RingProofParams) -> tuple[int, ...]: + return tuple(1 if i < params.max_ring_size else 0 for i in range(params.domain_size)) + + +def _verifier_key_transcript_data(params: RingProofParams, commitments: list[Any]) -> dict[str, Any]: + return { + "g1": params.pcs.srs.g1_points[0], + "g2": H.altered_points(params.pcs.srs.g2_points), + "commitments": [pcs_transcript_g1(params.pcs, commitment) for commitment in commitments], + } + + +@dataclass +class RingRoot: + px: Column + py: Column + s: Column + params: RingProofParams | None = None + + @classmethod + def from_ring(cls, ring: Ring, params: RingProofParams | None = None): + if params is None: + params = ring.params + # Px, Py, s points + px, py = H.unzip(ring.nm_points) + # Columns + px_col = Column("Px", px, size=params.domain_size) + py_col = Column("Py", py, size=params.domain_size) + s_col = _selector_column(params) + for col in (px_col, py_col): + col.interpolate(params.omega, params.prime) + col.commit(params.pcs) + return cls(px=px_col, py=py_col, s=s_col, params=params) + + def verifier_key(self, params: RingProofParams | None = None) -> tuple[list[Any], dict[str, Any]]: + """Return fixed-column commitments and their transcript verifier key.""" + if params is None: + params = self.params + if params is None: + raise ValueError("Ring root verifier key requires ring proof parameters") + + commitments = [ + require_commitment(self.px), + require_commitment(self.py), + require_commitment(self.s), + ] + + verifier_key = _verifier_key_transcript_data(params, commitments) + return commitments, verifier_key + + def verifier_transcript_prefix(self, params: RingProofParams | None = None, transcript_challenge: bytes | None = None): + """Return a transcript state after absorbing the fixed verifier key.""" + if params is None: + params = self.params + if params is None: + raise ValueError("Ring root verifier transcript requires ring proof parameters") + + _, verifier_key = self.verifier_key(params) + if transcript_challenge is None: + transcript_challenge = params.cv.curve.params.suite_id + + from dot_ring.ring_proof.transcript.serialize import serialize + from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript + + transcript = FiatShamirTranscript(params.prime, transcript_challenge) + # Verifier-key transcript prefix is reused before proof-local data is absorbed. + transcript.add_serialized(b"vk", serialize(verifier_key)) + return transcript + + def to_bytes(self) -> bytes: + pcs = self.params.pcs if self.params is not None else None + if pcs is not None: + return b"".join( + ( + pcs_compress_g1(pcs, require_commitment(self.px)), + pcs_compress_g1(pcs, require_commitment(self.py)), + pcs_compress_g1(pcs, require_commitment(self.s)), + ) + ) + for col in (self.px, self.py, self.s): + if col.commitment is None: + raise ValueError("Ring root is missing commitments") + comm_keys = ( + H.bls_g1_compress(cast(Any, require_commitment(self.px))), + H.bls_g1_compress(cast(Any, require_commitment(self.py))), + H.bls_g1_compress(cast(Any, require_commitment(self.s))), + ) + return bytes.fromhex(comm_keys[0]) + bytes.fromhex(comm_keys[1]) + bytes.fromhex(comm_keys[2]) + + @classmethod + def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "RingRoot": + if params is not None: + commitment_size = params.pcs.commitment_size + else: + commitment_size = KZG.commitment_size + expected = 3 * commitment_size + if len(data) != expected: + raise ValueError(f"invalid ring root length: ring root must be exactly {expected} bytes, got {len(data)}") + + pcs = params.pcs if params is not None else KZG + px_commitment, py_commitment, s_commitment = _decompressed_root_commitments( + bytes(data), + commitment_size, + pcs, + ) + + px = Column(name="px", evals=[], commitment=_copy_commitment(px_commitment)) + py = Column(name="py", evals=[], commitment=_copy_commitment(py_commitment)) + s = Column(name="s", evals=[], commitment=_copy_commitment(s_commitment)) + + return cls(px=px, py=py, s=s, params=params) + + def matches_ring(self, ring: Ring) -> bool: + params = ring.params + domain_size = params.domain_size + if ( + len(self.px.evals) >= domain_size + and len(self.py.evals) >= domain_size + and len(self.s.evals) >= domain_size + ): + return ( + tuple(self.px.evals[:domain_size]) == tuple(point[0] for point in ring.nm_points) + and tuple(self.py.evals[:domain_size]) == tuple(point[1] for point in ring.nm_points) + and tuple(self.s.evals[:domain_size]) == _ring_selector(params) + ) + return RingRoot.from_ring(ring, params).to_bytes() == self.to_bytes() diff --git a/dot_ring/vrf/ring/ring_vrf.py b/dot_ring/vrf/ring/vrf.py similarity index 67% rename from dot_ring/vrf/ring/ring_vrf.py rename to dot_ring/vrf/ring/vrf.py index d60c455..b49a04c 100644 --- a/dot_ring/vrf/ring/ring_vrf.py +++ b/dot_ring/vrf/ring/vrf.py @@ -1,27 +1,23 @@ +from __future__ import annotations + from dataclasses import dataclass from typing import Any, cast from dot_ring.curve.point import CurvePoint -from dot_ring.ring_proof.columns.columns import Column, require_commitment +from dot_ring.ring_proof.columns.columns import Column from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.opening import Opening -from dot_ring.ring_proof.pcs.protocol import G1Commitment -from dot_ring.ring_proof.helpers import Helpers as H -from dot_ring.ring_proof.verify import Verify -from dot_ring.vrf.pedersen.pedersen import PedersenVRF +from dot_ring.ring_proof.verify import RingProofFields, Verify +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.vrf import PreparedSecretKey from ..vrf import VRF -from .ring import Ring -from .ring_batch_item import RingBatchItem -from .ring_batch_verifier import RingBatchVerifier -from .ring_context import RingContext, RingSetup -from .ring_keys import parse_concatenated_keys as _parse_concatenated_keys -from .ring_proof_builder import RingProofBuilder -from .ring_proof_payload import RingProofPayload -from .ring_root import RingRoot -from .ring_serialization import ring_proof_len -from .ring_serialization import transcript_vk as _transcript_vk -from .ring_verifier_key_builder import RingVerifierKeyBuilder +from .members import Ring, parse_concatenated_keys as _parse_concatenated_keys +from .batch_verifier import RingBatchItem, RingBatchVerifier, _proof_relation_points, _proof_transcript_commitments +from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .proof_builder import RingProofBuilder +from .proof_payload import RingProofPayload, ring_proof_len +from .root import RingRoot @dataclass @@ -34,7 +30,7 @@ class RingVRF(VRF[Any]): Usage: >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.ring.ring_vrf import RingVRF + >>> from dot_ring.vrf.ring import RingVRF >>> proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, producer_key, keys) >>> verified = RingVRF[Bandersnatch].verify(ad, ring_root, proof) @@ -73,12 +69,11 @@ def to_bytes(self) -> bytes: Returns: bytes: Bytes representation of the Ring VRF proof """ - assert self.pedersen_proof is not None params = RingProofParams(cv=self.cv) - return cast(bytes, self.pedersen_proof.to_bytes()) + self._payload().to_bytes(params) + return cast(bytes, self.require_pedersen_proof().to_bytes()) + self._payload().to_bytes(params) @classmethod - def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": + def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> RingVRF: """ Deserialize the Ring VRF proof from bytes. @@ -92,9 +87,7 @@ def from_bytes(cls, proof: bytes, skip_pedersen: bool = False) -> "RingVRF": raise ValueError(f"invalid Ring VRF proof length: Ring VRF proof must be exactly {expected_len} bytes, got {len(proof)}") if not skip_pedersen: - point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) - scalar_size = (cls.cv.curve.ORDER.bit_length() + 7) // 8 - pedersen_len = 4 * point_len + 2 * scalar_size + pedersen_len = PedersenVRF[cast(Any, cls).cv].proof_len() # type: ignore[misc] pedersen_proof = PedersenVRF[cast(Any, cls).cv].from_bytes(proof[:pedersen_len]) # type: ignore[misc] offset = pedersen_len else: @@ -144,55 +137,67 @@ def _payload(self) -> RingProofPayload: open_l_zeta_omega=self.open_l_zeta_omega, ) + def ring_proof_tuple(self) -> RingProofFields: + """Return the ring-proof fields in the order expected by the SNARK verifier.""" + return RingProofFields( + self.c_b.commitment, + self.c_accip.commitment, + self.c_accx.commitment, + self.c_accy.commitment, + self.px_zeta, + self.py_zeta, + self.s_zeta, + self.b_zeta, + self.accip_zeta, + self.accx_zeta, + self.accy_zeta, + self.c_q.commitment, + self.l_zeta_omega, + self.open_agg_zeta.proof, + self.open_l_zeta_omega.proof, + ) + + def require_pedersen_proof(self) -> PedersenVRF: + if self.pedersen_proof is None: + raise ValueError("Pedersen proof is missing") + return self.pedersen_proof + def _ring_proof_verifier( self, message: bytes | CurvePoint, ring: Ring, ring_root: RingRoot, ) -> Verify: - fixed_cols_cmts = _commitments(ring_root.px, ring_root.py, ring_root.s) + fixed_cols_cmts, verifier_key = ring_root.verifier_key(ring.params) + transcript_prefix = ring_root.verifier_transcript_prefix(ring.params) if isinstance(message, bytes): try: - message = ring.params.cv.point.string_to_point(message) + message = ring.params.cv.string_to_point(message) except ValueError as exc: raise ValueError("Invalid message point") from exc - rltn = ring.params.point_to_ring_point(message) - res_plus_seed = ring.params.add_points(ring.params.seed_point, rltn) - - verifier_key: dict[str, Any] = _transcript_vk(ring.params, fixed_cols_cmts) + rltn, res_plus_seed = _proof_relation_points(self, message, ring.params) + witness_commitments, quotient_commitment = _proof_transcript_commitments(self, ring.params) return Verify( - ( - self.c_b.commitment, - self.c_accip.commitment, - self.c_accx.commitment, - self.c_accy.commitment, - self.px_zeta, - self.py_zeta, - self.s_zeta, - self.b_zeta, - self.accip_zeta, - self.accx_zeta, - self.accy_zeta, - self.c_q.commitment, - self.l_zeta_omega, - self.open_agg_zeta.proof, - self.open_l_zeta_omega.proof, - ), + self.ring_proof_tuple(), verifier_key, fixed_cols_cmts, rltn, res_plus_seed, ring.params.seed_point, ring.params.domain, - transcript_challenge=self.cv.curve.SUITE_ID or self.cv.curve.SUITE_STRING, + transcript_challenge=self.cv.curve.params.suite_id, padding_rows=ring.params.padding_rows, edwards_a=ring.params.ring_edwards_a, prime=ring.params.prime, omega=ring.params.omega, pcs=ring.params.pcs, + transcript_prefix=transcript_prefix, + domain_size_inv=pow(ring.params.domain_size, -1, ring.params.prime), + transcript_witness_commitments=witness_commitments, + transcript_quotient_commitment=quotient_commitment, ) def verify_ring_proof( @@ -204,7 +209,7 @@ def verify_ring_proof( """ Verifies the Ring Proof """ - if not _ring_root_matches_ring(ring, ring_root): + if not ring_root.matches_ring(ring): return False return cast(bool, self._ring_proof_verifier(message, ring, ring_root).is_valid()) @@ -217,7 +222,7 @@ def prove( producer_key: bytes, ring: Ring, ring_root: RingRoot | None = None, - ) -> "RingVRF": + ) -> RingVRF: """ Generate ring VRF proof (pedersen vrf proof + ring_proof). @@ -236,10 +241,24 @@ def prove( >>> ring = Ring(keys) # Automatically determines optimal domain size >>> proof = RingVRF[Bandersnatch].prove(alpha, ad, sk, pk, ring) """ - if producer_key != cls.get_public_key(secret_key): + return cls.prove_prepared(alpha, ad, cls.prepare_secret_key(secret_key), producer_key, ring, ring_root) + + @classmethod + def prove_prepared( + cls, + alpha: bytes, + ad: bytes, + secret_key: PreparedSecretKey[Any], + producer_key: bytes, + ring: Ring, + ring_root: RingRoot | None = None, + ) -> RingVRF: + if secret_key.curve is not cls.cv: + raise ValueError("prepared secret key uses a different curve") + if producer_key != secret_key.public_key_bytes: raise ValueError("producer_key does not match secret_key") - pedersen_proof = PedersenVRF[cast(Any, cls).cv].prove(alpha, secret_key, ad) # type: ignore[misc] + pedersen_proof = PedersenVRF[cast(Any, cls).cv].prove_prepared(alpha, secret_key, ad) # type: ignore[misc] ring_proof = RingProofBuilder( cls.cv, @@ -267,34 +286,15 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) """ Verify ring VRF proof (pedersen_proof + ring_proof) """ - if self.pedersen_proof is None: - raise ValueError("Pedersen proof is missing") - p_proof_valid = self.pedersen_proof.verify(input, ad_data) - ring_proof_valid = self.verify_ring_proof(self.pedersen_proof.blinded_pk, ring, ring_root) + pedersen_proof = self.require_pedersen_proof() + p_proof_valid = pedersen_proof.verify(input, ad_data) + ring_proof_valid = self.verify_ring_proof(pedersen_proof.blinded_pk, ring, ring_root) return p_proof_valid and ring_proof_valid @classmethod def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: - return cast(bytes, PedersenVRF[cast(Any, cls).cv].proof_to_hash(gamma, mul_cofactor)) # type: ignore[misc] - - -def _commitments(*columns: Column) -> list[G1Commitment]: - return [require_commitment(column) for column in columns] - - -def _ring_root_matches_ring(ring: Ring, ring_root: RingRoot) -> bool: - params = ring.params - if len(ring_root.px.evals) >= params.domain_size and len(ring_root.py.evals) >= params.domain_size: - px, py = H.unzip(ring.nm_points) - selector_vec = [1 if i < params.max_ring_size else 0 for i in range(params.domain_size)] - return ( - ring_root.px.evals[: params.domain_size] == px - and ring_root.py.evals[: params.domain_size] == py - and ring_root.s.evals[: params.domain_size] == selector_vec - ) - return RingRoot.from_ring(ring, params).to_bytes() == ring_root.to_bytes() - + return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) __all__ = [ "RingBatchItem", diff --git a/tests/test_dot_ring_vectors.py b/tests/test_dot_ring_vectors.py index 83fc6af..f8e1ad4 100644 --- a/tests/test_dot_ring_vectors.py +++ b/tests/test_dot_ring_vectors.py @@ -26,7 +26,7 @@ def test_dot_ring_tiny_vectors(suite: Suite) -> None: proof = TinyVRF[suite.curve].from_bytes(proof_bytes) assert TinyVRF[suite.curve].get_public_key(bytes.fromhex(vector["sk"])).hex() == vector["pk"] - assert suite.curve.point.encode_to_curve(alpha).point_to_string().hex() == vector["h"] + assert suite.curve.encode_to_curve(alpha).point_to_string().hex() == vector["h"] assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) assert TinyVRF[suite.curve].proof_to_hash(proof.output_point).hex() == vector["beta"] diff --git a/tests/test_ring_vrf/test_audit_regressions.py b/tests/test_ring_vrf/test_audit_regressions.py index 1fe5dc5..05225fe 100644 --- a/tests/test_ring_vrf/test_audit_regressions.py +++ b/tests/test_ring_vrf/test_audit_regressions.py @@ -3,8 +3,10 @@ from dot_ring import Bandersnatch, RingVRF from dot_ring.ring_proof.constants import PaddingPoint from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.pedersen.pedersen import PedersenVRF -from dot_ring.vrf.ring.ring_root import Ring, RingRoot +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.ring import RingContext +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.transcript import point_len def _keys(count: int) -> list[bytes]: @@ -12,7 +14,7 @@ def _keys(count: int) -> list[bytes]: def _point_tuple(key: bytes) -> tuple[int, int]: - point = Bandersnatch.point.string_to_point(key) + point = Bandersnatch.string_to_point(key) assert not isinstance(point, str) return (point.x, point.y) @@ -35,6 +37,16 @@ def test_ring_verification_rejects_mismatched_ring_for_same_root(): assert not proof.verify(b"audit-input", b"audit-ad", other_ring, ring_root) +def test_ring_root_match_rejects_mismatched_ring(): + _, pk, ring, ring_root, _ = _proof_fixture() + other_ring = Ring([pk, *_keys(7)], ring.params) + + assert ring_root.matches_ring(ring) + assert ring_root.matches_ring(ring) + assert not ring_root.matches_ring(other_ring) + assert not ring_root.matches_ring(other_ring) + + def test_ring_keys_pad_decode_failures_in_place(): pk1, pk2 = _keys(2) identity = (1).to_bytes(32, "little") @@ -55,12 +67,25 @@ def test_ring_vrf_from_bytes_rejects_trailing_bytes(): RingRoot.from_bytes(ring_root.to_bytes() + b"junk") +def test_ring_root_from_bytes_cache_returns_fresh_root(): + _, _, _, ring_root, _ = _proof_fixture() + root_bytes = ring_root.to_bytes() + + parsed1 = RingRoot.from_bytes(root_bytes, ring_root.params) + parsed2 = RingRoot.from_bytes(root_bytes, ring_root.params) + + assert parsed1.to_bytes() == root_bytes + assert parsed2.to_bytes() == root_bytes + assert parsed1 is not parsed2 + assert parsed1.px.commitment is not parsed2.px.commitment + + def test_pedersen_from_bytes_rejects_noncanonical_scalars(): _, _, _, _, proof = _proof_fixture() pedersen_bytes = bytearray(proof.pedersen_proof.to_bytes()) - scalar_offset = 4 * Bandersnatch.curve.POINT_LEN + scalar_offset = 4 * point_len(Bandersnatch) s = int.from_bytes(pedersen_bytes[scalar_offset : scalar_offset + 32], "little") - pedersen_bytes[scalar_offset : scalar_offset + 32] = (s + Bandersnatch.curve.ORDER).to_bytes(32, "little") + pedersen_bytes[scalar_offset : scalar_offset + 32] = (s + Bandersnatch.curve.params.subgroup_order).to_bytes(32, "little") with pytest.raises(ValueError, match="not canonical"): PedersenVRF[Bandersnatch].from_bytes(bytes(pedersen_bytes)) @@ -80,8 +105,25 @@ def test_prove_rejects_producer_key_that_does_not_match_secret(): RingVRF[Bandersnatch].prove(b"audit-input", b"audit-ad", sk1, pk2, ring, ring_root) +def test_verifier_key_builder_caches_root_when_full(): + keys = _keys(8) + params = RingProofParams(test_vectors=True, max_ring_size=8) + context = RingContext(params) + builder = context.verifier_key_builder() + + builder.append(keys) + built_root = builder.finalize() + direct_root = context.verifier_key(keys) + + assert built_root.to_bytes() == direct_root.to_bytes() + assert builder.finalize() is built_root + with pytest.raises(ValueError, match="too many keys"): + builder.push(keys[0]) + + def test_unsupported_params_fail_at_construction(): with pytest.raises(ValueError, match="padding_rows must be 4"): RingProofParams(padding_rows=5, max_ring_size=1) - with pytest.raises(ValueError, match="exceeds supported SRS domain size"): - RingProofParams.from_ring_size(1792) + params = RingProofParams.from_ring_size(2047) + assert params.domain_size == 4096 + assert params.max_ring_size == 3839 diff --git a/tests/test_ring_vrf/test_ring_vrf.py b/tests/test_ring_vrf/test_ring_vrf.py index 43c8194..f0e32e2 100644 --- a/tests/test_ring_vrf/test_ring_vrf.py +++ b/tests/test_ring_vrf/test_ring_vrf.py @@ -4,8 +4,8 @@ from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.ring import RingVRF HERE = os.path.dirname(__file__) From d1b62fa03acc0a6e4ead4af5dea4c28de48b48fc Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:40:23 +0530 Subject: [PATCH 26/33] reshape pedersen vrf --- dot_ring/vrf/pedersen/__init__.py | 3 +- dot_ring/vrf/pedersen/batch_verifier.py | 302 ++++++++++++++++++ dot_ring/vrf/pedersen/pedersen.py | 7 - dot_ring/vrf/pedersen/pedersen_batch_item.py | 18 -- .../vrf/pedersen/pedersen_batch_verifier.py | 90 ------ .../vrf/pedersen/{pedersen_vrf.py => vrf.py} | 78 +++-- tests/test_pedersen/test_pedersen_ark.py | 21 +- tests/test_pedersen/test_pedersen_base.py | 4 +- 8 files changed, 363 insertions(+), 160 deletions(-) create mode 100644 dot_ring/vrf/pedersen/batch_verifier.py delete mode 100644 dot_ring/vrf/pedersen/pedersen.py delete mode 100644 dot_ring/vrf/pedersen/pedersen_batch_item.py delete mode 100644 dot_ring/vrf/pedersen/pedersen_batch_verifier.py rename dot_ring/vrf/pedersen/{pedersen_vrf.py => vrf.py} (69%) diff --git a/dot_ring/vrf/pedersen/__init__.py b/dot_ring/vrf/pedersen/__init__.py index ea89805..cba4d38 100644 --- a/dot_ring/vrf/pedersen/__init__.py +++ b/dot_ring/vrf/pedersen/__init__.py @@ -1,3 +1,4 @@ -from .pedersen import PedersenBatchItem, PedersenBatchVerifier, PedersenVRF +from .batch_verifier import PedersenBatchItem, PedersenBatchVerifier +from .vrf import PedersenVRF __all__ = ["PedersenBatchItem", "PedersenBatchVerifier", "PedersenVRF"] diff --git a/dot_ring/vrf/pedersen/batch_verifier.py b/dot_ring/vrf/pedersen/batch_verifier.py new file mode 100644 index 0000000..c5ba563 --- /dev/null +++ b/dot_ring/vrf/pedersen/batch_verifier.py @@ -0,0 +1,302 @@ +from __future__ import annotations + +from dataclasses import dataclass +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import CHALLENGE_LEN, DomSep, VrfIo, challenge, squeeze_transcript_bytes, suite_id, vrf_transcript_scalars + +from .vrf import PedersenVRF, _blinding_base + + +@dataclass +class PedersenBatchItem: + c: int + ios: list[VrfIo] + zs: list[int] + pk_com: CurvePoint + r: CurvePoint + ok: CurvePoint + s: int + sb: int + + +def _signed_mod(value: int, order: int) -> int: + value %= order + if value > order >> 1: + value -= order + return value + + +def _fold_duplicate_points_by_identity(points: list[CurvePoint], scalars: list[int], order: int) -> tuple[list[CurvePoint], list[int]]: + seen: set[int] = set() + has_duplicate = False + for point in points: + point_id = id(point) + if point_id in seen: + has_duplicate = True + break + seen.add(point_id) + if not has_duplicate: + return points, scalars + + points_by_id: dict[int, CurvePoint] = {} + scalars_by_id: dict[int, int] = {} + for point, scalar in zip(points, scalars, strict=True): + scalar %= order + if scalar == 0: + continue + point_id = id(point) + if point_id in scalars_by_id: + scalars_by_id[point_id] = (scalars_by_id[point_id] + scalar) % order + else: + points_by_id[point_id] = point + scalars_by_id[point_id] = scalar + + folded_points = [] + folded_scalars = [] + for point_id, point in points_by_id.items(): + scalar = _signed_mod(scalars_by_id[point_id], order) + if scalar: + folded_points.append(point) + folded_scalars.append(scalar) + return folded_points, folded_scalars + + +def _batch_coefficient_pairs_from_bytes(item_count: int, item_bytes: bytes | bytearray, curve: CurveVariant) -> list[tuple[int, int]]: + if item_count == 0: + return [] + + absorbed = bytearray(suite_id(curve)) + absorbed.append(DomSep.BATCH_VERIFY) + absorbed.extend(item_count.to_bytes(8, "little")) + absorbed.extend(item_bytes) + + raw = squeeze_transcript_bytes(curve.curve.params.hash_fn, bytes(absorbed), 2 * CHALLENGE_LEN * item_count) + pairs = [] + for index in range(item_count): + offset = 2 * CHALLENGE_LEN * index + io_weight = int.from_bytes(raw[offset : offset + CHALLENGE_LEN], "little") or 1 + commitment_weight = int.from_bytes(raw[offset + CHALLENGE_LEN : offset + 2 * CHALLENGE_LEN], "little") or 1 + pairs.append((io_weight, commitment_weight)) + return pairs + + +def _batch_coefficient_item_bytes(item: PedersenBatchItem, scalar_size: int, order: int) -> bytes: + return ( + int(item.c % order).to_bytes(scalar_size, "little") + + int(item.s % order).to_bytes(scalar_size, "little") + + int(item.sb % order).to_bytes(scalar_size, "little") + ) + + +def _batch_coefficient_pairs(items: list[PedersenBatchItem], curve: CurveVariant) -> list[tuple[int, int]]: + order = curve.curve.params.subgroup_order + scalar_size = (order.bit_length() + 7) // 8 + item_bytes = bytearray() + for item in items: + item_bytes.extend(_batch_coefficient_item_bytes(item, scalar_size, order)) + return _batch_coefficient_pairs_from_bytes(len(items), item_bytes, curve) + + +class PedersenBatchVerifier: + def __init__(self, curve: CurveVariant): + self.cv = curve + self.items: list[PedersenBatchItem] = [] + self._order = curve.curve.params.subgroup_order + self._scalar_size = (self._order.bit_length() + 7) // 8 + self._coefficient_item_bytes = bytearray() + self._single_io_items = True + + @classmethod + def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[PedersenBatchVerifier] | Any: + if not isinstance(curve_variant, CurveVariant): + return cls + + class _SpecializedPedersenBatchVerifier(cls): + def __init__(self) -> None: + super().__init__(curve_variant) + + _SpecializedPedersenBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" + return _SpecializedPedersenBatchVerifier + + def prepare(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> PedersenBatchItem: + if len(ios) == 1: + return self._prepare_single(ios[0], additional_data, proof) + + transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) + transcript.absorb(proof.blinded_pk.point_to_string()) + c = challenge(self.cv, [proof.result_point, proof.ok], transcript) + return PedersenBatchItem( + c=c, + ios=list(ios), + zs=scalar_stream.take(len(ios)), + pk_com=proof.blinded_pk, + r=proof.result_point, + ok=proof.ok, + s=proof.s, + sb=proof.sb, + ) + + def _prepare_single(self, io: VrfIo, additional_data: bytes, proof: PedersenVRF) -> PedersenBatchItem: + absorbed = bytearray(suite_id(self.cv)) + absorbed.append(DomSep.PEDERSEN_VRF) + absorbed.extend((1).to_bytes(8, "little")) + absorbed.extend(io.input.point_to_string()) + absorbed.extend(io.output.point_to_string()) + absorbed.extend(len(additional_data).to_bytes(8, "little")) + absorbed.extend(additional_data) + absorbed.extend(proof.blinded_pk.point_to_string()) + absorbed.append(DomSep.CHALLENGE) + absorbed.extend(proof.result_point.point_to_string()) + absorbed.extend(proof.ok.point_to_string()) + c = _challenge_scalar_from_absorbed(self.cv, bytes(absorbed)) + return PedersenBatchItem( + c=c, + ios=[io], + zs=[1], + pk_com=proof.blinded_pk, + r=proof.result_point, + ok=proof.ok, + s=proof.s, + sb=proof.sb, + ) + + def push_prepared(self, item: PedersenBatchItem) -> None: + self.items.append(item) + self._single_io_items = self._single_io_items and len(item.ios) == 1 and len(item.zs) == 1 and item.zs[0] == 1 + self._coefficient_item_bytes.extend(_batch_coefficient_item_bytes(item, self._scalar_size, self._order)) + + def push(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> None: + self.push_prepared(self.prepare(ios, additional_data, proof)) + + def verify(self) -> bool: + if not self.items: + return True + + blinding_base = _blinding_base(self.cv) + generator = self.cv.generator_point() + order = self._order + expected_bytes = len(self.items) * 3 * self._scalar_size + if len(self._coefficient_item_bytes) == expected_bytes: + coefficients = _batch_coefficient_pairs_from_bytes(len(self.items), self._coefficient_item_bytes, self.cv) + else: + coefficients = _batch_coefficient_pairs(self.items, self.cv) + + if self._single_io_items: + points, scalars = self._verification_terms_single_io(coefficients, generator, blinding_base) + points, scalars = _fold_duplicate_points_by_identity(points, scalars, order) + return self.cv.msm(points, scalars).is_identity() + + points: list[CurvePoint] = [] + scalars: list[int] = [] + generator_scalar = 0 + blinding_scalar = 0 + + for item, (io_weight, commitment_weight) in zip(self.items, coefficients, strict=True): + + for io, z in zip(item.ios, item.zs, strict=True): + points.extend([io.input, io.output]) + scalars.extend( + [ + _signed_mod(io_weight * item.s * z, order), + _signed_mod(-(io_weight * item.c * z), order), + ] + ) + points.append(item.ok) + scalars.append(-io_weight) + + generator_scalar = (generator_scalar + commitment_weight * item.s) % order + blinding_scalar = (blinding_scalar + commitment_weight * item.sb) % order + + points.extend([item.pk_com, item.r]) + scalars.extend( + [ + _signed_mod(-(commitment_weight * item.c), order), + -commitment_weight, + ] + ) + + if generator_scalar: + points.append(generator) + scalars.append(_signed_mod(generator_scalar, order)) + if blinding_scalar: + points.append(blinding_base) + scalars.append(_signed_mod(blinding_scalar, order)) + + points, scalars = _fold_duplicate_points_by_identity(points, scalars, order) + return self.cv.msm(points, scalars).is_identity() + + def _verification_terms_single_io( + self, + coefficients: list[tuple[int, int]], + generator: CurvePoint, + blinding_base: CurvePoint, + ) -> tuple[list[CurvePoint], list[int]]: + items = self.items + order = self._order + half_order = order >> 1 + point_count = len(items) * 5 + 2 + points: list[CurvePoint | None] = [None] * point_count + scalars = [0] * point_count + generator_scalar = 0 + blinding_scalar = 0 + pos = 0 + + for item, (io_weight, commitment_weight) in zip(items, coefficients, strict=True): + io = item.ios[0] + + value = (io_weight * item.s) % order + if value > half_order: + value -= order + points[pos] = io.input + scalars[pos] = value + pos += 1 + + value = -(io_weight * item.c) % order + if value > half_order: + value -= order + points[pos] = io.output + scalars[pos] = value + pos += 1 + + points[pos] = item.ok + scalars[pos] = -io_weight + pos += 1 + + generator_scalar = (generator_scalar + commitment_weight * item.s) % order + blinding_scalar = (blinding_scalar + commitment_weight * item.sb) % order + + value = -(commitment_weight * item.c) % order + if value > half_order: + value -= order + points[pos] = item.pk_com + scalars[pos] = value + pos += 1 + + points[pos] = item.r + scalars[pos] = -commitment_weight + pos += 1 + + if generator_scalar: + value = generator_scalar + if value > half_order: + value -= order + points[pos] = generator + scalars[pos] = value + pos += 1 + if blinding_scalar: + value = blinding_scalar + if value > half_order: + value -= order + points[pos] = blinding_base + scalars[pos] = value + pos += 1 + + return points[:pos], scalars[:pos] # type: ignore[return-value] + + +def _challenge_scalar_from_absorbed(curve: CurveVariant, absorbed: bytes) -> int: + rnd = squeeze_transcript_bytes(curve.curve.params.hash_fn, absorbed, CHALLENGE_LEN) + return int.from_bytes(rnd, "little") % curve.curve.params.subgroup_order diff --git a/dot_ring/vrf/pedersen/pedersen.py b/dot_ring/vrf/pedersen/pedersen.py deleted file mode 100644 index a0cba76..0000000 --- a/dot_ring/vrf/pedersen/pedersen.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -from .pedersen_batch_item import PedersenBatchItem -from .pedersen_batch_verifier import PedersenBatchVerifier -from .pedersen_vrf import PedersenVRF - -__all__ = ["PedersenBatchItem", "PedersenBatchVerifier", "PedersenVRF"] diff --git a/dot_ring/vrf/pedersen/pedersen_batch_item.py b/dot_ring/vrf/pedersen/pedersen_batch_item.py deleted file mode 100644 index 01af170..0000000 --- a/dot_ring/vrf/pedersen/pedersen_batch_item.py +++ /dev/null @@ -1,18 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass - -from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import VrfIo - - -@dataclass -class PedersenBatchItem: - c: int - ios: list[VrfIo] - zs: list[int] - pk_com: CurvePoint - r: CurvePoint - ok: CurvePoint - s: int - sb: int diff --git a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py b/dot_ring/vrf/pedersen/pedersen_batch_verifier.py deleted file mode 100644 index fe34eab..0000000 --- a/dot_ring/vrf/pedersen/pedersen_batch_verifier.py +++ /dev/null @@ -1,90 +0,0 @@ -from __future__ import annotations - -import secrets -from typing import Any - -from dot_ring.curve.curve import CurveVariant -from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, vrf_transcript_scalars - -from .pedersen_batch_item import PedersenBatchItem -from .pedersen_vrf import PedersenVRF, _blinding_base - - -def _batch_coefficients(count: int, order: int) -> list[int]: - if count == 0: - return [] - coefficients = [1] - for _ in range(count - 1): - coefficient = 0 - while coefficient == 0: - coefficient = secrets.randbelow(order) - coefficients.append(coefficient) - return coefficients - - -class PedersenBatchVerifier: - def __init__(self, curve: CurveVariant): - self.cv = curve - self.items: list[PedersenBatchItem] = [] - - @classmethod - def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[PedersenBatchVerifier] | Any: - if not isinstance(curve_variant, CurveVariant): - return cls - - class _SpecializedPedersenBatchVerifier(cls): - def __init__(self) -> None: - super().__init__(curve_variant) - - _SpecializedPedersenBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" - return _SpecializedPedersenBatchVerifier - - def push(self, ios: list[VrfIo], additional_data: bytes, proof: PedersenVRF) -> None: - transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) - transcript.absorb_point(proof.blinded_pk) - c = challenge(self.cv, [proof.result_point, proof.ok], transcript) - self.items.append( - PedersenBatchItem( - c=c, - ios=list(ios), - zs=scalar_stream.take(len(ios)), - pk_com=proof.blinded_pk, - r=proof.result_point, - ok=proof.ok, - s=proof.s, - sb=proof.sb, - ) - ) - - def verify(self) -> bool: - if not self.items: - return True - - blinding_base = _blinding_base(self.cv) - generator = self.cv.point.generator_point() - points: list[CurvePoint] = [] - scalars: list[int] = [] - coefficients = _batch_coefficients(2 * len(self.items), self.cv.curve.ORDER) - - for index, item in enumerate(self.items): - io_weight = coefficients[2 * index] - commitment_weight = coefficients[2 * index + 1] - - for io, z in zip(item.ios, item.zs, strict=True): - points.extend([io.input, io.output]) - scalars.extend([io_weight * item.s * z, -(io_weight * item.c * z)]) - points.append(item.ok) - scalars.append(-io_weight) - - points.extend([generator, blinding_base, item.pk_com, item.r]) - scalars.extend( - [ - commitment_weight * item.s, - commitment_weight * item.sb, - -(commitment_weight * item.c), - -commitment_weight, - ] - ) - - return self.cv.point.msm(points, scalars).is_identity() diff --git a/dot_ring/vrf/pedersen/pedersen_vrf.py b/dot_ring/vrf/pedersen/vrf.py similarity index 69% rename from dot_ring/vrf/pedersen/pedersen_vrf.py rename to dot_ring/vrf/pedersen/vrf.py index 9fbc4f6..7ac5a2a 100644 --- a/dot_ring/vrf/pedersen/pedersen_vrf.py +++ b/dot_ring/vrf/pedersen/vrf.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, TypeVar, cast +from typing import Any, TypeVar from dot_ring.curve.curve import CurveVariant from dot_ring.curve.point import CurvePoint @@ -10,6 +10,7 @@ VrfIo, challenge, nonce, + point_len, point_to_hash, scalar_decode, scalar_encode, @@ -17,15 +18,16 @@ vrf_transcript, ) -from ..vrf import VRF +from ..vrf import VRF, PreparedSecretKey C = TypeVar("C", bound=CurveVariant) def _blinding_base(curve: CurveVariant) -> CurvePoint: - if curve.curve.BBx is None or curve.curve.BBy is None: + blinding_base = curve.curve.params.auxiliary_points.blinding_base + if blinding_base is None: raise ValueError(f"{curve.name} does not define a Pedersen blinding base") - return curve.point(cast(int, curve.curve.BBx), cast(int, curve.curve.BBy)) + return curve.point(blinding_base[0], blinding_base[1]) @dataclass @@ -38,7 +40,7 @@ class PedersenVRF(VRF[C]): Usage: >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.pedersen.pedersen import PedersenVRF + >>> from dot_ring.vrf.pedersen import PedersenVRF >>> proof = PedersenVRF[Bandersnatch].prove(alpha, secret_key, additional_data) >>> verified = PedersenVRF[Bandersnatch].verify(input_point, additional_data, proof) """ @@ -53,29 +55,30 @@ class PedersenVRF(VRF[C]): @classmethod def proof_len(cls) -> int: - point_length = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + point_length = point_len(cls.cv) return 4 * point_length + 2 * scalar_len(cls.cv) @classmethod def from_bytes(cls, proof: bytes) -> PedersenVRF: - point_length = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + point_length = point_len(cls.cv) scalar_size = scalar_len(cls.cv) expected = cls.proof_len() if len(proof) != expected: raise ValueError(f"invalid Pedersen VRF proof length: expected {expected}, got {len(proof)}") try: - output_point = cls.cv.point.string_to_point(proof[0:point_length]) - public_key_cp = cls.cv.point.string_to_point(proof[point_length : 2 * point_length]) - r = cls.cv.point.string_to_point(proof[2 * point_length : 3 * point_length]) - ok = cls.cv.point.string_to_point(proof[3 * point_length : 4 * point_length]) + output_point = cls.cv.string_to_point(proof[0:point_length]) + public_key_cp = cls.cv.string_to_point(proof[point_length : 2 * point_length]) + r = cls.cv.string_to_point(proof[2 * point_length : 3 * point_length]) + ok = cls.cv.string_to_point(proof[3 * point_length : 4 * point_length]) except ValueError as exc: raise ValueError("Invalid point in proof") from exc s = scalar_decode(cls.cv, proof[4 * point_length : 4 * point_length + scalar_size]) sb = scalar_decode(cls.cv, proof[4 * point_length + scalar_size :]) - if s >= cls.cv.curve.ORDER: + order = cls.cv.curve.params.subgroup_order + if s >= order: raise ValueError("Response scalar s is not canonical") - if sb >= cls.cv.curve.ORDER: + if sb >= order: raise ValueError("Response scalar sb is not canonical") return cls( @@ -99,7 +102,7 @@ def to_bytes(self) -> bytes: @classmethod def _io_from_alpha(cls, alpha: bytes, secret_scalar: int, salt: bytes = b"") -> VrfIo: - input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) + input_point = cls.cv.encode_to_curve(alpha, salt) output_point = input_point * secret_scalar return VrfIo(input_point, output_point) @@ -111,10 +114,20 @@ def prove( additional_data: bytes, salt: bytes = b"", ) -> PedersenVRF: - secret_scalar = scalar_decode(cls.cv, secret_key) - io = cls._io_from_alpha(alpha, secret_scalar, salt) - public_key = cls.cv.point.generator_point() * secret_scalar - return cls.prove_ios([io], secret_scalar, public_key, additional_data) + return cls.prove_prepared(alpha, cls.prepare_secret_key(secret_key), additional_data, salt) + + @classmethod + def prove_prepared( + cls, + alpha: bytes, + secret_key: PreparedSecretKey[Any], + additional_data: bytes, + salt: bytes = b"", + ) -> PedersenVRF: + if secret_key.curve is not cls.cv: + raise ValueError("prepared secret key uses a different curve") + io = cls._io_from_alpha(alpha, secret_key.secret_scalar, salt) + return cls.prove_ios([io], secret_key.secret_scalar, secret_key.public_key, additional_data) @classmethod def prove_ios( @@ -127,18 +140,19 @@ def prove_ios( transcript, merged = vrf_transcript(cls.cv, DomSep.PEDERSEN_VRF, ios, additional_data) blinding_factor = cls.blinding_scalar(secret_scalar, transcript) blinding_base = _blinding_base(cls.cv) - generator = cls.cv.point.generator_point() + generator = cls.cv.generator_point() blinded_pk = public_key + blinding_base * blinding_factor - transcript.absorb_point(blinded_pk) + transcript.absorb(blinded_pk.point_to_string()) k = nonce(cls.cv, secret_scalar, transcript) kb = nonce(cls.cv, blinding_factor, transcript) - result_point = generator * k + blinding_base * kb + result_point = cls.cv.msm([generator, blinding_base], [k, kb]) ok = merged.input * k c = challenge(cls.cv, [result_point, ok], transcript) - s = (k + c * secret_scalar) % cls.cv.curve.ORDER - sb = (kb + c * blinding_factor) % cls.cv.curve.ORDER + order = cls.cv.curve.params.subgroup_order + s = (k + c * secret_scalar) % order + sb = (kb + c * blinding_factor) % order output_point = ios[0].output if len(ios) == 1 else merged.output return cls( @@ -152,27 +166,27 @@ def prove_ios( ) def verify(self, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: - input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) + input_point = self.cv.encode_to_curve(input, salt) return self.verify_ios([VrfIo(input_point, self.output_point)], additional_data) def verify_ios(self, ios: list[VrfIo], additional_data: bytes) -> bool: transcript, merged = vrf_transcript(self.cv, DomSep.PEDERSEN_VRF, ios, additional_data) - transcript.absorb_point(self.blinded_pk) + transcript.absorb(self.blinded_pk.point_to_string()) c = challenge(self.cv, [self.result_point, self.ok], transcript) blinding_base = _blinding_base(self.cv) - generator = self.cv.point.generator_point() + generator = self.cv.generator_point() - lhs1 = self.cv.point.msm([merged.input, merged.output], [self.s, -c]) + lhs1 = self.cv.msm([merged.input, merged.output], [self.s, -c]) if lhs1 != self.ok: return False - lhs2 = self.cv.point.msm([generator, blinding_base, self.blinded_pk], [self.s, self.sb, -c]) + lhs2 = self.cv.msm([generator, blinding_base, self.blinded_pk], [self.s, self.sb, -c]) return lhs2 == self.result_point @classmethod def blinding_scalar(cls, secret_scalar: int, transcript: Any) -> int: - t = transcript.clone() - t.absorb_raw(bytes([DomSep.PEDERSEN_BLINDING])) + t = transcript.copy() + t.absorb(bytes([DomSep.PEDERSEN_BLINDING])) return nonce(cls.cv, secret_scalar, t) @classmethod @@ -183,7 +197,7 @@ def blinding(cls, secret: bytes, input_point: bytes, add: bytes) -> int: @classmethod def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: if mul_cofactor: - gamma = gamma * cls.cv.curve.COFACTOR + gamma = gamma * cls.cv.curve.params.cofactor return point_to_hash(cls.cv, gamma) @classmethod @@ -191,7 +205,7 @@ def ecvrf_proof_to_hash(cls, output_point_bytes: bytes | str) -> bytes: if not isinstance(output_point_bytes, bytes): output_point_bytes = bytes.fromhex(output_point_bytes) try: - output_point = cls.cv.point.string_to_point(output_point_bytes) + output_point = cls.cv.string_to_point(output_point_bytes) except ValueError as exc: raise ValueError("Invalid output point") from exc return cls.proof_to_hash(output_point) diff --git a/tests/test_pedersen/test_pedersen_ark.py b/tests/test_pedersen/test_pedersen_ark.py index cfcd9e2..24a95f5 100644 --- a/tests/test_pedersen/test_pedersen_ark.py +++ b/tests/test_pedersen/test_pedersen_ark.py @@ -6,10 +6,11 @@ from dot_ring.curve.specs.baby_jubjub import BabyJubJub from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW -from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.ed25519 import Ed25519_TAI from dot_ring.curve.specs.jubjub import JubJub -from dot_ring.curve.specs.p256 import P256_NU -from dot_ring.vrf.pedersen.pedersen import PedersenVRF +from dot_ring.curve.specs.p256 import P256_TAI +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.transcript import scalar_len HERE = os.path.dirname(__file__) @@ -17,9 +18,9 @@ (Bandersnatch, "bandersnatch_ed_sha512_ell2_pedersen", "ark-vrf"), (BabyJubJub, "babyjubjub_sha512_tai_pedersen", "ark-vrf"), (Bandersnatch_SW, "bandersnatch_sw_sha512_tai_pedersen", "ark-vrf"), - (Ed25519_NU, "ed25519_sha512_tai_pedersen.json", "ark-vrf"), + (Ed25519_TAI, "ed25519_sha512_tai_pedersen.json", "ark-vrf"), (JubJub, "jubjub_sha512_tai_pedersen.json", "ark-vrf"), - (P256_NU, "secp256r1_sha256_tai_pedersen", "ark-vrf"), + (P256_TAI, "secp256r1_sha256_tai_pedersen", "ark-vrf"), ] @@ -48,7 +49,7 @@ def test_pedersen_ietf(curve_variant, file_prefix, subdir): pk_bytes = PedersenVRF[curve_variant].get_public_key(secret_scalar) # Input Point check - input_point = curve_variant.point.encode_to_curve(alpha) + input_point = curve_variant.encode_to_curve(alpha) if "h" in vector: assert input_point.point_to_string().hex() == vector["h"] @@ -62,12 +63,12 @@ def test_pedersen_ietf(curve_variant, file_prefix, subdir): assert proof.result_point.point_to_string().hex() == vector["proof_r"] assert proof.ok.point_to_string().hex() == vector["proof_ok"] assert proof.s.to_bytes( - (curve_variant.curve.PRIME_FIELD.bit_length() + 7) // 8, - curve_variant.curve.ENDIAN, + scalar_len(curve_variant), + curve_variant.curve.encoding_endian(), ) == bytes.fromhex(vector["proof_s"]) assert proof.sb.to_bytes( - (curve_variant.curve.PRIME_FIELD.bit_length() + 7) // 8, - curve_variant.curve.ENDIAN, + scalar_len(curve_variant), + curve_variant.curve.encoding_endian(), ) == bytes.fromhex(vector["proof_sb"]) assert PedersenVRF[curve_variant].ecvrf_proof_to_hash(proof.output_point.point_to_string()).hex() == vector["beta"] diff --git a/tests/test_pedersen/test_pedersen_base.py b/tests/test_pedersen/test_pedersen_base.py index 6968d3d..6cad1b8 100644 --- a/tests/test_pedersen/test_pedersen_base.py +++ b/tests/test_pedersen/test_pedersen_base.py @@ -11,7 +11,7 @@ from dot_ring.curve.specs.p384 import P384_RO from dot_ring.curve.specs.p521 import P521_RO from dot_ring.curve.specs.secp256k1 import Secp256k1_RO -from dot_ring.vrf.pedersen.pedersen import PedersenVRF +from dot_ring.vrf.pedersen import PedersenVRF HERE = os.path.dirname(__file__) @@ -51,7 +51,7 @@ def test_pedersen_base(curve_variant, file_prefix, slice_end): alpha = bytes.fromhex(vector["alpha"]) additional_data = bytes.fromhex(vector["ad"]) - curve_variant.point.encode_to_curve(alpha) + curve_variant.encode_to_curve(alpha) proof = PedersenVRF[curve_variant].prove(alpha, secret_scalar, additional_data) proof_bytes = proof.to_bytes() From 5ab8320a8f7adb1304718626ed1ee7ff5b6669eb Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:40:54 +0530 Subject: [PATCH 27/33] reshape ietf vrf --- dot_ring/__init__.py | 20 +-- dot_ring/vrf/ietf/__init__.py | 5 +- dot_ring/vrf/ietf/ietf.py | 10 -- dot_ring/vrf/ietf/ietf_vrf.py | 7 - dot_ring/vrf/ietf/thin.py | 166 +++++++++++++++++++ dot_ring/vrf/ietf/thin_batch_item.py | 15 -- dot_ring/vrf/ietf/thin_batch_verifier.py | 64 -------- dot_ring/vrf/ietf/thin_vrf.py | 99 ------------ dot_ring/vrf/ietf/{tiny_vrf.py => tiny.py} | 96 ++++++++--- scripts/generate_test_vectors.py | 23 +-- tests/benchmark/bench_ietf.py | 175 ++++++++++----------- tests/test_ark_vrf.py | 41 +++-- tests/test_bandersnatch_ark.py | 31 ++-- tests/test_curve_ops/test_gaps.py | 98 ++++-------- tests/test_ietf/test_ietf_ark.py | 24 +-- tests/test_ietf/test_ietf_base.py | 8 +- tests/test_keygen.py | 4 +- tests/test_vectors.py | 45 +++--- 18 files changed, 455 insertions(+), 476 deletions(-) delete mode 100644 dot_ring/vrf/ietf/ietf.py delete mode 100644 dot_ring/vrf/ietf/ietf_vrf.py create mode 100644 dot_ring/vrf/ietf/thin.py delete mode 100644 dot_ring/vrf/ietf/thin_batch_item.py delete mode 100644 dot_ring/vrf/ietf/thin_batch_verifier.py delete mode 100644 dot_ring/vrf/ietf/thin_vrf.py rename dot_ring/vrf/ietf/{tiny_vrf.py => tiny.py} (51%) diff --git a/dot_ring/__init__.py b/dot_ring/__init__.py index 82b98af..4014d9f 100644 --- a/dot_ring/__init__.py +++ b/dot_ring/__init__.py @@ -40,27 +40,27 @@ from dot_ring.curve.specs.curve448 import Curve448_NU, Curve448_RO from dot_ring.curve.specs.curve25519 import Curve25519_NU, Curve25519_RO from dot_ring.curve.specs.ed448 import Ed448_NU, Ed448_RO -from dot_ring.curve.specs.ed25519 import Ed25519_NU, Ed25519_RO +from dot_ring.curve.specs.ed25519 import Ed25519_NU, Ed25519_RO, Ed25519_TAI from dot_ring.curve.specs.jubjub import JubJub # NIST curves -from dot_ring.curve.specs.p256 import P256_NU, P256_RO +from dot_ring.curve.specs.p256 import P256_NU, P256_RO, P256_TAI from dot_ring.curve.specs.p384 import P384_NU, P384_RO from dot_ring.curve.specs.p521 import P521_NU, P521_RO from dot_ring.curve.specs.secp256k1 import Secp256k1_NU, Secp256k1_RO from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.ietf import IETF_VRF, ThinBatchVerifier, ThinVRF, TinyVRF +from dot_ring.vrf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF from dot_ring.vrf.pedersen import PedersenBatchVerifier, PedersenVRF -from dot_ring.vrf.ring import Ring, RingBatchVerifier, RingContext, RingRoot, RingSetup, RingVerifierKeyBuilder, RingVRF +from dot_ring.vrf.ring import Ring, RingBatchContext, RingBatchVerifier, RingContext, RingRoot, RingSetup, RingVerifierKeyBuilder, RingVRF # ============================================================================= # Convenience aliases # ============================================================================= -Ed25519 = Ed25519_NU +Ed25519 = Ed25519_TAI Ed448 = Ed448_RO Curve25519 = Curve25519_RO Curve448 = Curve448_RO -P256 = P256_NU +P256 = P256_TAI P384 = P384_RO P521 = P521_RO Secp256k1 = Secp256k1_RO @@ -77,21 +77,22 @@ "TinyVRF", "ThinVRF", "ThinBatchVerifier", - "IETF_VRF", "PedersenVRF", "PedersenBatchVerifier", "RingVRF", "RingSetup", "RingContext", "RingVerifierKeyBuilder", + "RingBatchContext", "RingBatchVerifier", # Primary curves "Bandersnatch", "Bandersnatch_SHAKE128", "Bandersnatch_SW", "Ed25519", - "Ed25519_RO", "Ed25519_NU", + "Ed25519_RO", + "Ed25519_TAI", "Ed448", "Ed448_RO", "Ed448_NU", @@ -103,8 +104,9 @@ "Curve448_NU", # NIST curves "P256", - "P256_RO", "P256_NU", + "P256_RO", + "P256_TAI", "P384", "P384_RO", "P384_NU", diff --git a/dot_ring/vrf/ietf/__init__.py b/dot_ring/vrf/ietf/__init__.py index 9c88d21..3369ced 100644 --- a/dot_ring/vrf/ietf/__init__.py +++ b/dot_ring/vrf/ietf/__init__.py @@ -1,3 +1,4 @@ -from .ietf import IETF_VRF, ThinBatchItem, ThinBatchVerifier, ThinVRF, TinyVRF +from .thin import ThinBatchVerifier, ThinVRF +from .tiny import TinyVRF -__all__ = ["IETF_VRF", "ThinBatchItem", "ThinBatchVerifier", "ThinVRF", "TinyVRF"] +__all__ = ["ThinBatchVerifier", "ThinVRF", "TinyVRF"] diff --git a/dot_ring/vrf/ietf/ietf.py b/dot_ring/vrf/ietf/ietf.py deleted file mode 100644 index 8d7f25e..0000000 --- a/dot_ring/vrf/ietf/ietf.py +++ /dev/null @@ -1,10 +0,0 @@ -from __future__ import annotations - -from .thin_batch_item import ThinBatchItem -from .thin_batch_verifier import ThinBatchVerifier -from .thin_vrf import ThinVRF -from .tiny_vrf import TinyVRF - -IETF_VRF = TinyVRF - -__all__ = ["IETF_VRF", "ThinBatchItem", "ThinBatchVerifier", "ThinVRF", "TinyVRF"] diff --git a/dot_ring/vrf/ietf/ietf_vrf.py b/dot_ring/vrf/ietf/ietf_vrf.py deleted file mode 100644 index 2e5b546..0000000 --- a/dot_ring/vrf/ietf/ietf_vrf.py +++ /dev/null @@ -1,7 +0,0 @@ -from __future__ import annotations - -from .tiny_vrf import TinyVRF - -IETF_VRF = TinyVRF - -__all__ = ["IETF_VRF"] diff --git a/dot_ring/vrf/ietf/thin.py b/dot_ring/vrf/ietf/thin.py new file mode 100644 index 0000000..709b610 --- /dev/null +++ b/dot_ring/vrf/ietf/thin.py @@ -0,0 +1,166 @@ +from __future__ import annotations + +import secrets +from dataclasses import dataclass +from typing import Any + +from dot_ring.curve.curve import CurveVariant +from dot_ring.curve.point import CurvePoint +from dot_ring.vrf.transcript import ( + DomSep, + VrfIo, + challenge, + nonce, + point_len, + point_to_hash, + scalar_decode, + scalar_encode, + scalar_len, + schnorr_ios, + vrf_transcript, + vrf_transcript_scalars, +) + +from ..vrf import VRF + + +@dataclass +class ThinBatchItem: + c: int + ios: list[VrfIo] + zs: list[int] + r: CurvePoint + s: int + + +@dataclass +class ThinVRF(VRF[Any]): + """Batch-friendly VRF-AD proof storing the nonce commitment.""" + + output_point: CurvePoint + r: CurvePoint + s: int + + @classmethod + def from_bytes(cls, proof_bytes: bytes) -> ThinVRF: + encoded_point_len = point_len(cls.cv) + scalar_size = scalar_len(cls.cv) + expected = 2 * encoded_point_len + scalar_size + if len(proof_bytes) != expected: + raise ValueError(f"invalid Thin VRF proof length: expected {expected}, got {len(proof_bytes)}") + try: + output_point = cls.cv.string_to_point(proof_bytes[:encoded_point_len]) + r = cls.cv.string_to_point(proof_bytes[encoded_point_len : 2 * encoded_point_len]) + except ValueError as exc: + raise ValueError("Invalid point in proof") from exc + s = scalar_decode(cls.cv, proof_bytes[2 * encoded_point_len :]) + if s >= cls.cv.curve.params.subgroup_order: + raise ValueError("Response scalar s is not less than the curve order") + return cls(output_point, r, s) + + def to_bytes(self) -> bytes: + return self.output_point.point_to_string() + self.r.point_to_string() + scalar_encode(self.cv, self.s) + + @classmethod + def prove( + cls, + alpha: bytes, + secret_key: bytes, + additional_data: bytes, + salt: bytes = b"", + ) -> ThinVRF: + secret_scalar = scalar_decode(cls.cv, secret_key) + public_key = cls.cv.generator_point() * secret_scalar + input_point = cls.cv.encode_to_curve(alpha, salt) + output_point = input_point * secret_scalar + return cls.prove_ios([VrfIo(input_point, output_point)], secret_scalar, public_key, additional_data) + + @classmethod + def prove_ios( + cls, + ios: list[VrfIo], + secret_scalar: int, + public_key: CurvePoint, + additional_data: bytes, + ) -> ThinVRF: + transcript, merged = vrf_transcript(cls.cv, DomSep.THIN_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) + k = nonce(cls.cv, secret_scalar, transcript) + r = merged.input * k + c = challenge(cls.cv, [r], transcript) + s = (k + c * secret_scalar) % cls.cv.curve.params.subgroup_order + output_point = ios[0].output if len(ios) == 1 else merged.output + return cls(output_point, r, s) + + def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: + input_point = self.cv.encode_to_curve(input, salt) + try: + public_key_pt = self.cv.string_to_point(public_key) + except ValueError as exc: + raise ValueError("Invalid public key") from exc + return self.verify_ios(public_key_pt, [VrfIo(input_point, self.output_point)], additional_data) + + def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: + transcript, merged = vrf_transcript(self.cv, DomSep.THIN_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) + c = challenge(self.cv, [self.r], transcript) + lhs = self.cv.msm([merged.input, merged.output], [self.s, -c]) + return lhs == self.r + + @classmethod + def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: + if mul_cofactor: + gamma = gamma * cls.cv.curve.params.cofactor + return point_to_hash(cls.cv, gamma) + + +def _batch_coefficients(count: int, order: int) -> list[int]: + if count == 0: + return [] + coefficients = [1] + for _ in range(count - 1): + coefficient = 0 + while coefficient == 0: + coefficient = secrets.randbelow(order) + coefficients.append(coefficient) + return coefficients + + +class ThinBatchVerifier: + def __init__(self, curve: CurveVariant): + self.cv = curve + self.items: list[ThinBatchItem] = [] + + @classmethod + def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[ThinBatchVerifier] | Any: + if not isinstance(curve_variant, CurveVariant): + return cls + + class _SpecializedThinBatchVerifier(cls): + def __init__(self) -> None: + super().__init__(curve_variant) + + _SpecializedThinBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" + return _SpecializedThinBatchVerifier + + def push(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes, proof: ThinVRF) -> None: + chained_ios = schnorr_ios(self.cv, public_key, ios) + transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.THIN_VRF, chained_ios, additional_data) + c = challenge(self.cv, [proof.r], transcript) + self.items.append(ThinBatchItem(c, chained_ios, scalar_stream.take(len(chained_ios)), proof.r, proof.s)) + + def verify(self) -> bool: + if not self.items: + return True + + points: list[CurvePoint] = [] + scalars: list[int] = [] + order = self.cv.curve.params.subgroup_order + for coefficient, item in zip(_batch_coefficients(len(self.items), order), self.items, strict=False): + weighted_c = coefficient * item.c + weighted_s = coefficient * item.s + + for io, z in zip(item.ios, item.zs, strict=True): + points.extend([io.input, io.output]) + scalars.extend([weighted_s * z, -(weighted_c * z)]) + points.append(item.r) + scalars.append(-coefficient) + return self.cv.msm(points, scalars).is_identity() diff --git a/dot_ring/vrf/ietf/thin_batch_item.py b/dot_ring/vrf/ietf/thin_batch_item.py deleted file mode 100644 index a9754f8..0000000 --- a/dot_ring/vrf/ietf/thin_batch_item.py +++ /dev/null @@ -1,15 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass - -from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import VrfIo - - -@dataclass -class ThinBatchItem: - c: int - ios: list[VrfIo] - zs: list[int] - r: CurvePoint - s: int diff --git a/dot_ring/vrf/ietf/thin_batch_verifier.py b/dot_ring/vrf/ietf/thin_batch_verifier.py deleted file mode 100644 index 4accc37..0000000 --- a/dot_ring/vrf/ietf/thin_batch_verifier.py +++ /dev/null @@ -1,64 +0,0 @@ -from __future__ import annotations - -import secrets -from typing import Any - -from dot_ring.curve.curve import CurveVariant -from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import DomSep, VrfIo, challenge, schnorr_ios, vrf_transcript_scalars - -from .thin_batch_item import ThinBatchItem -from .thin_vrf import ThinVRF - - -def _batch_coefficients(count: int, order: int) -> list[int]: - if count == 0: - return [] - coefficients = [1] - for _ in range(count - 1): - coefficient = 0 - while coefficient == 0: - coefficient = secrets.randbelow(order) - coefficients.append(coefficient) - return coefficients - - -class ThinBatchVerifier: - def __init__(self, curve: CurveVariant): - self.cv = curve - self.items: list[ThinBatchItem] = [] - - @classmethod - def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[ThinBatchVerifier] | Any: - if not isinstance(curve_variant, CurveVariant): - return cls - - class _SpecializedThinBatchVerifier(cls): - def __init__(self) -> None: - super().__init__(curve_variant) - - _SpecializedThinBatchVerifier.__name__ = f"{cls.__name__}[{curve_variant.name}]" - return _SpecializedThinBatchVerifier - - def push(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes, proof: ThinVRF) -> None: - chained_ios = schnorr_ios(self.cv, public_key, ios) - transcript, scalar_stream = vrf_transcript_scalars(self.cv, DomSep.THIN_VRF, chained_ios, additional_data) - c = challenge(self.cv, [proof.r], transcript) - self.items.append(ThinBatchItem(c, chained_ios, scalar_stream.take(len(chained_ios)), proof.r, proof.s)) - - def verify(self) -> bool: - if not self.items: - return True - - points: list[CurvePoint] = [] - scalars: list[int] = [] - for coefficient, item in zip(_batch_coefficients(len(self.items), self.cv.curve.ORDER), self.items, strict=False): - weighted_c = coefficient * item.c - weighted_s = coefficient * item.s - - for io, z in zip(item.ios, item.zs, strict=True): - points.extend([io.input, io.output]) - scalars.extend([weighted_s * z, -(weighted_c * z)]) - points.append(item.r) - scalars.append(-coefficient) - return self.cv.point.msm(points, scalars).is_identity() diff --git a/dot_ring/vrf/ietf/thin_vrf.py b/dot_ring/vrf/ietf/thin_vrf.py deleted file mode 100644 index 6d5b835..0000000 --- a/dot_ring/vrf/ietf/thin_vrf.py +++ /dev/null @@ -1,99 +0,0 @@ -from __future__ import annotations - -from dataclasses import dataclass -from typing import Any, cast - -from dot_ring.curve.point import CurvePoint -from dot_ring.vrf.transcript import ( - DomSep, - VrfIo, - challenge, - nonce, - point_to_hash, - scalar_decode, - scalar_encode, - scalar_len, - schnorr_ios, - vrf_transcript, -) - -from ..vrf import VRF - - -@dataclass -class ThinVRF(VRF[Any]): - """Batch-friendly VRF-AD proof storing the nonce commitment.""" - - output_point: CurvePoint - r: CurvePoint - s: int - - @classmethod - def from_bytes(cls, proof_bytes: bytes) -> ThinVRF: - point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) - scalar_size = scalar_len(cls.cv) - expected = 2 * point_len + scalar_size - if len(proof_bytes) != expected: - raise ValueError(f"invalid Thin VRF proof length: expected {expected}, got {len(proof_bytes)}") - try: - output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) - r = cls.cv.point.string_to_point(proof_bytes[point_len : 2 * point_len]) - except ValueError as exc: - raise ValueError("Invalid point in proof") from exc - s = scalar_decode(cls.cv, proof_bytes[2 * point_len :]) - if s >= cls.cv.curve.ORDER: - raise ValueError("Response scalar s is not less than the curve order") - return cls(output_point, r, s) - - def to_bytes(self) -> bytes: - return self.output_point.point_to_string() + self.r.point_to_string() + scalar_encode(self.cv, self.s) - - @classmethod - def prove( - cls, - alpha: bytes, - secret_key: bytes, - additional_data: bytes, - salt: bytes = b"", - ) -> ThinVRF: - secret_scalar = scalar_decode(cls.cv, secret_key) - public_key = cls.cv.point.generator_point() * secret_scalar - input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) - output_point = input_point * secret_scalar - return cls.prove_ios([VrfIo(input_point, output_point)], secret_scalar, public_key, additional_data) - - @classmethod - def prove_ios( - cls, - ios: list[VrfIo], - secret_scalar: int, - public_key: CurvePoint, - additional_data: bytes, - ) -> ThinVRF: - transcript, merged = vrf_transcript(cls.cv, DomSep.THIN_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) - k = nonce(cls.cv, secret_scalar, transcript) - r = merged.input * k - c = challenge(cls.cv, [r], transcript) - s = (k + c * secret_scalar) % cls.cv.curve.ORDER - output_point = ios[0].output if len(ios) == 1 else merged.output - return cls(output_point, r, s) - - def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: - input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) - try: - public_key_pt = self.cv.point.string_to_point(public_key) - except ValueError as exc: - raise ValueError("Invalid public key") from exc - return self.verify_ios(public_key_pt, [VrfIo(input_point, self.output_point)], additional_data) - - def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: - transcript, merged = vrf_transcript(self.cv, DomSep.THIN_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) - c = challenge(self.cv, [self.r], transcript) - lhs = self.cv.point.msm([merged.input, merged.output], [self.s, -c]) - return lhs == self.r - - @classmethod - def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: - if mul_cofactor: - gamma = gamma * cls.cv.curve.COFACTOR - return point_to_hash(cls.cv, gamma) diff --git a/dot_ring/vrf/ietf/tiny_vrf.py b/dot_ring/vrf/ietf/tiny.py similarity index 51% rename from dot_ring/vrf/ietf/tiny_vrf.py rename to dot_ring/vrf/ietf/tiny.py index 312decd..212f669 100644 --- a/dot_ring/vrf/ietf/tiny_vrf.py +++ b/dot_ring/vrf/ietf/tiny.py @@ -1,7 +1,7 @@ from __future__ import annotations from dataclasses import dataclass -from typing import Any, cast +from typing import Any from dot_ring.curve.point import CurvePoint from dot_ring.vrf.transcript import ( @@ -10,15 +10,17 @@ VrfIo, challenge, nonce, + point_len, point_to_hash, scalar_decode, scalar_encode, scalar_len, schnorr_ios, vrf_transcript, + vrf_transcript_scalars, ) -from ..vrf import VRF +from ..vrf import VRF, PreparedSecretKey @dataclass @@ -31,7 +33,7 @@ class TinyVRF(VRF[Any]): Usage: >>> from dot_ring.curve.specs.bandersnatch import Bandersnatch - >>> from dot_ring.vrf.ietf.ietf import TinyVRF + >>> from dot_ring.vrf.ietf import TinyVRF >>> proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, additional_data) >>> verified = proof.verify(public_key, input_point, additional_data) """ @@ -42,18 +44,19 @@ class TinyVRF(VRF[Any]): @classmethod def from_bytes(cls, proof_bytes: bytes) -> TinyVRF: - point_len = cls.cv.curve.POINT_LEN * (2 if cls.cv.curve.UNCOMPRESSED else 1) + encoded_point_len = point_len(cls.cv) scalar_size = scalar_len(cls.cv) - expected = point_len + CHALLENGE_LEN + scalar_size + expected = encoded_point_len + CHALLENGE_LEN + scalar_size if len(proof_bytes) != expected: raise ValueError(f"invalid Tiny VRF proof length: expected {expected}, got {len(proof_bytes)}") try: - output_point = cls.cv.point.string_to_point(proof_bytes[:point_len]) + output_point = cls.cv.string_to_point(proof_bytes[:encoded_point_len]) except ValueError as exc: raise ValueError("Invalid output point") from exc - c = int.from_bytes(proof_bytes[point_len : point_len + CHALLENGE_LEN], "little") % cls.cv.curve.ORDER - s = scalar_decode(cls.cv, proof_bytes[point_len + CHALLENGE_LEN :]) - if s >= cls.cv.curve.ORDER: + order = cls.cv.curve.params.subgroup_order + c = int.from_bytes(proof_bytes[encoded_point_len : encoded_point_len + CHALLENGE_LEN], "little") % order + s = scalar_decode(cls.cv, proof_bytes[encoded_point_len + CHALLENGE_LEN :]) + if s >= order: raise ValueError("Response scalar s is not less than the curve order") return cls(output_point, c, s) @@ -62,7 +65,7 @@ def to_bytes(self) -> bytes: @classmethod def _io_from_alpha(cls, alpha: bytes, secret_scalar: int, salt: bytes = b"") -> VrfIo: - input_point = cast(Any, cls.cv.point).encode_to_curve(alpha, salt) + input_point = cls.cv.encode_to_curve(alpha, salt) output_point = input_point * secret_scalar return VrfIo(input_point, output_point) @@ -74,10 +77,20 @@ def prove( additional_data: bytes, salt: bytes = b"", ) -> TinyVRF: - secret_scalar = scalar_decode(cls.cv, secret_key) - public_key = cls.cv.point.generator_point() * secret_scalar - io = cls._io_from_alpha(alpha, secret_scalar, salt) - return cls.prove_ios([io], secret_scalar, public_key, additional_data) + return cls.prove_prepared(alpha, cls.prepare_secret_key(secret_key), additional_data, salt) + + @classmethod + def prove_prepared( + cls, + alpha: bytes, + secret_key: PreparedSecretKey[Any], + additional_data: bytes, + salt: bytes = b"", + ) -> TinyVRF: + if secret_key.curve is not cls.cv: + raise ValueError("prepared secret key uses a different curve") + io = cls._io_from_alpha(alpha, secret_key.secret_scalar, salt) + return cls.prove_ios([io], secret_key.secret_scalar, secret_key.public_key, additional_data) @classmethod def prove_ios( @@ -87,37 +100,80 @@ def prove_ios( public_key: CurvePoint, additional_data: bytes, ) -> TinyVRF: + if len(ios) == 1: + transcript, scalars = vrf_transcript_scalars( + cls.cv, + DomSep.TINY_VRF, + schnorr_ios(cls.cv, public_key, ios), + additional_data, + ) + z0 = scalars.next() + z1 = scalars.next() + k = cls.generate_nonce(secret_scalar, transcript) + generator = cls.cv.generator_point() + order = cls.cv.curve.params.subgroup_order + r = cls.cv.msm( + [generator, ios[0].input], + [(k * z0) % order, (k * z1) % order], + ) + c = challenge(cls.cv, [r], transcript) + s = (k + c * secret_scalar) % order + return cls(ios[0].output, c, s) + transcript, merged = vrf_transcript(cls.cv, DomSep.TINY_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) k = cls.generate_nonce(secret_scalar, transcript) r = merged.input * k c = challenge(cls.cv, [r], transcript) - s = (k + c * secret_scalar) % cls.cv.curve.ORDER + s = (k + c * secret_scalar) % cls.cv.curve.params.subgroup_order output_point = ios[0].output if len(ios) == 1 else merged.output return cls(output_point, c, s) @classmethod def generate_nonce(cls, secret_scalar: int, transcript: Any) -> int: - if not hasattr(transcript, "clone"): + if not hasattr(transcript, "copy"): return super().generate_nonce(secret_scalar, transcript) return nonce(cls.cv, secret_scalar, transcript) def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: - input_point = cast(Any, self.cv.point).encode_to_curve(input, salt) + input_point = self.cv.encode_to_curve(input, salt) try: - public_key_pt = self.cv.point.string_to_point(public_key) + public_key_pt = self.cv.string_to_point(public_key) except ValueError as exc: raise ValueError("Invalid public key") from exc io = VrfIo(input_point, self.output_point) return self.verify_ios(public_key_pt, [io], additional_data) def verify_ios(self, public_key: CurvePoint, ios: list[VrfIo], additional_data: bytes) -> bool: + if len(ios) == 1: + transcript, scalars = vrf_transcript_scalars( + self.cv, + DomSep.TINY_VRF, + schnorr_ios(self.cv, public_key, ios), + additional_data, + ) + z0 = scalars.next() + z1 = scalars.next() + generator = self.cv.generator_point() + order = self.cv.curve.params.subgroup_order + r = self.cv.msm( + [generator, ios[0].input, public_key, ios[0].output], + [ + (self.s * z0) % order, + (self.s * z1) % order, + (-self.c * z0) % order, + (-self.c * z1) % order, + ], + ) + expected_c = challenge(self.cv, [r], transcript) + return self.c == expected_c + transcript, merged = vrf_transcript(self.cv, DomSep.TINY_VRF, schnorr_ios(self.cv, public_key, ios), additional_data) - r = self.cv.point.msm([merged.input, merged.output], [self.s, -self.c]) + r = self.cv.msm([merged.input, merged.output], [self.s, -self.c]) expected_c = challenge(self.cv, [r], transcript) return self.c == expected_c @classmethod def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: if mul_cofactor: - gamma = gamma * cls.cv.curve.COFACTOR + gamma = gamma * cls.cv.curve.params.cofactor return point_to_hash(cls.cv, gamma) diff --git a/scripts/generate_test_vectors.py b/scripts/generate_test_vectors.py index 0d65da7..8db4d2a 100644 --- a/scripts/generate_test_vectors.py +++ b/scripts/generate_test_vectors.py @@ -13,15 +13,15 @@ from dot_ring.curve.specs.baby_jubjub import BabyJubJub from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_SHAKE128 from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW -from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.ed25519 import Ed25519_TAI from dot_ring.curve.specs.jubjub import JubJub -from dot_ring.curve.specs.p256 import P256_NU +from dot_ring.curve.specs.p256 import P256_TAI from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ietf import ThinVRF, TinyVRF from dot_ring.vrf.pedersen import PedersenVRF from dot_ring.vrf.ring import Ring, RingRoot, RingVRF -from dot_ring.vrf.transcript import scalar_encode +from dot_ring.vrf.transcript import point_len, scalar_encode, scalar_len DEFAULT_OUT_DIR = Path(__file__).resolve().parent.parent / "tests" / "vectors" / "dot-ring" RING_SIZE = 8 @@ -38,8 +38,8 @@ class Suite: Suite("bandersnatch_sha-512_ell2", Bandersnatch), Suite("bandersnatch_sw_sha-512_tai", Bandersnatch_SW), Suite("bandersnatch_shake128_ell2", Bandersnatch_SHAKE128), - Suite("ed25519_sha-512_tai", Ed25519_NU), - Suite("secp256r1_sha-256_tai", P256_NU), + Suite("ed25519_sha-512_tai", Ed25519_TAI), + Suite("secp256r1_sha-256_tai", P256_TAI), Suite("jubjub_sha-512_tai", JubJub), Suite("baby-jubjub_sha-512_tai", BabyJubJub), ] @@ -49,7 +49,6 @@ class Suite: Suite("bandersnatch_sw_sha-512_tai", Bandersnatch_SW), Suite("bandersnatch_shake128_ell2", Bandersnatch_SHAKE128), Suite("jubjub_sha-512_tai", JubJub), - Suite("baby-jubjub_sha-512_tai", BabyJubJub), ] VECTOR_CASES = [ @@ -69,21 +68,13 @@ def seed_bytes(seed: int) -> bytes: return bytes(seed_value) -def point_len(curve: CurveVariant) -> int: - size = curve.curve.POINT_LEN - if curve.curve.UNCOMPRESSED: - size *= 2 - return int(size) - - def pedersen_len(curve: CurveVariant) -> int: - scalar_len = (curve.curve.ORDER.bit_length() + 7) // 8 - return 4 * point_len(curve) + 2 * scalar_len + return 4 * point_len(curve) + 2 * scalar_len(curve) def base_fields(suite: Suite, scheme: str, index: int, seed: int, alpha: bytes, ad: bytes) -> tuple[bytes, dict[str, str]]: pk, sk = secret_from_seed(seed_bytes(seed), suite.curve) - input_point = suite.curve.point.encode_to_curve(alpha) + input_point = suite.curve.encode_to_curve(alpha) gamma = input_point * int.from_bytes(sk, "little") beta = TinyVRF[suite.curve].proof_to_hash(gamma) return sk, { diff --git a/tests/benchmark/bench_ietf.py b/tests/benchmark/bench_ietf.py index 4a58b66..2ce4e9e 100644 --- a/tests/benchmark/bench_ietf.py +++ b/tests/benchmark/bench_ietf.py @@ -1,117 +1,106 @@ #!/usr/bin/env python3 -""" -Benchmark script for IETF VRF proof generation and verification. +"""Benchmark fresh IETF/Tiny VRF proof and verification timings.""" -Run with: - python tests/bench_ietf.py - -- Runs multiple warmup iterations -- Measures over multiple iterations -- Reports min, mean, and std deviation -""" +from __future__ import annotations +import argparse +import hashlib import statistics import time +from dataclasses import dataclass from dot_ring import Bandersnatch from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.ietf.ietf import IETF_VRF +from dot_ring.vrf.ietf import TinyVRF -def load_test_data(): - """Create deterministic benchmark data.""" - _, sk = secret_from_seed(bytes(32), Bandersnatch) - return {"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex(), "salt": ""} +@dataclass(frozen=True) +class Timing: + prove_ms: float + verify_ms: float + total_ms: float + proof_size: int -def benchmark_ietf_vrf(warmup_iters: int = 5, bench_iters: int = 100): - """Benchmark IETF VRF proof generation and verification.""" +def _seed(*parts: object) -> bytes: + h = hashlib.sha256() + for part in parts: + match part: + case bytes(): + h.update(part) + case int(): + h.update(part.to_bytes(8, "little", signed=False)) + case str(): + h.update(part.encode()) + case _: + raise TypeError(f"unsupported seed part: {type(part).__name__}") + h.update(b"\0") + return h.digest() - print("=" * 60) - print("IETF VRF Benchmark (Python/Cython + gmpy2)") - print("=" * 60) - print() - # Load test data - data = load_test_data() - s_k = bytes.fromhex(data["sk"]) - alpha = bytes.fromhex(data["alpha"]) - ad = bytes.fromhex(data["ad"]) - salt = bytes.fromhex(data.get("salt", "")) +def benchmark_once(sample_index: int) -> Timing: + public_key, secret_key = secret_from_seed(_seed("ietf-signer", sample_index), Bandersnatch) + alpha = b"bench-ietf-input" + sample_index.to_bytes(8, "little") + ad = b"bench-ietf-ad" + sample_index.to_bytes(8, "little") + salt = b"bench-ietf-salt" + sample_index.to_bytes(8, "little") - # Get public key - p_k = IETF_VRF[Bandersnatch].get_public_key(s_k) + total_start = time.perf_counter() - print("Curve: Bandersnatch (Twisted Edwards)") - print(f"Warmup iterations: {warmup_iters}") - print(f"Benchmark iterations: {bench_iters}") - print() + start = time.perf_counter() + proof = TinyVRF[Bandersnatch].prove(alpha, secret_key, ad, salt) + prove_ms = (time.perf_counter() - start) * 1000 - # ========================================================================= - # Warmup - # ========================================================================= - print("Warming up...") - for _ in range(warmup_iters): - proof = IETF_VRF[Bandersnatch].prove(alpha, s_k, ad, salt) - proof.verify(p_k, alpha, ad, salt) - - # ========================================================================= - # Benchmark Proof Generation - # ========================================================================= - print("Benchmarking Proof Generation...") - proof_times = [] - proofs = [] - for _ in range(bench_iters): - start = time.perf_counter() - proof = IETF_VRF[Bandersnatch].prove(alpha, s_k, ad, salt) - elapsed = (time.perf_counter() - start) * 1000 - proof_times.append(elapsed) - proofs.append(proof) - - # ========================================================================= - # Benchmark Verification - # ========================================================================= - print("Benchmarking Verification...") - verify_times = [] - for proof in proofs: - start = time.perf_counter() - result = proof.verify(p_k, alpha, ad, salt) - elapsed = (time.perf_counter() - start) * 1000 - verify_times.append(elapsed) - assert result, "Verification failed!" - - # ========================================================================= - # Results - # ========================================================================= - print() - print("=" * 60) - print("RESULTS") - print("=" * 60) + start = time.perf_counter() + verified = proof.verify(public_key, alpha, ad, salt) + verify_ms = (time.perf_counter() - start) * 1000 + + if not verified: + raise AssertionError("IETF VRF verification failed") + + return Timing( + prove_ms=prove_ms, + verify_ms=verify_ms, + total_ms=(time.perf_counter() - total_start) * 1000, + proof_size=len(proof.to_bytes()), + ) + + +def _stats(values: list[float]) -> tuple[float, float, float, float]: + stddev = statistics.stdev(values) if len(values) > 1 else 0.0 + return min(values), statistics.mean(values), max(values), stddev + + +def _print_stats(label: str, values: list[float]) -> None: + minimum, mean, maximum, stddev = _stats(values) + print(f"{label:<8} min={minimum:8.2f} ms mean={mean:8.2f} ms max={maximum:8.2f} ms stddev={stddev:8.2f} ms") + + +def run(samples: int) -> None: + if samples <= 0: + raise ValueError("samples must be positive") + + timings = [benchmark_once(sample_index) for sample_index in range(samples)] + + print(f"IETF/Tiny VRF benchmark: samples={samples}") + print("Scope: each sample builds fresh key material, input, proof, and verification.") print() + print("sample prove_ms verify_ms total_ms") + for index, timing in enumerate(timings, start=1): + print(f"{index:>6} {timing.prove_ms:>8.2f} {timing.verify_ms:>9.2f} {timing.total_ms:>8.2f}") - def print_stats(name: str, times: list): - min_t = min(times) - mean_t = statistics.mean(times) - std_t = statistics.stdev(times) if len(times) > 1 else 0 - print(f"{name}:") - print(f" Min: {min_t:8.2f} ms") - print(f" Mean: {mean_t:8.2f} ms") - print(f" Stddev: {std_t:8.2f} ms") - print() - return min_t, mean_t - - proof_min, proof_mean = print_stats("Proof Generation", proof_times) - verify_min, verify_mean = print_stats("Verification", verify_times) - - print("-" * 60) - print(f"Total (Proof + Verify) Min: {proof_min + verify_min:8.2f} ms") - print(f"Total (Proof + Verify) Mean: {proof_mean + verify_mean:8.2f} ms") print() + _print_stats("prove", [timing.prove_ms for timing in timings]) + _print_stats("verify", [timing.verify_ms for timing in timings]) + _print_stats("total", [timing.total_ms for timing in timings]) + print(f"proof_size: {timings[-1].proof_size} bytes") + - # Proof size - proof_bytes = proofs[0].to_bytes() - print(f"Proof size: {len(proof_bytes)} bytes") +def main() -> None: + parser = argparse.ArgumentParser(description="Benchmark fresh IETF/Tiny VRF prove and verify timings.") + parser.add_argument("-s", "--samples", type=int, default=30, help="number of fresh samples to run") + args = parser.parse_args() + run(args.samples) if __name__ == "__main__": - benchmark_ietf_vrf(warmup_iters=5, bench_iters=100) + main() diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py index dbeaa9e..5b0959c 100644 --- a/tests/test_ark_vrf.py +++ b/tests/test_ark_vrf.py @@ -7,14 +7,14 @@ from dot_ring.curve.specs.baby_jubjub import BabyJubJub from dot_ring.curve.specs.bandersnatch import Bandersnatch, Bandersnatch_SHAKE128 from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW -from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.ed25519 import Ed25519_TAI from dot_ring.curve.specs.jubjub import JubJub -from dot_ring.curve.specs.p256 import P256_NU +from dot_ring.curve.specs.p256 import P256_TAI from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ietf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF -from dot_ring.vrf.pedersen.pedersen import PedersenBatchVerifier, PedersenVRF -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingBatchVerifier, RingContext, RingVRF +from dot_ring.vrf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF +from dot_ring.vrf.pedersen import PedersenBatchVerifier, PedersenVRF +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.ring import RingBatchVerifier, RingContext, RingVRF from dot_ring.vrf.transcript import VrfIo from scripts.generate_test_vectors import SUITES as GENERATED_SUITES from scripts.generate_test_vectors import scheme_vectors @@ -26,8 +26,8 @@ (Bandersnatch, "bandersnatch_sha-512_ell2", 32), (Bandersnatch_SW, "bandersnatch_sw_sha-512_tai", 33), (Bandersnatch_SHAKE128, "bandersnatch_shake128_ell2", 32), - (Ed25519_NU, "ed25519_sha-512_tai", 32), - (P256_NU, "secp256r1_sha-256_tai", 33), + (Ed25519_TAI, "ed25519_sha-512_tai", 32), + (P256_TAI, "secp256r1_sha-256_tai", 33), (JubJub, "jubjub_sha-512_tai", 32), (BabyJubJub, "baby-jubjub_sha-512_tai", 32), ] @@ -53,7 +53,7 @@ def test_tiny_vectors(curve, prefix: str, point_len: int) -> None: proof_bytes = proof.to_bytes() assert TinyVRF[curve].get_public_key(sk).hex() == vector["pk"] - assert curve.point.encode_to_curve(alpha).point_to_string().hex() == vector["h"] + assert curve.encode_to_curve(alpha).point_to_string().hex() == vector["h"] assert proof_bytes == tiny_proof_bytes(vector) assert TinyVRF[curve].proof_to_hash(proof.output_point).hex() == vector["beta"] assert proof.verify(bytes.fromhex(vector["pk"]), alpha, ad) @@ -116,7 +116,6 @@ def test_rejects_invalid_point_encodings(curve, prefix: str, point_len: int) -> (Bandersnatch_SW, "bandersnatch_sw_sha-512_tai_ring.json"), (Bandersnatch_SHAKE128, "bandersnatch_shake128_ell2_ring.json"), (JubJub, "jubjub_sha-512_tai_ring.json"), - (BabyJubJub, "baby-jubjub_sha-512_tai_ring.json"), ], ) def test_ring_vectors(curve, filename: str) -> None: @@ -140,11 +139,11 @@ def test_multi_input_apis() -> None: secret = bytes.fromhex(load("bandersnatch_sha-512_ell2_tiny.json")[0]["sk"]) public_key = TinyVRF[Bandersnatch].get_public_key(secret) secret_scalar = int.from_bytes(secret, "little") - public_key_point = Bandersnatch.point.string_to_point(public_key) + public_key_point = Bandersnatch.string_to_point(public_key) ios = [] for alpha in (b"first", b"second"): - input_point = Bandersnatch.point.encode_to_curve(alpha) + input_point = Bandersnatch.encode_to_curve(alpha) ios.append(VrfIo(input_point, input_point * secret_scalar)) tiny = TinyVRF[Bandersnatch].prove_ios(ios, secret_scalar, public_key_point, b"ad") @@ -164,9 +163,9 @@ def test_batch_apis() -> None: for vector in vectors: alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) - pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) + pk_point = Bandersnatch.string_to_point(bytes.fromhex(vector["pk"])) thin = ThinVRF[Bandersnatch].prove(alpha, bytes.fromhex(vector["sk"]), ad) - input_point = Bandersnatch.point.encode_to_curve(alpha) + input_point = Bandersnatch.encode_to_curve(alpha) thin_batch.push(pk_point, [VrfIo(input_point, thin.output_point)], ad, thin) pedersen = PedersenVRF[Bandersnatch].prove(alpha, bytes.fromhex(vector["sk"]), ad) @@ -181,11 +180,11 @@ def test_batch_rejects_invalid_items() -> None: alpha = bytes.fromhex(vector["alpha"]) ad = bytes.fromhex(vector["ad"]) secret = bytes.fromhex(vector["sk"]) - pk_point = Bandersnatch.point.string_to_point(bytes.fromhex(vector["pk"])) - input_point = Bandersnatch.point.encode_to_curve(alpha) + pk_point = Bandersnatch.string_to_point(bytes.fromhex(vector["pk"])) + input_point = Bandersnatch.encode_to_curve(alpha) thin = ThinVRF[Bandersnatch].prove(alpha, secret, ad) - bad_thin = ThinVRF[Bandersnatch](thin.output_point, thin.r, (thin.s + 1) % Bandersnatch.curve.ORDER) + bad_thin = ThinVRF[Bandersnatch](thin.output_point, thin.r, (thin.s + 1) % Bandersnatch.curve.params.subgroup_order) thin_batch = ThinBatchVerifier[Bandersnatch]() thin_batch.push(pk_point, [VrfIo(input_point, bad_thin.output_point)], ad, bad_thin) assert not thin_batch.verify() @@ -196,7 +195,7 @@ def test_batch_rejects_invalid_items() -> None: pedersen.blinded_pk, pedersen.result_point, pedersen.ok, - (pedersen.s + 1) % Bandersnatch.curve.ORDER, + (pedersen.s + 1) % Bandersnatch.curve.params.subgroup_order, pedersen.sb, ) pedersen_batch = PedersenBatchVerifier[Bandersnatch]() @@ -280,12 +279,12 @@ def test_negative_and_malformed_proofs() -> None: sk = bytes.fromhex(tiny_vector["sk"]) tiny = TinyVRF[Bandersnatch].prove(alpha, sk, ad) - pk_point = Bandersnatch.point.string_to_point(pk) - wrong_output = Bandersnatch.point.encode_to_curve(b"wrong-output") + pk_point = Bandersnatch.string_to_point(pk) + wrong_output = Bandersnatch.encode_to_curve(b"wrong-output") assert not tiny.verify(pk, alpha, b"wrong-ad") assert not tiny.verify(pk, b"wrong-input", ad) - assert not tiny.verify_ios(pk_point, [VrfIo(Bandersnatch.point.encode_to_curve(alpha), wrong_output)], ad) + assert not tiny.verify_ios(pk_point, [VrfIo(Bandersnatch.encode_to_curve(alpha), wrong_output)], ad) with pytest.raises(ValueError, match="invalid Tiny VRF proof length"): TinyVRF[Bandersnatch].from_bytes(tiny.to_bytes()[:-1]) diff --git a/tests/test_bandersnatch_ark.py b/tests/test_bandersnatch_ark.py index 41a6d04..3298afa 100644 --- a/tests/test_bandersnatch_ark.py +++ b/tests/test_bandersnatch_ark.py @@ -6,10 +6,11 @@ from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ietf.ietf import IETF_VRF -from dot_ring.vrf.pedersen.pedersen import PedersenVRF -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF +from dot_ring.vrf.ietf import TinyVRF +from dot_ring.vrf.pedersen import PedersenVRF +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.ring import RingVRF +from dot_ring.vrf.transcript import scalar_len HERE = os.path.dirname(__file__) @@ -30,18 +31,18 @@ def test_ietf_ark_bandersnatch(): ) # Public Key check - pk_bytes = IETF_VRF[Bandersnatch].get_public_key(secret_scalar) - public_key = Bandersnatch.point.string_to_point(pk_bytes) + pk_bytes = TinyVRF[Bandersnatch].get_public_key(secret_scalar) + public_key = Bandersnatch.string_to_point(pk_bytes) assert public_key.point_to_string().hex() == vector["pk"] # Input Point check - input_point = Bandersnatch.point.encode_to_curve(vector["alpha"]) + input_point = Bandersnatch.encode_to_curve(vector["alpha"]) if "h" in vector: assert input_point.point_to_string().hex() == vector["h"] - proof = IETF_VRF[Bandersnatch].prove(alpha, secret_scalar, additional_data) + proof = TinyVRF[Bandersnatch].prove(alpha, secret_scalar, additional_data) proof_bytes = proof.to_bytes() - proof_rt = IETF_VRF[Bandersnatch].from_bytes(proof_bytes) + proof_rt = TinyVRF[Bandersnatch].from_bytes(proof_bytes) # Proof components check gamma = proof_bytes[:gamma_len] @@ -53,7 +54,7 @@ def test_ietf_ark_bandersnatch(): assert proof_s.hex() == vector["proof_s"] if "beta" in vector: - assert IETF_VRF[Bandersnatch].ecvrf_proof_to_hash(proof_bytes).hex() == vector["beta"] + assert TinyVRF[Bandersnatch].ecvrf_proof_to_hash(proof_bytes).hex() == vector["beta"] assert proof.verify(pk_bytes, alpha, additional_data) assert proof_rt.to_bytes() == proof_bytes @@ -77,7 +78,7 @@ def test_pedersen_ark_bandersnatch(): pk_bytes = PedersenVRF[Bandersnatch].get_public_key(secret_scalar) # Input Point check - input_point = Bandersnatch.point.encode_to_curve(alpha) + input_point = Bandersnatch.encode_to_curve(alpha) if "h" in vector: assert input_point.point_to_string().hex() == vector["h"] @@ -91,12 +92,12 @@ def test_pedersen_ark_bandersnatch(): assert proof.result_point.point_to_string().hex() == vector["proof_r"] assert proof.ok.point_to_string().hex() == vector["proof_ok"] assert proof.s.to_bytes( - (Bandersnatch.curve.PRIME_FIELD.bit_length() + 7) // 8, - Bandersnatch.curve.ENDIAN, + scalar_len(Bandersnatch), + Bandersnatch.curve.encoding_endian(), ) == bytes.fromhex(vector["proof_s"]) assert proof.sb.to_bytes( - (Bandersnatch.curve.PRIME_FIELD.bit_length() + 7) // 8, - Bandersnatch.curve.ENDIAN, + scalar_len(Bandersnatch), + Bandersnatch.curve.encoding_endian(), ) == bytes.fromhex(vector["proof_sb"]) assert PedersenVRF[Bandersnatch].ecvrf_proof_to_hash(proof.output_point.point_to_string()).hex() == vector["beta"] diff --git a/tests/test_curve_ops/test_gaps.py b/tests/test_curve_ops/test_gaps.py index d06e0dd..f73d020 100644 --- a/tests/test_curve_ops/test_gaps.py +++ b/tests/test_curve_ops/test_gaps.py @@ -1,16 +1,17 @@ import pytest -from dot_ring import IETF_VRF, Bandersnatch, PedersenVRF +from dot_ring import Bandersnatch, PedersenVRF, TinyVRF from dot_ring.curve.specs.ed448 import Ed448_RO from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring, RingRoot +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.transcript import point_len, scalar_len class TestCoverageGaps: def test_vrf_generate_nonce_invalid_input(self): """Test generate_nonce raises ValueError for invalid input point type.""" with pytest.raises(ValueError, match="Input point must be a CurvePoint"): - IETF_VRF[Bandersnatch].generate_nonce(123, 456) # type: ignore + TinyVRF[Bandersnatch].generate_nonce(123, 456) # type: ignore def test_ecvrf_decode_proof_string_input(self): """Test ecvrf_decode_proof handles hex string input.""" @@ -18,11 +19,11 @@ def test_ecvrf_decode_proof_string_input(self): alpha = b"test" sk = b"secret" ad = b"ad" - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() proof_hex = proof_bytes.hex() - gamma, c, s = IETF_VRF[Bandersnatch].ecvrf_decode_proof(proof_hex) + gamma, c, s = TinyVRF[Bandersnatch].ecvrf_decode_proof(proof_hex) assert gamma == proof.output_point assert c == proof.c assert s == proof.s @@ -33,26 +34,26 @@ def test_ecvrf_decode_proof_invalid_gamma(self): # Bandersnatch point length is 33. invalid_gamma = b"\xff" * 33 # Valid C and S lengths - c_len = Bandersnatch.curve.CHALLENGE_LENGTH - s_len = (Bandersnatch.curve.ORDER.bit_length() + 7) // 8 + c_len = Bandersnatch.curve.params.encoding.challenge_len + s_len = scalar_len(Bandersnatch) dummy_c = b"\x00" * c_len dummy_s = b"\x00" * s_len invalid_proof = invalid_gamma + dummy_c + dummy_s with pytest.raises(ValueError, match="Invalid gamma point"): - IETF_VRF[Bandersnatch].ecvrf_decode_proof(invalid_proof) + TinyVRF[Bandersnatch].ecvrf_decode_proof(invalid_proof) def test_ecvrf_decode_proof_invalid_s(self): """Test ecvrf_decode_proof raises ValueError if S >= order.""" alpha = b"test" sk = b"secret" ad = b"ad" - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() # Modify S to be >= order - order = Bandersnatch.curve.ORDER + order = Bandersnatch.curve.params.subgroup_order invalid_s = order + 1 s_len = (order.bit_length() + 7) // 8 @@ -77,48 +78,47 @@ def test_ecvrf_decode_proof_invalid_s(self): invalid_proof = gamma_bytes + c_bytes + invalid_s_bytes with pytest.raises(ValueError, match="Response scalar S is not less than the curve order"): - IETF_VRF[Bandersnatch].ecvrf_decode_proof(invalid_proof) + TinyVRF[Bandersnatch].ecvrf_decode_proof(invalid_proof) def test_ecvrf_proof_to_hash_string_input(self): """Test ecvrf_proof_to_hash handles hex string input.""" alpha = b"test" sk = b"secret" ad = b"ad" - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() proof_hex = proof_bytes.hex() - hash_bytes = IETF_VRF[Bandersnatch].ecvrf_proof_to_hash(proof_hex) + hash_bytes = TinyVRF[Bandersnatch].ecvrf_proof_to_hash(proof_hex) assert isinstance(hash_bytes, bytes) def test_ietf_from_bytes_invalid_point(self): - """Test IETF_VRF.from_bytes raises ValueError for invalid output point.""" - point_len = Bandersnatch.curve.POINT_LEN * (2 if Bandersnatch.curve.UNCOMPRESSED else 1) - invalid_point = b"\xff" * point_len - c_len = Bandersnatch.curve.CHALLENGE_LENGTH - s_len = (Bandersnatch.curve.ORDER.bit_length() + 7) // 8 + """Test TinyVRF.from_bytes raises ValueError for invalid output point.""" + invalid_point = b"\xff" * point_len(Bandersnatch) + c_len = Bandersnatch.curve.params.encoding.challenge_len + s_len = scalar_len(Bandersnatch) dummy_c = b"\x00" * c_len dummy_s = b"\x00" * s_len invalid_proof = invalid_point + dummy_c + dummy_s with pytest.raises(ValueError, match="Invalid output point"): - IETF_VRF[Bandersnatch].from_bytes(invalid_proof) + TinyVRF[Bandersnatch].from_bytes(invalid_proof) def test_ietf_from_bytes_invalid_s(self): - """Test IETF_VRF.from_bytes raises ValueError if s >= order.""" + """Test TinyVRF.from_bytes raises ValueError if s >= order.""" alpha = b"test" sk = b"secret" ad = b"ad" - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() - order = Bandersnatch.curve.ORDER + order = Bandersnatch.curve.params.subgroup_order invalid_s = order + 1 s_len = (order.bit_length() + 7) // 8 - gamma_len = Bandersnatch.curve.POINT_LEN * (2 if Bandersnatch.curve.UNCOMPRESSED else 1) - c_len = Bandersnatch.curve.CHALLENGE_LENGTH + gamma_len = point_len(Bandersnatch) + c_len = Bandersnatch.curve.params.encoding.challenge_len gamma_bytes = proof_bytes[:gamma_len] c_bytes = proof_bytes[gamma_len : gamma_len + c_len] @@ -130,14 +130,14 @@ def test_ietf_from_bytes_invalid_s(self): invalid_proof = gamma_bytes + c_bytes + invalid_s_bytes with pytest.raises(ValueError, match="Response scalar s is not less than the curve order"): - IETF_VRF[Bandersnatch].from_bytes(invalid_proof) + TinyVRF[Bandersnatch].from_bytes(invalid_proof) def test_ietf_verify_invalid_public_key(self): - """Test IETF_VRF.verify raises ValueError for invalid public key.""" + """Test TinyVRF.verify raises ValueError for invalid public key.""" alpha = b"test" sk = b"secret" ad = b"ad" - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad) invalid_pk = b"\xff" * 33 @@ -146,21 +146,10 @@ def test_ietf_verify_invalid_public_key(self): def test_pedersen_from_bytes_invalid_point(self): """Test PedersenVRF.from_bytes raises ValueError for invalid points.""" - # Pedersen proof has multiple points. - # Structure: 4 points + 2 scalars. - # Just messing up the first point (output point) should trigger it? - # Wait, PedersenVRF.from_bytes parses: - # output_point (33) + B (33) + U (33) + V (33) + s (32) + sb (32) - # Actually it depends on implementation. - - # Let's check PedersenVRF.from_bytes implementation details if needed. - # But generally passing garbage bytes of correct length should fail point decoding. - point_len = 33 scalar_len = 32 invalid_proof = b"\xff" * (point_len * 4 + scalar_len * 2) - # It might raise ValueError from string_to_point with pytest.raises(ValueError): # Message might vary depending on which point fails first PedersenVRF[Bandersnatch].from_bytes(invalid_proof) @@ -190,18 +179,18 @@ def test_uncompressed_curve_decode(self): ad = b"ad" try: - proof = IETF_VRF[Ed448_RO].prove(alpha, sk, ad) + proof = TinyVRF[Ed448_RO].prove(alpha, sk, ad) proof_bytes = proof.to_bytes() # Decode - gamma, c, s = IETF_VRF[Ed448_RO].ecvrf_decode_proof(proof_bytes) + gamma, c, s = TinyVRF[Ed448_RO].ecvrf_decode_proof(proof_bytes) assert gamma == proof.output_point except Exception as e: pytest.skip(f"Ed448 not fully supported or failed: {e}") def test_ring_from_bytes_skip_pedersen(self): """Test RingVRF.from_bytes with skip_pedersen=True.""" - from dot_ring.vrf.ring.ring_vrf import RingVRF + from dot_ring.vrf.ring import RingVRF # Generate a valid proof alpha = b"test" @@ -216,26 +205,14 @@ def test_ring_from_bytes_skip_pedersen(self): proof = RingVRF[Bandersnatch].prove(alpha, ad, sk, pk, ring, ring_root) proof_bytes = proof.to_bytes() - # Parse with skip_pedersen=True - # Note: from_bytes expects full proof bytes, but if skip_pedersen is True, - # it assumes the bytes start with ring proof (skipping first 192 bytes of pedersen proof). - # Wait, looking at code: - # if not skip_pedersen: pedersen_proof = ...; offset = 192 - # else: pedersen_proof = None; offset = 0 - # So if we pass full proof bytes with skip_pedersen=True, it will try to read ring proof from offset 0. - # But offset 0 contains pedersen proof! - # So we must slice the proof bytes if we use skip_pedersen=True? - # Or does it mean the input `proof` bytes should ONLY contain ring proof? - # The docstring says "Bytes representation of the Ring VRF proof". - # If skip_pedersen is True, it implies the input bytes do NOT contain pedersen proof. - + # skip_pedersen=True expects bytes that start at the ring proof payload. ring_proof_bytes = proof_bytes[192:] parsed = RingVRF[Bandersnatch].from_bytes(ring_proof_bytes, skip_pedersen=True) assert parsed.pedersen_proof is None def test_ring_verify_missing_pedersen(self): """Test RingVRF.verify raises ValueError if pedersen_proof is missing.""" - from dot_ring.vrf.ring.ring_vrf import RingVRF + from dot_ring.vrf.ring import RingVRF # Create a dummy RingVRF with pedersen_proof=None # We can use the one parsed above @@ -276,7 +253,7 @@ def test_ring_construct_ring_root_invalid_keys(self): def test_ring_verify_ring_proof_bytes_input(self): """Test verify_ring_proof handles bytes input for message.""" - from dot_ring.vrf.ring.ring_vrf import RingVRF + from dot_ring.vrf.ring import RingVRF alpha = b"test" ad = b"ad" @@ -289,13 +266,6 @@ def test_ring_verify_ring_proof_bytes_input(self): ring_root = RingRoot.from_ring(ring, params) proof = RingVRF[Bandersnatch].prove(alpha, ad, sk, pk, ring, ring_root) - # message is usually a point (blinded_pk), but verify_ring_proof accepts bytes too. - # But wait, verify_ring_proof takes `message: bytes | CurvePoint`. - # In `verify`, it calls `self.verify_ring_proof(self.pedersen_proof.blinded_pk, ring, ring_root)`. - # `blinded_pk` is a CurvePoint. - # If we pass bytes, it tries to decode. - - # Let's call verify_ring_proof directly with bytes blinded_pk_bytes = proof.pedersen_proof.blinded_pk.point_to_string() valid = proof.verify_ring_proof(blinded_pk_bytes, ring, ring_root) @@ -303,7 +273,7 @@ def test_ring_verify_ring_proof_bytes_input(self): def test_ring_verify_ring_proof_invalid_message(self): """Test verify_ring_proof raises ValueError for invalid message point.""" - from dot_ring.vrf.ring.ring_vrf import RingVRF + from dot_ring.vrf.ring import RingVRF alpha = b"test" ad = b"ad" diff --git a/tests/test_ietf/test_ietf_ark.py b/tests/test_ietf/test_ietf_ark.py index 78674ed..7f3229c 100644 --- a/tests/test_ietf/test_ietf_ark.py +++ b/tests/test_ietf/test_ietf_ark.py @@ -6,10 +6,10 @@ from dot_ring.curve.specs.baby_jubjub import BabyJubJub from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.curve.specs.bandersnatch_sw import Bandersnatch_SW -from dot_ring.curve.specs.ed25519 import Ed25519_NU +from dot_ring.curve.specs.ed25519 import Ed25519_TAI from dot_ring.curve.specs.jubjub import JubJub -from dot_ring.curve.specs.p256 import P256_NU -from dot_ring.vrf.ietf.ietf import IETF_VRF +from dot_ring.curve.specs.p256 import P256_TAI +from dot_ring.vrf.ietf import TinyVRF HERE = os.path.dirname(__file__) @@ -18,10 +18,10 @@ (Bandersnatch, "bandersnatch_ed_sha512_ell2_ietf", "ark-vrf", 32), (BabyJubJub, "babyjubjub_sha512_tai_ietf", "ark-vrf", 32), (Bandersnatch_SW, "bandersnatch_sw_sha512_tai_ietf", "ark-vrf", 33), - (Ed25519_NU, "ed25519_sha512_tai_ietf.json", "ark-vrf", 32), + (Ed25519_TAI, "ed25519_sha512_tai_ietf.json", "ark-vrf", 32), (JubJub, "jubjub_sha_512_tai_ietf", "ark-vrf", 32), - (P256_NU, "secp256r1_sha256_tai_ietf.json", "ark-vrf", 33), - (P256_NU, "secp256r1_sha256_tai_ietf_rfc_9381.json", "ark-vrf", 33), + (P256_TAI, "secp256r1_sha256_tai_ietf.json", "ark-vrf", 33), + (P256_TAI, "secp256r1_sha256_tai_ietf_rfc_9381.json", "ark-vrf", 33), ] @@ -48,18 +48,18 @@ def test_ietf_ark(curve_variant, file_prefix, subdir, gamma_len): salt = bytes.fromhex(vector.get("salt", "")) # Public Key check - pk_bytes = IETF_VRF[curve_variant].get_public_key(secret_scalar) - public_key = curve_variant.point.string_to_point(pk_bytes) + pk_bytes = TinyVRF[curve_variant].get_public_key(secret_scalar) + public_key = curve_variant.string_to_point(pk_bytes) assert public_key.point_to_string().hex() == vector["pk"] # Input Point check - input_point = curve_variant.point.encode_to_curve(alpha, salt) + input_point = curve_variant.encode_to_curve(alpha, salt) if "h" in vector: assert input_point.point_to_string().hex() == vector["h"] - proof = IETF_VRF[curve_variant].prove(alpha, secret_scalar, additional_data, salt) + proof = TinyVRF[curve_variant].prove(alpha, secret_scalar, additional_data, salt) proof_bytes = proof.to_bytes() - proof_rt = IETF_VRF[curve_variant].from_bytes(proof_bytes) + proof_rt = TinyVRF[curve_variant].from_bytes(proof_bytes) # Proof components check gamma = proof_bytes[:gamma_len] @@ -73,7 +73,7 @@ def test_ietf_ark(curve_variant, file_prefix, subdir, gamma_len): assert int(proof_s.hex(), 16) == int(vector["proof_s"], 16) if "beta" in vector: - assert IETF_VRF[curve_variant].ecvrf_proof_to_hash(proof_bytes).hex() == vector["beta"] + assert TinyVRF[curve_variant].ecvrf_proof_to_hash(proof_bytes).hex() == vector["beta"] assert proof.verify(pk_bytes, alpha, additional_data, salt) assert proof_rt.to_bytes() == proof_bytes diff --git a/tests/test_ietf/test_ietf_base.py b/tests/test_ietf/test_ietf_base.py index 0a3d15b..b5bb5da 100644 --- a/tests/test_ietf/test_ietf_base.py +++ b/tests/test_ietf/test_ietf_base.py @@ -11,7 +11,7 @@ from dot_ring.curve.specs.p384 import P384_RO from dot_ring.curve.specs.p521 import P521_RO from dot_ring.curve.specs.secp256k1 import Secp256k1_RO -from dot_ring.vrf.ietf.ietf import IETF_VRF +from dot_ring.vrf.ietf import TinyVRF HERE = os.path.dirname(__file__) @@ -50,11 +50,11 @@ def test_ietf_base(curve_variant, file_prefix): additional_data = bytes.fromhex(vector["ad"]) # Public Key - pk_bytes = IETF_VRF[curve_variant].get_public_key(secret_scalar) + pk_bytes = TinyVRF[curve_variant].get_public_key(secret_scalar) - proof = IETF_VRF[curve_variant].prove(alpha, secret_scalar, additional_data) + proof = TinyVRF[curve_variant].prove(alpha, secret_scalar, additional_data) proof_bytes = proof.to_bytes() - proof_rt = IETF_VRF[curve_variant].from_bytes(proof_bytes) + proof_rt = TinyVRF[curve_variant].from_bytes(proof_bytes) # Verify verified = proof.verify(pk_bytes, alpha, additional_data) diff --git a/tests/test_keygen.py b/tests/test_keygen.py index 558720d..e361cc9 100644 --- a/tests/test_keygen.py +++ b/tests/test_keygen.py @@ -1,7 +1,7 @@ import pytest from dot_ring import Bandersnatch, secret_from_seed -from dot_ring.vrf.ietf.ietf import IETF_VRF +from dot_ring.vrf.ietf import TinyVRF @pytest.mark.parametrize( @@ -28,7 +28,7 @@ def test_secret_from_seed_vectors(seed: int, expected_pk: str, expected_sk: str) def test_secret_from_seed_public_key_roundtrip() -> None: seed = (2**32 - 1).to_bytes(32, "little") pk, sk = secret_from_seed(seed, Bandersnatch) - assert pk == IETF_VRF[Bandersnatch].get_public_key(sk) + assert pk == TinyVRF[Bandersnatch].get_public_key(sk) def test_secret_from_seed_type_errors() -> None: diff --git a/tests/test_vectors.py b/tests/test_vectors.py index a0c613a..a021a7b 100644 --- a/tests/test_vectors.py +++ b/tests/test_vectors.py @@ -5,10 +5,11 @@ import pytest -from dot_ring import IETF_VRF, P256, Bandersnatch, Ed25519, PedersenVRF, RingVRF +from dot_ring import P256, Bandersnatch, Ed25519, PedersenVRF, RingVRF, TinyVRF from dot_ring.ring_proof.helpers import Helpers from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring, RingRoot +from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.transcript import point_len, scalar_len # Alias Secp256r1 = P256 @@ -58,27 +59,26 @@ def verify_ietf_vector(vector: dict[str, Any], curve) -> None: expected_s = bytes.fromhex(vector["proof_s"]) # Generate proof - proof = IETF_VRF[curve].prove(alpha, sk, ad, salt) + proof = TinyVRF[curve].prove(alpha, sk, ad, salt) # Verify output point matches gamma_bytes = proof.output_point.point_to_string() assert gamma_bytes == expected_gamma, f"gamma mismatch: expected {expected_gamma.hex()}, got {gamma_bytes.hex()}" # Verify proof challenge - challenge_len = curve.curve.CHALLENGE_LENGTH - c_bytes = Helpers.int_to_str(proof.c, curve.curve.ENDIAN, challenge_len) + challenge_len = curve.curve.params.encoding.challenge_len + c_bytes = Helpers.int_to_str(proof.c, curve.curve.encoding_endian(), challenge_len) assert c_bytes == expected_c, f"challenge mismatch: expected {expected_c.hex()}, got {c_bytes.hex()}" # Verify proof response - scalar_len = (curve.curve.PRIME_FIELD.bit_length() + 7) // 8 - s_bytes = Helpers.int_to_str(proof.s, curve.curve.ENDIAN, scalar_len) + s_bytes = Helpers.int_to_str(proof.s, curve.curve.encoding_endian(), scalar_len(curve)) assert s_bytes == expected_s, f"response mismatch: expected {expected_s.hex()}, got {s_bytes.hex()}" # Verify the proof assert proof.verify(pk, alpha, ad, salt), "Proof verification failed" # Verify output hash - beta = IETF_VRF[curve].proof_to_hash(proof.output_point) + beta = TinyVRF[curve].proof_to_hash(proof.output_point) assert beta == expected_beta, f"beta mismatch: expected {expected_beta.hex()}, got {beta.hex()}" @@ -175,11 +175,10 @@ def verify_pedersen_vector(vector: dict[str, Any], curve) -> None: assert proof.ok.point_to_string() == expected_ok, "Ok mismatch" - scalar_len = (curve.curve.PRIME_FIELD.bit_length() + 7) // 8 - s_bytes = Helpers.int_to_str(proof.s, curve.curve.ENDIAN, scalar_len) + s_bytes = Helpers.int_to_str(proof.s, curve.curve.encoding_endian(), scalar_len(curve)) assert s_bytes == expected_s, f"s mismatch: expected {expected_s.hex()}, got {s_bytes.hex()}" - sb_bytes = Helpers.int_to_str(proof.sb, curve.curve.ENDIAN, scalar_len) + sb_bytes = Helpers.int_to_str(proof.sb, curve.curve.encoding_endian(), scalar_len(curve)) assert sb_bytes == expected_sb, f"sb mismatch: expected {expected_sb.hex()}, got {sb_bytes.hex()}" # Verify the proof @@ -266,10 +265,10 @@ def verify_ring_vector(vector: dict[str, Any], curve) -> None: ring_pks_bytes = bytes.fromhex(vector["ring_pks"]) # Parse ring public keys - point_len = curve.curve.POINT_LEN + serialized_point_len = point_len(curve) ring_pks = [] - for i in range(0, len(ring_pks_bytes), point_len): - ring_pks.append(ring_pks_bytes[i : i + point_len]) + for i in range(0, len(ring_pks_bytes), serialized_point_len): + ring_pks.append(ring_pks_bytes[i : i + serialized_point_len]) # Construct ring and ring root (test_vectors=True for deterministic proofs) params = RingProofParams(test_vectors=True) @@ -330,14 +329,14 @@ def test_wrong_public_key_ietf(self): sk1 = bytes.fromhex("0101010101010101010101010101010101010101010101010101010101010101") sk2 = bytes.fromhex("0202020202020202020202020202020202020202020202020202020202020202") - pk1 = IETF_VRF[Bandersnatch].get_public_key(sk1) - pk2 = IETF_VRF[Bandersnatch].get_public_key(sk2) + pk1 = TinyVRF[Bandersnatch].get_public_key(sk1) + pk2 = TinyVRF[Bandersnatch].get_public_key(sk2) alpha = b"test_input" ad = b"test_ad" # Generate proof with sk1 - proof = IETF_VRF[Bandersnatch].prove(alpha, sk1, ad) + proof = TinyVRF[Bandersnatch].prove(alpha, sk1, ad) # Verify with correct key should pass assert proof.verify(pk1, alpha, ad) @@ -348,14 +347,14 @@ def test_wrong_public_key_ietf(self): def test_wrong_input_ietf(self): """IETF VRF verification should fail with wrong input.""" sk = bytes.fromhex("0101010101010101010101010101010101010101010101010101010101010101") - pk = IETF_VRF[Bandersnatch].get_public_key(sk) + pk = TinyVRF[Bandersnatch].get_public_key(sk) alpha1 = b"correct_input" alpha2 = b"wrong_input" ad = b"test_ad" # Generate proof with alpha1 - proof = IETF_VRF[Bandersnatch].prove(alpha1, sk, ad) + proof = TinyVRF[Bandersnatch].prove(alpha1, sk, ad) # Verify with correct input should pass assert proof.verify(pk, alpha1, ad) @@ -366,14 +365,14 @@ def test_wrong_input_ietf(self): def test_wrong_ad_ietf(self): """IETF VRF verification should fail with wrong additional data.""" sk = bytes.fromhex("0101010101010101010101010101010101010101010101010101010101010101") - pk = IETF_VRF[Bandersnatch].get_public_key(sk) + pk = TinyVRF[Bandersnatch].get_public_key(sk) alpha = b"test_input" ad1 = b"correct_ad" ad2 = b"wrong_ad" # Generate proof with ad1 - proof = IETF_VRF[Bandersnatch].prove(alpha, sk, ad1) + proof = TinyVRF[Bandersnatch].prove(alpha, sk, ad1) # Verify with correct ad should pass assert proof.verify(pk, alpha, ad1) @@ -448,8 +447,8 @@ def test_ietf_deterministic(self): alpha = b"deterministic_test" ad = b"test_ad" - proof1 = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) - proof2 = IETF_VRF[Bandersnatch].prove(alpha, sk, ad) + proof1 = TinyVRF[Bandersnatch].prove(alpha, sk, ad) + proof2 = TinyVRF[Bandersnatch].prove(alpha, sk, ad) assert proof1.output_point.point_to_string() == proof2.output_point.point_to_string() assert proof1.c == proof2.c From a6aa5ca7ff4b13f1e580df07e15a060afd59a7e2 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:41:29 +0530 Subject: [PATCH 28/33] simplify benchmarks --- tests/benchmark/bench_pedersen.py | 174 ++++---- tests/benchmark/bench_ring_large.py | 179 -------- tests/benchmark/bench_ring_proof.py | 389 ++++++++++++------ tests/benchmark/memory_regression.py | 182 ++++---- tests/benchmark/test_bench_ietf.py | 83 ---- tests/benchmark/test_bench_pedersen.py | 80 ---- tests/benchmark/test_bench_ring.py | 116 ------ tests/benchmark/test_bench_ring_proof.py | 54 --- .../ark-vrf/baby-jubjub_sha-512_tai_ring.json | 135 ------ tests/vectors/dot-ring/README.md | 2 +- .../baby-jubjub_sha-512_tai_ring.json | 149 ------- 11 files changed, 446 insertions(+), 1097 deletions(-) delete mode 100644 tests/benchmark/bench_ring_large.py delete mode 100644 tests/benchmark/test_bench_ietf.py delete mode 100644 tests/benchmark/test_bench_pedersen.py delete mode 100644 tests/benchmark/test_bench_ring.py delete mode 100644 tests/benchmark/test_bench_ring_proof.py delete mode 100644 tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json delete mode 100644 tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json diff --git a/tests/benchmark/bench_pedersen.py b/tests/benchmark/bench_pedersen.py index 4564513..cb7e733 100644 --- a/tests/benchmark/bench_pedersen.py +++ b/tests/benchmark/bench_pedersen.py @@ -1,116 +1,106 @@ #!/usr/bin/env python3 -""" -Benchmark script for Pedersen VRF proof generation and verification. +"""Benchmark fresh Pedersen VRF proof and verification timings.""" -Run with: - python tests/bench_pedersen.py - -- Runs multiple warmup iterations -- Measures over multiple iterations -- Reports min, mean, and std deviation -""" +from __future__ import annotations +import argparse +import hashlib import statistics import time +from dataclasses import dataclass from dot_ring import Bandersnatch from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.pedersen.pedersen import PedersenVRF +from dot_ring.vrf.pedersen import PedersenVRF -def load_test_data(): - """Create deterministic benchmark data.""" - _, sk = secret_from_seed(bytes(32), Bandersnatch) - return {"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex()} +@dataclass(frozen=True) +class Timing: + prove_ms: float + verify_ms: float + total_ms: float + proof_size: int -def benchmark_pedersen_vrf(warmup_iters: int = 5, bench_iters: int = 100): - """Benchmark Pedersen VRF proof generation and verification.""" +def _seed(*parts: object) -> bytes: + h = hashlib.sha256() + for part in parts: + match part: + case bytes(): + h.update(part) + case int(): + h.update(part.to_bytes(8, "little", signed=False)) + case str(): + h.update(part.encode()) + case _: + raise TypeError(f"unsupported seed part: {type(part).__name__}") + h.update(b"\0") + return h.digest() - print("=" * 60) - print("Pedersen VRF Benchmark (Python/Cython + gmpy2)") - print("=" * 60) - print() - # Load test data - data = load_test_data() - s_k = bytes.fromhex(data["sk"]) - alpha = bytes.fromhex(data["alpha"]) - ad = bytes.fromhex(data["ad"]) +def benchmark_once(sample_index: int) -> Timing: + _, secret_key = secret_from_seed(_seed("pedersen-signer", sample_index), Bandersnatch) + alpha = b"bench-pedersen-input" + sample_index.to_bytes(8, "little") + ad = b"bench-pedersen-ad" + sample_index.to_bytes(8, "little") + salt = b"bench-pedersen-salt" + sample_index.to_bytes(8, "little") - # Get public key - PedersenVRF[Bandersnatch].get_public_key(s_k) # type: ignore + total_start = time.perf_counter() - print("Curve: Bandersnatch (Twisted Edwards)") - print(f"Warmup iterations: {warmup_iters}") - print(f"Benchmark iterations: {bench_iters}") - print() + start = time.perf_counter() + proof = PedersenVRF[Bandersnatch].prove(alpha, secret_key, ad, salt) + prove_ms = (time.perf_counter() - start) * 1000 - # ========================================================================= - # Warmup - # ========================================================================= - print("Warming up...") - for _ in range(warmup_iters): - proof = PedersenVRF[Bandersnatch].prove(alpha, s_k, ad) # type: ignore - proof.verify(alpha, ad) - - # ========================================================================= - # Benchmark Proof Generation - # ========================================================================= - print("Benchmarking Proof Generation...") - proof_times = [] - proofs = [] - for _ in range(bench_iters): - start = time.perf_counter() - proof = PedersenVRF[Bandersnatch].prove(alpha, s_k, ad) # type: ignore - elapsed = (time.perf_counter() - start) * 1000 - proof_times.append(elapsed) - proofs.append(proof) - - # ========================================================================= - # Benchmark Verification - # ========================================================================= - print("Benchmarking Verification...") - verify_times = [] - for proof in proofs: - start = time.perf_counter() - result = proof.verify(alpha, ad) - elapsed = (time.perf_counter() - start) * 1000 - verify_times.append(elapsed) - assert result, "Verification failed!" - - # ========================================================================= - # Results - # ========================================================================= - print() - print("=" * 60) - print("RESULTS") - print("=" * 60) + start = time.perf_counter() + verified = proof.verify(alpha, ad, salt) + verify_ms = (time.perf_counter() - start) * 1000 + + if not verified: + raise AssertionError("Pedersen VRF verification failed") + + return Timing( + prove_ms=prove_ms, + verify_ms=verify_ms, + total_ms=(time.perf_counter() - total_start) * 1000, + proof_size=len(proof.to_bytes()), + ) + + +def _stats(values: list[float]) -> tuple[float, float, float, float]: + stddev = statistics.stdev(values) if len(values) > 1 else 0.0 + return min(values), statistics.mean(values), max(values), stddev + + +def _print_stats(label: str, values: list[float]) -> None: + minimum, mean, maximum, stddev = _stats(values) + print(f"{label:<8} min={minimum:8.2f} ms mean={mean:8.2f} ms max={maximum:8.2f} ms stddev={stddev:8.2f} ms") + + +def run(samples: int) -> None: + if samples <= 0: + raise ValueError("samples must be positive") + + timings = [benchmark_once(sample_index) for sample_index in range(samples)] + + print(f"Pedersen VRF benchmark: samples={samples}") + print("Scope: each sample builds fresh key material, input, proof, and verification.") print() + print("sample prove_ms verify_ms total_ms") + for index, timing in enumerate(timings, start=1): + print(f"{index:>6} {timing.prove_ms:>8.2f} {timing.verify_ms:>9.2f} {timing.total_ms:>8.2f}") - def print_stats(name: str, times: list): - min_t = min(times) - mean_t = statistics.mean(times) - std_t = statistics.stdev(times) if len(times) > 1 else 0 - print(f"{name}:") - print(f" Min: {min_t:8.2f} ms") - print(f" Mean: {mean_t:8.2f} ms") - print(f" Stddev: {std_t:8.2f} ms") - print() - return min_t, mean_t - - proof_min, proof_mean = print_stats("Proof Generation", proof_times) - verify_min, verify_mean = print_stats("Verification", verify_times) - - print("-" * 60) - print(f"Total (Proof + Verify) Min: {proof_min + verify_min:8.2f} ms") - print(f"Total (Proof + Verify) Mean: {proof_mean + verify_mean:8.2f} ms") print() + _print_stats("prove", [timing.prove_ms for timing in timings]) + _print_stats("verify", [timing.verify_ms for timing in timings]) + _print_stats("total", [timing.total_ms for timing in timings]) + print(f"proof_size: {timings[-1].proof_size} bytes") + - # Proof size - proof_bytes = proofs[0].to_bytes() - print(f"Proof size: {len(proof_bytes)} bytes") +def main() -> None: + parser = argparse.ArgumentParser(description="Benchmark fresh Pedersen VRF prove and verify timings.") + parser.add_argument("-s", "--samples", type=int, default=30, help="number of fresh samples to run") + args = parser.parse_args() + run(args.samples) if __name__ == "__main__": - benchmark_pedersen_vrf(warmup_iters=5, bench_iters=100) + main() diff --git a/tests/benchmark/bench_ring_large.py b/tests/benchmark/bench_ring_large.py deleted file mode 100644 index 272e65d..0000000 --- a/tests/benchmark/bench_ring_large.py +++ /dev/null @@ -1,179 +0,0 @@ -import statistics -import sys -import time -from pathlib import Path - -# Add blst to path if needed -sys.path.insert(0, str(Path(__file__).parent / "blst" / "bindings" / "python")) - -from dot_ring import Bandersnatch, Ring, RingRoot, RingVRF -from dot_ring.keygen import secret_from_seed -from dot_ring.ring_proof.params import RingProofParams - - -def generate_ring_keys(ring_size: int) -> tuple[list[bytes], bytes, bytes]: - """Generate ring keys for benchmarking.""" - print(f"Generating {ring_size} keys for the ring...") - - keys = [] - prover_sk = None - prover_pk = None - - # Generate keys deterministically from seeds - for i in range(ring_size): - seed = i.to_bytes(32, "little") - pk, sk = secret_from_seed(seed, Bandersnatch) - keys.append(pk) - - # Use the middle key as the prover - if i == ring_size // 2: - prover_sk = sk - prover_pk = pk - - assert prover_sk is not None - assert prover_pk is not None - print(f"Generated {len(keys)} keys") - - return keys, prover_sk, prover_pk - - -def benchmark_large_ring_proof( - ring_size: int = 1023, - domain_size: int = 2048, - warmup_iters: int = 4, - bench_iters: int = 5, -): - """Benchmark 1024 ring-sized proof generation and verification over 2048 domain.""" - - print("=" * 60) - print("1024 Ring VRF Proof Benchmark") - print("=" * 60) - print() - print(f"Ring size: {ring_size} members") - print(f"Domain size: {domain_size}") - print(f"Warmup iterations: {warmup_iters}") - print(f"Benchmark iterations: {bench_iters}") - print() - - # Generate test data - keys, s_k, p_k = generate_ring_keys(ring_size) - alpha = b"test_alpha" - ad = b"" - - # Create parameters for large ring - params = RingProofParams(domain_size=domain_size, max_ring_size=ring_size) - - print("Parameters configured:") - print(f" Domain size: {params.domain_size}") - print(f" Max ring size: {params.max_ring_size}") - print(f" Padding rows: {params.padding_rows}") - print() - - # ========================================================================= - # Construct Ring Root (one-time setup) - # ========================================================================= - start = time.perf_counter() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - ring_root_time = (time.perf_counter() - start) * 1000 - print(f"Ring root constructed in {ring_root_time:.2f} ms") - print() - - # ========================================================================= - # Warmup - # ========================================================================= - print("Warming up...") - for i in range(warmup_iters): - print(f" Warmup iteration {i + 1}/{warmup_iters}...") - # Pass ring_root to avoid rebuilding it - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - ring_vrf_proof.verify(alpha, ad, ring, ring_root) - print("Warmup complete") - - # ========================================================================= - # Benchmark Ring Root Construction - # ========================================================================= - print("\nBenchmarking ring root construction...") - root_const_times = [] - for _ in range(bench_iters): - start = time.perf_counter() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - elapsed = (time.perf_counter() - start) * 1000 - root_const_times.append(elapsed) - - # ========================================================================= - # Benchmark Proof Generation - # ========================================================================= - print("\nBenchmarking Proof Generation...") - proof_times = [] - proofs = [] - for i in range(bench_iters): - print(f" Iteration {i + 1}/{bench_iters}...") - start = time.perf_counter() - # Pass ring_root to avoid rebuilding - this is the key optimization! - ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - elapsed = (time.perf_counter() - start) * 1000 - proof_times.append(elapsed) - proofs.append(ring_vrf_proof) - - # ========================================================================= - # Benchmark Verification - # ========================================================================= - print("\nBenchmarking Verification...") - verify_times = [] - for i, proof in enumerate(proofs): - print(f" Iteration {i + 1}/{bench_iters}...") - start = time.perf_counter() - result = proof.verify(alpha, ad, ring, ring_root) - elapsed = (time.perf_counter() - start) * 1000 - verify_times.append(elapsed) - assert result, "Verification failed!" - - # ========================================================================= - # Results - # ========================================================================= - print() - print("=" * 60) - print("RESULTS") - print("=" * 60) - print() - - def print_stats(name: str, times: list): - min_t = min(times) - mean_t = statistics.mean(times) - std_t = statistics.stdev(times) if len(times) > 1 else 0 - print(f"{name}:") - print(f" Min: {min_t:8.2f} ms") - print(f" Mean: {mean_t:8.2f} ms") - print(f" Stddev: {std_t:8.2f} ms") - print() - return min_t, mean_t - - print() - proof_min, proof_mean = print_stats("Ring Root Construction", root_const_times) - proof_min, proof_mean = print_stats("Proof Generation", proof_times) - verify_min, verify_mean = print_stats("Verification", verify_times) - - print("-" * 60) - print(f"Total (Proof + Verify) Min: {proof_min + verify_min:8.2f} ms") - print(f"Total (Proof + Verify) Mean: {proof_mean + verify_mean:8.2f} ms") - print() - - # Proof size - proof_bytes = ring_vrf_proof.to_bytes() - print(f"Proof size: {len(proof_bytes)} bytes") - print() - - return { - "ring_root_time": ring_root_time, - "proof": proof_times, - "verify": verify_times, - "proof_size": len(proof_bytes), - } - - -if __name__ == "__main__": - benchmark_large_ring_proof(ring_size=1023, domain_size=2048, warmup_iters=1, bench_iters=3) diff --git a/tests/benchmark/bench_ring_proof.py b/tests/benchmark/bench_ring_proof.py index 84a5353..ad6339e 100644 --- a/tests/benchmark/bench_ring_proof.py +++ b/tests/benchmark/bench_ring_proof.py @@ -1,154 +1,281 @@ #!/usr/bin/env python3 -""" -Benchmark script for ring proof generation and verification. +"""Benchmark fresh ring root, proof, and verification timings for one ring size.""" -Run with: - python3.12 bench_ring_proof.py - -- Runs multiple warmup iterations -- Measures over multiple iterations -- Reports min, mean, and std deviation -""" +from __future__ import annotations +import argparse +import hashlib +import os import statistics -import sys import time -from pathlib import Path - -# Add blst to path if needed -sys.path.insert(0, str(Path(__file__).parent / "blst" / "bindings" / "python")) +from concurrent.futures import ProcessPoolExecutor +from dataclasses import dataclass from dot_ring import Bandersnatch from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF - - -def load_test_data(): - """Create deterministic benchmark data.""" - pk, sk = secret_from_seed(bytes(32), Bandersnatch) - keys = [] - for i in range(8): - member_pk, _ = secret_from_seed((i + 1).to_bytes(32, "little"), Bandersnatch) - keys.append(member_pk) - keys[3] = pk - return { - "sk": sk.hex(), - "alpha": b"bench input data".hex(), - "ad": b"ad".hex(), - "ring_pks": b"".join(keys).hex(), - } - - -def benchmark_ring_proof(warmup_iters: int = 3, bench_iters: int = 10): - """Benchmark ring proof generation and verification.""" - - print("=" * 60) - print("Ring VRF Proof Benchmark") - print("=" * 60) - print() +from dot_ring.vrf.ring import RingBatchVerifier, RingRoot, RingVRF +from dot_ring.vrf.ring.members import Ring - # Load test data - data = load_test_data() - s_k = bytes.fromhex(data["sk"]) - alpha = bytes.fromhex(data["alpha"]) - ad = bytes.fromhex(data["ad"]) - ring_pks_bytes = bytes.fromhex(data["ring_pks"]) +SIGNER_INDEX = 3 - # Parse keys once - keys = RingVRF[Bandersnatch].parse_keys(ring_pks_bytes) - p_k = RingVRF[Bandersnatch].get_public_key(s_k) - print(f"Ring size: {len(keys)} members") - print(f"Warmup iterations: {warmup_iters}") - print(f"Benchmark iterations: {bench_iters}") - print() +@dataclass(frozen=True) +class Timing: + ring_id: bytes + ring_root_ms: float + prove_ms: float + verify_ms: float + total_ms: float + proof_size: int - # ========================================================================= - # Warmup - # ========================================================================= - print("Warming up...") - params = RingProofParams() - for _ in range(warmup_iters): - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root=ring_root) - ring_vrf_proof.verify(alpha, ad, ring, ring_root) - - # ========================================================================= - # Benchmark Ring Root Construction - # ========================================================================= - print("\nBenchmarking Ring Root Construction...") - ring_root_times = [] - ring_root = None - for _ in range(bench_iters): - start = time.perf_counter() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - elapsed = (time.perf_counter() - start) * 1000 - ring_root_times.append(elapsed) - - # ========================================================================= - # Benchmark Proof Generation - # ========================================================================= - print("Benchmarking Proof Generation...") - proof_times = [] - proofs = [] + +@dataclass(frozen=True) +class ProofFixture: + proof: RingVRF + alpha: bytes + ad: bytes + + +@dataclass(frozen=True) +class SerializedProofFixture: + index: int + proof: bytes + alpha: bytes + ad: bytes + + +def _seed(*parts: object) -> bytes: + h = hashlib.sha256() + for part in parts: + match part: + case bytes(): + h.update(part) + case int(): + h.update(part.to_bytes(8, "little", signed=False)) + case str(): + h.update(part.encode()) + case _: + raise TypeError(f"unsupported seed part: {type(part).__name__}") + h.update(b"\0") + return h.digest() + + +def _keypair(label: str, sample_index: int, member_index: int = 0) -> tuple[bytes, bytes]: + public_key, secret_key = secret_from_seed(_seed(label, sample_index, member_index), Bandersnatch) + return public_key, secret_key + + +def _ring_keys(ring_size: int, sample_index: int, signer_key: bytes) -> list[bytes]: + signer_index = min(SIGNER_INDEX, ring_size - 1) + keys: list[bytes] = [] + for member_index in range(ring_size): + if member_index == signer_index: + keys.append(signer_key) + else: + public_key, _ = _keypair("ring-member", sample_index, member_index) + keys.append(public_key) + return keys + + +def benchmark_once(ring_size: int, sample_index: int) -> Timing: + public_key, secret_key = _keypair("signer", sample_index) + keys = _ring_keys(ring_size, sample_index, public_key) + params = RingProofParams.from_ring_size(ring_size) + alpha = b"bench-ring-input" + sample_index.to_bytes(8, "little") + ad = b"bench-ring-ad" + sample_index.to_bytes(8, "little") + + total_start = time.perf_counter() + + start = time.perf_counter() ring = Ring(keys, params) ring_root = RingRoot.from_ring(ring, params) - for _ in range(bench_iters): - start = time.perf_counter() - ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - elapsed = (time.perf_counter() - start) * 1000 - proof_times.append(elapsed) - proofs.append(ring_vrf_proof) - - # ========================================================================= - # Benchmark Verification - # ========================================================================= - print("Benchmarking Verification...") - verify_times = [] - for proof in proofs: - start = time.perf_counter() - result = proof.verify(alpha, ad, ring, ring_root) - elapsed = (time.perf_counter() - start) * 1000 - verify_times.append(elapsed) - assert result, "Verification failed!" - - # ========================================================================= - # Results - # ========================================================================= + ring_root_ms = (time.perf_counter() - start) * 1000 + + start = time.perf_counter() + proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, public_key, ring, ring_root) + prove_ms = (time.perf_counter() - start) * 1000 + + start = time.perf_counter() + verified = proof.verify(alpha, ad, ring, ring_root) + verify_ms = (time.perf_counter() - start) * 1000 + + if not verified: + raise AssertionError("ring proof verification failed") + + return Timing( + ring_id=ring_root.to_bytes(), + ring_root_ms=ring_root_ms, + prove_ms=prove_ms, + verify_ms=verify_ms, + total_ms=(time.perf_counter() - total_start) * 1000, + proof_size=len(proof.to_bytes()), + ) + + +def _stats(values: list[float]) -> tuple[float, float, float, float]: + stddev = statistics.stdev(values) if len(values) > 1 else 0.0 + return min(values), statistics.mean(values), max(values), stddev + + +def _print_stats(label: str, values: list[float]) -> None: + minimum, mean, maximum, stddev = _stats(values) + print(f"{label:<12} min={minimum:8.2f} ms mean={mean:8.2f} ms max={maximum:8.2f} ms stddev={stddev:8.2f} ms") + + +def _batch_sizes(max_size: int) -> list[int]: + sizes: list[int] = [] + size = 1 + while size <= max_size: + sizes.append(size) + size *= 2 + return sizes + + +def _prove_batch_fixture_range(ring_size: int, start: int, stop: int) -> list[SerializedProofFixture]: + public_key, secret_key = _keypair("batch-signer", 0) + keys = _ring_keys(ring_size, 0, public_key) + params = RingProofParams.from_ring_size(ring_size) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + + fixtures: list[SerializedProofFixture] = [] + for fixture_index in range(start, stop): + alpha = b"bench-batch-input" + fixture_index.to_bytes(8, "little") + ad = b"bench-batch-ad" + fixture_index.to_bytes(8, "little") + proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, public_key, ring, ring_root) + fixtures.append(SerializedProofFixture(fixture_index, proof.to_bytes(), alpha, ad)) + return fixtures + + +def _chunk_ranges(count: int, workers: int) -> list[tuple[int, int]]: + chunk_count = min(count, workers) + base, remainder = divmod(count, chunk_count) + ranges: list[tuple[int, int]] = [] + start = 0 + for chunk_index in range(chunk_count): + stop = start + base + (1 if chunk_index < remainder else 0) + ranges.append((start, stop)) + start = stop + return ranges + + +def _build_batch_fixtures_parallel(ring_size: int, fixture_count: int, workers: int) -> tuple[Ring, RingRoot, list[ProofFixture]]: + public_key, _ = _keypair("batch-signer", 0) + keys = _ring_keys(ring_size, 0, public_key) + params = RingProofParams.from_ring_size(ring_size) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + + if workers <= 1: + serialized = _prove_batch_fixture_range(ring_size, 0, fixture_count) + else: + serialized = [] + with ProcessPoolExecutor(max_workers=workers) as executor: + futures = [ + executor.submit(_prove_batch_fixture_range, ring_size, start, stop) + for start, stop in _chunk_ranges(fixture_count, workers) + ] + for future in futures: + serialized.extend(future.result()) + + serialized.sort(key=lambda fixture: fixture.index) + fixtures = [ + ProofFixture( + RingVRF[Bandersnatch].from_bytes(fixture.proof), + fixture.alpha, + fixture.ad, + ) + for fixture in serialized + ] + return ring, ring_root, fixtures + + +def run( + ring_size: int, + samples: int, + max_batch_size: int, + batch_fixtures: int | None, + batch_workers: int, +) -> None: + if samples <= 0: + raise ValueError("samples must be positive") + if batch_fixtures is None: + batch_fixtures = max_batch_size + + timings = [benchmark_once(ring_size, sample_index) for sample_index in range(samples)] + + print(f"Ring proof benchmark: n={ring_size}, samples={samples}") print() - print("=" * 60) - print("RESULTS") - print("=" * 60) + print("sample ring_root_ms prove_ms verify_ms total_ms") + for timing in timings: + print( + f"{timing.ring_id.hex()[:8]:>6} " + f"{timing.ring_root_ms:>12.2f} " + f"{timing.prove_ms:>8.2f} " + f"{timing.verify_ms:>9.2f} " + f"{timing.total_ms:>8.2f}" + ) + + print() + _print_stats("ring_root", [timing.ring_root_ms for timing in timings]) + _print_stats("prove", [timing.prove_ms for timing in timings]) + _print_stats("verify", [timing.verify_ms for timing in timings]) + _print_stats("total", [timing.total_ms for timing in timings]) + print(f"proof_size: {timings[-1].proof_size} bytes") + + fixture_count, workers = batch_fixtures, batch_workers + if max_batch_size <= 0: + return + if fixture_count <= 0: + raise ValueError("batch fixture count must be positive") + if fixture_count < max_batch_size: + raise ValueError("batch fixture count must be at least batch max so every batch entry is unique") + if samples <= 0: + raise ValueError("batch samples must be positive") + if workers <= 0: + raise ValueError("batch workers must be positive") + workers = min(workers, fixture_count) + print() + print(f"Batch verify: max_n={max_batch_size}, unique_fixtures={fixture_count}") + print(f"Preparing proofs... | workers={workers}") + + ring, ring_root, fixtures = _build_batch_fixtures_parallel(ring_size, fixture_count, workers) + + start = time.perf_counter() + context = RingBatchVerifier.prepare_context(ring, ring_root) + prepared_fixtures = [context.prepare(fixture.proof, fixture.alpha, fixture.ad) for fixture in fixtures] - def print_stats(name: str, times: list): - min_t = min(times) - mean_t = statistics.mean(times) - std_t = statistics.stdev(times) if len(times) > 1 else 0 - print(f"{name}:") - print(f" Min: {min_t:8.2f} ms") - print(f" Mean: {mean_t:8.2f} ms") - print(f" Stddev: {std_t:8.2f} ms") - print() - return min_t, mean_t - - print_stats("Ring Root Construction", ring_root_times) - proof_min, proof_mean = print_stats("Proof Generation", proof_times) - verify_min, verify_mean = print_stats("Verification", verify_times) - - print("-" * 60) - print(f"Total (Proof + Verify) Min: {proof_min + verify_min:8.2f} ms") - print(f"Total (Proof + Verify) Mean: {proof_mean + verify_mean:8.2f} ms") print() + print("batch_n verify_ms verify_ms_per_proof") + for batch_size in _batch_sizes(max_batch_size): + items = prepared_fixtures[:batch_size] + times: list[float] = [] + verifier = RingBatchVerifier(context) + for item in items: + verifier.push_prepared(item) + + start = time.perf_counter() + verified = verifier.verify() + elapsed_ms = (time.perf_counter() - start) * 1000 + if not verified: + raise AssertionError(f"batch verification failed for n={batch_size}") + + print(f"{batch_size:>7} {elapsed_ms:>13.2f} {elapsed_ms / batch_size:>18.2f}") + + - # Proof size - proof_bytes = ring_vrf_proof.to_bytes() - print(f"Proof size: {len(proof_bytes)} bytes") +def main() -> None: + parser = argparse.ArgumentParser(description="Benchmark fresh ring root, prove, and verify timings.") + parser.add_argument("n", type=int, nargs="?", default=1024, help="ring size") + parser.add_argument("-s", "--samples", type=int, default=3, help="number of fresh samples to run") + parser.add_argument("--batch-max", type=int, default=256, help="largest batched verification size to benchmark") + parser.add_argument("--batch-fixtures", type=int, default=None, help="number of unique proofs to prepare; defaults to --batch-max") + parser.add_argument("--batch-workers", type=int, default=os.cpu_count() or 1, help="parallel worker processes for batch proof generation") + args = parser.parse_args() + run(args.n, args.samples, args.batch_max, args.batch_fixtures, args.batch_workers) if __name__ == "__main__": - benchmark_ring_proof(warmup_iters=2, bench_iters=8) + main() diff --git a/tests/benchmark/memory_regression.py b/tests/benchmark/memory_regression.py index ca11a6e..27ba316 100644 --- a/tests/benchmark/memory_regression.py +++ b/tests/benchmark/memory_regression.py @@ -1,17 +1,27 @@ +#!/usr/bin/env python3 +"""Run simple RSS regression probes for repeated dot-ring operations.""" + from __future__ import annotations import argparse import gc -import json +import hashlib import os import subprocess import time from collections.abc import Callable from pathlib import Path -from dot_ring import Bandersnatch, Ring, RingRoot, RingVRF +from dot_ring import Bandersnatch +from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.vrf.ring.members import Ring +from dot_ring.vrf.ring import RingRoot +from dot_ring.vrf.ring import RingVRF + +SIGNER_INDEX = 3 +RSS_EPSILON_MB = 0.1 def rss_mb() -> float: @@ -24,133 +34,151 @@ def rss_mb() -> float: return int(output.strip()) / 1024 +def _seed(*parts: object) -> bytes: + h = hashlib.sha256() + for part in parts: + match part: + case bytes(): + h.update(part) + case int(): + h.update(part.to_bytes(8, "little", signed=False)) + case str(): + h.update(part.encode()) + case _: + raise TypeError(f"unsupported seed part: {type(part).__name__}") + h.update(b"\0") + return h.digest() + + +def _ring_fixture(ring_size: int, sample_index: int) -> tuple[bytes, bytes, bytes, bytes, Ring, RingRoot]: + public_key, secret_key = secret_from_seed(_seed("memory-signer", sample_index), Bandersnatch) + signer_index = min(SIGNER_INDEX, ring_size - 1) + keys: list[bytes] = [] + for member_index in range(ring_size): + if member_index == signer_index: + keys.append(public_key) + else: + member_key, _ = secret_from_seed(_seed("memory-ring-member", sample_index, member_index), Bandersnatch) + keys.append(member_key) + + params = RingProofParams.from_ring_size(ring_size) + ring = Ring(keys, params) + ring_root = RingRoot.from_ring(ring, params) + alpha = b"memory-ring-input" + sample_index.to_bytes(8, "little") + ad = b"memory-ring-ad" + sample_index.to_bytes(8, "little") + return secret_key, public_key, alpha, ad, ring, ring_root + + +def _ring_proof_fixture(ring_size: int, sample_index: int) -> tuple[bytes, bytes, Ring, RingRoot, RingVRF]: + secret_key, public_key, alpha, ad, ring, ring_root = _ring_fixture(ring_size, sample_index) + proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, public_key, ring, ring_root) + if not proof.verify(alpha, ad, ring, ring_root): + raise AssertionError("initial ring proof verification failed") + return alpha, ad, ring, ring_root, proof + + +def _verify_or_raise(proof: RingVRF, alpha: bytes, ad: bytes, ring: Ring, ring_root: RingRoot) -> None: + if not proof.verify(alpha, ad, ring, ring_root): + raise AssertionError("verification failed") + + def run_probe( label: str, iterations: int, warmup_iterations: int, - step: int, max_growth_mb: float, max_steady_growth_mb: float, - operation: Callable[[], None], -) -> float: - print(f"{label}: warming up {warmup_iterations} iterations", flush=True) - for _ in range(warmup_iterations): - operation() - - # RSS can keep allocator arenas after first use. Measure only after the - # workload has reached its normal steady-state allocation pattern. + operation: Callable[[int], None], +) -> None: + if iterations <= 0: + return + + print(f"{label}: warmup={warmup_iterations} iterations={iterations}", flush=True) + for index in range(warmup_iterations): + operation(index) + gc.collect() baseline = rss_mb() started = time.perf_counter() - max_growth = 0.0 + step = max(1, iterations // 5) samples = [(0, baseline)] print(f"{label}: measured_iter=0 rss={baseline:.1f} MB delta=+0.0 MB elapsed=0.0s", flush=True) - for iteration in range(1, iterations + 1): - operation() - if iteration % step == 0 or iteration == iterations: + for index in range(1, iterations + 1): + operation(warmup_iterations + index) + if index % step == 0 or index == iterations: gc.collect() current = rss_mb() - growth = current - baseline - max_growth = max(max_growth, growth) - samples.append((iteration, current)) - elapsed = time.perf_counter() - started + samples.append((index, current)) print( - f"{label}: measured_iter={iteration} rss={current:.1f} MB delta={growth:+.1f} MB elapsed={elapsed:.1f}s", + f"{label}: measured_iter={index} rss={current:.1f} MB " + f"delta={current - baseline:+.1f} MB elapsed={time.perf_counter() - started:.1f}s", flush=True, ) final_growth = samples[-1][1] - baseline steady_samples = samples[len(samples) // 2 :] steady_growth = samples[-1][1] - min(rss for _, rss in steady_samples) + peak_growth = max(rss for _, rss in samples) - baseline print( - f"{label}: final_delta={final_growth:+.1f} MB peak_delta={max_growth:+.1f} MB steady_delta={steady_growth:+.1f} MB", + f"{label}: final_delta={final_growth:+.1f} MB peak_delta={peak_growth:+.1f} MB steady_delta={steady_growth:+.1f} MB", flush=True, ) - if final_growth > max_growth_mb: + if final_growth > max_growth_mb + RSS_EPSILON_MB: raise AssertionError(f"{label} RSS grew by {final_growth:.1f} MB after warmup; limit is {max_growth_mb:.1f} MB") - - if steady_growth > max_steady_growth_mb: + if steady_growth > max_steady_growth_mb + RSS_EPSILON_MB: raise AssertionError(f"{label} RSS kept growing by {steady_growth:.1f} MB in the steady-state window; limit is {max_steady_growth_mb:.1f} MB") - return final_growth - - -def load_ring_fixture() -> tuple[bytes, bytes, bytes, Ring, RingRoot, bytes]: - vector_path = Path("tests/vectors/ark-vrf/bandersnatch_ed_sha512_ell2_ring.json") - with vector_path.open() as f: - item = json.load(f)[0] - secret_key = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(item["ring_pks"])) - params = RingProofParams() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - public_key = RingVRF[Bandersnatch].get_public_key(secret_key) - return secret_key, alpha, ad, ring, ring_root, public_key - - -def main() -> int: - parser = argparse.ArgumentParser(description="Run repeated dot-ring operations and fail on sustained RSS growth.") - parser.add_argument("--kzg-iters", type=int, default=2000) - parser.add_argument("--verify-iters", type=int, default=5000) - parser.add_argument("--prove-iters", type=int, default=200) - parser.add_argument("--kzg-warmup-iters", type=int, default=200) - parser.add_argument("--verify-warmup-iters", type=int, default=1000) - parser.add_argument("--prove-warmup-iters", type=int, default=50) - parser.add_argument("--max-kzg-growth-mb", type=float, default=1.0) - parser.add_argument("--max-verify-growth-mb", type=float, default=1.0) - parser.add_argument("--max-prove-growth-mb", type=float, default=3.0) - parser.add_argument("--max-kzg-steady-growth-mb", type=float, default=0.5) - parser.add_argument("--max-verify-steady-growth-mb", type=float, default=0.5) - parser.add_argument("--max-prove-steady-growth-mb", type=float, default=1.0) +def main() -> None: + parser = argparse.ArgumentParser(description="Run simple RSS regression probes for dot-ring operations.") + parser.add_argument("-n", "--ring-size", type=int, default=8, help="ring size for ring probes") + parser.add_argument("--kzg-iters", type=int, default=20) + parser.add_argument("--verify-iters", type=int, default=50) + parser.add_argument("--prove-iters", type=int, default=10) + parser.add_argument("--warmups", type=int, default=3) + parser.add_argument("--max-kzg-growth-mb", type=float, default=8.0) + parser.add_argument("--max-verify-growth-mb", type=float, default=16.0) + parser.add_argument("--max-prove-growth-mb", type=float, default=32.0) + parser.add_argument("--max-steady-growth-mb", type=float, default=1.0) args = parser.parse_args() coeffs = list(range(1, 2049)) run_probe( "kzg_commit_2048", args.kzg_iters, - args.kzg_warmup_iters, - max(1, args.kzg_iters // 4), + args.warmups, args.max_kzg_growth_mb, - args.max_kzg_steady_growth_mb, - lambda: KZG.commit(coeffs), + args.max_steady_growth_mb, + lambda _: KZG.commit(coeffs), ) - secret_key, alpha, ad, ring, ring_root, public_key = load_ring_fixture() - proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, public_key, ring, ring_root) - if not proof.verify(alpha, ad, ring, ring_root): - raise AssertionError("initial proof verification failed") - + alpha, ad, ring, ring_root, proof = _ring_proof_fixture(args.ring_size, 0) run_probe( "ring_verify", args.verify_iters, - args.verify_warmup_iters, - max(1, args.verify_iters // 5), + args.warmups, args.max_verify_growth_mb, - args.max_verify_steady_growth_mb, - lambda: proof.verify(alpha, ad, ring, ring_root), + args.max_steady_growth_mb, + lambda _: _verify_or_raise(proof, alpha, ad, ring, ring_root), ) - def prove_and_verify() -> None: - fresh_proof = RingVRF[Bandersnatch].prove(alpha, ad, secret_key, public_key, ring, ring_root) - if not fresh_proof.verify(alpha, ad, ring, ring_root): + def prove_and_verify(sample_index: int) -> None: + secret_key, public_key, fresh_alpha, fresh_ad, fresh_ring, fresh_root = _ring_fixture(args.ring_size, sample_index) + fresh_proof = RingVRF[Bandersnatch].prove(fresh_alpha, fresh_ad, secret_key, public_key, fresh_ring, fresh_root) + if not fresh_proof.verify(fresh_alpha, fresh_ad, fresh_ring, fresh_root): raise AssertionError("proof verification failed") run_probe( "ring_prove_verify", args.prove_iters, - args.prove_warmup_iters, - max(1, args.prove_iters // 4), + args.warmups, args.max_prove_growth_mb, - args.max_prove_steady_growth_mb, + args.max_steady_growth_mb, prove_and_verify, ) - return 0 if __name__ == "__main__": - raise SystemExit(main()) + main() diff --git a/tests/benchmark/test_bench_ietf.py b/tests/benchmark/test_bench_ietf.py deleted file mode 100644 index efc7565..0000000 --- a/tests/benchmark/test_bench_ietf.py +++ /dev/null @@ -1,83 +0,0 @@ -import os -import sys -from pathlib import Path - -# Add tests directory to path to import utils -sys.path.insert(0, str(Path(__file__).parent)) - -from dot_ring.curve.specs.bandersnatch import Bandersnatch -from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.ietf.ietf import IETF_VRF - -from ..utils.profiler import Profiler - -HERE = os.path.dirname(__file__) -RESULTS_DIR = os.path.join(HERE, "results") - - -def load_test_data(): - """Create deterministic benchmark data.""" - pk, sk = secret_from_seed(bytes(32), Bandersnatch) - return [{"sk": sk.hex(), "pk": pk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex(), "salt": ""}] - - -def test_bench_ietf_prove(): - """Benchmark IETF VRF proof generation with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - s_k = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - salt = bytes.fromhex(item.get("salt", "")) - - # Benchmark proof generation with profiling - with Profiler( - f"ietf_prove_sample_{index + 1}", - save_stats=True, - print_stats=True, - sort_by="cumulative", - limit=40, - ): - proof = IETF_VRF[Bandersnatch].prove(alpha, s_k, ad, salt) - - assert proof.verify(bytes.fromhex(item["pk"]), alpha, ad, salt) - - print("📊 Profile saved to: perf/results/") - - -def test_bench_ietf_verify(): - """Benchmark IETF VRF verification with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - s_k = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - salt = bytes.fromhex(item.get("salt", "")) - - # Generate proof first - proof = IETF_VRF[Bandersnatch].prove(alpha, s_k, ad, salt) - pk = IETF_VRF[Bandersnatch].get_public_key(s_k) - - # Benchmark verification with profiling - with Profiler( - f"ietf_verify_sample_{index + 1}", - save_stats=True, - print_stats=True, - sort_by="cumulative", - limit=40, - ): - result = proof.verify(pk, alpha, ad, salt) - - assert result, "Verification Failed" - - print("📊 Profile saved to: perf/results/") diff --git a/tests/benchmark/test_bench_pedersen.py b/tests/benchmark/test_bench_pedersen.py deleted file mode 100644 index 6d62259..0000000 --- a/tests/benchmark/test_bench_pedersen.py +++ /dev/null @@ -1,80 +0,0 @@ -import os -import sys -from pathlib import Path - -# Add tests directory to path to import utils -sys.path.insert(0, str(Path(__file__).parent)) - -from dot_ring.curve.specs.bandersnatch import Bandersnatch -from dot_ring.keygen import secret_from_seed -from dot_ring.vrf.pedersen.pedersen import PedersenVRF - -from ..utils.profiler import Profiler - -HERE = os.path.dirname(__file__) -RESULTS_DIR = os.path.join(HERE, "results") - - -def load_test_data(): - """Create deterministic benchmark data.""" - _, sk = secret_from_seed(bytes(32), Bandersnatch) - return [{"sk": sk.hex(), "alpha": b"bench input data".hex(), "ad": b"ad".hex()}] - - -def test_bench_pedersen_prove(): - """Benchmark Pedersen VRF proof generation with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - s_k = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - - # Benchmark proof generation with profiling - with Profiler( - f"pedersen_prove_sample_{index + 1}", - save_stats=True, - print_stats=True, - sort_by="cumulative", - limit=40, - ): - proof = PedersenVRF[Bandersnatch].prove(alpha, s_k, ad) - - assert proof.verify(alpha, ad) - - print("📊 Profile saved to: perf/results/") - - -def test_bench_pedersen_verify(): - """Benchmark Pedersen VRF verification with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - s_k = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - - # Generate proof first - proof = PedersenVRF[Bandersnatch].prove(alpha, s_k, ad) - - # Benchmark verification with profiling - with Profiler( - f"pedersen_verify_sample_{index + 1}", - save_stats=True, - print_stats=True, - sort_by="cumulative", - limit=40, - ): - result = proof.verify(alpha, ad) - - assert result, "Verification Failed" - - print("📊 Profile saved to: perf/results/") diff --git a/tests/benchmark/test_bench_ring.py b/tests/benchmark/test_bench_ring.py deleted file mode 100644 index 06aa3e2..0000000 --- a/tests/benchmark/test_bench_ring.py +++ /dev/null @@ -1,116 +0,0 @@ -import os -import sys -from pathlib import Path - -# Add tests directory to path to import utils -sys.path.insert(0, str(Path(__file__).parent)) - -from dot_ring.curve.specs.bandersnatch import Bandersnatch -from dot_ring.keygen import secret_from_seed -from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF - -from ..utils.profiler import Profiler - -HERE = os.path.dirname(__file__) -RESULTS_DIR = os.path.join(HERE, "results") - - -def load_test_data(): - """Create deterministic benchmark data.""" - pk, sk = secret_from_seed(bytes(32), Bandersnatch) - keys = [] - for i in range(8): - member_pk, _ = secret_from_seed((i + 1).to_bytes(32, "little"), Bandersnatch) - keys.append(member_pk) - keys[3] = pk - return [ - { - "sk": sk.hex(), - "pk": pk.hex(), - "alpha": b"bench input data".hex(), - "ad": b"ad".hex(), - "ring_pks": b"".join(keys).hex(), - } - ] - - -def test_bench_ring_prove(): - """Benchmark Ring VRF proof generation with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - s_k = bytes.fromhex(item["sk"]) - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(item["ring_pks"])) - - # Construct ring and ring root - params = RingProofParams() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - p_k = RingVRF[Bandersnatch].get_public_key(s_k) - - # Benchmark proof generation with profiling - with Profiler( - f"ring_prove_sample_{index + 1}", - save_stats=True, - print_stats=False, - sort_by="cumulative", - limit=25, - ): - _ = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - - assert p_k.hex() == item["pk"], "Invalid Public Key" - # expected_proof = ( - # item["gamma"] - # + item["proof_pk_com"] - # + item["proof_r"] - # + item["proof_ok"] - # + item["proof_s"] - # + item["proof_sb"] - # + item["ring_proof"] - # ) - # assert ring_vrf_proof.to_bytes().hex() == expected_proof, "Unexpected Proof" - - print("📊 Profile saved to: perf/results/") - - -def test_bench_ring_verify(): - """Benchmark Ring VRF verification with profiling""" - data = load_test_data() - - # Create results directory - os.makedirs(RESULTS_DIR, exist_ok=True) - - for index in range(len(data)): - item = data[index] - alpha = bytes.fromhex(item["alpha"]) - ad = bytes.fromhex(item["ad"]) - keys = RingVRF[Bandersnatch].parse_keys(bytes.fromhex(item["ring_pks"])) - - # Construct ring and ring root - params = RingProofParams() - ring = Ring(keys, params) - ring_root = RingRoot.from_ring(ring, params) - p_k = RingVRF[Bandersnatch].get_public_key(bytes.fromhex(item["sk"])) - ring_vrf_proof = RingVRF[Bandersnatch].prove(alpha, ad, bytes.fromhex(item["sk"]), p_k, ring, ring_root) - - # Benchmark verification with profiling - with Profiler( - f"ring_verify_sample_{index + 1}", - save_stats=True, - print_stats=False, - sort_by="cumulative", - limit=100, - ): - result = ring_vrf_proof.verify(alpha, ad, ring, ring_root) - - assert result, "Verification Failed" - - print("📊 Profile saved to: perf/results/") diff --git a/tests/benchmark/test_bench_ring_proof.py b/tests/benchmark/test_bench_ring_proof.py deleted file mode 100644 index 3d6223e..0000000 --- a/tests/benchmark/test_bench_ring_proof.py +++ /dev/null @@ -1,54 +0,0 @@ -import sys -from pathlib import Path - -import pytest - -# Add blst to path if needed -sys.path.insert(0, str(Path(__file__).parent.parent / "dot_ring" / "blst" / "bindings" / "python")) - -from dot_ring import Bandersnatch -from dot_ring.vrf.ring.ring_root import Ring, RingRoot -from dot_ring.vrf.ring.ring_vrf import RingVRF - - -# MAX_RING_SIZE is 255 per the constraint system -@pytest.fixture(scope="module", params=[255]) -def ring_data(request): - ring_size = request.param - - s_k = b"secret_key_seed" * 2 - s_k = s_k[:32] - p_k = RingVRF[Bandersnatch].get_public_key(s_k) - - keys = [p_k] - for i in range(ring_size - 1): - sk_i = (int.from_bytes(s_k, "little") + i + 1).to_bytes(32, "little") - pk_i = RingVRF[Bandersnatch].get_public_key(sk_i) - keys.append(pk_i) - - alpha = b"test_message" - ad = b"test_ad" - - ring = Ring(keys) - ring_root = RingRoot.from_ring(ring, ring.params) - - # Pre-calculate a proof for verification benchmark - proof = RingVRF[Bandersnatch].prove(alpha, ad, s_k, p_k, ring, ring_root) - - return {"s_k": s_k, "p_k": p_k, "ring": ring, "alpha": alpha, "ad": ad, "ring_root": ring_root, "proof": proof} - - -def test_prove(benchmark, ring_data): - benchmark( - RingVRF[Bandersnatch].prove, - ring_data["alpha"], - ring_data["ad"], - ring_data["s_k"], - ring_data["p_k"], - ring_data["ring"], - ring_data["ring_root"], - ) - - -def test_verify(benchmark, ring_data): - benchmark(ring_data["proof"].verify, ring_data["alpha"], ring_data["ad"], ring_data["ring"], ring_data["ring_root"]) diff --git a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json b/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json deleted file mode 100644 index e2e1671..0000000 --- a/tests/vectors/ark-vrf/baby-jubjub_sha-512_tai_ring.json +++ /dev/null @@ -1,135 +0,0 @@ -[ - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-1", - "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", - "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", - "alpha": "", - "ad": "", - "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", - "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", - "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", - "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", - "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", - "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", - "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", - "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", - "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249906c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "8c70bb1f00851b158af9e9dae73708b9f45eee4256c7465df0c7e21fb6a0881f6514da270928137e6ec8ec856e95505988a2566bb5b8b211ce512706577c1907da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "f9a870a23b1fdf1f483ca4835de8cd6c7ffb6a4a6685370eb8eb92c647ba11a287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16557befdaf649a92c4333257b279c463e299f1932ac92322bd4d925d4fc4d7826b48f42b6cff2c2103890bc09a9d02da15aa08e85aa641e286fb729562271780479052269827ed3813237500d22b66683b73a28a9533b5163bb65e059cf6e480ea195ee129559ef01be7b3ba53124e4105bb02595d9ee3a5d2156b7c051fc9d197cf6db735dcd2cd3d2d7845836e2e5fe65925c6d68f4318294008136a9b5e80efd945d684eeee33db6f0dc714954d18cd6b9651f59047714d724a74f0bd75622f7128f1ac69c29a78f563a4d293d1fef156c1029ea684495d1371c15e6fbf02d1a6f96989d7b9741dbc460a16dcf16d2f50c456490471dd5c5088296743a2c09115919ee5a9592af04c33182b1f47ec110a57ec2762d688ed0c6e1f9c167f3166aaa6ec0de0f9ad2af10a3629ca8ccb2d078fadfba98bb929357124e5d8dc9047abfa891f35000893dc616551584388b87efbfbebc0142b2f7cd7af25a9bdd2faa30b4d907347a63215c2bca7c19c23ec205dd14422fe6619d3c18542bda1096dc6d442bad4c5e3d5e64eef78bcd1b8cbae8b05a50b99d650b8a78bcaf608f8e" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-2", - "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", - "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", - "alpha": "0a", - "ad": "", - "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", - "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", - "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", - "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", - "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", - "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", - "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", - "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", - "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6aec054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "dd5be8dec224fda0729eb1d3fb96cd3f48051aa1b4d5d34d8caab01b0d7bb92cf1c8e44a8a205c509c1f7665a97aa34daba181c2401a03d8c66260ab310b81a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "dd90ec2b3cd494c96d94bac1646292dd1fc07d6850ed567f50cee86b35a285a787b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161e5138357243a1b8c6576780706bf64e7c9532bb70e802a5429ed7ecceabdf981d803a7a414775707c35e688d011731819ee0c7df456479d352d01c568ebe2a8db0bedeae1d767ccec4fbe90431185edc4304b7d2796d4d85a6162dcf3244922558fa9d8b824bf7ffbf795c01838d7b6b2330d27c12f922cafb42425ab83fd223e78c0136771f57c9f037b7f92d9ced342e276d125b977f5eee81cf6adfce804283d984993ed33525fcdb56311e947cc9dc3995a458255eed97f0ed8830e3d0c2ba428e1b33498c25d26ea6d0e99c4cd6e4d5dbaa1491a1aa499c4f44f0a792f0de348765d1e7c38b808824a9cdac91d26a0867d9e7866f2282852b8d18bec095f20b1ab4e635182567e0fdb2fd317ada1d081721238e6dcf65a56b475591225011e3eb5d1c14db462f625924c55b25eb2afcfd4f207f4acee3afc227a157916e318df5c3d6c6070a2cda06bece28e3302f9b9409133a49b5146b24db9ef3c2ebc687a33ae0d2beeea954d18bb55f5f66bda5b14a2908d8de0d730b82bdeb91ed7a7236cd427798326b723653acd4a0189ca134d392250a09433a72ab3dadf98" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-3", - "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", - "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", - "alpha": "", - "ad": "0b8c", - "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", - "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", - "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", - "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", - "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", - "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", - "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", - "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", - "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42caec054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "2b7bd9be066892749d8099f3d6bf094d148cce0f69e221a0fe5fce9989ff5b307049bf1d5f36ce918533f7328e23ef77b9140095c5c06a1f91eb4655cae2efaada317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "b8766db2369fe13a4d3b6b4d525c054c51a36b6f91cfc87592e1cb228eb9e00287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16e11765f73130ac9cfac4ec7351eac9d008ba59fd23aa1a321f7c17878eb9db108b3a9836ce1365d49b50c5cec15ad87c9a94d510c2a08d0c07281f89273a742aa0508fd4676803a6f76993af9391d8e03140ea4db09b279dc9d92ad181746f0b597867657e8b7320acdd7ecee921035c30982effb4e8bab347e57f162875721236600bad5959993e80febdd869a9b13d51e032f8dfd8ee4063ecaa2fe2a7f5192c97398be7f78847dc5616c1e26320c6c5b98076bad8e97309efd23b66f6e211e8345c7d62b44ed675f9257ca0bd1fb6a51f26170a0100d276914f9eeb95722b074394b1b1569a8e828013500b1d3a4e626d82643206268704efe428876cf3240d025d017532fb20de4473208d08eed85db83f710e5c4b88a186ab47c2e0611e8e0845b5d26c60137bd59f02d1f530eb860f81cd9764212d131679e58e96fa9329a71dc16395a17672ec4a9ba7dbd9403788c2b203c75f54b0701477f2692a0f438c563b306b9e22c7442c780ae8b59a90b5e2c807ccd07da566eeac2836a02c9cbf9d919e964ea17229c1d8da14518ee2560dd50e9fae7358a7c2328558d0ad" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-4", - "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", - "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", - "alpha": "73616d706c65", - "ad": "", - "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", - "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", - "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", - "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", - "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", - "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", - "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", - "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", - "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b45224990311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "c090b59415fda5b5d56480bb15612d2c0581cc2e250a9fd1cc11a5ca6cf75e2becfb046f3e954eb592d18ac20a1f240deb3bd02cc7e08d163cf200dc981864afda317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6deb42de7ef3b5f2b77fe4897d5101a6af66ecbe984ce49806952c048734b69a87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161f151ef843b2205d4f220617cea8bd935b3a981298feaeaf86dbe50eae9b181fd73005b022d755de9fa36cfc53c9c1594e8cd77693191679caa05a8713f1fb859ad7ad7a63b7251cadd91aca0b823a93e83d43b88070e02fe92db0f00eea4e26fdaff2ae586be1848e212d8648af3532588715f7d3952027ff3e7ddda911430421dc2095a3dea539afd8ac02880751d83a2aa3e1469ea0bfe76ea1208d421b25eb8477aaba550c7b3dab40c9c43f63a0ee5b9df36a68ca54586641d4f54367270d2f9ebddb01ce178bd0efb5326fbefe2f85721cc8562d117bdada060cc20124136976b219d9373747c1f4331a708715a5fbf8c0c353fe71aab7aa41153a4c2d1d5e42fd8c3d080994074687da4f63985972a6276e8de049fa6d905333b48415e54bc15c7a44316551a474b58a3016145ade5ee3255111238d34125f79e9b426d1928a89355391a8d81cf874e0a1e91b335e2b76c5c8c30d0bbd7e82b898cc2504395cbc2b64e65a53f00e1cdd4ffc295783a5608fcdcce845fe1be69795560bd62911088b296ebe5325ca5dd7a574aef40da0072702658b9956b73bca1e6ea1" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-5", - "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", - "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", - "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", - "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", - "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", - "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", - "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", - "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", - "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249905ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "226d5e8397f7d1e7669a302b0f21744ca5a72eff12ac3c2e90201bd5576dd929bf9fe4a64ffa6a9e524c786384acb461e058bcf762b014bd6edfe9839c249c96da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "51bd9b960c09879d634914f14dc10badd69a52fd4066208aadcef0b4f55bf7ac87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16731ec27f7f83d53b72ad03d3dc58c3577a643ee06fc8a1c8dfec21d01131a5910380ed20e9dd96482988303151e20af5187eebbcb7c754bafeecf7e1a947472c361e8c8526d53b8c1c5d2fb9439ba640176f8ef70593555e94fb8b5c56a7ad041384b3e7819bc468e26882f5bcd1214510cdb351e881f4ce40827e84cfa7751e84c07f55defd454618554d90ee954026f10f7055a9b068bc8a435bfb6576372dd462bd25be390e9fa3af8d1393ae63c390fcaecfc37d2b941a42fc519a2c942bce81c8f6e3082f549e8b0ee3e4d7326b19b7d288bf97a96c1ad09b14786cdc0c395dff374d4e2650dfe14dbbce1edb844da66d0329bc93e5c96ce1e6ee18cb19439598f19033cbf788c032dd2b59e8fa9bd8db096bcb7fcfa8ca29ea70041d1f876109f36c4e57c097a0b5e794929f40e955d9b18226c452ea3bc48eb43bd30dcc121d12014b2088d47da08208216a1c0ee3839954d63d4d64a920916a8fae1304ed4a5f8be4ce3f9084b365a37e84a26bd61533e567ec603e3045e1b42876198488e0d854ff3671d31f8c2a74a16beb725767d5321d2a5506bc315d57de6389" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-6", - "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", - "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "1f42", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", - "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", - "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", - "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", - "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", - "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", - "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", - "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249905ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "226d5e8397f7d1e7669a302b0f21744ca5a72eff12ac3c2e90201bd5576dd929bf9fe4a64ffa6a9e524c786384acb461e058bcf762b014bd6edfe9839c249c96da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6ce421727f29371d9a332d1180d3c04be61a1370f8cfc34a06f0a5272aa2c29287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16c9deb39b90acda84aac8c1888991f458e86093ddb6173c9453e8aa3e456ed5a3e3506e65a63192ac6dda1cd836b4eac1d3ee7ebb1276dd7744fa7f37a3c1bb14b8b137b5d5237aaf4a8a625b92066eb65d98556c265142191bc11378a6fda9167db5267fb8c265fb1ac98ab6a249459f8ca6cd686c10676eafc06696b76e3a02fcf5bec5336e60770ff345f726a9d72ffab19ff74232072aba4eadbf5e2e9b2e47dc1ba8d4ece4b37a3d66465ba60d4a46deb979af6bc84bf3c62a48d10a6b2d085fb4b5a7270dc80cee6080762910c65a9055d711658a028969f3a0c8ca1811d4cb151541deee365c7445526818dd086632df5837d93db4cc73c55bf4d46a2df24b624e8dcac6a0ecd78f1b7553ef42db37e033c7fd13154afa74f40288e72d11eb5baaa9cc910bbc615c776299cadc2a971960288f9f7aa186929831a5599b212f07073d26ea2106883fd0fa6882480843760fa9eae2159b79270dc59f3d141ac892cc1879461a5a074ca69e09f12029c686add3b820679c0da8c8212be22ab49133780ec83d2c55efe00d8e3a054d0e40ef1c446066a5da940af05d8a6d9e" - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-7", - "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", - "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "1f42", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", - "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", - "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", - "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", - "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", - "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", - "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", - "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401", - "ring_pks": "c02556abb3f4c9914da3f9af42c7a804739f0a5f086cb3f823aae63eda36578a05afbf4921fbddc08c07324420d5d2c767245bf5e555604c0c8a187194eb1e15e6351e4871c4e9c276f37cfc5f7802752e9286af3fb805ef31f9df1b452249903db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0c054177deda6559dccff172a886a962d2d7d4a6a33d5c2e92d500e9aeca9e3922f0f85152a71884bf7b49c9f9260b6120789a774a0cb1af3c74569922774bf1471b63077461315a258e4862500b2f49bd403852bcf840add0082748a58b6d8943d63fe0b0a33dd66c5cd00b7f9cf69845daaaf715d9d6b25cbcb8a6ead464300", - "ring_pks_com": "16ca4899f97ae8fc410cbd241537f59458338892f9c556f21250d3204c0d8d880444c6f49adbd3a89adbe8f46b6736915f930ab09914f5f1e069b726ee8d7e16da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6b037a603dca77d7a2afa8228479b7c67770b9aa8b30a5051c43f5cd0268bbad87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16cb9d594fbe506e4b3fb33be87b4db9ea4cf8f55f791c3b202462c78b9c9fef9429c9f901e9e340da5ba29fbdc38ca3558b37deeba102b4b8df79b72abf9aac9b848fc111c3baa0eb5771c13eebf2b6333d2beb391e096928c011f7231c403d18f3e2bedf1bf755a8269940859fe6ddb0eae6f78ae5292d57dae7877351db7508acddaef8de9097ff761a6f2f847a084dc4f40dc09f8094a30c92d8862752512964105542141fbd64806f8763b270fe14109b4aca3511aba27b9baf5f04965f2a33e1903bd289bac5152478465d5f0aacb2697067505c58b003b9111542d0c00c29831a431b2b1baf1df7910884cf8abe8fa53b0e9567969d212dae4cd3f59220c7e7e2d24a827835b46fa4bea26e1c8d333505c5554fcd3576b2942858a6d602917742ddc6648768873fe45753d9292e64eeeb9e15c856c9ee9ca8c6e83b5ca35c6da3f7108f546c38d9212b6e6ab765a2f78506406bb1c5e02d1de4dbf66504f4bb2ba3c90112e93b8c617426719b7f23ab9a1d7d809b5c074f1deb90b7490e2c8e1042fd12f6ce7ec016aa47f88d8cef580224a58dac8843c7690eb9b45c87" - } -] \ No newline at end of file diff --git a/tests/vectors/dot-ring/README.md b/tests/vectors/dot-ring/README.md index 06ca1c4..48c84e7 100644 --- a/tests/vectors/dot-ring/README.md +++ b/tests/vectors/dot-ring/README.md @@ -19,4 +19,4 @@ Schemes: - Pedersen: `gamma || proof_pk_com || proof_r || proof_ok || proof_s || proof_sb` - Ring: Pedersen fields plus raw `ring_proof` -Ring vectors are generated for Bandersnatch, Bandersnatch-SW, Bandersnatch-SHAKE128, JubJub, and Baby-JubJub. They include the concatenated ring public keys in `ring_pks`, the verifier key/root commitment in `ring_pks_com`, the fixed `ring_size`, and the deterministic `prover_idx`. +Ring vectors are generated for Bandersnatch, Bandersnatch-SW, Bandersnatch-SHAKE128, and JubJub. They include the concatenated ring public keys in `ring_pks`, the verifier key/root commitment in `ring_pks_com`, the fixed `ring_size`, and the deterministic `prover_idx`. diff --git a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json b/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json deleted file mode 100644 index 7a08152..0000000 --- a/tests/vectors/dot-ring/baby-jubjub_sha-512_tai_ring.json +++ /dev/null @@ -1,149 +0,0 @@ -[ - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-1", - "sk": "75c75d41f91fbca3267abd6560f1d9bd2511a12062ae58a65a9d29543e4ce002", - "pk": "6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200", - "alpha": "", - "ad": "", - "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", - "gamma": "95dfb9fca03856e821b4747d97ec2f6d461b0bce12e792e8721eda5c2b0d1d97", - "beta": "03f65e3c19f8ad8322b4d6543280e3a6c6afd0928da29c1acbf0a01ce71b1407", - "blinding": "e88dc5e3abc4e8ef7446c0f0c729854d7e3b9413078dd674e4d159b17dde1e04", - "proof_pk_com": "3a144be2e6f6e686cf415ea95e9e5d5263af9375e2aadc99021076fea2db3d1e", - "proof_r": "e29d5a859eb5cb13028e0811e458026c343d2e35b1520ae8b227989b764ac425", - "proof_ok": "71f638949a642386df91d38acf61a790a9ad21c2b0e1c27a79a1515cd7e3e014", - "proof_s": "0d949fccb7fad1f525af4c3b3ab1041cc828303bb154d038d5eb1fb3e08c5500", - "proof_sb": "3391c22ea038a0b5d24d257bb84257b96de10a2055289056ec1a528c971ddd01", - "ring_pks": "7c72597deed7e9f1454139d631276dc3c06d576448519c0eb7667e78e0400895eed9a3e41726dde77f93828e8442e09b118b2952543f64354c08782b70e09f26b2d2a8d0cbf02ce3d8f3eda1b9ccd2e5325ddb4e99d1364b5c2ea63e6b1eec8f6c9e6f1bac4f9cc706f424cdd1adb22c7d5b8a25063cd6152b5a1ed948861200271442f492fa743e52e8c9c78e85d8ab601d7bd52d50a8bf117056f9e1af71108123a888708dc105d0d530f8ecebd3f75f4a836717cc6045a9843c44c7a35e16e7d8fbf066ff21f40fd2290d57c34f36dcbf9d693ae89a5856f0ea37547d262f0d9e01aa57cfff424b3395aa19f56ab5f971a1cdc5dd915f4246cb3eed180e91", - "ring_pks_com": "dc2ba94ad6f31ba7b1ad02433a3f0d42f50a652218a1115418106bfa2d1777a34187202f7757dc82ff6576b18b685109873a774ad4d6b3d3ecfc7b7b0c7a1312da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "f9a870a23b1fdf1f483ca4835de8cd6c7ffb6a4a6685370eb8eb92c647ba11a287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16557befdaf649a92c4333257b279c463e299f1932ac92322bd4d925d4fc4d7826b48f42b6cff2c2103890bc09a9d02da15aa08e85aa641e286fb7295622717804653d3423f5dfa7d123951377c1962209cee0bd486e3cc7f3b2ad49196d31a700ed58626378b929f5536808eaaf37b0e8f36334e2db51cf6ce2f25e4239428703fd8791ee01b1e7d622a11f2975cff50bd86f62c0ab3c3289456d5ef1fc0a1d0270e298c606cb4ab1dfd9c3c1381ce37bc40455af7b40b5333c5d41efb66684148d6214b211e95dc71573553dda6b4fd3b27895109aee850dd29ad77f6138d8253df5b4a70197fde024019449c983f0d344aec3c3cc6b4209f00bcfc9cabf6705f7f628d778e8d9746f1031de057d00c6e6d17ea3753691bd7e5d62dc2e064c19ab53c0961b6c992468c280fe32322e0ad8d3bb14cf990c9ed72e117326f365032f4c7c583f42bf195b8b9966bca141d7a2c100a02996414b76fb475968d6cd07f9e070800c4e17b4c7b32f4afb18ccd6ee388c1d0cd6c70009da3fb014a1ef8a8c59c981345bc243542359b7142a0676bdf9322ca7b5d00689e1dc281e34841e", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-2", - "sk": "8244450428fd0ae3a72bd2095f562389e2a78dbce614d9854e99507d1d527802", - "pk": "e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6ae", - "alpha": "0a", - "ad": "", - "h": "980356ff4b852833fd681f9ff056073fd2fadb1bc9cd8f46d7e9f290e4eea68b", - "gamma": "0fcae8899ded28352ac272cb19ddbf06c8a68179eaa9380e2c4d1cd46915c2ad", - "beta": "cf990824162b61902f65dfcab58dc2d23d5c0c9c78df2c28ba1571516834f62c", - "blinding": "7715eb8647ef0773498ba792afe65075d4b4c3319c97f98ae7ba5244d0277100", - "proof_pk_com": "f77f57cef4c6378056510bc438acf832002120988e55ca2b86d931175ad11b24", - "proof_r": "07387000c8b6ee73f1f6f9744a82c429bf1c564400aae8e605bc31610f458d9e", - "proof_ok": "c221ebfe6070bed69ebe65fdee50178a53cbe65b62ff685803a20d2ddd955e16", - "proof_s": "6e6a5a326fe285031297051f9187a735d7c7a0dc074da0b04e7234508bca1f04", - "proof_sb": "c58cf9d1ddb86c9bf29f663ff47fda3e00a20d4b6f7747186ff39ba3acda8403", - "ring_pks": "412e2b2c036b79c6ecd7a63db946276d4be039f3b9273e3a6b302f9f8eeda9880f0c9bc45affd54b2611c72259c3cea3c9fab7077ebb08a2fbcc9ca65c428d06a6b10742e0af9e7f1973c62312527eac96f7f2a91c47554ad761743b896ea8a3e590fdb2f00b182e9b6e77ec71878cb13705232befd0a43d62b733ecf5b7e6aef8985c46a7a2414f11331008bd0080f888f8a2bc2734eb3fe2bba9b9ae8d5893727a027568f0074c515de472f670fb5d32a242ebcb9337ed8d30fe0dc6f7b816eb06269af0bee3e8f2fd1194711b8e06b3d06f46e02c648e2e7586a627ef4800ab9ef88055b85b9a62dbc3705b91b079b47d8019cd2a11a470316256215d742e", - "ring_pks_com": "49879e1ab940c170b98e3d4566bb0760af86e076db2b7ef09a8fbf9c50ead29e4316a81ec77d2e7e2533084dc2b67fc9db4f91c3c2594d2de6024ff48cf342a3da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "dd90ec2b3cd494c96d94bac1646292dd1fc07d6850ed567f50cee86b35a285a787b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161e5138357243a1b8c6576780706bf64e7c9532bb70e802a5429ed7ecceabdf981d803a7a414775707c35e688d011731819ee0c7df456479d352d01c568ebe2a8aa084b01e89ba741f97aecd97b18cbbc39159e9ab949d1d7259354cb6fe55a2854aa23be55d5d599ad56e2004a77a71343a1850d785d788993daf8a796e1111cb3841ccd8b6898c8527da88de055c6190418532bd616952094663563dfa4f52bb823374d5c3f67db8e2c1362309baeb93e0618794efa8289be33da2fdc40c5080d8f74778f290107d991a03f01cb2e48f0f09a025025f860c0ea609c65acc322badda836ab2bcf7dcf62f087fbb9dbe2897c3d89a256226555715fbc216bdd1d7ac2332cdf700e6d6d8dc683ef193986f46303e94428ebad41547214e637ed0f42142fe1c9a0cf93607613fd8d41278bd1e208f9f560ea77f2266441d69c9f2f2a8b8aa3e1343a071679ef87d0b998f1edaaa578184f23d144a1234656f87a27b376be5d2aadd3af2a2c8cc38c415d8efde19ee4868ff223c5b41a70aeb9d30db9e739c6d3ff41e4f02063c48e9f337fccfb3babfe5c428ebe0d6022e8dda98c", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-3", - "sk": "a3a96e2f150ea6a93d9924ed1a7142d134940f610e48a56241a2f3cc7ff8a203", - "pk": "494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae", - "alpha": "", - "ad": "0b8c", - "h": "1d9289985dab3e6864df5efc85b97ea4a79bc7943a3b865d9c22e0adf9c19299", - "gamma": "2c7df8b3dbbe5942c00203838142749ea74b64a932f8783fdba47d81ae192905", - "beta": "f8364f237a518324fe260a3b906cb410f3b581def3d85eb5946071f6ed14110a", - "blinding": "fdc30bc7b71643e0076181e74e53c26f25f600a15864d44f9b470a94f616ac00", - "proof_pk_com": "8dde098f5e52134b803472e25dc084c4044379df4e82265b27790e2859c8f111", - "proof_r": "2d625f6a462074db59777c5bc8ea8b49f9abe094530c63ac6a04c0eb24f1479c", - "proof_ok": "8018912496267b68bd70d89d83d3bde3bf9805cef870fff98001d40ceba9e3a5", - "proof_s": "de4f831801ae20ab48656fbf9eeec5907e85252efc5f2a5c3608187263da3100", - "proof_sb": "253a147708b8ece4ba0f9e802eb9390af657f4e1e3d960c9c1064a8b46f1c105", - "ring_pks": "8d18615a037b0c2a33780e63e94d922703b3c02aac995df2c23c4e31310bec96aac5683720e7087e06f506f4e3c5e5df9cbf9637df1d2402cb692ad8f2df19020943a9e1dc819fc7c64cd8c832fbb7bba65e19f55282b16ab95020143378c32d494a5383019c0ca307cc5f4e31b965ca5f1c295da3b0b2822c17c037ccf42cae6d52a510ff0e896ace2ad063219e29f19446bc355124bb8ab4c1656c78d90aa20cb4ad24a1a98aaf988bde038ebe3f26891544e666cf7d9c61419c076c592a8f3803eb3bad25a4d50d248f8bdff0fa6e56d451d9572a62afe0304f34382cb79320d0840ca7076800a9902420dcaac2a98ab84c3a87130406c0d280d33b6a9d1c", - "ring_pks_com": "770b13dac04e3757ccd1ea19488a114dc154132904952e2b4bcd0f963ac6aa86bd803f91d73e58dbb3e3772900e693204a0a06075101eacad6941d0466ac1f8bda317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "b8766db2369fe13a4d3b6b4d525c054c51a36b6f91cfc87592e1cb228eb9e00287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16e11765f73130ac9cfac4ec7351eac9d008ba59fd23aa1a321f7c17878eb9db108b3a9836ce1365d49b50c5cec15ad87c9a94d510c2a08d0c07281f89273a742a0ee015d076833de0a643414f8b9856a2f3d64c5bc4dc108b2e223c428c402c27fd9b5d047b6729bc410356c2c51831ad33aab82b16a076e1de9e1c2b1046af29166b5865fa1719b7ad9a5a3f3f48bb716dfba9ba1cb67a0169d609c417670d1de3e0f65287167f58bf92d9fe843cda96c290d8a7df2485c330dab9b55e0208073fc8847e5c87febb0eae5edecbc01aab47e27302e0847708ae006a5ffbee800807cd1dd757b490bd6576c84c4d4513b6e761d696970268fccfcc735998f67609b733b2bd97494094198743ad7dc3d2916dcb37b77bea49c53ad925a7a0fbe82e2979189fb4d3a78e8307bc588a936cd00ef14f4a9d2dbb508eedbe86fe29588eb5709e18c95c7573b6898e7b33b0c27f56faed8592b1efc48e6b91f59b93481f558f561c308e2b26eec034269179c916efa2e3d89799f486108f6c2e45195f1011a513307ef71c7f322be1a4ee53c7870bd18e9dc3750188e94a1c13b6e6868d", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-4", - "sk": "a2cda2be43f2a2968588cc54c70311985fa4766e0de3e76748cc401cdc9c1801", - "pk": "311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92", - "alpha": "73616d706c65", - "ad": "", - "h": "e238e9cb991472563e1f635da3cd56d90111ddbf6d12449004327e3c41a43d92", - "gamma": "6cf0585162db08a9273a82614273dd6a3ef96a64d4a8f7e24fc05aa11a6ea88b", - "beta": "c3605fdb279de0329f0242655742cb11e082919b056399deec5746261abaf592", - "blinding": "ca972f3ffe03d1ddc5255e4657d3805bc72bf2e65a061f486df327a29398c103", - "proof_pk_com": "0e0485eddf91d9fa0e05150e517c7362b988003f42b030f067ad8a02dbe6baa4", - "proof_r": "26e972e1e6b180c1da433f63927823944863a611468e4156aee85e6a27d1d2a8", - "proof_ok": "502801631c54dd390a55a5ac438b8eb1f912d57d71ec52fbf95d762c0bff938a", - "proof_s": "014c033f19b82d406ebdea991a5b55df91eca2d639ec5cc53ac71569a0fec404", - "proof_sb": "773abed365078c2a9f3ef030fd25018b69afcf99dce984d14fdc1ef6a81eb105", - "ring_pks": "8d97f89798358e2f025ce77a1f8f5d1e15aae1e7adc3117b52da812b5f7fe5a711c730fe2bf1a8595d76ffc25bdd7ed894ba0fccc0d88a3e51104256d7f617ab11ed85a426db24dfec1d1c19f6665293443a9287cbf24d26502e336c44ad6fa7311a038654756b3a646f2a359764eafdf1c0766412dd5aa9d7e1cd1a28760e92b3e10628f090e0751fd4cc6f5e4e3c5e9f5144fd5cbbf79592a1f9d320c7c6224e76d5a2203c9127dfba15ea9c52b4d0ea27bbe9e65d4ac5b935daf778f2cf11159d38e015ce2059914b951f0554b783add482161391d3710cefe9438f00380eb1f767ba11cd83edd1b0586ba97ec89ec90395295a27dadea3c19fefd28baf26", - "ring_pks_com": "fd9f605aa94a5eae1ec661fdcfa6c79ab858b84461df3d42ce4d18f64dad1822ddd6ac79532b37c5d9d5f0198daa02a2e776be3cf1998f8daeb3eb6db61f4d05da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6deb42de7ef3b5f2b77fe4897d5101a6af66ecbe984ce49806952c048734b69a87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e161f151ef843b2205d4f220617cea8bd935b3a981298feaeaf86dbe50eae9b181fd73005b022d755de9fa36cfc53c9c1594e8cd77693191679caa05a8713f1fb856bd0d60103fd2bbcff876078381de77ab5d17fae63a3f1c14f58a1bf6b8e4d252107da03e0e554f5e255c719f5701440ba6e89a8f8b84eaafe9401bad0ac661d1a86d7828c6686d79b6a2f1543066497e664c132866a44114af78218c627791496e29bf5d1e270a2e02d84a0f95b463fd481e3b5abd19c5af884bf0c87abce2ca5d3daee9d9c54c5e7102709e6acafb745ee3b038cda1f1f82b10d5b1e6ae622651eda850112e3d3f6d9475e9e96b886ea11224b583af99ca44b5d069a8bb71489cf28e7db305ccef945f59d67f699b3fee67f202eb666682e0c00f25f32aa1a5f60a663147fc6987469753a5cb1648d53cfe96a9960e98d960660533d9281885edb905316b81f115fdb54e56bb11ac1b57bb614f5d3a8d392ac66a104b0e321bc9ded5e04badf41ab9c41781416aeb8d0b442a6a595a691df96e59e92139da56d158c50bcb1377c4bb37301314a13ea962c0eddf8d56054b00be45e696be204", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-5", - "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", - "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", - "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", - "blinding": "89de5bee8ba71d6642aff6cd00dd52b4a9dc94a2f48dbf80d38a9e223158e803", - "proof_pk_com": "a0654df619beadfaf02d641a5fa4c66ba5b8fa1e5fe2dd6e879212efc58b8e17", - "proof_r": "2b18a5a2f2a1f134bb78cd9d6a782d8b022974aa84bbf5306321b7e24128fd10", - "proof_ok": "00be471bacbc169ec4840f2d79f3327d3540430a837f4ae5659eca6a3a327516", - "proof_s": "e7f2065afc570fd125ffca7daa78cda31a8d658036fe56fb71898ad4e4e7e802", - "proof_sb": "0dddd9121f6bb7fa5b231c6f88a8dc144147a2b9b4a082bd292de23356a71603", - "ring_pks": "f69aa870343224fd26eb055cec05f58dac0a47e2c5903e89a3971e05b1e9d1ac14d0796a59e525da94b474bdfb67d8b705f74ecbf4922b281f6e1647c80dec238636a6752c40e1795e1be4faa253ceaaa4c4839236c3da8d3a02846b83ced90f5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b18592505d33231d1d2a13cc6503ebc1e97c9f226cc0f2812d58743c65e9159a978b0f3918512eceb4f42c1002ade53a8f86e0d07be415accd456c0e59924edaed2b366d196d6d879977fd362d5fafa00ce8ec8627d209c7df1e7368f4ae5309aaaa7b65b1ad2626d843ec49ccf0e7c688ced9271d6fb0678faa6cc5033b2bf90e05", - "ring_pks_com": "522876664bf0045a971a34508432934fd96fa4cddc68eb63b4628c605053f38284bc283ea4bb77af7b16b667d804d95bdb8c20d3fce1b78babe691f5da5525a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "51bd9b960c09879d634914f14dc10badd69a52fd4066208aadcef0b4f55bf7ac87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16731ec27f7f83d53b72ad03d3dc58c3577a643ee06fc8a1c8dfec21d01131a5910380ed20e9dd96482988303151e20af5187eebbcb7c754bafeecf7e1a947472c5accb6ba30e4892ab691a9dd6ecd37103f9afc6673eebfbcce0e0c2b3536a80f45efb38ca0891df51884f85532be97fcf75c6ee6799067678224ce7f0798a61bc245931215931b61f4985ec0290f031418558630507e1a98870346e0c204862988ab1dd59ce40d4f0c85c3703e9dca445fb06a6bc21ec7061b57d5ce14d2a114cef5a124803044df8de930b6bf6ede68f52334f8b17fc03041b780f53c93e202b7a7da1818a5cfa12186416212161660c7412d5c923d8edbd0094b48ee213b19c6cde3d48c37897bdf3fe0da86ffa2b7eb3b054ef028a480af803c4c005c3202daed8cc7f357415989f4bcf3b472e4972c353182b37fdd890d53cd616e3ed294c3f84c9fcd3ea573b97b970eb0d2a5bde0b4aff8bd58542c86fa592b07a755281b279dd53d08c83357766ac0dd848f2ba1d4eb021ff611f71397248bf29a71277a392b9cc4d15477158483963d9d035c4396ba41ca93c11149b9d0ebd1dee317", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-6", - "sk": "8303383ad90d1e1e1c1c1664f4f90058e3ca36a7e7c2c4ccb0d9c1ddae4df701", - "pk": "5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b185", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "1f42", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "73ec0fcb15baa30832f91e5413dd66956755b987dd90e5d3d34e07c803adcc27", - "beta": "19beae839d5569cbfc3a4345fe6078f8474de9be3c62526f97bbac351ecba598", - "blinding": "4bd0f8c66f3374b36c37447236090605c7ac99ada64a7d9a7d6fe39fcbe36f05", - "proof_pk_com": "ebffedabcdd64705da3ca30c21ff4198b82e702eb76d50dbd27eacd2ca8ca1ad", - "proof_r": "4d3c33a8317738b28e3a3c3d5a298f23252156eb6a972af120e74f38c8f914a2", - "proof_ok": "146eb43bc4af6a5d3a49bfd99c570804085a8a2475b75f6913544c20b9e663ac", - "proof_s": "692a417bb5566311941d491e9d6e03073ee39c5f9bd2ea9ee1fa2de706d45503", - "proof_sb": "bcc067acf6df9ff83c5797a6fa5b11764e25a4fd106986d6ea4e439e07a3e001", - "ring_pks": "f69aa870343224fd26eb055cec05f58dac0a47e2c5903e89a3971e05b1e9d1ac14d0796a59e525da94b474bdfb67d8b705f74ecbf4922b281f6e1647c80dec238636a6752c40e1795e1be4faa253ceaaa4c4839236c3da8d3a02846b83ced90f5ffa68eca7adc6230bb03b887a748abb32257d06ccdb9c431ac0a0c7c807b18592505d33231d1d2a13cc6503ebc1e97c9f226cc0f2812d58743c65e9159a978b0f3918512eceb4f42c1002ade53a8f86e0d07be415accd456c0e59924edaed2b366d196d6d879977fd362d5fafa00ce8ec8627d209c7df1e7368f4ae5309aaaa7b65b1ad2626d843ec49ccf0e7c688ced9271d6fb0678faa6cc5033b2bf90e05", - "ring_pks_com": "522876664bf0045a971a34508432934fd96fa4cddc68eb63b4628c605053f38284bc283ea4bb77af7b16b667d804d95bdb8c20d3fce1b78babe691f5da5525a7da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6ce421727f29371d9a332d1180d3c04be61a1370f8cfc34a06f0a5272aa2c29287b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16c9deb39b90acda84aac8c1888991f458e86093ddb6173c9453e8aa3e456ed5a3e3506e65a63192ac6dda1cd836b4eac1d3ee7ebb1276dd7744fa7f37a3c1bb14e9db9641cbb486ecdcfb349c444bef5ccb181eb4ee57fb1a07bfc36e523755221e2b9abc0c04ea2030ea612b80cf531ad172ab6ab0bc8a84fe965f576d5f291d90f8ad87183f5c7dcae02385373e3697ad6e615edc434ca2bba7b158092b7c186bfe3f572e423d44890484c6abf7ab6d5bf25e204adbd952cac857bf2642861fa76a682f8f1adc6454e1b50b8ccdf01b13aefb7bfab338aa20b98f4c8da46f1655cdf484eaf88c69de3bb96c4d23cc97ac67eb814de38c7476a8ef41db6f4220da8c5bba7086db6aebd69934ddeada73b65f7f44559621f4232d94807d1b6509326bac4a6107ef1731147e67fd12f16795830dbe4b8421063f42d51b6b6dd1a0e26148668628b6458f2d415bab4792f7f884fc82d2f97351c975c48ddd87f90ab64a7ac3e24a9c0182dbb50ccd6a254db316628a76180edcd1348fa85b23731b9951c601cad91ca36555fa8f947e42d3990062218142436c0b972ec965ecb2a3", - "ring_size": 8, - "prover_idx": 3 - }, - { - "comment": "baby-jubjub_sha-512_tai_ring - vector-7", - "sk": "5bdbbae35f50a4bd2a2c22f666c7eb76a9b009b7355d0fff8cfd93706a8af402", - "pk": "3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a0", - "alpha": "42616e646572736e6174636820766563746f72", - "ad": "1f42", - "h": "017b9cf18f3a889a2517dda29ce2950245212febd74b8f83ffb421b1e9528c1f", - "gamma": "a4fcbe20925d4ed283653fc89ceafbf0bef39d9d3c234da05c7ab27ec40f351d", - "beta": "9b35bb4efe4f216fa467905e03be0b42b38c02f73f9d676127477b5935bf8d37", - "blinding": "31bcf8b3bfdde64943f79f7041219631a83e3df4940130cfdc6512b86bc25300", - "proof_pk_com": "e3c481b7f7c767fb48817470662a18007f2de73727725bcef8c6032475639d2f", - "proof_r": "b7ba55002a81709186f73bc1575aba514b7b83a7322196425ef9d4f0b29b881a", - "proof_ok": "d547b9f37a1dbef7c2211869cc245b8e057b413e98059889f83adf0ab8adf41e", - "proof_s": "035b6e3b11460fac43da43e95dd9f17018ef4b3d323283296b9faa1317720e03", - "proof_sb": "e78d3d3cbb0816d0d6c28e7ee0064b70accac39d736d1f536e64cc5978f38401", - "ring_pks": "a677a8194c4cdd1e9d85114ccfe6a350f014a1cd818055f0566f7060627c38951e35e40bfeecb6015ecbc77ff455cd7045bcb8ac7fcd099f6028f64a85137f1b4623b8c7b65ec47a9521fdbd00b6f5cba174b36b0a2ddea04753c1424cdecc0e3db2666a89ee66c4024405f27fafe22d349e0632b0ce03dd9fcaae2290ee11a06bea01a9166a00137636f80e6550714aa48ff5f87c86f0d4b315abd62d3b2811081ec41b15c045d9dba6e9fab7465709cfcfe8ef94cdda74cec86f97a8ab2086b297d8721f2c8b8f8b5310a19ac4dc880e9a391a08bb17c28aec7c3ff732a287daa09270ea40b719fcfea71ef6ce2bfda3277b62d1bea7ba903cd67f7ec112ac", - "ring_pks_com": "7cd2ab3e50186bf385177f1ba281b33580fb01411c0752126c4a0228a87e9e09706c790573fc90d8ce365f37cd728d64d252951dbef1a64e1d909468fcf17a19da317d63f11e336a8353c7dd9855f9a21f562fbe2108af00d0e37063f4617b98", - "ring_proof": "6b037a603dca77d7a2afa8228479b7c67770b9aa8b30a5051c43f5cd0268bbad87b112aebcd268914a73aebb110115be8eafe91e3fc677a6545bd276f2304e16cb9d594fbe506e4b3fb33be87b4db9ea4cf8f55f791c3b202462c78b9c9fef9429c9f901e9e340da5ba29fbdc38ca3558b37deeba102b4b8df79b72abf9aac9bef73d482835c93e1bd238169705322e14ec123964e181cda1f58025b759e871151a1da33a3d7769a19c37444fdb743eb689377cadc9364da078273fe918c1d183496b9145c565563e595bd430686bbc78bdc1de5be99091ca32c2510d0c953182e5ced6d83b10cc4377b60ee5b5fe318216db1d827caadd202b0d72ebc601521b028c9befb5880727203492a2be816b7303ae18b01d4f9b12d75184368f59d2aad5622f0695a4d86e95a277305854c1071ce12acdcaaf710d44001cd0e4c1e1f8d2072d30a479c37e6800b0f190618ef76e78b25953e19af5adc41f706698718a9194eaa69239722bdf7b53e25bb59e3efb09bb9ad238d66fa0e5b1abc8e3f16dc6ff738c8f29ad254fc6f40cf1036d40087e8f1fc0dbaeaf8aa669a9bf7771f3de49dd86d7a3c7a722f866c41a8c9fa1529e9eaca6bee71e256b6125543c605a691d00a402e6aa09847506f5566fa0a628c93b9437a692dc2d93371369d2ba0", - "ring_size": 8, - "prover_idx": 3 - } -] \ No newline at end of file From 97b8d72d18d2d2ce53322529ca62aaddaa9fabdb Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:44:09 +0530 Subject: [PATCH 29/33] fix: linting issues --- dot_ring/curve/curve.py | 10 +- dot_ring/curve/montgomery/mg_curve.py | 1 - dot_ring/curve/specs/bls12_381_G2.py | 124 ++++++++++-------- dot_ring/curve/specs/parameters.py | 1 - dot_ring/curve/twisted_edwards/te_curve.py | 2 +- dot_ring/ring_proof/constants.py | 6 +- dot_ring/ring_proof/pcs/utils.py | 4 +- dot_ring/ring_proof/verify.py | 6 +- dot_ring/scripts/export_python_proof.py | 2 +- dot_ring/vrf/pedersen/batch_verifier.py | 1 - dot_ring/vrf/ring/__init__.py | 2 +- dot_ring/vrf/ring/batch_verifier.py | 8 +- dot_ring/vrf/ring/root.py | 6 +- dot_ring/vrf/ring/vrf.py | 4 +- tests/benchmark/bench_ring_proof.py | 15 +-- tests/benchmark/memory_regression.py | 3 +- tests/test_ark_vrf.py | 3 +- tests/test_bandersnatch_ark.py | 3 +- tests/test_coverage/test_columns.py | 3 +- tests/test_ring_vrf/test_audit_regressions.py | 3 +- tests/test_ring_vrf/test_ring_vrf.py | 3 +- 21 files changed, 90 insertions(+), 120 deletions(-) diff --git a/dot_ring/curve/curve.py b/dot_ring/curve/curve.py index 03e5164..8a7fa13 100644 --- a/dot_ring/curve/curve.py +++ b/dot_ring/curve/curve.py @@ -67,10 +67,7 @@ def _validate(self): if isinstance(generator_x, Fp2) or isinstance(generator_y, Fp2): if not ( - isinstance(generator_x, Fp2) - and isinstance(generator_y, Fp2) - and generator_x.p == field_modulus - and generator_y.p == field_modulus + isinstance(generator_x, Fp2) and isinstance(generator_y, Fp2) and generator_x.p == field_modulus and generator_y.p == field_modulus ): raise ValueError("Generator coordinates must be Fp2 elements with the correct field modulus") if not self.is_on_curve((generator_x, generator_y)): # type: ignore[attr-defined] @@ -154,10 +151,7 @@ def expand_message_xmd(self, msg: bytes, len_in_bytes: int) -> bytes: dst = self.hash_to_curve_dst() if ell > 255 or len_in_bytes > 65535 or len(dst) > 255: - raise ValueError( - "Invalid XMD input size parameters: " - f"ell={ell}, len_in_bytes={len_in_bytes}, dst_len={len(dst)}" - ) + raise ValueError(f"Invalid XMD input size parameters: ell={ell}, len_in_bytes={len_in_bytes}, dst_len={len(dst)}") DST_prime = dst + self.I2OSP(len(dst), 1) Z_pad = self.I2OSP(0, cast(int, hash_to_curve.expand_len)) diff --git a/dot_ring/curve/montgomery/mg_curve.py b/dot_ring/curve/montgomery/mg_curve.py index ec6884c..8f0879c 100644 --- a/dot_ring/curve/montgomery/mg_curve.py +++ b/dot_ring/curve/montgomery/mg_curve.py @@ -1,7 +1,6 @@ from __future__ import annotations from dataclasses import dataclass - from typing import TYPE_CHECKING, Any, TypeVar from ..curve import Curve diff --git a/dot_ring/curve/specs/bls12_381_G2.py b/dot_ring/curve/specs/bls12_381_G2.py index a25489c..2893942 100644 --- a/dot_ring/curve/specs/bls12_381_G2.py +++ b/dot_ring/curve/specs/bls12_381_G2.py @@ -32,68 +32,76 @@ def _fp2_values(values: tuple[tuple[int, int], ...]) -> tuple[Fp2, ...]: BLS12_381_G2_ISOGENY = RationalIsogeny[Fp2]( map_curve=ShortWeierstrassModel(a=_fp2(0, 240), b=_fp2(1012, 1012)), - x_numerator=_fp2_values(( + x_numerator=_fp2_values( ( - 0x171D6541FA38CCFAED6DEA691F5FB614CB14B4E7F4E810AA22D6108F142B85757098E38D0F671C7188E2AAAAAAAA5ED1, - 0, - ), - ( - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71E, - 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38D, - ), - ( - 0, - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71A, - ), - ( - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, - ), - )), - x_denominator=_fp2_values(( - (1, 0), - ( - 0xC, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA9F, - ), - ( - 0, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA63, - ), - )), - y_numerator=_fp2_values(( - ( - 0x124C9AD43B6CF79BFBF7043DE3811AD0761B0F37A1E26286B0E977C69AA274524E79097A56DC4BD9E1B371C71C718B10, - 0, - ), - ( - 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71C, - 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38F, - ), - ( - 0, - 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97BE, - ), - ( - 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, - 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, - ), - )), - y_denominator=_fp2_values(( - (1, 0), + ( + 0x171D6541FA38CCFAED6DEA691F5FB614CB14B4E7F4E810AA22D6108F142B85757098E38D0F671C7188E2AAAAAAAA5ED1, + 0, + ), + ( + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71E, + 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38D, + ), + ( + 0, + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71A, + ), + ( + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97D6, + ), + ) + ), + x_denominator=_fp2_values( ( - 0x12, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA99, - ), + (1, 0), + ( + 0xC, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA9F, + ), + ( + 0, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA63, + ), + ) + ), + y_numerator=_fp2_values( ( - 0, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA9D3, - ), + ( + 0x124C9AD43B6CF79BFBF7043DE3811AD0761B0F37A1E26286B0E977C69AA274524E79097A56DC4BD9E1B371C71C718B10, + 0, + ), + ( + 0x11560BF17BAA99BC32126FCED787C88F984F87ADF7AE0C7F9A208C6B4F20A4181472AAA9CB8D555526A9FFFFFFFFC71C, + 0x8AB05F8BDD54CDE190937E76BC3E447CC27C3D6FBD7063FCD104635A790520C0A395554E5C6AAAA9354FFFFFFFFE38F, + ), + ( + 0, + 0x5C759507E8E333EBB5B7A9A47D7ED8532C52D39FD3A042A88B58423C50AE15D5C2638E343D9C71C6238AAAAAAAA97BE, + ), + ( + 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, + 0x1530477C7AB4113B59A4C18B076D11930F7DA5D4A07F649BF54439D87D27E500FC8C25EBF8C92F6812CFC71C71C6D706, + ), + ) + ), + y_denominator=_fp2_values( ( - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, - 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, - ), - )), + (1, 0), + ( + 0x12, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFAA99, + ), + ( + 0, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA9D3, + ), + ( + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, + 0x1A0111EA397FE69A4B1BA7B6434BACD764774B84F38512BF6730D2A0F6B0F6241EABFFFEB153FFFFB9FEFFFFFFFFA8FB, + ), + ) + ), ) diff --git a/dot_ring/curve/specs/parameters.py b/dot_ring/curve/specs/parameters.py index 2f01b05..db55bba 100644 --- a/dot_ring/curve/specs/parameters.py +++ b/dot_ring/curve/specs/parameters.py @@ -24,7 +24,6 @@ class Elligator2MontgomeryMap: b: int - @dataclass(frozen=True, kw_only=True) class RationalIsogeny(Generic[Coord]): """Rational isogeny map from an auxiliary curve to the target curve.""" diff --git a/dot_ring/curve/twisted_edwards/te_curve.py b/dot_ring/curve/twisted_edwards/te_curve.py index e2e63da..b84b96b 100644 --- a/dot_ring/curve/twisted_edwards/te_curve.py +++ b/dot_ring/curve/twisted_edwards/te_curve.py @@ -92,4 +92,4 @@ def map_to_curve_ell2(self, u: int) -> tuple[int, int]: if e2 ^ e3: ym = -ym % pm - return int((x * map_b) % pm), int((ym * map_b) % pm) \ No newline at end of file + return int((x * map_b) % pm), int((ym * map_b) % pm) diff --git a/dot_ring/ring_proof/constants.py b/dot_ring/ring_proof/constants.py index 906c182..966d9d0 100644 --- a/dot_ring/ring_proof/constants.py +++ b/dot_ring/ring_proof/constants.py @@ -3,11 +3,7 @@ from dot_ring.curve.specs.bandersnatch import BANDERSNATCH_PARAMS _AUXILIARY_POINTS = BANDERSNATCH_PARAMS.auxiliary_points -if ( - _AUXILIARY_POINTS.blinding_base is None - or _AUXILIARY_POINTS.accumulator_base is None - or _AUXILIARY_POINTS.padding_point is None -): +if _AUXILIARY_POINTS.blinding_base is None or _AUXILIARY_POINTS.accumulator_base is None or _AUXILIARY_POINTS.padding_point is None: raise ValueError("Bandersnatch ring-proof auxiliary points are not configured") S_PRIME: int = BANDERSNATCH_PARAMS.field_modulus # Finite‑field modulus p diff --git a/dot_ring/ring_proof/pcs/utils.py b/dot_ring/ring_proof/pcs/utils.py index fdced91..a8f5dc8 100644 --- a/dot_ring/ring_proof/pcs/utils.py +++ b/dot_ring/ring_proof/pcs/utils.py @@ -1,4 +1,4 @@ -from typing import Any, NamedTuple, TypeAlias +from typing import Any, NamedTuple import py_ecc.optimized_bls12_381 as bls from py_ecc.bls import point_compression @@ -7,7 +7,7 @@ Scalar = int CoeffVector = list[Scalar] -G1Term: TypeAlias = tuple[Any, Scalar] +G1Term = tuple[Any, Scalar] class PcsVerification(NamedTuple): diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index 7c541aa..194389a 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -420,11 +420,7 @@ def __init__( else [self._transcript_g1(cmt) for cmt in proof_fields.witness_commitments] ) # Add quotient and evaluations once we have their serialized form. - quotient_commitment = ( - transcript_quotient_commitment - if transcript_quotient_commitment is not None - else self._transcript_g1(proof_fields.c_q) - ) + quotient_commitment = transcript_quotient_commitment if transcript_quotient_commitment is not None else self._transcript_g1(proof_fields.c_q) if transcript_prefix is not None: self.t, self.alpha_list, self.zeta_p, self.V_list = derive_challenges_after_vk( self.t, diff --git a/dot_ring/scripts/export_python_proof.py b/dot_ring/scripts/export_python_proof.py index 613c966..6f888ab 100644 --- a/dot_ring/scripts/export_python_proof.py +++ b/dot_ring/scripts/export_python_proof.py @@ -21,8 +21,8 @@ from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.srs import srs -from dot_ring.vrf.ring.proof_builder import RingProofBuilder from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.ring.proof_builder import RingProofBuilder from tests.utils.python_to_rust_serde import ( serialize_bls12_381_g1, serialize_bls12_381_g2, diff --git a/dot_ring/vrf/pedersen/batch_verifier.py b/dot_ring/vrf/pedersen/batch_verifier.py index c5ba563..ddceb13 100644 --- a/dot_ring/vrf/pedersen/batch_verifier.py +++ b/dot_ring/vrf/pedersen/batch_verifier.py @@ -195,7 +195,6 @@ def verify(self) -> bool: blinding_scalar = 0 for item, (io_weight, commitment_weight) in zip(self.items, coefficients, strict=True): - for io, z in zip(item.ios, item.zs, strict=True): points.extend([io.input, io.output]) scalars.extend( diff --git a/dot_ring/vrf/ring/__init__.py b/dot_ring/vrf/ring/__init__.py index 8106362..2725890 100644 --- a/dot_ring/vrf/ring/__init__.py +++ b/dot_ring/vrf/ring/__init__.py @@ -1,6 +1,6 @@ -from .members import Ring from .batch_verifier import RingBatchContext, RingBatchItem, RingBatchVerifier from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .members import Ring from .root import RingRoot from .vrf import RingVRF diff --git a/dot_ring/vrf/ring/batch_verifier.py b/dot_ring/vrf/ring/batch_verifier.py index d69d2c7..2399ee3 100644 --- a/dot_ring/vrf/ring/batch_verifier.py +++ b/dot_ring/vrf/ring/batch_verifier.py @@ -10,8 +10,7 @@ ) from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript from dot_ring.ring_proof.verify import Verify, prepare_linear_pcs_verifications_fast -from dot_ring.vrf.pedersen.batch_verifier import PedersenBatchItem -from dot_ring.vrf.pedersen.batch_verifier import PedersenBatchVerifier +from dot_ring.vrf.pedersen.batch_verifier import PedersenBatchItem, PedersenBatchVerifier from dot_ring.vrf.transcript import VrfIo from .members import Ring @@ -275,9 +274,6 @@ def verify(self) -> bool: return False try: - return all( - _verify_pcs_batch(pcs, verifications) - for pcs, verifications in self.pcs_batches.values() - ) + return all(_verify_pcs_batch(pcs, verifications) for pcs, verifications in self.pcs_batches.values()) except (AssertionError, TypeError, ValueError): return False diff --git a/dot_ring/vrf/ring/root.py b/dot_ring/vrf/ring/root.py index df76b10..c16bb75 100644 --- a/dot_ring/vrf/ring/root.py +++ b/dot_ring/vrf/ring/root.py @@ -185,11 +185,7 @@ def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "Ring def matches_ring(self, ring: Ring) -> bool: params = ring.params domain_size = params.domain_size - if ( - len(self.px.evals) >= domain_size - and len(self.py.evals) >= domain_size - and len(self.s.evals) >= domain_size - ): + if len(self.px.evals) >= domain_size and len(self.py.evals) >= domain_size and len(self.s.evals) >= domain_size: return ( tuple(self.px.evals[:domain_size]) == tuple(point[0] for point in ring.nm_points) and tuple(self.py.evals[:domain_size]) == tuple(point[1] for point in ring.nm_points) diff --git a/dot_ring/vrf/ring/vrf.py b/dot_ring/vrf/ring/vrf.py index b49a04c..63a20ee 100644 --- a/dot_ring/vrf/ring/vrf.py +++ b/dot_ring/vrf/ring/vrf.py @@ -12,9 +12,10 @@ from dot_ring.vrf.vrf import PreparedSecretKey from ..vrf import VRF -from .members import Ring, parse_concatenated_keys as _parse_concatenated_keys from .batch_verifier import RingBatchItem, RingBatchVerifier, _proof_relation_points, _proof_transcript_commitments from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .members import Ring +from .members import parse_concatenated_keys as _parse_concatenated_keys from .proof_builder import RingProofBuilder from .proof_payload import RingProofPayload, ring_proof_len from .root import RingRoot @@ -296,6 +297,7 @@ def verify(self, input: bytes, ad_data: bytes, ring: Ring, ring_root: RingRoot) def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: return PedersenVRF[cls.cv].proof_to_hash(gamma, mul_cofactor) + __all__ = [ "RingBatchItem", "RingBatchVerifier", diff --git a/tests/benchmark/bench_ring_proof.py b/tests/benchmark/bench_ring_proof.py index ad6339e..5a4b43c 100644 --- a/tests/benchmark/bench_ring_proof.py +++ b/tests/benchmark/bench_ring_proof.py @@ -172,10 +172,7 @@ def _build_batch_fixtures_parallel(ring_size: int, fixture_count: int, workers: else: serialized = [] with ProcessPoolExecutor(max_workers=workers) as executor: - futures = [ - executor.submit(_prove_batch_fixture_range, ring_size, start, stop) - for start, stop in _chunk_ranges(fixture_count, workers) - ] + futures = [executor.submit(_prove_batch_fixture_range, ring_size, start, stop) for start, stop in _chunk_ranges(fixture_count, workers)] for future in futures: serialized.extend(future.result()) @@ -210,11 +207,7 @@ def run( print("sample ring_root_ms prove_ms verify_ms total_ms") for timing in timings: print( - f"{timing.ring_id.hex()[:8]:>6} " - f"{timing.ring_root_ms:>12.2f} " - f"{timing.prove_ms:>8.2f} " - f"{timing.verify_ms:>9.2f} " - f"{timing.total_ms:>8.2f}" + f"{timing.ring_id.hex()[:8]:>6} {timing.ring_root_ms:>12.2f} {timing.prove_ms:>8.2f} {timing.verify_ms:>9.2f} {timing.total_ms:>8.2f}" ) print() @@ -223,7 +216,7 @@ def run( _print_stats("verify", [timing.verify_ms for timing in timings]) _print_stats("total", [timing.total_ms for timing in timings]) print(f"proof_size: {timings[-1].proof_size} bytes") - + fixture_count, workers = batch_fixtures, batch_workers if max_batch_size <= 0: return @@ -251,7 +244,6 @@ def run( print("batch_n verify_ms verify_ms_per_proof") for batch_size in _batch_sizes(max_batch_size): items = prepared_fixtures[:batch_size] - times: list[float] = [] verifier = RingBatchVerifier(context) for item in items: verifier.push_prepared(item) @@ -265,7 +257,6 @@ def run( print(f"{batch_size:>7} {elapsed_ms:>13.2f} {elapsed_ms / batch_size:>18.2f}") - def main() -> None: parser = argparse.ArgumentParser(description="Benchmark fresh ring root, prove, and verify timings.") parser.add_argument("n", type=int, nargs="?", default=1024, help="ring size") diff --git a/tests/benchmark/memory_regression.py b/tests/benchmark/memory_regression.py index 27ba316..b6bcf88 100644 --- a/tests/benchmark/memory_regression.py +++ b/tests/benchmark/memory_regression.py @@ -16,9 +16,8 @@ from dot_ring.keygen import secret_from_seed from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.vrf.ring import RingRoot, RingVRF from dot_ring.vrf.ring.members import Ring -from dot_ring.vrf.ring import RingRoot -from dot_ring.vrf.ring import RingVRF SIGNER_INDEX = 3 RSS_EPSILON_MB = 0.1 diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py index 5b0959c..bfd3265 100644 --- a/tests/test_ark_vrf.py +++ b/tests/test_ark_vrf.py @@ -13,8 +13,7 @@ from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF from dot_ring.vrf.pedersen import PedersenBatchVerifier, PedersenVRF -from dot_ring.vrf.ring import Ring, RingRoot -from dot_ring.vrf.ring import RingBatchVerifier, RingContext, RingVRF +from dot_ring.vrf.ring import Ring, RingBatchVerifier, RingContext, RingRoot, RingVRF from dot_ring.vrf.transcript import VrfIo from scripts.generate_test_vectors import SUITES as GENERATED_SUITES from scripts.generate_test_vectors import scheme_vectors diff --git a/tests/test_bandersnatch_ark.py b/tests/test_bandersnatch_ark.py index 3298afa..f209f09 100644 --- a/tests/test_bandersnatch_ark.py +++ b/tests/test_bandersnatch_ark.py @@ -8,8 +8,7 @@ from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ietf import TinyVRF from dot_ring.vrf.pedersen import PedersenVRF -from dot_ring.vrf.ring import Ring, RingRoot -from dot_ring.vrf.ring import RingVRF +from dot_ring.vrf.ring import Ring, RingRoot, RingVRF from dot_ring.vrf.transcript import scalar_len HERE = os.path.dirname(__file__) diff --git a/tests/test_coverage/test_columns.py b/tests/test_coverage/test_columns.py index 2746e3d..eedf2b6 100644 --- a/tests/test_coverage/test_columns.py +++ b/tests/test_coverage/test_columns.py @@ -4,8 +4,7 @@ from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder from dot_ring.ring_proof.constants import DEFAULT_SIZE, OMEGAS, S_PRIME from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring import Ring -from dot_ring.vrf.ring import RingVRF +from dot_ring.vrf.ring import Ring, RingVRF def test_column_interpolate_rejects_oversize_evals(): diff --git a/tests/test_ring_vrf/test_audit_regressions.py b/tests/test_ring_vrf/test_audit_regressions.py index 05225fe..85f0fb6 100644 --- a/tests/test_ring_vrf/test_audit_regressions.py +++ b/tests/test_ring_vrf/test_audit_regressions.py @@ -4,8 +4,7 @@ from dot_ring.ring_proof.constants import PaddingPoint from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.pedersen import PedersenVRF -from dot_ring.vrf.ring import RingContext -from dot_ring.vrf.ring import Ring, RingRoot +from dot_ring.vrf.ring import Ring, RingContext, RingRoot from dot_ring.vrf.transcript import point_len diff --git a/tests/test_ring_vrf/test_ring_vrf.py b/tests/test_ring_vrf/test_ring_vrf.py index f0e32e2..9016daa 100644 --- a/tests/test_ring_vrf/test_ring_vrf.py +++ b/tests/test_ring_vrf/test_ring_vrf.py @@ -4,8 +4,7 @@ from dot_ring.curve.specs.bandersnatch import Bandersnatch from dot_ring.ring_proof.params import RingProofParams -from dot_ring.vrf.ring import Ring, RingRoot -from dot_ring.vrf.ring import RingVRF +from dot_ring.vrf.ring import Ring, RingRoot, RingVRF HERE = os.path.dirname(__file__) From 82021da8789d2672e848f530279a381a057759df Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:51:51 +0530 Subject: [PATCH 30/33] lazy load srs --- dot_ring/ring_proof/params.py | 3 --- dot_ring/ring_proof/pcs/kzg.py | 2 ++ tests/test_coverage/test_kzg.py | 19 ++++++++++--------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/dot_ring/ring_proof/params.py b/dot_ring/ring_proof/params.py index 31693da..00becff 100644 --- a/dot_ring/ring_proof/params.py +++ b/dot_ring/ring_proof/params.py @@ -212,9 +212,6 @@ def __post_init__(self) -> None: object.__setattr__(self, "max_ring_size", max_supported) elif self.max_ring_size > max_supported: raise ValueError(f"max_ring_size {self.max_ring_size} exceeds supported size {max_supported}") - ensure_srs_size = getattr(self.pcs, "ensure_srs_size", None) - if ensure_srs_size is not None: - ensure_srs_size(self.required_srs_degree) @property def omega(self) -> int: diff --git a/dot_ring/ring_proof/pcs/kzg.py b/dot_ring/ring_proof/pcs/kzg.py index 56f9f9c..62748f2 100644 --- a/dot_ring/ring_proof/pcs/kzg.py +++ b/dot_ring/ring_proof/pcs/kzg.py @@ -158,6 +158,8 @@ def commit(cls, coeffs: CoeffVector) -> Any: Returns: G1Point: Commitment point """ + if len(coeffs) > 0: + cls.ensure_srs_size(len(coeffs) - 1) srs = cls.srs if len(coeffs) > len(srs.g1): raise ValueError("polynomial degree exceeds SRS size") diff --git a/tests/test_coverage/test_kzg.py b/tests/test_coverage/test_kzg.py index 35821c5..5e8802f 100644 --- a/tests/test_coverage/test_kzg.py +++ b/tests/test_coverage/test_kzg.py @@ -1,10 +1,7 @@ """Tests for KZG module to improve coverage.""" -import pytest - from dot_ring import blst from dot_ring.ring_proof.pcs.kzg import KZG, Opening -from dot_ring.ring_proof.pcs.srs import srs class TestKZGCommit: @@ -44,13 +41,17 @@ def test_commit_sparse_poly(self): assert commitment is not None - def test_commit_exceeds_srs_raises(self): - """Test that committing to too large polynomial raises.""" - # Create polynomial larger than SRS - coeffs = [1] * (len(srs.g1) + 100) + def test_commit_requests_srs_capacity(self, monkeypatch): + """Test that committing asks KZG to load enough SRS points.""" + requested = [] + + def ensure_srs_size(max_degree: int) -> None: + requested.append(max_degree) + + monkeypatch.setattr(KZG, "ensure_srs_size", ensure_srs_size) + KZG.commit([1, 2, 3]) - with pytest.raises(ValueError, match="polynomial degree exceeds SRS size"): - KZG.commit(coeffs) + assert requested == [2] class TestKZGOpen: From 0fda52d9b1273f6c51d2d3462aa5987e42d1bb5b Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 03:58:17 +0530 Subject: [PATCH 31/33] remove pcs wrappers --- dot_ring/ring_proof/pcs/utils.py | 12 ------ dot_ring/ring_proof/verify.py | 10 ++--- dot_ring/vrf/ring/batch_verifier.py | 12 +++--- dot_ring/vrf/ring/proof_builder.py | 11 +++--- dot_ring/vrf/ring/proof_payload.py | 17 ++++---- dot_ring/vrf/ring/root.py | 61 ++++++----------------------- 6 files changed, 35 insertions(+), 88 deletions(-) diff --git a/dot_ring/ring_proof/pcs/utils.py b/dot_ring/ring_proof/pcs/utils.py index a8f5dc8..1e28752 100644 --- a/dot_ring/ring_proof/pcs/utils.py +++ b/dot_ring/ring_proof/pcs/utils.py @@ -24,18 +24,6 @@ class LinearPcsVerification(NamedTuple): value: Scalar -def pcs_compress_g1(pcs: Any, point: Any) -> bytes: - return pcs.compress_g1(point) - - -def pcs_decompress_g1(pcs: Any, data: bytes) -> Any: - return pcs.decompress_g1(data) - - -def pcs_transcript_g1(pcs: Any, point: Any) -> Any: - return pcs.serialize_g1_uncompressed(point) - - def synthetic_div(poly: CoeffVector, x: Scalar, y: Scalar) -> CoeffVector: """Return q(X) such that f(X)−y = (X−x)·q(X). Checks remainder.""" q, rem = synthetic_div_with_eval(poly, x) diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index 194389a..07608b3 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -9,7 +9,6 @@ LinearPcsVerification, PcsVerification, g1_to_blst, - pcs_transcript_g1, ) from dot_ring.ring_proof.transcript.phases import ( derive_challenges_after_vk, @@ -417,10 +416,12 @@ def __init__( witness_commitments = ( transcript_witness_commitments if transcript_witness_commitments is not None - else [self._transcript_g1(cmt) for cmt in proof_fields.witness_commitments] + else [self.pcs.serialize_g1_uncompressed(cmt) for cmt in proof_fields.witness_commitments] ) # Add quotient and evaluations once we have their serialized form. - quotient_commitment = transcript_quotient_commitment if transcript_quotient_commitment is not None else self._transcript_g1(proof_fields.c_q) + quotient_commitment = ( + transcript_quotient_commitment if transcript_quotient_commitment is not None else self.pcs.serialize_g1_uncompressed(proof_fields.c_q) + ) if transcript_prefix is not None: self.t, self.alpha_list, self.zeta_p, self.V_list = derive_challenges_after_vk( self.t, @@ -453,9 +454,6 @@ def _domain_omega(self) -> int: raise ValueError(f"omega must be supplied for domain size {domain_size}") return pow(OMEGA_2048, 2048 // domain_size, self.prime) - def _transcript_g1(self, point: Any) -> Any: - return pcs_transcript_g1(self.pcs, point) - def contributions_to_constraints_eval_at_zeta(self) -> tuple[Any, Any, Any, Any, Any, Any, Any]: # Convert to Scalar for optimized arithmetic zeta = Scalar(self.zeta_p) diff --git a/dot_ring/vrf/ring/batch_verifier.py b/dot_ring/vrf/ring/batch_verifier.py index 2399ee3..2982018 100644 --- a/dot_ring/vrf/ring/batch_verifier.py +++ b/dot_ring/vrf/ring/batch_verifier.py @@ -6,7 +6,6 @@ from dot_ring.ring_proof.pcs.utils import ( LinearPcsVerification, g1_to_blst, - pcs_transcript_g1, ) from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript from dot_ring.ring_proof.verify import Verify, prepare_linear_pcs_verifications_fast @@ -160,7 +159,7 @@ def _proof_transcript_commitments(proof: RingVRF, params: Any) -> tuple[bytes, A proof.c_accy.commitment, proof.c_q.commitment, ) - transcript_commitments = tuple(pcs_transcript_g1(params.pcs, commitment) for commitment in commitments) + transcript_commitments = tuple(params.pcs.serialize_g1_uncompressed(commitment) for commitment in commitments) witness_commitments = b"".join(transcript_commitments[:4]) quotient_commitment = transcript_commitments[4] return witness_commitments, quotient_commitment @@ -172,10 +171,6 @@ def _proof_relation_points(proof: RingVRF, message: Any, params: Any) -> tuple[t return relation, result_plus_seed -def _verify_pcs_batch(pcs: Any, verifications: list[Any]) -> bool: - return bool(pcs.batch_verify_linear_preconverted(cast(list[LinearPcsVerification], verifications))) - - class RingBatchVerifier: def __init__(self, context: RingBatchContext | None = None) -> None: self.items: list[RingBatchItem] = [] @@ -274,6 +269,9 @@ def verify(self) -> bool: return False try: - return all(_verify_pcs_batch(pcs, verifications) for pcs, verifications in self.pcs_batches.values()) + return all( + pcs.batch_verify_linear_preconverted(cast(list[LinearPcsVerification], verifications)) + for pcs, verifications in self.pcs_batches.values() + ) except (AssertionError, TypeError, ValueError): return False diff --git a/dot_ring/vrf/ring/proof_builder.py b/dot_ring/vrf/ring/proof_builder.py index 66f8242..2437baf 100644 --- a/dot_ring/vrf/ring/proof_builder.py +++ b/dot_ring/vrf/ring/proof_builder.py @@ -6,7 +6,6 @@ from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder, require_commitment from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder -from dot_ring.ring_proof.pcs.utils import pcs_transcript_g1 from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft from dot_ring.ring_proof.polynomial.ops import poly_multiply, vect_add, vect_scalar_mul from dot_ring.ring_proof.proof.aggregation_poly import AggPoly @@ -75,7 +74,7 @@ def build(self) -> RingProofPayload: l_agg = LAggPoly( transcript, - pcs_transcript_g1(params.pcs, c_q), + params.pcs.serialize_g1_uncompressed(c_q), [ring_root.px, ring_root.py, ring_root.s], list(witness_columns), alpha, @@ -135,10 +134,10 @@ def _phase1_transcript( params = self.ring.params c_b, c_accx, c_accy, c_accip = witness_columns witness_commitments = [ - pcs_transcript_g1(params.pcs, require_commitment(c_b)), - pcs_transcript_g1(params.pcs, require_commitment(c_accip)), - pcs_transcript_g1(params.pcs, require_commitment(c_accx)), - pcs_transcript_g1(params.pcs, require_commitment(c_accy)), + params.pcs.serialize_g1_uncompressed(require_commitment(c_b)), + params.pcs.serialize_g1_uncompressed(require_commitment(c_accip)), + params.pcs.serialize_g1_uncompressed(require_commitment(c_accx)), + params.pcs.serialize_g1_uncompressed(require_commitment(c_accy)), ] transcript = ring_root.verifier_transcript_prefix(params, self.transcript_challenge).copy() return cast( diff --git a/dot_ring/vrf/ring/proof_payload.py b/dot_ring/vrf/ring/proof_payload.py index 84cbf84..5a4e7e4 100644 --- a/dot_ring/vrf/ring/proof_payload.py +++ b/dot_ring/vrf/ring/proof_payload.py @@ -8,7 +8,6 @@ from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.opening import Opening from dot_ring.ring_proof.pcs.protocol import G1Commitment -from dot_ring.ring_proof.pcs.utils import pcs_compress_g1, pcs_decompress_g1 RING_SCALAR_LEN = 32 @@ -75,10 +74,10 @@ def as_tuple( def to_bytes(self, params: RingProofParams) -> bytes: return b"".join( ( - pcs_compress_g1(params.pcs, require_commitment(self.c_b)), - pcs_compress_g1(params.pcs, require_commitment(self.c_accip)), - pcs_compress_g1(params.pcs, require_commitment(self.c_accx)), - pcs_compress_g1(params.pcs, require_commitment(self.c_accy)), + params.pcs.compress_g1(require_commitment(self.c_b)), + params.pcs.compress_g1(require_commitment(self.c_accip)), + params.pcs.compress_g1(require_commitment(self.c_accx)), + params.pcs.compress_g1(require_commitment(self.c_accy)), H.to_l_endian(self.px_zeta), H.to_l_endian(self.py_zeta), H.to_l_endian(self.s_zeta), @@ -86,10 +85,10 @@ def to_bytes(self, params: RingProofParams) -> bytes: H.to_l_endian(self.accip_zeta), H.to_l_endian(self.accx_zeta), H.to_l_endian(self.accy_zeta), - pcs_compress_g1(params.pcs, require_commitment(self.c_q)), + params.pcs.compress_g1(require_commitment(self.c_q)), H.to_l_endian(self.l_zeta_omega), - pcs_compress_g1(params.pcs, self.open_agg_zeta.proof), - pcs_compress_g1(params.pcs, self.open_l_zeta_omega.proof), + params.pcs.compress_g1(self.open_agg_zeta.proof), + params.pcs.compress_g1(self.open_l_zeta_omega.proof), ) ) @@ -128,7 +127,7 @@ def __init__(self, data: bytes, params: RingProofParams) -> None: def commitment(self) -> G1Commitment: commitment_size = self.params.pcs.commitment_size end = self.offset + commitment_size - commitment = pcs_decompress_g1(self.params.pcs, self.data[self.offset : end]) + commitment = self.params.pcs.decompress_g1(self.data[self.offset : end]) self.offset = end return commitment diff --git a/dot_ring/vrf/ring/root.py b/dot_ring/vrf/ring/root.py index c16bb75..20747ce 100644 --- a/dot_ring/vrf/ring/root.py +++ b/dot_ring/vrf/ring/root.py @@ -6,20 +6,10 @@ from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG -from dot_ring.ring_proof.pcs.utils import pcs_compress_g1, pcs_decompress_g1, pcs_transcript_g1 from .members import Ring -def _pcs_cache_token(pcs: Any) -> tuple[int, int, int | None]: - pcs_srs = getattr(pcs, "srs", None) - return ( - id(pcs_srs), - id(getattr(pcs_srs, "blst_g1_memory", None)), - getattr(pcs, "commitment_size", None), - ) - - # Global bounded cache: selector columns are fixed for a params/PCS tuple. @lru_cache(maxsize=16) def _selector_column_data( @@ -28,7 +18,6 @@ def _selector_column_data( omega: int, prime: int, pcs: Any, - _pcs_token: tuple[int, int, int | None], ) -> tuple[tuple[int, ...], tuple[int, ...], Any]: selector_vec = [1 if i < max_ring_size else 0 for i in range(domain_size)] selector_col = Column("s", selector_vec, size=domain_size) @@ -46,7 +35,6 @@ def _selector_column(params: RingProofParams) -> Column: params.omega, params.prime, params.pcs, - _pcs_cache_token(params.pcs), ) return Column("s", list(evals), list(coeffs), commitment=_copy_commitment(commitment), size=params.domain_size) @@ -55,33 +43,6 @@ def _copy_commitment(commitment: Any) -> Any: return commitment.dup() if hasattr(commitment, "dup") else commitment -def _decompressed_root_commitments( - data: bytes, - commitment_size: int, - pcs: Any, -) -> tuple[Any, Any, Any]: - expected = 3 * commitment_size - if len(data) != expected: - raise ValueError(f"invalid ring root length: ring root must be exactly {expected} bytes, got {len(data)}") - return ( - pcs_decompress_g1(pcs, data[0:commitment_size]), - pcs_decompress_g1(pcs, data[commitment_size : commitment_size * 2]), - pcs_decompress_g1(pcs, data[commitment_size * 2 : expected]), - ) - - -def _ring_selector(params: RingProofParams) -> tuple[int, ...]: - return tuple(1 if i < params.max_ring_size else 0 for i in range(params.domain_size)) - - -def _verifier_key_transcript_data(params: RingProofParams, commitments: list[Any]) -> dict[str, Any]: - return { - "g1": params.pcs.srs.g1_points[0], - "g2": H.altered_points(params.pcs.srs.g2_points), - "commitments": [pcs_transcript_g1(params.pcs, commitment) for commitment in commitments], - } - - @dataclass class RingRoot: px: Column @@ -117,7 +78,11 @@ def verifier_key(self, params: RingProofParams | None = None) -> tuple[list[Any] require_commitment(self.s), ] - verifier_key = _verifier_key_transcript_data(params, commitments) + verifier_key = { + "g1": params.pcs.srs.g1_points[0], + "g2": H.altered_points(params.pcs.srs.g2_points), + "commitments": [params.pcs.serialize_g1_uncompressed(commitment) for commitment in commitments], + } return commitments, verifier_key def verifier_transcript_prefix(self, params: RingProofParams | None = None, transcript_challenge: bytes | None = None): @@ -144,9 +109,9 @@ def to_bytes(self) -> bytes: if pcs is not None: return b"".join( ( - pcs_compress_g1(pcs, require_commitment(self.px)), - pcs_compress_g1(pcs, require_commitment(self.py)), - pcs_compress_g1(pcs, require_commitment(self.s)), + pcs.compress_g1(require_commitment(self.px)), + pcs.compress_g1(require_commitment(self.py)), + pcs.compress_g1(require_commitment(self.s)), ) ) for col in (self.px, self.py, self.s): @@ -170,10 +135,10 @@ def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "Ring raise ValueError(f"invalid ring root length: ring root must be exactly {expected} bytes, got {len(data)}") pcs = params.pcs if params is not None else KZG - px_commitment, py_commitment, s_commitment = _decompressed_root_commitments( - bytes(data), - commitment_size, - pcs, + px_commitment, py_commitment, s_commitment = ( + pcs.decompress_g1(data[0:commitment_size]), + pcs.decompress_g1(data[commitment_size : commitment_size * 2]), + pcs.decompress_g1(data[commitment_size * 2 : expected]), ) px = Column(name="px", evals=[], commitment=_copy_commitment(px_commitment)) @@ -189,6 +154,6 @@ def matches_ring(self, ring: Ring) -> bool: return ( tuple(self.px.evals[:domain_size]) == tuple(point[0] for point in ring.nm_points) and tuple(self.py.evals[:domain_size]) == tuple(point[1] for point in ring.nm_points) - and tuple(self.s.evals[:domain_size]) == _ring_selector(params) + and tuple(self.s.evals[:domain_size]) == tuple(1 if i < params.max_ring_size else 0 for i in range(domain_size)) ) return RingRoot.from_ring(ring, params).to_bytes() == self.to_bytes() From 11478d044bea0cadf59c876a637f518a09f3c57f Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 04:23:58 +0530 Subject: [PATCH 32/33] clean ring proof helpers --- dot_ring/__init__.py | 5 +- dot_ring/ring_proof/columns/columns.py | 9 +- dot_ring/ring_proof/columns/h_vec.json | 2050 ----------------- dot_ring/ring_proof/pcs/utils.py | 8 - dot_ring/ring_proof/polynomial/fft.py | 48 - .../ring_proof/polynomial/interpolation.py | 61 - dot_ring/ring_proof/polynomial/ops.py | 45 +- dot_ring/ring_proof/proof/__init__.py | 0 dot_ring/ring_proof/proof/aggregation_poly.py | 47 - .../ring_proof/proof/linearization_poly.py | 106 - dot_ring/ring_proof/proof/quotient_poly.py | 35 - dot_ring/vrf/pedersen/vrf.py | 5 - dot_ring/vrf/protocol.py | 34 - dot_ring/vrf/ring/__init__.py | 5 +- dot_ring/vrf/ring/context.py | 36 +- dot_ring/vrf/ring/members.py | 29 +- dot_ring/vrf/ring/proof_builder.py | 178 +- dot_ring/vrf/ring/vrf.py | 5 +- pyproject.toml | 8 +- setup.py | 1 - tests/test_ark_vrf.py | 6 +- tests/test_coverage/test_fft.py | 74 - tests/test_coverage/test_interpolation.py | 37 - tests/test_coverage/test_ops.py | 79 +- tests/test_coverage/test_pcs_utils.py | 51 +- tests/test_curve_ops/test_curve.py | 10 - tests/test_ring_vrf/test_audit_regressions.py | 6 +- 27 files changed, 218 insertions(+), 2760 deletions(-) delete mode 100644 dot_ring/ring_proof/columns/h_vec.json delete mode 100644 dot_ring/ring_proof/polynomial/interpolation.py delete mode 100644 dot_ring/ring_proof/proof/__init__.py delete mode 100644 dot_ring/ring_proof/proof/aggregation_poly.py delete mode 100644 dot_ring/ring_proof/proof/linearization_poly.py delete mode 100644 dot_ring/ring_proof/proof/quotient_poly.py delete mode 100644 dot_ring/vrf/protocol.py delete mode 100644 tests/test_coverage/test_interpolation.py diff --git a/dot_ring/__init__.py b/dot_ring/__init__.py index 4014d9f..3696c05 100644 --- a/dot_ring/__init__.py +++ b/dot_ring/__init__.py @@ -51,7 +51,7 @@ from dot_ring.keygen import secret_from_seed from dot_ring.vrf.ietf import ThinBatchVerifier, ThinVRF, TinyVRF from dot_ring.vrf.pedersen import PedersenBatchVerifier, PedersenVRF -from dot_ring.vrf.ring import Ring, RingBatchContext, RingBatchVerifier, RingContext, RingRoot, RingSetup, RingVerifierKeyBuilder, RingVRF +from dot_ring.vrf.ring import Ring, RingBatchContext, RingBatchVerifier, RingContext, RingRoot, RingRootBuilder, RingVRF # ============================================================================= # Convenience aliases @@ -80,9 +80,8 @@ "PedersenVRF", "PedersenBatchVerifier", "RingVRF", - "RingSetup", "RingContext", - "RingVerifierKeyBuilder", + "RingRootBuilder", "RingBatchContext", "RingBatchVerifier", # Primary curves diff --git a/dot_ring/ring_proof/columns/columns.py b/dot_ring/ring_proof/columns/columns.py index ea1dfd1..6814e0d 100644 --- a/dot_ring/ring_proof/columns/columns.py +++ b/dot_ring/ring_proof/columns/columns.py @@ -1,7 +1,5 @@ from __future__ import annotations -import json -import os import secrets from collections.abc import Sequence from dataclasses import dataclass @@ -12,10 +10,7 @@ from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment -from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft - -_H_VEC_DEFAULT = json.load(open(os.path.join(os.path.dirname(__file__), "h_vec.json"))) -_H_VEC_DEFAULT = [tuple(pt) for pt in _H_VEC_DEFAULT] +from dot_ring.ring_proof.polynomial.fft import inverse_fft Scalar = int @@ -52,7 +47,7 @@ def interpolate( if len(self.evals) > self.size: raise ValueError(f"{self.name} evals length {len(self.evals)} exceeds column size {self.size}") self.evals += [0] * (self.size - len(self.evals)) - self.coeffs = poly_interpolate_fft(self.evals, domain_omega, prime) + self.coeffs = inverse_fft(self.evals, domain_omega, prime) def commit(self, pcs: type[PCS] = KZG) -> None: if self.coeffs is None: diff --git a/dot_ring/ring_proof/columns/h_vec.json b/dot_ring/ring_proof/columns/h_vec.json deleted file mode 100644 index 659cd4a..0000000 --- a/dot_ring/ring_proof/columns/h_vec.json +++ /dev/null @@ -1,2050 +0,0 @@ -[ - [ - 6150229251051246713677296363717454238956877613358614224171740096471278798312, - 28442734166467795856797249030329035618871580593056783094884474814923353898473 - ], - [ - 45814679702293870727815102382322792973312871502164900445864672286879719929432, - 10366663060843840980443461644682509010070253730569921980532011475960063024720 - ], - [ - 2294410531712626963515511376415486344864096436787198521892438634300079773618, - 45666564743483398291416943638827203456632965427469970485691572684599423229115 - ], - [ - 37781602926202079691192485945240718165362563267301061306318602868108499053019, - 23249168975537931202603012898051222655856170608557373941830453761509361705456 - ], - [ - 48892308395444907517643935758101461306077215930777489400152866006755873864571, - 30440149211889700918365699960964616055855289660370355556369810786380814924480 - ], - [ - 46710821676261173954448865936270698450589949160201191084600493816687873437089, - 43712008156687655788569134346656742906891403883432923036907113676374102586740 - ], - [ - 33959725772791636740726307435248874262183452318829320287829368841435916907428, - 32354052685321206225714597606791493661748858534811367595014257717645002483752 - ], - [ - 20730388405121238205586056240321141157041688861516059904445021289388152534968, - 674484918737064324014917158516409868671510364133388239939257396058934255464 - ], - [ - 16131362598942923370742648627493404284158046398356455674532304006392699963784, - 41759051449636039640940435593943212438174603342641870174156918396979830436505 - ], - [ - 31746504740235994508019184983056639484113543027265130538444973301468014574675, - 40020661812774684076139962317628013115043125515842648412913638336815424480321 - ], - [ - 10658330210247741704130702787632269475186125280005649475741514069841203448788, - 51211381474299144263769069135814912006294003406761207076438553256538295596903 - ], - [ - 26989183194975812980653758758148556666249424346979585560447166136015804274887, - 7923042378685699684270772257473608797919241770366004858753557601395186893241 - ], - [ - 46758996075230581319927526207320462313576663594102872818260662759156533030988, - 37371854080171482430947161560713637722103700764036260827355446741680682234036 - ], - [ - 44874626644563352699637540076877456455144713010634584762260243866588545765484, - 42842156496943640371098049017669084660385349236625894444117457679793821442541 - ], - [ - 8806838455184927646453615075702846391580224825905117782424074962183379656298, - 27945304194937699485109991241774701361157019190115156700635684080365479287587 - ], - [ - 29269297219212512657594381637709413479744755537104394121270504656275445601940, - 14877828372697743222106278352333151062161845113732131810468481816097973895869 - ], - [ - 36119725529088264166244575792191399880808545923807049745952161066186918513432, - 3356938371532916992340782348445707582750090530195487048641685246149684602949 - ], - [ - 47956441946952512609582858549225606136138434780344535960442549086939056864346, - 11788531131703821868939578476236600121728787666626560125979554109641620571382 - ], - [ - 29734627287614769165475421834769833627407536280713880527417009384449131335937, - 22933354479875483386360275957381763134503770759478514930883475965221289701783 - ], - [ - 28633297236503994300558520694329305262346497957530556328310863263188364255764, - 14463309003168429193213155945298537967389316383287506303676067264584670207265 - ], - [ - 43892957263758742824076138014666106063564740361863902039768060734699183844008, - 43445809266335826884859249726025798571136893063222778068085778421826591572666 - ], - [ - 40700431972617538560825825225897544425188527227058705893401022950798293535580, - 17433618999532481791433440421299223180808333490554622532678879326163205439020 - ], - [ - 1829735289256150024015524984663402633980184263148653328509623654356693632010, - 29705539053276763006498859928557966784257635091372794433837610351252404048637 - ], - [ - 16519555038211821891048428367531020945970002685319187161337391824539163494665, - 42792603217478225249814946296909333586109458328301560948796437681225725564145 - ], - [ - 29458507028459097710326609312165154384891607409011939726155349515263584973910, - 30439909443480008512487428105120608006643664886637692090389245549325372445959 - ], - [ - 28304886311290122075884580105196187336888523044537561486877966257830272604696, - 12264658190120547025425833838110514254969572681172354754541292774285743400711 - ], - [ - 23789862256149602052372861235345568280135627502057100729964123692171016373757, - 30019542948952705941701399201839777301075089779524394630937077489393250240880 - ], - [ - 2922052213591219428632693799905217485976229995963664495977077909421809230003, - 19827213841167810175453980051966696053250869666488804997518739854239110223782 - ], - [ - 10596741362841192023038680916070419650275819793953998204421084140209023106983, - 1528672027793421672342522725644711534125532449297938273195875373406168037837 - ], - [ - 15219813822669087131842004509364833923708689152484606416800384742450268610961, - 36417948538030974764866593489505617693322037646104635842111620794883559789621 - ], - [ - 3252914891406338037978801401375010612214820486185395882364688219113913269871, - 2130787092367035235701742033734471894591232701148155680212160270550134193309 - ], - [ - 7520090189053207183355864216262748182500352492145974373206933059356409251699, - 38431220709059609216995581254319231004126317016479610911567967257507845383329 - ], - [ - 42539326922111038410403152777027856318094995251532032069004733401308542443653, - 28076541068096463421759332739555134429807388390711286025054098968027517491236 - ], - [ - 21803698571810285500519649899541592069517626995371597779234917850875058299923, - 11102957339239588974799779241686224597020822083572936180825282238797831901219 - ], - [ - 17275494439603989156504258222361458121148233911455315005661154501842599278871, - 40361784943924149045682202358788306148419731322942810178310948031434294550852 - ], - [ - 21225643298626850769858740467733930392404793827484553548330621727632451086095, - 21738084865369718463224006954034564072345397223792996331327611890275539498498 - ], - [ - 24018545575654202917731022495740505294269036298751059833226829118819251329745, - 39144754797567919632102765031145300468488339023318124611526887236947392788791 - ], - [ - 33385993996868839117550199976351028416224483587901338452469191877231019549442, - 31345970493855166377343023367212221789709258107728307575966256584773144136257 - ], - [ - 11310694447547312128460079717169731670270507857560701729044289269210774949557, - 7626931294890001328893010072014005937355876274277505414835286217814114319422 - ], - [ - 18799712896408196535144960975051026836401421622233298193485070040343740152704, - 8803194744617559355416855938346559338940958089517554654570085314778107280694 - ], - [ - 13848229875770287715305090392896501151241993173286266067749454497720957640141, - 6442804505944975765046669335220293556053214988784083811058901839412977533761 - ], - [ - 20056730840310577373213288901299144833711217375562732576567572124771048176471, - 38759895815513850322554657763737152214469269395042448010805672287683776034728 - ], - [ - 543389101338800467986407041745504512845108624860149345265020235124774760096, - 30570501700894356886357465446523542986353202137148757669158262010876000125531 - ], - [ - 33899761640097023104741572449614885193907107327784005956736405663431047458513, - 16361973609159696387493749885536345482338253014919350986374950015857537179647 - ], - [ - 17040641779548008623197716286420803861410630356848477794247372546494686292504, - 2883410000903933277120916093006922637162781178910895037345153483912803964135 - ], - [ - 33303021869822708655134332577085455666329909088744593371525067815890222019738, - 32463714243223035321306625068528492357138750641872642368426844834315903452398 - ], - [ - 23060374269993896145642081186176404819764125112949280627321245259593770857933, - 46716242298914006746039675051254962454663512272620134231516463188934134176839 - ], - [ - 49698191802806359441205817314830268015572568919576035617366120327707924654807, - 1279405484879452974555349969096987798231770400550308453650590065663474015571 - ], - [ - 35718356650698617460736706917896210633915182950925185366981579666188989542583, - 7618488849383902628124894953737181059463784982844492868485439949378308630300 - ], - [ - 7701988903327248716579757749415789697559248961457558154666457421521205264151, - 2753495646337966885246755702046090022197211284323236067267571154451847179369 - ], - [ - 37278606325993644503229414191736934181958938338596506912948018674864466265937, - 39782836613774037063754595222596822639838963467678837826666680253349081635459 - ], - [ - 38078293013396221016642101918298110220695716125859078766520376914336220233945, - 26502166328074690410572835819936327622522640771049436597057196556220863586074 - ], - [ - 23339086643788763906810934984862866279810423517043171005780992357486228465103, - 49699920170480361402755502249543146156115660174413018323428666777172762863861 - ], - [ - 38543488144414011571071647045682609009489673676137681390133226896160675021089, - 20137739018455549527293442508085234634168931166819561526095908775472635750977 - ], - [ - 31252898341334420346016265019737625274720144325727566696134137548415441287021, - 7215143971205706176002063809607948255638414001631308613700435108494980951582 - ], - [ - 51734464069787106431048496054031201483495706502041864649721627484772019217272, - 30342095935170059096625290751064810926671607866567284626926479907731412402006 - ], - [ - 40500904341045244300971433953643353744593032685703993709499135750698527218398, - 4823078754992642981161897537826153878117924763988924287209412312063279016672 - ], - [ - 10055395301802389406848163715577138107601196331181824739260577636014864929938, - 22339787765939089006733590023348469641873435663513782678283411661586673076833 - ], - [ - 24212820649587794786945052213132501612074421911684031864832935209259182607680, - 32243414713213691452648102966552483974596650036935118503567289119427981347324 - ], - [ - 25583577899705201176215380483525754954348869969894469303282422211206045647316, - 27221627606687798155434149837599266465152127253716319247630753333163586286918 - ], - [ - 29195764229821907037145926162114240080575896697864093370624223084968605101547, - 43881126380712825305464551044685541079273953239677148382709681153790832255882 - ], - [ - 19893206053308349237837892280761921382560041178750588327732436416129758025784, - 6638373224867007627519439073899652404635801474850554768632727265695804582006 - ], - [ - 5371448867817717734535244503671703981020834895370392886424585591057934926530, - 47864094065372762013342113196834825166233279705123866182323488218256789329023 - ], - [ - 7073360799715780664706509384774196743839728250147449456883204898499664230159, - 17058932592793807888911023203359124583101208999640222081083544187526760568668 - ], - [ - 10802953986417484121434677394342147254895147777906631859791729097304406802795, - 46169089165584240594301128237234710408388873473490329084353340478842789552005 - ], - [ - 27246726279330881962692704237550189392708787596326449387010360673329902336931, - 32871163470944158919551821783784119086156485557123262257201534293652150979131 - ], - [ - 2200497623761330403565079250590964605876723604348908809550501732846446790279, - 19457336826020947787938183467761402196271983482255164459673620956328480494038 - ], - [ - 1900882287611640313471408642393022088273780369908384521447282795851901824030, - 17637417602918751696175767223508514677264524237046183235904225465260404122441 - ], - [ - 20383833841029029135672695938236094250551596623576113754946483333771882946961, - 18543091098050738698035016927621077108475620158337356976592566163051488598371 - ], - [ - 37197953552604477525016196562468470289499075930820093665289293466020079208941, - 8146195605861039574415343150058994134278238302106217890158553443028817574130 - ], - [ - 13380526046449491894272183280513383350965989737995735590992420063635027147895, - 52417295150848872173620066994747422058856761988672290018406562886957719841875 - ], - [ - 51912959057093812819176382380164457692228588804893241227084681963495070320962, - 25964803975463112848489433005945576484343465399374941390642032588824858780137 - ], - [ - 50038171757275485494689707413418706130280660874729992507106442635127240010745, - 38562943933097885914521826968785199225791299001606390492800930968811648493024 - ], - [ - 7922347152010970304007474406651341342631461882601135980473617362451138880461, - 45429919026117806993699460135551335581848589775786170711475589209768193856127 - ], - [ - 48203274326187906799576844392258609827827555378853862866121500804354723106990, - 8894112497613783664720821774981439446057007141573799727338962239023891814844 - ], - [ - 51029248841351211997647466116704643653614946420065773320917275517742681662315, - 33527575442015890838666419392557192692852886773207393306941593706618211532994 - ], - [ - 27311921309420340839039329151118904815311393357863128273367664404164814178935, - 49513485813846536991880250595694194156626699815601688050245107224143182394350 - ], - [ - 20493203289769804638609148203341801869726827387981851894556811164642298326464, - 2553137786863798115193067611864961390875887891786789325243041518242077999062 - ], - [ - 573372895297618047902578909307540508116435874295038533913092360674260317213, - 21875784442106474381298545479235326321387468592946979921961991847823563527194 - ], - [ - 37115629595012366413237257684301545908894917030125766684524887402993922499122, - 6798882961669521074259320526498097410009225108945912286193064292388005811069 - ], - [ - 19339351340771520012939532066987327693822235499449588462963788763203274167916, - 14147737870451210092800576385403044106977050942370546950462415257522485855441 - ], - [ - 43634483216978253109865049587075547006546380566635208809789277417425189385529, - 9146389220523629660078013106770603510398191268980526481189913915134002824430 - ], - [ - 17845142656937603390092161920388064780418293537614715090177345647508743292085, - 10393801895072987292928649989074943704292856460374832677308143515656237051698 - ], - [ - 30019729098904348124811917943299876296288610236925007699717215679608196154714, - 4620148460919120507456818771483001283969221973854159788103699150922120137341 - ], - [ - 24033383490567082947953362980124945397683742515154727875202867397012964525374, - 32895752376668284957058971542364295144165489642708138790037842200805393566110 - ], - [ - 1503920349582570376960976609481670751384405627833819305212700276679966294485, - 35905331147410815367543643559507163817241807936373214694115254003488991945396 - ], - [ - 14545766476207854652565481315159953253340685939455215485197524092172129924568, - 37911011855995010308973690989727978261697194067851022342424845515896174830008 - ], - [ - 8474559048761204310271314005867011642221829651553671528983712787230237577189, - 36242782954997526388002581306856295601439888079141649525394334524261199544146 - ], - [ - 41006156730423161933656549080383338541953495516654355553392369415837711616820, - 31800888422537433751103763710585948985738142230661012526205494097238168113473 - ], - [ - 37081223036118661123085135517930834196698271531424991993642307210433116424535, - 43008327504757110947789059295303211761817203373447227275811620143186748685563 - ], - [ - 43650533591739014072368030367251417094587657527201718878386605102541543166662, - 50906649708611390118153555240916720856515663401454996147504027795495408919967 - ], - [ - 46204990233605032158399483270778374408772215491801113191108794839202300801991, - 9385343522051358817740423264194778053122088830501214839007081539799611773816 - ], - [ - 46311849378669846819196703895043259914223466002906063279122489566844839743085, - 31721719130227444016979792793913105293838291018594688048654685998447786880775 - ], - [ - 5321770769061056790337209583928841747646097724472184266958364320238419907605, - 2212738990213256508029960262514862353107699670672940203669358685910625662313 - ], - [ - 7472666993810744700359319376351848545402053341415282691495631674897124445638, - 46803815544979811193623472532515558256968417283099632985863289731843489855298 - ], - [ - 27339892361141095076757917889952001857497531006806014763288167074874256893947, - 5325071908996582051742690904547301567001928602290892164571346535818850868870 - ], - [ - 47136492147652941023165508337161824961560096576334960897934216744387917543055, - 29241324138688301168370431543815887032949571700785878180135693299092105691595 - ], - [ - 47135632411304847123614882477830273830750423924173700586656178655114517699086, - 48183733616665304157630803180272939315883020249125499547466498813734071344036 - ], - [ - 12305545563695375964603362056643542787423584905376138224311490390902943027059, - 16345005371486679665256627608536946829831746964708811815157119872291015546996 - ], - [ - 12067089633230201488777462779076693236456296890045945608312543561727272430525, - 44869777376922747077031421503952305900444040293851978848363130878477960218617 - ], - [ - 51760113656537860885269715833349908631840151277458051164160554701805462528570, - 28121347228161198845652659963501681249513830168918134611149512314454635337585 - ], - [ - 38011830652239924278437676620094549499345784851692420427943736423512192922165, - 36861243768181445484735335228542502070782404605773177433847453100850089986853 - ], - [ - 40413491697032497847501867518250828391615391250734288496128449699051812738654, - 33671400550079486523990584727805443161559284744912120774334670094029641596961 - ], - [ - 11101612934882409310956247267641887840109391756137081614477201449296956866624, - 37787361950555805357819709691207276163788361437993231285426705777139050398128 - ], - [ - 25048278573072926443676569915135441931599072180136504452823396526315422449723, - 27393125470503556590371373148916527398963171468747447595774493021392021782357 - ], - [ - 48282293980314225935995980631893493802621894863792879765603568989639981283526, - 47672842012063821365301381202423784898981246752306163850440889939787074270692 - ], - [ - 43306188464324572569892152685126050208629169911873351343341410949885807402280, - 29531739419304310347349719667111978902654350919005926550015410852631476802833 - ], - [ - 10327881054200440583752484543166914156508740737054117532958441239838089421544, - 46357733676158475249454272218258344576994192747009826514978374655193425056703 - ], - [ - 44794091184182092950885742579848914680079102514603698558704135001347397283529, - 44878017415247356854352223725493618214571761012708769845444553073525210128023 - ], - [ - 26656184309644764519911420403584869950261279057502908896840260259705060033492, - 31342123539109177242613093934404354373489516662532677395458206564281075045519 - ], - [ - 41641090088211596270590810091128475954006349592690171350104560191479593909607, - 34597775524107945008105029116831288778352058520152305426652965034879305977648 - ], - [ - 8597353254294096704881498212010426825373001626258274453369418123706810569279, - 33174837280406714271871054680666365568394399142968096656450686772015674207864 - ], - [ - 12201406835509855182578748146313861561796732942177576356310790085688353121325, - 26269163677547222105357445025262370538259947139011092290904139501729493650615 - ], - [ - 15900236317183547094519777126287999835019280329291454161847570890277954533121, - 16509449318299099873118680661994784504702641676085918594429161841909239689315 - ], - [ - 12909900500921486890097420362150237509220706309950401554755523068463313067636, - 48098839130302077134680138857577749428500385957363529362302761072966913358700 - ], - [ - 48991775332828423054687995026310888669771538663325442608961720895274300441184, - 44858300042743507039518379582257814823370548109285009083811707416417191261696 - ], - [ - 41480405647331819662768406099594022385297186019509225019009593298776339126043, - 44839197485956344014658170363398068400472242888564174721844624266431132165927 - ], - [ - 30750899133194482970116331331258821801439693632061148340153313895189289634192, - 35224153666089855847599181854265220578742575816692947822411329498067593703748 - ], - [ - 31272279186264841041046811823842400207137904472191873956821565242430618223256, - 21067802708452854743449194991490172658131509974698116180576673019258433863318 - ], - [ - 15983846702214840191329614462205432634397930344982357305471559298808357232460, - 38238153743661700798052716399459155290938117676316626555230133825555753950840 - ], - [ - 32676107344515129542055389651489135244453036600595275809233250868955625974987, - 24512827460447995715704560629168669646001596765007529669457185856811717079688 - ], - [ - 956081933427937956175585954447814483596778129328138353582048067661225586199, - 39265365352317739614384451149176817374496150755440131961189020362912767011766 - ], - [ - 42901984593832544401445056434006545547263838137182376457720122120485777563020, - 29306013376228479063273695715145131192034904532673200625017402537239522229434 - ], - [ - 25232100469321198697216934908367643007753427431282607649915103185579888442267, - 4594932774602146871992949604937795155043045651470118683907907828535013053501 - ], - [ - 32656196505002041343887090400422459463064075208224808621934341672967324756618, - 51479724205860392517912792008736396241035047225466509311724385343813595412903 - ], - [ - 13151799765421279292454657548138270148250687959923003902379708296305646016841, - 19501133679898304900017347563305608529364170124681075170522115012348329570830 - ], - [ - 30088540703836083629767218607949408735551185454739268710310165954346767143452, - 10495891470299162020324774486043365686174405207775674677875550148838220993150 - ], - [ - 35541767796347844722820374679746872886210730312760909649003676418432378843085, - 51819843439711516485244108659892523674586818671772690359375283870744219100465 - ], - [ - 13362050514546775637681980586398882209986731940219581788489478001358885093856, - 8797311382743385001558900756308956621028158215966512655200032236090180490818 - ], - [ - 18574479202872833761124352820798333067008239560233727042416608875130825971701, - 35052483683671184443747444273857803238126739487681494915060629095823981586091 - ], - [ - 20787978144831188819703034216046031907200153745188379806235872698093217821363, - 7703680691229620165551943513325152793630858068270958287174785125337828892716 - ], - [ - 43437291832417168958786079804534729738763181235566480677892904876192524917216, - 30911578673450177582902853865814238271429398361250264536880441678265104094329 - ], - [ - 551634174434622705647040519208511882540165156260749673132461746584212629166, - 15606542520758754456455570731514864509838257961740509358375084281340655482818 - ], - [ - 2806593466939723872191373123476325512803308674209195956090914391016988737314, - 51053526238852765304753858663271276801313878430783577290655690317975850046639 - ], - [ - 34932494705791208552366958213649291305953315010002394304464984334144503950770, - 21593671682613139693343135028459805700067328813887482611459815377315961843189 - ], - [ - 17453707719380907756750747321688945906854968333997977281734329533923125400081, - 48660903290355777459167306851945619775786637165788378651556436282710409198216 - ], - [ - 47076411656517200483559757734764655319475949979650718257117635882036260141646, - 10428459357051563215188603494176062285355342092056656651438069269944661611583 - ], - [ - 18365744872594248363162915164854043420037423433712772012247182537076089724121, - 33664374099011795645710751544990097354578213308460295666089243882030961801801 - ], - [ - 23578583706807514368764790721350287909402324132917974758776930945226329684936, - 19586218461055307195999382355620585370357258475895992416597845530473851464368 - ], - [ - 50897943840399781330461838592722019349951315236315610159935251120393235655187, - 5517854567172642959981450150653642805123562583236381922466781337867803102009 - ], - [ - 48862764368818460902536930356774297164494788393590806686929655850848327283581, - 198920641283129463584503567478726358056048238563096954264958541625047707789 - ], - [ - 15540395343161287405665320017600873419710329386263347821910536685904520700398, - 46882608792008415101296727737071489519255201362249453092956846051061762410729 - ], - [ - 47563406848037389745449537986684130761541169071644912920008694467759008345715, - 22596850619469802066144466578526594024669364805802153335586766670158439378612 - ], - [ - 37339183657815850471328632949405291178902470397984064479965193498910772956006, - 282295273353481149039815842313807288310640368313791687136858495934228907118 - ], - [ - 1580462839202246945147135366485726103765842092623141224104138208164820729444, - 4343277570643796988449623134101664709644782798352603504817546910354511027977 - ], - [ - 11170768031896332773302564009145423701734996730640617300033848635661832948697, - 40043969037450032039274417996518643065438232089232743271917523724435963121673 - ], - [ - 26856907783419251724429792286211414100435315499356823598953357780532810818173, - 38829885760781736371379595012159764606416026810013768142386879573504281188313 - ], - [ - 19878704852962068638158377352450289532968699582639729412672053994379113942075, - 38238022917503894099526399270029084575609975886854650220856125772854641636988 - ], - [ - 36261966811657051320737204758070330153337166127616984932773065152578342594413, - 35878977666583099594542481257208283233658801516422215544871503620133376752566 - ], - [ - 43229855614377553320661787898335022753112790158049464397696803505367128751559, - 407945287056026499253439560000191605290248179842278749559254632655217380007 - ], - [ - 18899009600540989776625164013445963966902435197939575395415771573460998290465, - 12433339169259449713933837842229651186466259204686439743293879689778019763986 - ], - [ - 29586285825540862458778263021756759736551758399510962341625303786381100615588, - 12730054546154870053608332867374653753972815520472795646936638369239193289926 - ], - [ - 40495136798146280653762565035002205822714440200126293174792096877349092898475, - 4968212436446081629558682505143079664054745392028825801370526824362277402890 - ], - [ - 16770778844514691265528477081178208543962484071014814557497952515593458386151, - 47675098879856756645630458954335274282874993701500914616486460934987516802187 - ], - [ - 16393423926799359357808820903075058689281450066551661427402650381506508567659, - 35262023327123688211532647109023518204836363012472273535847374858433589478107 - ], - [ - 35777791131104909183995831725450754722145349750989450609382921726035353786452, - 44033168132642711511427362900898261688382820159578048628897762724002077778377 - ], - [ - 47602292821956057994112714094788797434417985907394962291842700112591227290784, - 43682781577152499537621040276909388992249692893413843950672268787317991850376 - ], - [ - 2637949168884732682926893487349677527221911903036942132630778352641310727080, - 43254630424124981023292323022414165734988391163933164847072310574552640041973 - ], - [ - 13189567284441049411149292189188290527714706334048882244628069593820319894950, - 50996087934274233095667970520295262036423366727401511499087751091508426939639 - ], - [ - 12452758637809614154375030410109208650119338714931899671399926107340162646095, - 25510762495099069442990771444067074360287817858848828696967501902065212014107 - ], - [ - 44745844624422648587478397768415942515599769657505744595917379502361336218892, - 25104496832006876801315108989603766225354076864838032334821497347755969575961 - ], - [ - 21325449356256056081037738688680071892539448925748969091366708855706368204692, - 38214852009607279107959259048407596686770558255405647092252374856239095136664 - ], - [ - 42520517035512549507477339774011070340539549639618746812620233812473133101845, - 48560210321879526144272910299001381989351369388635280898003243515564578423916 - ], - [ - 36645806019910990230452523057215193437887829575108510801839966286549518299129, - 34433165563442456227373710766299136928185614744909298420066916061372835696623 - ], - [ - 29373623611492378944363701779896978522559647349484656328310146258323291127267, - 27188108474718048081459891868823036806406477439074253267637731031152069179835 - ], - [ - 12130239721996322624536103315037364495639580092557065767512515182831040545016, - 45493137345189350269741369703556337463711262454589898961968917037587991775025 - ], - [ - 38261506319147315213211431345381635729502127372874719767516606705864994559415, - 37625661942666348333748022520122045277636070877120278124649217415927457432461 - ], - [ - 44649178164485745773310285264083358970969995999515794475401311841189880861726, - 33188512923139715994780317623743132949667970992480421461554531677248323082345 - ], - [ - 27613166411502911965921840253754324891344599056022259234521210235106570273973, - 48606882746424112969952502226262175779362444670092825569358699497983810468581 - ], - [ - 41553536104627716500408567379578043924109162678180570694277610758176259417589, - 39794240611840882903731679023352262326566254300372562671407218830959803584392 - ], - [ - 28992161345724821410116466263594079628780720488174324185291881496955758513213, - 43967672372116360403349984370316047616526251880470571096360979194482390554350 - ], - [ - 43118143878282629532181263426009820929278369142797581456867837271656995626805, - 11256953897511995204267454005683618090185047634983654593322160763622453774041 - ], - [ - 32201282329614176029051640195043478577534827656755296368598883443562399028928, - 27142846313232474144722116724625872691626902317778132386523130531056301627235 - ], - [ - 20586094000461962548881608777027808918867234207218450449205693618999277021070, - 31366806236550025866299066848771298520689538593953017613511668955723037875322 - ], - [ - 51797770355865457174328203627968396870417770368711354837812955815522043223942, - 22604903231750974927369507178175122143915723926451355430918271640131834525020 - ], - [ - 49885147306147628033450104752630049958546641943705180806782519946994440701783, - 32188718880295064262803835619193670817807304955393320723169181571842363690657 - ], - [ - 39822526604597560658025033739669164818436204868104422953232375250884329297283, - 51787228950279955800325103710950891543071142224054900300921880558015732874653 - ], - [ - 22805260156517590146097387166512422753836424389975120407385973921633105187541, - 2582474107724752600772646554799214493255450816506611536706773755525400389989 - ], - [ - 47178178412450274706232791648429439401309421957407390227363510564133970874486, - 52363439908490003543271810186060192807197084234930474408922189290575012475994 - ], - [ - 4088007764776378710212038156704497077906930373538904436140110534492297655948, - 50479214750702105438101426744773584177606845491336668993432145009472730988249 - ], - [ - 17636674609369589892162330278879817950220779628456692925809273839857015536785, - 36040732138061038990789657020954136395731295180647264091377429244446035492015 - ], - [ - 8207419588679676650473690342594217167360076754680629207671937356916081509524, - 11039316039082445781580677810298674603992226495710586819268193926086054340423 - ], - [ - 10578337133230619226096380322520667046998639562500874194166563851058734871486, - 47317132682297254914449508822387584970838303112574987690915879413181666231562 - ], - [ - 13148577941593124761707188557993279913281542134162798167918744549599189222344, - 31742868610511272298076834739999056051507023410783736123579149001750352475452 - ], - [ - 1509693785690678497399536528686617177940434434000797764196109961792783464919, - 693432721443647797901391291031440624744448161651634226420088202859513065161 - ], - [ - 34865431669596079979790365570596434819310745709717069000447527142748890326771, - 41407654642670896521666313737574471046624042176268491526339009615171187170810 - ], - [ - 22662272326514617511146499102922298615569923233902551844317662988090766127769, - 47046609541276402857774301808495238169186579235400378709019778547967841299803 - ], - [ - 52225506507528832239526528697508132235524166982298583093222573265314823165546, - 41729671646123184733014511428955428884823534227839926110455472591880345518257 - ], - [ - 52226874135455184635039581602061202521485568118686035542165425883014531914009, - 9090234379575640995431620661188150751869360998617015206560285313258771691124 - ], - [ - 43534526985488093112891449357318073855466174924142991769633639292318651718508, - 9375042614863573306924313015939888589225935965956751575155268336684914057742 - ], - [ - 47885489832191608465757391205022082988606570533833136368644370985658993780861, - 30618406420289510771478323740846235702378775761852968478966701055891841137021 - ], - [ - 36539747886868870022547882514263570507219444205188157697119179290635370832486, - 1541220453273480710635670232345104494057954151916251825926320965130417795292 - ], - [ - 8103971695426296734829205733706422786242174945930083007302673681996216389100, - 18780793347830746802199658814064758763279095777650407174395039757185963506661 - ], - [ - 51192024854659905853370522749448508386981626405735730216103536542190625782034, - 4754118971498245163130346177897353052506680741486641336660127617485795538208 - ], - [ - 34270259883619532288718381906602317198835875622905190946616290315450484946601, - 22742139315487236072659056612750607866250802513604909205916216654939973274865 - ], - [ - 2509956601224468698818490537307571565759359759998165438143103879959859774333, - 23145502291747159601056279970506107137958267018320483409695023632829051653656 - ], - [ - 9621009472607533810864981754758525068473437816619567807287134298720330238628, - 20313772189562316679843075082142001527976281296024199484225992006281561371221 - ], - [ - 17161750145720739527973572417320723266004489976253330962671149280745103985781, - 25435251986557138807702989120755931884500438648293774151791801727734089210325 - ], - [ - 1034046624615978508825336064356365041527247561725262177976077127426216023076, - 41678120873765929523510088102332730266023109612033101250685051483707954506261 - ], - [ - 37441298527979686354538513517209597301529521156825216323509429810759057042025, - 26227554883860178269051679261099236844680750049710881203962049144180983004895 - ], - [ - 23100676652656583701921532404339124366926418899297369492527819143536933390667, - 29248759711904586543120439465492312309476687707575861444598903034657090880297 - ], - [ - 27667362267956113400539778981181625952166140771850294750281263873292716772520, - 30065709626209683581516052928200783002115487189258670441379291759696246890554 - ], - [ - 1730012072506199865808011812864777873025497353364340878272206302110637861742, - 6452459301849638284449484388600923133017945442610833864554357676337123063059 - ], - [ - 6987142702516449797550730713918590494513216827347128132465361024816152868594, - 6949187637272985471951912462589237277490404238729669025920387574207598302962 - ], - [ - 37424868723273497806015580979472300804286399551887998039601299565534352067551, - 23452981732485905241621993556848119414957309122766411172572591507743835530734 - ], - [ - 13645071886729443767846109161632222525766270853219907253119559347341481883256, - 49822648195473271759864876932597697234153600370805512606586187569342132605553 - ], - [ - 31824906445117667123405325130209065884034550048555125352762287575933792867956, - 50467847301152026589509718875272094560704615782399231331046179435108517501734 - ], - [ - 29838272852276932984233456018159674723002286641194831584074155918082287486121, - 39381490155839502874408839399823513216454459253407023359764646190362181121871 - ], - [ - 12149921469297688516231546458689801857415336542543945602124334550244141807560, - 23871831277920575581242590296120600859927334741636466901364922232106317294274 - ], - [ - 10477475954054620152227264519250965424084326606602290314390720030572475754174, - 39397421997778148033753679302213836673382408921805844350558612145563295373580 - ], - [ - 40917764845639872185976297511578221065623432766141564371500805912915161864149, - 42529263994827685089307533779858447955947951159660672397028024875509196869159 - ], - [ - 44978742877306827217275687943919321246038488770019601657606287301198408447964, - 38939676825049416369825992357154709420404149338139548855185669501936418313265 - ], - [ - 51072867167961355804059247652498143096033209733292409585430702478473631348641, - 12928868576137929228251776954470943402443184192092183930137289289571299189568 - ], - [ - 13652622782677452035153795954428286555643334620496936753128267446366630047102, - 48433999594282153834482313709452197450210967132533738236910861112332396488342 - ], - [ - 13268371280514530178724711345003354336341053735958468313667498393369709066041, - 36843524273008882843662978284517108832820955728020287646342954840528917783664 - ], - [ - 28716811674462102892771045501661786870492265339075727238022455001277712367694, - 27523476503030783279419359981230656363809516754859201170014366473242083148085 - ], - [ - 40082516957376434932496564858567939037274889816285959359759139804871658057612, - 50687159378159949702243168489853325059789577023164239569269916292904719362936 - ], - [ - 50530076105191133235656801775634650112549680698258309489956093548771798777384, - 1255132129993913969980540182447425614142277120690416165981818444348173585314 - ], - [ - 6243971043290184862921048871424885327502024054990536466794761236585313557090, - 15829784195894748035119163454274745570163036879027045509854754870336150834164 - ], - [ - 29530736080638665784673062268732880635323157727438884150477255931529091162764, - 18772686493863209644330410773863749138091929042732047182309971052202542123042 - ], - [ - 12140097860444075389998213930676915903571307456491056724124949469521651242101, - 45946957952452703547340555247924618743792586786284448278910832167819501059569 - ], - [ - 19949159654634537932514024708214660343772345875681021168628403191979839363946, - 5775039020996609078510411187513544118242939348795558254437011523567550542437 - ], - [ - 19855898831945035927676594977595089133003179820321666518165303345962301250883, - 23446067513928096424018454649123556780295285913719380970533045983070388258119 - ], - [ - 8031054363383169048354014581849015666804582571715397249939735020466475911410, - 47260449587588772010510840161443436136314872023234056195210151119940855177914 - ], - [ - 48786218385609600591528157851874802911591348728487689189352450470339615643615, - 47325974532091177739144048509151952970158411270996039628028831926064426509524 - ], - [ - 1414719632440624649206248011529605161042072365266979569541170542852744324999, - 1752526060920454352227715525276229181373213136882212910198844685114642164817 - ], - [ - 39714617752510599067196471754239988999860795692696243026650787677640315133389, - 30509423034888459157666700151897016478146153950125009326153417338856643920373 - ], - [ - 1300949251479927693755089447025592117621493756050637249510461584813960566067, - 40456991707603177701795470587351936446838806073179282320858652392042934573340 - ], - [ - 47164462400659569622114773407807378049705343331812917190546756367693814455852, - 49539753681460324820903285650713974716078285132036427952735379556984087582921 - ], - [ - 3480442779117845170160842792236947099341798085328248620958060495447619523716, - 21443879199716750044866209989830058613314526817659367659935572048999857530841 - ], - [ - 2673763073750222700491735205567287499760937998792374402343864103174142574295, - 8035885544958053382875029124120807341387436936901449667169648837921633542490 - ], - [ - 7138684992386411806800746742742110845674888472829310176368429481861698746999, - 36551790963213895561032933985679390999544863850904862137878305114067552067706 - ], - [ - 42393602503653045549308603142459493596795418107415372323074084123447643261018, - 17961073377515655301365734907211394248444702018095239634133970679786967733082 - ], - [ - 46719531350738172623708418273985693290274212864793251591423528768206504816421, - 9964558612845070382765653641734797566157808476217871715634543101983128357931 - ], - [ - 26840122014131376685123696715874200155634476692446017496280261714416910675698, - 50067150711959241584060570833010349782908807639820729516963576647513090311518 - ], - [ - 49497080158720342355298483260507025047485985330388634009560683701214409895492, - 51899343214636832074535830264407623299556368671009406215417297887270671869326 - ], - [ - 40761948410239012906424356518393728741879398298688524741935392687520099794066, - 20465392399280456751973113391149353344793192784327818533859020855454516641779 - ], - [ - 5745738518037962207573120465875081331442559571853421695649814706888319754739, - 46453532484135527074793330756930509815988968623816531800239308119275400684805 - ], - [ - 43616121611630366650220012023782065868185928035713879344538576576472912804761, - 34764700515985972766218946000862400162210039018418052302091401107126649468959 - ], - [ - 37471335903230831491842251269091159426682566673421750245335367142646353611352, - 9771620000383064982324100560527703962817576417454662164942065280694809127336 - ], - [ - 28015748650946293758882263423557035343689966232639815671890938032732269739483, - 49585422337140757800964646258369976479718469598126202611640574595233898904043 - ], - [ - 2181535661216170260993241120488813716108226191417156686945237322656711890511, - 20067688488033246973839817574218265743757007976662633098060000961408342815322 - ], - [ - 3883720999370818257500598680097563135380353459631384659904321567858174836482, - 32009939314326575910431792827754683145262935206172330556137541084141010754846 - ], - [ - 44709995120587785644379692654615658030667685872869567408590878534128413268218, - 7689619629332712340989625684958006953641249478840863504893932129403525922526 - ], - [ - 45353229266404481946003092689148983635123264312388143444765506897249342037571, - 3420240259646002325769973582280526409148200854040231151117293080756378590375 - ], - [ - 31258107529014463022910203595856354504888341581335200511023298622307833662857, - 23667268527507515694344480221008456010570658901804247352037453755882671880271 - ], - [ - 51228025645237797476739893785221530628682209446041144443111058121001543687943, - 49256881685401065793648168295919493127593254960801233030583684407667234780607 - ], - [ - 38148231296047916781791330357132269642586713472132507760897476969857109365134, - 10867414097610190798021258094848047529509356375514117799304453303933345081331 - ], - [ - 9797035235460900421829336184764510168998579919886840221094315149475790471137, - 34122006681799173047903903585004644539739364435479964621682003498403228223362 - ], - [ - 32592780117307417917096398246712988520175415939658971703975862196016736780929, - 43684337648095622504305389701607046708940085854875885161756386948159689215874 - ], - [ - 29235964893200802319492031401033765936305738656351774286506147762739440602614, - 9413393654087890171603378253943184027711480964459803754207912851151817638866 - ], - [ - 16881699788880679011227209678493398116320911340875886518398579521448538459705, - 22616584773542098445780088514875913347491986525979464825113689571530420634083 - ], - [ - 49601201589835441909995278885338331101895521028097202664424940043136661361868, - 31176655915244630484577122697337584112939403395532198225684134660665658131952 - ], - [ - 923178970117596303260097061813605356433239192023462917900813699396548555171, - 2430863175660824080514805662668306360877417819425242029457050739619503730850 - ], - [ - 17332518913991522899266122336441454437810760599720354962215638044545651411573, - 12238941613942481371435755592263669292483961321188157021143733652679743121089 - ], - [ - 50430907269417331144484650811290940118907418329655700161464732781052041609195, - 23334413799924608882789492924604934666581789664930131063289880095476566780192 - ], - [ - 14462314223358272339023261149111593843504042440995744575103026823842974856012, - 45076883452748077360772942000247219264343639237941167753991208836292121964354 - ], - [ - 19611443490469977048305538545868317906450660061135469767625450331960893535951, - 11743861373167454011615454077371380595327462397976332762544070257828620066917 - ], - [ - 47535180048337064453540832389667127753217349273284493380381163092992154587718, - 49880834122231829123824336101188015047518290748568391817345701960830271331520 - ], - [ - 8676559621810774202724512940476502801399752540219643098083405656993979779518, - 23746448434905870520249694004430245369402297285246199093386403821724406009840 - ], - [ - 41818816818117271196990053064173652626817035294769014709008838189211842190628, - 4557320306429607457554102890758677015607989736208766552649695287025961539849 - ], - [ - 47376154159298052417426931145002163292647696360124281124416921645581292468053, - 5450851522352165941969293896336072705269285811874215821849541541381914303825 - ], - [ - 4899495226861564203049759254801860018712756116055425446563595127791538158825, - 50425440911681271380964838105534474717823098530230298406387141804628287538345 - ], - [ - 5318642191981079466099132290547857177263881437675332188910551047122730172333, - 20128759536896966852185458867652062413017609013675641243771945825202600891554 - ], - [ - 9526660458232783641771381546269660878550302225369880102847877168105320933022, - 36457909156112283346670360223824454327690288119982588688974204581941474594385 - ], - [ - 19178943202861651420230489508312500114514672062763478960234930500588388784620, - 44503872313967186586741737918499744271578270941819330401672074654323474755433 - ], - [ - 38394666007960199265911596324464409734900468691702125107273339893931740119488, - 832793070110478573008654211160806122753061556833288842056991440567009415983 - ], - [ - 48083973462246293544146319842449582007838060536256242580079697826389079000056, - 39841124161656327323887685847870021486830264640789802735026320456601734770198 - ], - [ - 36030124439854216499498419061365718017444891800738125773018084273473200483637, - 8499180157608094595797339619820839717850728815476899176204437572658748175517 - ], - [ - 43257261203909494045538123085990346276387029798739064996117624304751260101687, - 22333543028625096579248765896516281522236653225872862948318884958351181010055 - ], - [ - 52039358364027682506715291857511059500443593773509766504993515914814208240347, - 22287208726459625884977289184889117941271033586899018545839490129266710652820 - ], - [ - 14988384082805548808039414387184664637985735308320407935669563611296304644651, - 34305166051852001119387348541055177278768725653010942852965189248032527548139 - ], - [ - 9873121390055765867027828784182845351647066967936582421382196127263668316927, - 9763252532139867737828270823494645278436735400344735016522737770512878414946 - ], - [ - 29289297586507087669894816249193359144110324896651410332354068271660316527468, - 5713280156984024628636490382062840721533064264350439580052093068804213366428 - ], - [ - 46912281793883941093835977434777671805989032660892112089138416839688657133618, - 29936776776984317264983448275861615105371670589391844202808368066171989548269 - ], - [ - 21529335550420085406333399275369833818996718282020099969671873397035102670413, - 51271313229588430767426971404908329575723717858160493606001428614180457783605 - ], - [ - 41823025398416046713826136437398868523058227079367256973049862809572172581673, - 33528743066465384049047888962120540763217797205441090839763759949280790749130 - ], - [ - 5798648999482079122445681581942317221053682170032028616554362251814183168281, - 17552675477192536393606980539590414734671262625727839636581314951657146084823 - ], - [ - 38869298754428500131415260100157004828932823432901470052539706757439380696931, - 12532883833959902434628972529208806062032673883603405916184002447036050663174 - ], - [ - 8486564839021856112714901229192675851313243442067503051608604216083490253314, - 42358563222531283521717909391151534003831692727308985492395234949477082000197 - ], - [ - 2445844573675035706312134085441476040247781194630977778734060267127100787841, - 4898344945518613535449841162403892777778538246337911761863362349074704559771 - ], - [ - 24445778208130079447661176103569341286114647327604758192924403816422539769297, - 16567415346259353693681889104660308644309317841562049021858922229311160808276 - ], - [ - 3963498795644769152736216971155362530994354163899794998010716114432815553467, - 27259474931041734099024641103622534394822511078788488665102610833581505325017 - ], - [ - 32913546143609358169191076425986005391477125696028048504797443930409599097878, - 32667624806042879339200612162046675007549952073607598904110537679102633438209 - ], - [ - 3005960504419706591549294851528691969637974354631679261159807617441976917136, - 24663260378517122981383921512190584488562316068229218382554735304856431750856 - ], - [ - 49533757065658911457165367097433890516893712396471892178559942056268218547265, - 4124854995759576443071361808282564092993704755047003909556422265553501455756 - ], - [ - 36873987844323152048696042612884834181454543157828437151430236984010318754717, - 20613770749256889680540768815443084025316644417874458360550704104371775369267 - ], - [ - 25716854258378865515712600117674753562147746943218046592269301979484206057096, - 29200302959288639277796119770915014851075349179474194342047225765336579052708 - ], - [ - 42911560815992809542187647398406526614655751338089810210811171485846767630340, - 52243648168734422103104146063960238055209337495084239762818300335456103145627 - ], - [ - 39081309997211710422590160365169998920843542197224487979752440434567242066218, - 51792352345666095746973385040061186858694568629669676388955801353318366278054 - ], - [ - 13417079138708551594883504647406003445317431853550618087243646523994252546329, - 45825395667480572846675273207390385054524422171210329124535321637160295305509 - ], - [ - 44412345321212871026454642671321767602491674216628155880799407611492930452431, - 26752578203291804925203752349164087269060794605061222095076808133517757654583 - ], - [ - 8975159844443205034284920174424221532518001869696294929711021579272114532153, - 29780107729477494243160427509396943389176997188695048961989043256367644657787 - ], - [ - 34435684342158284475283213374766015373786908259912434564240988300871866286006, - 18862244570687843434474189442824794857514997617106896539383047412580172558931 - ], - [ - 45332211537330250839801544401039681457134006746652527983039176821239728413361, - 5504916668630633545914202065187439541677305183349031670953733015802397155424 - ], - [ - 39391402705953349822036216553766768041698162534362956649814360085654540331654, - 44579435648552401981465855935759465948754961403432163406421043046109509243100 - ], - [ - 2391405785649090229285987915013369169470856471839783478446132389509588047800, - 52164969608997360040867076113068647667231816788492547214163428720207453588911 - ], - [ - 42491417368481378407203976749254355434245085737001380508577982803990262963431, - 40456506669311874699041414550758115549608516246550995889572446474807389036739 - ], - [ - 16878883369464088141232906520453889040210448602693072948258450421857308980033, - 19412720200729738074573802570725056274448999858527815449415511296267960527993 - ], - [ - 41855369849464903542322597101957727705462903339593763502220739959473838065036, - 20547491801250082134100934078325171699840717966280362560722561591436507995978 - ], - [ - 5256908769127062258064266008466007649854824448235795128522261147267806416562, - 19474966794780490100117472442798723247358965612398901225376470555614523193131 - ], - [ - 14234812243189163545860653956454191088659287899159410741320721115671440014946, - 18062436552144427214037867564684284595995515416997731235679271843879405032993 - ], - [ - 6665546155875621687655864082715914393470863005894065944836359319331016264337, - 30679151114304893996856722823760577072034540137281217907572374316239324334072 - ], - [ - 51169531245721073103966725961050917921313698693429129380801859537960664695334, - 45041480176176257532586629257481566947449308306772303319565838885304016201031 - ], - [ - 22557490567113484990786659366446560599951460869862618596071718559686227152545, - 25013510614627611847929851970546489001526694635795500928997008083926517343039 - ], - [ - 37510024698366909791512572258662791608773264998428749509756145309164918254246, - 21816126229762324577869002545473647279621035131983490547659430927899199961787 - ], - [ - 36159167776655876756618165273086060472969153982683268707740443513392794252395, - 48782511182070837728821542166456457723300989616872101688375293466461664440323 - ], - [ - 31677063669949123230542026557749692409865322643454471292506085091341656619469, - 51743120449752716018075109519026168810939393147074598510632814633678425474498 - ], - [ - 35679193974304218463556849193542880088870365245308732013809685147347466482739, - 25286645616175358565804704410336572711023239849470482873907383038326186590724 - ], - [ - 45470292783397293725403982268588098476918701718010510479468101042532804238887, - 40122787787845836682148256408101594080044684224961362923609668325020322814354 - ], - [ - 48968120966055636967998182663080061038110421029639661059744342697447582334433, - 46134822938929659553017742208270840228900294668287884029295574086662165558580 - ], - [ - 45263318566953546040723728380108680130789423328659111176783413197549227269398, - 2323112496579419287693219856642983529553390624335365499980400271518404120794 - ], - [ - 13098304292161283823517409049321515177067452533680203062241955543073211954008, - 36511858780525541056530188202551440465132365571283170366455891285904147630901 - ], - [ - 22545372880525631273553955944541447929730022131252123076818079111948658779661, - 19132146490073236509000793216956729295315618267435059226778254139457869050107 - ], - [ - 9759551573403632334058285131231452290926273215517125406013173421676460075360, - 32551930992732560443186307222150474011043258363698242304115673211092721046016 - ], - [ - 25535510109832372506110142998916046365804761617844286714625428907459087390525, - 12301505033201655569801115908427799123221698587660999993270810129146942296197 - ], - [ - 28912655025359194625502991405143614009045997110242668002500130684238889690659, - 21672213739387828477186990724820468116468148852032484541567729810212944922452 - ], - [ - 52162950387534396640435186313107935802328877170041713229303327895690887640057, - 51974880467521381594385227073986293413021304593079717269906121318685809516699 - ], - [ - 729162924492286293897058400000016270677291514278575782095778073676396246743, - 36152653275066299645209814566844347965278710907885207751438180530717971575323 - ], - [ - 39680176850558107181282626804569198042507255766032169307559628565830415464014, - 16624607354238150051267330294295688203249232547368943802767531946884541926622 - ], - [ - 45885028531524263758595975254624559773946162633767272782892201954439838399843, - 9310730833093208429090527099226157705061502872155879778661534589832712701290 - ], - [ - 43082410588921689320652450902903550042743570423367866145146890562567649179364, - 22085566014831019631569758960865551569620159650890464334501062104239058170692 - ], - [ - 20630229086238703620240010603250704940946722543964456109302697869579983245023, - 23403871199352963131788737788590599749872244418846419940373599408614731116190 - ], - [ - 1275454645378477241225856461631859347439683338213159712593221366022393003050, - 23940677024391352595442526016965987526838799598085418119023847315515341818992 - ], - [ - 174755997067861382565474100145774500727476852620619310974734391490370190454, - 5345683640762590147670963376091899517065256706256925831019720051833991800921 - ], - [ - 6714316361371682363512711108635182659044873544217042807172855187055287355357, - 43216121105963286245068319515102386769098183977059974577870346074092503893375 - ], - [ - 49287961735178532142670673176491029062120400308630117877154090040350032039820, - 39387218379614543254606855154096293123707161932650120165637243699450762172257 - ], - [ - 10760536170420356429585465620953119207333996655290035965079689736784007372420, - 29915395160133936825366894138279821513357629660886047440469855723732201363586 - ], - [ - 38260304077634719845992972380620804413918676665180958246080961043241646924069, - 22523609674437102276485994390156968398917591917857855197889608126949341524545 - ], - [ - 33623154513446903405765765842879358475866161202662480674580002096918978170625, - 15048654796799878802626753896148597894818959598390857595478391315489304546229 - ], - [ - 50685536086398151946343801587795673304118692810362326974765709894514796627753, - 36790357932918822582808572471138465677889652328434737113330303557207770999618 - ], - [ - 22381660927940252701141726691915942176112498825455028466350777336045885024192, - 4895595177197822199261066097335571811610253811884396552301820006760927391137 - ], - [ - 35718886019882818117127774003698693490327673753017445070814831757173214402897, - 16424540735016456636602194680010266762916599227438686816969427126326058222549 - ], - [ - 45001203760762459652464502164712502046801975496260650965909746298332550439501, - 11735500046128803092957382926476187305991082257962357309226174352086056391760 - ], - [ - 52245568278112476902284471779428184018181482351928884697240817096152731777607, - 27359172378882675474463685262913525086836224491407937482806757479225612119697 - ], - [ - 256501360496778141610332724211071337479553415010867786371205839181833497021, - 32955553660817568420570815495010846483287617458764265874954645299520121558089 - ], - [ - 1962273863224352856228117785015480157777199816825517623234100069107300126624, - 48815662628551923257563635879222776307685683639123908067470937032496264118348 - ], - [ - 41062116878603929979067843098541783009229180414728873557663891461585447486209, - 50948877574667132774124158698904134736085720277286574162607875886181610297369 - ], - [ - 583560613579289509837540598534143815278237178576482952531952107733357529194, - 3154975011577492793178315031258150346628242711996607164885653231753169158560 - ], - [ - 17330561320852301974993264578239774340263434740109115933027236429682660260714, - 28362332515676794238205044713197689978582658971715240256589629621486916584036 - ], - [ - 11783163081112992159026907238763105411935370447363282999465271590690527544536, - 17589633841001211233241858577848242926292655847232234113117654776244672277278 - ], - [ - 48279677634544620023514883631536542242146196743402989799328928967249378791317, - 11177378887979543253773524298143835956016263280322211517149728594114918739591 - ], - [ - 18812594166364512157703764992040843637676293785442453695875270362324872931845, - 23739594375064421056679974979026396887717528129498396083992430965944699110003 - ], - [ - 30493866339954454524480332572613433590332703768592049511360700958742048962867, - 4941519702987295048235260551311907025515595393387757456730723696871044804713 - ], - [ - 3159544095578996766034485247965247767620259611542535193272261898982127068533, - 46125301174661399491727557387669816347894215041888964141851899739669385822779 - ], - [ - 5557035577325541028982660863011399762015544709140779792219774195777109775979, - 10771105784123743922954818973062439664221729055577235693449247249358258104311 - ], - [ - 38586067195479813835088454725575267870941075024545394034072817001382438987828, - 49721309226478345564249764865106143545451235542711208742394567272315585470240 - ], - [ - 5238514231066079463343872487902150785323230681886691615847055753108431102840, - 50359460070734964699731355871155003203394648258774210207120216585022186061083 - ], - [ - 50343952226907929006219859611008369402656479523727183182074470979041744009037, - 24055988330020533465127518130890792273787046072672236992425419628320440779812 - ], - [ - 5802449549449568576077377208877034089042675227714719097142911538892451760358, - 37740563156329016105645175255232225049901020595495516771647755794163926362737 - ], - [ - 30425977125067613585649990969871266924636123154049523155664894092038091489560, - 26668037311368447722693191059144737454115524515061489494255632008385027617326 - ], - [ - 40538374993794486973003028184262576485686365881828795603988842911771897670598, - 29086957306896775832757539167712633146045391916778982164632181618784368469921 - ], - [ - 37737997489826814578919977767434182687390754062026485148795432944873332088075, - 18948494709854718372731495898748038467889470251968506833127933679644940343783 - ], - [ - 38211820961173071562435720760222341411006978338053507179856667165898835017287, - 51105155503419248181641487405578791161721047647120367316210222588552785039390 - ], - [ - 27216918134885422052205368115452896991408872779447465045866392503823219579457, - 36473174891049794422196105961100139310724339115316760968711241757274337059961 - ], - [ - 29415977699252153451376870287847356037828891679721752947755714099106142339903, - 27268394418136799700198781008060619966588609023753972192645892230627721228703 - ], - [ - 10768025600603915025281437680709737791214258686496254519238069890205153978924, - 2517321573278452508151807928740021819765746308104188290950247430417246042188 - ], - [ - 31697284418947441720317557101882810618054702395384070381292996957229710887041, - 34345306077710538168653042205703939972050338525348203149281342977008542135930 - ], - [ - 47785661311737527520665207847918258003654882800533237474089298976486122678354, - 35289024929911748098600785666637497868888675213257016159122603357108240998474 - ], - [ - 2382923594591359033572214534879117597594722627331837413235406449133857931276, - 890237093813487428676195585501226811023680786089816115696679272532852672773 - ], - [ - 17325956185477233973752410795150579836506578322370713751090177596312788046997, - 40358156929708837374656102692389258938231810672805543753520525132531749688396 - ], - [ - 12106159879876554556593128801528942679921999685228780568509041618883896223652, - 11367303345504196263716044581620764670683519827013773146582838314812513351352 - ], - [ - 51090722369417572285021895950435186526233371345848720449996432301479795533011, - 40070327258836427127869000490214176707182268822526601920580608392958833163806 - ], - [ - 40379221283971721246607842475081005184954503922097915992587609085734472924732, - 47562247622275671664233314789819304181812290148153578074552768348680076864675 - ], - [ - 44012959375652877869913363805534079957426556261807730679817657114438238667501, - 498793394479117673935320085827494004961351178028476890643669526098128282482 - ], - [ - 7488945461730474285004689573645157243789129559959916643801185476270896567943, - 2741314677382174659471692846909913912661062894357687363859250451984214085612 - ], - [ - 29147695108278361516926913589237464610008711915380153186092175614685594755862, - 12189313428877920482374343517866277047848462430787146488643656492595054148843 - ], - [ - 17322571167464748332139320297822173817617662940346895681154290072779313969994, - 44239822534543342426711429517317981998449839157754453373484497188202961019532 - ], - [ - 4498776468787507978328341941036679275513119217042172421387450334242021727735, - 47892243501064381239491769213216286449543646739400546449206510356979336213884 - ], - [ - 13261071672777728306384023707328834568659498803423255585106592124611286878077, - 43317155615304465265598961759212869865346178805214840695500801859589047001495 - ], - [ - 6279428152276214603654905246512714759191482510607956597723869673696121192764, - 36995119035753894909150045043447103042409570749648121423617382864661333985790 - ], - [ - 27304275566874946442972422099445942020467924072444650899792270316629026348220, - 39956235257342782785537249362056801918093503249916389298612355763676322054588 - ], - [ - 52240284069481027893013127070877861793205261393573257906417112587909101735608, - 13723070447391995682174232679200468542336470096098957708886820199919336899433 - ], - [ - 20062549386223642123770059116497192859993150535253851931545748014916869468985, - 20649390064178276673595018477521851496970745440269423844497730203942689896648 - ], - [ - 44564832263386436360123939807715191888606367152349423315184901258847286178796, - 41801120071365826945195054091232680199601998576733193669058031906854981987313 - ], - [ - 5375930039219374784565614144437972076333189111785042755760922921652932374902, - 31707554805032125081747934571996542514982319863644300767493173294812808344440 - ], - [ - 12621629856907907992291362717954491886578814355508959778615030562145616616999, - 49883780551119203976167487523928757449218119378533459708284930888715142996835 - ], - [ - 40064047662872521080406144557413152330865132879423284116483587751329566800678, - 38148038978200712199451075793591829201471700399986426242596004514911132960837 - ], - [ - 48778290201653299418051627567036048811797957690894580058021826823124977283731, - 33281596586784735753773681269428898505821183971334120797266848377841706033269 - ], - [ - 49872067329364175039761199974710513043099803864203559155486740886338044486697, - 35395336102492867296688955520503268970448616687008476001262409926052383071529 - ], - [ - 18244224710003547111624201696520654612281708358724434479148792273818913389835, - 21600312539422455822184496421202012505760969371884556732520547957363152545019 - ], - [ - 17754468880568823331798431106121657021179070075055997396892124029858917971360, - 7208991280370893687057391096173365694737910238094634389849358923742596065213 - ], - [ - 14225483644866927620075915993378816674803533871184862784639089875290189517501, - 21132031982490821283110674881776577145775695568030623540737643422951471942398 - ], - [ - 19499185649977680250115831544981287179239505291049451279940868989167897492616, - 22328363803616268984019721053837035050694535743416674118251002340802140796681 - ], - [ - 12361665559416937552887925828980145683945343748345786176303727015460675755950, - 28881133635497233951452283654713911627613106719382103859759238183158945994862 - ], - [ - 34729066716145058622421491923894261030918588018758972635106955517855131957624, - 35315561547234519289694729816401196390327960433746010438731274237405202940092 - ], - [ - 1266341989944894439647011300615432964104234844850382916460571355944293253081, - 5330843651339201261374138330694964797256076111478369225375893294465980548413 - ], - [ - 52255708801497927685389435395498974627835471177192265101329523580498266345914, - 33273937278364327425839758236764023260273916857267805620094090952654569803492 - ], - [ - 33580607704967304950948509277085611755724673826916361615029630334374369155256, - 36111023057051143008299587222893444419873998251016088002640022784082490136611 - ], - [ - 46913178239456299566473144817775756327455213071412305778159657523913613719875, - 14357668875359810337395764160712385624874478358271018841147931900517653495993 - ], - [ - 28081670707710526253057006079555241494954064976951922269783884479312001390375, - 16135814273275834334834700622735166408480284998190443638358118499860970237118 - ], - [ - 19216703729601383154213553584593230172539619714521015569892105493756318491384, - 885599276696526854153896813260772291559055321941944317024765689497642233783 - ], - [ - 41065334421659840042333057313661565592825526952350745297476281449232420272767, - 38894304072184519326015377289855030080402092238708223434735171005693673643379 - ], - [ - 32481233952804054255474369696586433228894400381678021051950055977705303645877, - 49815585335900427612826214428934457336560015480520660340929063230638149812854 - ], - [ - 21471492466012302618106478921297357653370082659181333459104945949945351934766, - 47362773217071736919170705667879549548626400012167550974071849882885350479729 - ], - [ - 16905187166792003319018175200459096412984309982791912714113862907505352248378, - 25818780877132770709062721361663492964467853608437245591877630437526608379408 - ], - [ - 4866847375140919221714485776579840511850604546852728450567710632907237238062, - 35989125484817071074837798442292028712360161827319803300366916272892392364677 - ], - [ - 41885789412912246759036967039834425480464661058191266933422348257919909104273, - 49038072875741096525055091703194932965771605100829157172688380876468219030788 - ], - [ - 26907935779806576975506633815342736468309835934543128784669980176764450533267, - 23987730164755246923612859670600816173785525132519190708949578990382511514197 - ], - [ - 6831447143942281051496595311332252016332724327867349730697067039830501858139, - 51826016799217973652004205612056922220210721495424221091658351148480240847073 - ], - [ - 9636542963076669981950598037386805772123467633606545205424509424495842866201, - 12253490471102825015734944923046381571541924579941995451486085587503109795767 - ], - [ - 48829525853167410315735319463993582791068978678881440215007998517433150421561, - 28768719733502936439526092012773670526196038954176759873733994345317833918616 - ], - [ - 2022633466287960908924195083699690294518045625577005242642080486704628729977, - 30889988327433864569277308269926930598199767184548256216768605029949534211383 - ], - [ - 1761159059923454796317052352322378188304869992661104011337765331458470240673, - 12558673498273135505307713116382769591472420000077227670556546914483895143445 - ], - [ - 10589187028791274117900185895888248324632189617736379781221346121020016637007, - 40109191869989069768830553779943185599919345312356796256625722207899348138332 - ], - [ - 43060515425979754372633625117506528272433355976220724977448096883997164870241, - 31412803598319718827786808913676122226042439614367970988273858797319523587189 - ], - [ - 49942788967357881323355123694104526799377017403986036042066525937126325556215, - 15731403138443356033479447526857019257361957076191351003404943349543947717123 - ], - [ - 3800291203381004832379708043168255045495672311040315672836018655156824206215, - 9963007914047486808793938277869672565955811258159965343299482413006042558117 - ], - [ - 6923364322230299947489580502995087530173503469296348822752289049428508585354, - 2323105435266906988575416310589570020806573861486089348733957234268536122506 - ], - [ - 33254743181661676694858281780902381351272106866254319369641160883693267130162, - 31856055254671438903862011817067158764023003707195159191591747698854756257785 - ], - [ - 14733555028435031551213088935091069122126208844236317148792971057291050345967, - 14452203018700162350000157808227745454717884732665647998226621970365621420478 - ], - [ - 2596914137988943652339524031581250112111412826413558327702577019072847459448, - 6879529072458293233713809524341686480122841677130524878244899435549891863814 - ], - [ - 6457499269662033131290802774428094742793121814870744292251721216885069250601, - 15519309269940001378098018793074928495310478980047349313513595957439902281669 - ], - [ - 10279366883814626015623311521804083436993382756102671695162063917709431953206, - 41155567855997032749442229579221455310503577663486430465812748044075822946835 - ], - [ - 37312315224383617941133429306319214873462362567866478743171752516485328848886, - 39145247650743452971976915565938143194389185105273418465527479915461095049662 - ], - [ - 46549647374514858667572903822552357739852650623886450408104505897643835534662, - 4287046106856394503437695569466626574657507057739401637444795773215869610725 - ], - [ - 27343806747310897259849753138003304539612411021881923812293161553509418527201, - 4493794313564223016503875067750876693300440885088005937856068542995095346262 - ], - [ - 652786128127922861848137645492803332523553197504043413970000269592514871932, - 48810802522665551253306643275030026185187697146888383278844718959783760314799 - ], - [ - 18720008310776774373294011903520047435395655812914940264023768432316287974711, - 41949388766905763964666127505715602237922876050617696191071978917233797708150 - ], - [ - 32959386502717961732520957797556901505924197920405194847609344672872884232808, - 28338484942559233515930163715466082209141830013674260215235267608780385663520 - ], - [ - 40737494445261586419652469419584082010580084280825399278721601328844833659468, - 40108898987200436149555570524769933604380532778049266051129862083940933652164 - ], - [ - 39298870046604717667076419797721289153114306806531229634690626412426925647318, - 21316610514963074346205694088422818778914873909151805780000299145894474657420 - ], - [ - 7422177466884881171945790758212064331338619331983862330345758621541904848085, - 19017385594632621700747070561245181498634625190454814828544545886244302005437 - ], - [ - 7771660308119401795384712135977626463273345481417518547188372838365305990527, - 40040774208045067318886331522427980844598882618911658901686104151069444168029 - ], - [ - 37888564244331208120361075213801549417664931586661648904501244368786292221995, - 44788663077296610607402333257505143896661961249349165959602983306580475341285 - ], - [ - 50219903907139549086028769253379853869116250372039479531087920118632789408401, - 3071737740850612327206792485280635601942190142903920664313258242572420217858 - ], - [ - 33405403170704933630479946685446487245351272746972398920874614614700511349508, - 816595846207631333042825450509253157116381890498837130153201731370187322393 - ], - [ - 45288566746930701674896279671129557290772331006058228975462397752016471425018, - 48006677259601545069091510530345027331694526990769182028311977412284202297638 - ], - [ - 1783739363215166678876715916350757682201936469131964822905549050755409711674, - 2191272520847210156197878773506058116207889864282894254379514830070496466120 - ], - [ - 47688580684464201771026015902810240083604379770141017265268531644354795864848, - 28444120433085978803409917694156111279263282674338731995810025113235065601833 - ], - [ - 4249487631457354065053532543879391688139228240247489970339485150870948722713, - 20850917171937936407695165470134131960416757360174986917806263196489282779942 - ], - [ - 8233264067851627566313825183350093196138836747043252922047419065763244213157, - 42668409211345495161800253481893933320174451570002454854213715117964315382243 - ], - [ - 7723560569885608316358505087876887121710020709015199755360509835574184995161, - 28214673025964173539560997110635426153284359077893609547633286861216483539335 - ], - [ - 272030899859916858299225505943699528305276836365854496761075471516709044945, - 33324531882569136731993514385126118439317823676231806518404446726547580934364 - ], - [ - 45521368996824784187856680878212620992802138142262182735298338675493781756193, - 6311706266422240676427426918287044670406762025329741096407398107191977524325 - ], - [ - 18054885290963734110934787791327934717135273441343714395055709212733779831337, - 28207206975490397154691337289190574978904451953346879548512097755217798227555 - ], - [ - 17684688590636159820052505157093761464277594869517924488394315222852489984306, - 21668353152311786767564072429177836590813577798197409095598970142277356518313 - ], - [ - 44057649140698645968025414489766868809412750468642735996389838122372372884738, - 39772757680286158159973103583551717961200771773360725190673009190578535742020 - ], - [ - 671092167627621177640288056538706082852984989427398663566938406219656662578, - 37467908527370037096050987596919781663833867350827110501343702205070994267419 - ], - [ - 10486987844020825503139565195397905622762687471138565627413086807128758762994, - 26301722959551980772971027071200562701856960031513935992990669133057597798223 - ], - [ - 48375619897004038834704329475532871118920998562920324846865230786812383450389, - 40391620793124781475106947091896948399631072189625360842662456156078952920308 - ], - [ - 41724153911973286519825410408716616124268856759459935196985303410744804518737, - 44235711540510199733197823305322614901659690155804800696508626573317276720506 - ], - [ - 32686364769721410554768021743017987676154818057123157731586339586841722669259, - 4395500979665069111561350506284650547333188703796818791097088168560178469802 - ], - [ - 47398759358459438783625052173928370866728998304164200785387523557260061085759, - 565170496107833690227674926719270749721552744519549828280550395156453574070 - ], - [ - 40171141386340382207338666942397686385492978952379491685302030099625315942992, - 33803599082942692639978183750617238937206473217113899719257770664444575437460 - ], - [ - 38931211485697168377733353975775135726691708560364839189002977971417223868848, - 15699601086878158565635469141383152087045595540777623733440536257337723167799 - ], - [ - 28285718300387490567782432257361408451633791864082769859046587850007337417724, - 25771763231744226746034067302570618464058375225325437640885829865814560181102 - ], - [ - 44827668977286916093578170209493456588530849447221901202339527882238798125360, - 25276060178943264709421445467287881982519271158387552542263486145655837870127 - ], - [ - 39202382554943192978627594247256492365615364420448640455239699741107181063174, - 11717779461961394466883500616188167546339200920018946992090493265900851183808 - ], - [ - 49016169894514363758687132974204046985744006624493086134914382683419942385258, - 47997267139441778000553106684317181629488180310935495569833366093554519487594 - ], - [ - 40653756614548852682158068454697838241995508214610631110648582333756282337237, - 3865842576863461778507600119001492204621902834683569679252376494635758103315 - ], - [ - 2828725703770402781309740526791527872345713834026794086406579509092865618794, - 32776162900745795209793233176610672853596796573984838901574121483026955841174 - ], - [ - 10893711428454652549065265439824795613923453694544509646654732566651106966324, - 19785047828419457547649771013390638720504396319242117988747057670384140034572 - ], - [ - 37595939471957405194317136494953325116415212802745042596796901602326052293086, - 485116815810007717801306930825243530753624698513333851285860840614565628872 - ], - [ - 46724542393496419815920113091215936778914247168708515143292121008627230753618, - 953265519521236462848943995716152827969286533017219008773161827400059825977 - ], - [ - 6861771253654748088374080681532739151284744079804520159074180421664261274165, - 31430599891140194167350339210160649561890135905633945386618460628605384261491 - ], - [ - 38149457109946884774232436790032722175168462816016397881463402235882912780627, - 39739405078470484774812637258990950845336751629836290761302431168342599348600 - ], - [ - 13205555696669837985897470182698835642770037179157268147507288301734915851425, - 31132788347346513107745949627899956943539902841258090965619518650859845658233 - ], - [ - 2859128193181938549160498452759762945123044754642163372973562880417900733615, - 48139215270814225455648608660518288728088738164330370069262613010932323341859 - ], - [ - 35685141092904807578798276323942544426823914715583259779618578691774102408938, - 49136538824750643401689765664663017886653471468181731143088232719048031402930 - ], - [ - 26230193140767883997146105608144658044482500888396546529667739169416666862354, - 14674417076434243908975346680368853178166526646006656724167970863711606504769 - ], - [ - 33803752902183345068354244970735064710170125161545238359922194271888969469325, - 4660841738925255725048176545110793430545660900092344354395317934462169605381 - ], - [ - 26600237535024013076863728215742497351867675426706174133539813088638586548278, - 18761237761580952347861063754357636179885324080986417040586703481789596179800 - ], - [ - 50925196521786823246628248312411076579481993733877671530342191006483094609605, - 35336707128142829113192802756931531043509357631632360196971121723060388373774 - ], - [ - 19772303818452937344607701044901585800892248589797906520064387326999388531799, - 45127825778019753738353961632681012067782069903137831874589910180174724920476 - ], - [ - 12683216767010200695966060897431991059062254570267228541343874102165941721585, - 43960576730626378332589785586053599017129955277672752483250251426324528589979 - ], - [ - 5645264488370671728192933030079039003199328527537872619585257159877977176256, - 9372903118670367818439262146126170159198374083716445347860218967052597967775 - ], - [ - 37061498949605113412241734639599208775603535124572250850160484224479418364049, - 7654520446775148027250854872108035622778919704988873002182381054669925634573 - ], - [ - 9713500626572654802224066370442409389256977092329666834990224604320178493096, - 19170314235870806585234037428261333538568289129949316181424080663180499523161 - ], - [ - 36446340732008874544457392468715664463850636607057491658917826218187700687487, - 6306633548024826508632452457908794225287184510814017776979588972235642422841 - ], - [ - 32109796515922621785246710900366092604218142684188512797849996640251905602217, - 32407331812981165871228870670801887224331772542078369573471758457453424792253 - ], - [ - 15992145132263351742385727324899580108973002191452454007315134013091490803003, - 19800934282994001007986367069357609397232920713140915785500446660831536547175 - ], - [ - 3809174032731211293338099811470009988565826972803155964405853686431922062282, - 29676262844905998759226996244423828173408102054650470720068422010319069461244 - ], - [ - 15894280777368266269264018134137975642631905925524704767801821156620399591917, - 42688030053026527633000911512783277513233838252991146415951953506579037933406 - ], - [ - 49788690822910963237619340985604165330975316828723906324361748658435865938481, - 9160457317619847468199279494729182355814931271749334957035465477468683017549 - ], - [ - 20961105066073023833062106702090156152443713695562256662770308844303588216673, - 43097043987820854643170782194128204525944134671085372191992451182531402750257 - ], - [ - 28687724744387177831442766738649755602928107594401022985392669142474950654665, - 22133182809521799085614542511063630896998665720068682457190445244146333901220 - ], - [ - 13024317261584557227426620794639410859234550621839920088861201168605735730323, - 6350787232314156254479253312735616511940701645844577708341471052910784160981 - ], - [ - 49096279514173868679589285560147843475499866533654697837826344096070689345202, - 36586109655823474277374633516142679054624697469511406094099349731931982857719 - ], - [ - 16109572642794235899349760158444930716859921456709304102542788247524508983230, - 27917078860007234283625638873778847515595506035754398349241480042045223929228 - ], - [ - 35892827564323741461620453657862736540069353441140975858005543846489133202357, - 10078472306834683392839770798623104644572228605242359500607164801052552531457 - ], - [ - 18484978253994335400900391204872706929356752108863355797363450176397717295241, - 16376561672960785273107532926246325808298275538391382138095048736370155644579 - ], - [ - 22319153093923364050592567170301282028901825668308835584635645640062967977586, - 43094637986305093220370800433533772538842907423031976535948354136461830310757 - ], - [ - 21264659310846003937809382185372595844153519913840346005559899008780267377432, - 17899544417444782836006019498342349953205919546101994365716213753260954147867 - ], - [ - 20480360925288054246631999255379210531262038885357492171206037664287821189773, - 5929056425061841048000631056120058279018678311487023069423389586302854767202 - ], - [ - 30604906270940088793292040322458029331197003834904904220304009700983650794578, - 26725638327252622552656848820932733174318905904975062242546351947964618337640 - ], - [ - 28226173461390820967392783115004374755548666828037949712430744395903084863460, - 44496494213319021932409699125517420794005741060448987680453140177876985139771 - ], - [ - 47834881548573633565109238679225199808198256939096900792769713673453073268613, - 26138367309127462507576424096618831842156015914362411731377872050630096690457 - ], - [ - 16955201362337304847889113553652325098025611790543604971436851667500076282662, - 32545377464946854132213485433928297418595860481809440221440553775301152240135 - ], - [ - 22462704943327682912223776666081413986385195446440640832545252439970415013682, - 10031161310582706143645363854562178613445161971357889041561192212863635508732 - ], - [ - 33383909240025264863664021575181953806761989002774939137394733594084888857824, - 36575337965034156263300354094905517478254783877339325594742240125644981305692 - ], - [ - 13755501701228969197731145842077529780333810789476732860302035388829622416518, - 42296820875017386362744532513654998019727189977972353577791054289473399565622 - ], - [ - 34928013385538043351898336745093534719556260681129244134142503970540636326506, - 40840019202675653496611280040525143614540293077842284370612524984883474682019 - ], - [ - 34752771093962569919222635342171520170340330638593723545238994087381089500261, - 46818709375956021166366899458822038620931558256058644323187206268105097388143 - ], - [ - 36802566677011098432081216082554099483307961475735711000664943873037846154959, - 11228865490270986103805988346731586582579340256086372012404332744630841835401 - ], - [ - 40019120925672383230677563627552939058860578846108880598785608045150348275308, - 52005997658592615363941506568104488602533715755691769407794447155314866098375 - ], - [ - 9516005105055759340963775740083721794182919934950959900283156977657762024834, - 141284052120481692119895002065919057488055095381084320124167840926936869522 - ], - [ - 44383548976867049644710404060358290680759423631182407432725187142538866197217, - 16570079550683759053210944706241451617300182295430047582262703672791704500968 - ], - [ - 21196782632057193149632059028229465255223068724003245576635927171413465502464, - 36282777307132948872743922260498231676835301116732766105372996088395910856816 - ], - [ - 22533195959015067496144489372584819356860684237558141683715617539239990243033, - 10150985522275370954590825658288299106786199137668257962602381048008075733626 - ], - [ - 23527446030167385183672301288880458514670867290260020861386751837170703826185, - 42162791829135875524009775826654539406873380314920591006221571344884646367290 - ], - [ - 25363725502715937501794488413007101573901206975393627613038124076802469757935, - 34386153019049692323479786933752830263961515247985952890350696982953684116934 - ], - [ - 28836317605438118050738530576696059115077048618020093897063793299583531192434, - 21476093676064967192612682845829299579687632947506435807215044660301428458658 - ], - [ - 19470043182075152912623059712793161159955944049312895288312656765153082764587, - 17205127350167283325057362120918162632747050544457575690535033868332442386210 - ], - [ - 39225168571176616096422189736775670197531168133093019852257146162907908834098, - 38318823754889551400763216021445709766753868569820104963352054931972120961244 - ], - [ - 3142734667903054343964730262664674846019968859042302728040824980202409042195, - 42435540029089345139957895843522598805263356868757409076510579446616534628156 - ], - [ - 20881640009753068009889978253249438511980029543200475508978512796038891822829, - 27968609902807217352706839169055210416306119266743972159646846139577153321146 - ], - [ - 6202878911951482584791440140675075028003226366441084032538126321848133088273, - 24851320978805441304196414672326898173230285718326922381560663848223170102647 - ], - [ - 50070881978905881790688154485363911795896102164461504932917590031909500985704, - 43699660924644847032936380042415449426432396553316376125903955630994215373471 - ], - [ - 39050911904675050026537096930453299489301613892833411147399076382608918690553, - 23065138191144874213305796767304509147666299465390223147896688385250190864754 - ], - [ - 43248564896736773616271483377912399618070545196686996456065036741196923875785, - 31721938930225437875325316728027640162244123179872753655598024108046977290917 - ], - [ - 33816032572798871402707296012525763396838266638976216810437332330642035512151, - 1946612142431753251478630793690835505948833864135360052351593237847378887719 - ] -] \ No newline at end of file diff --git a/dot_ring/ring_proof/pcs/utils.py b/dot_ring/ring_proof/pcs/utils.py index 1e28752..1321c6a 100644 --- a/dot_ring/ring_proof/pcs/utils.py +++ b/dot_ring/ring_proof/pcs/utils.py @@ -24,14 +24,6 @@ class LinearPcsVerification(NamedTuple): value: Scalar -def synthetic_div(poly: CoeffVector, x: Scalar, y: Scalar) -> CoeffVector: - """Return q(X) such that f(X)−y = (X−x)·q(X). Checks remainder.""" - q, rem = synthetic_div_with_eval(poly, x) - if rem != y: - raise ValueError("point/value pair inconsistent with polynomial") - return q - - def synthetic_div_with_eval(poly: CoeffVector, x: Scalar) -> tuple[CoeffVector, Scalar]: """Return quotient by ``X-x`` and ``f(x)`` in one Horner pass.""" n = len(poly) diff --git a/dot_ring/ring_proof/polynomial/fft.py b/dot_ring/ring_proof/polynomial/fft.py index 8e5cde6..db85b58 100644 --- a/dot_ring/ring_proof/polynomial/fft.py +++ b/dot_ring/ring_proof/polynomial/fft.py @@ -56,18 +56,6 @@ def _get_twiddle_factors(n: int, omega: int, prime: int) -> list[list[int]]: return twiddles -@lru_cache(maxsize=1024) -def _get_roots(n: int, omega: int, prime: int) -> list[int]: - """Get precomputed roots of unity (legacy, for compatibility).""" - powers = [1] * (n // 2) - curr = 1 - for i in range(1, n // 2): - curr = (curr * omega) % prime - powers[i] = curr - - return powers - - def _use_native_ntt(prime: int) -> bool: return prime == S_PRIME @@ -154,42 +142,6 @@ def inverse_fft(values: list[int], omega: int, prime: int) -> list[int]: return coeffs -def evaluate_poly_over_domain(poly: list[int], domain: list[int], omega: int, prime: int) -> list[int]: - """Evaluate polynomial over a structured domain using FFT. - - Assumes domain = [omega^0, omega^1, ..., omega^(n-1)] mod prime. - - Args: - poly: Polynomial coefficients (lowest degree first) - domain: Evaluation domain (must be powers of omega) - omega: Primitive n-th root of unity mod prime - prime: Field modulus - - Returns: - List of polynomial evaluations at each domain point - """ - n = len(domain) - - # Pad or truncate coefficients to domain size - # If poly has more coefficients than domain size, we need to reduce mod (X^n - 1) - coeffs = poly[:] if len(poly) <= n else poly[:] - - # Reduce polynomial modulo X^n - 1 by folding coefficients - if len(poly) > n: - result = [0] * n - for i, c in enumerate(poly): - result[i % n] = (result[i % n] + c) % prime - coeffs = result - else: - # Pad with zeros if needed - coeffs = coeffs + [0] * (n - len(coeffs)) - - # Perform FFT - _fft_in_place(coeffs, omega, prime) - - return coeffs - - def evaluate_poly_fft(poly: list[int], domain_size: int, omega: int, prime: int, coset_offset: int = 1) -> list[int]: """Evaluate polynomial over a coset domain using FFT. diff --git a/dot_ring/ring_proof/polynomial/interpolation.py b/dot_ring/ring_proof/polynomial/interpolation.py deleted file mode 100644 index cbacac1..0000000 --- a/dot_ring/ring_proof/polynomial/interpolation.py +++ /dev/null @@ -1,61 +0,0 @@ -from dot_ring.ring_proof.polynomial.fft import _fft_in_place, _fft_in_place_scaled - - -def is_power_of_two(n: int) -> bool: - # is n==2**x - return bool(n and (n & (n - 1)) == 0) - - -def modinv(a: int, p: int) -> int: - # find x such that a*x= 1 mod p - return pow(a, -1, p) - - -# In general fft -def fft(a: list[int], omega: int, p: int) -> list[int]: # coeffs to evaluation points - n = len(a) - if n == 1: - return a - - # Use optimized in-place FFT - coeffs = list(a) - _fft_in_place(coeffs, omega, p) - return coeffs - - -def poly_interpolate_fft(a: list[int], omega: int, p: int) -> list[int]: # funcs like inverse_fft from evals to poly coeffs - n = len(a) - omega_inv = modinv(omega, p) - n_inv = modinv(n, p) - coeffs = list(a) - _fft_in_place_scaled(coeffs, omega_inv, p, n_inv) - return coeffs - - -def next_power_of_two(n: int) -> int: - return 1 << (n - 1).bit_length() - - -def poly_mul_fft(a: list[int], b: list[int], prime: int) -> list[int]: - target_len = 2048 - N = next_power_of_two(target_len) - omega = 49307615728544765012166121802278658070711169839041683575071795236746050763237 - # Scale root of unity if needed - root_order = N - omega_N = pow(omega, (2048 // root_order), prime) # If omega is for 2048, scale down - - # Pad inputs - A = a + [0] * (N - len(a)) - B = b + [0] * (N - len(b)) - - # FFT both - A_eval = fft(A, omega_N, prime) - B_eval = fft(B, omega_N, prime) - - # Multiply evaluations - C_eval = [(ae * be) % prime for ae, be in zip(A_eval, B_eval, strict=False)] - - # Inverse FFT to get result - C = poly_interpolate_fft(C_eval, omega_N, prime) - # Trim to true degree - return C[:target_len] diff --git a/dot_ring/ring_proof/polynomial/ops.py b/dot_ring/ring_proof/polynomial/ops.py index 3d068d3..b22cf57 100644 --- a/dot_ring/ring_proof/polynomial/ops.py +++ b/dot_ring/ring_proof/polynomial/ops.py @@ -48,17 +48,6 @@ def get_root_of_unity(n: int, prime: int) -> int: candidate += 1 -def vect_scalar_mul(vec: Sequence[int], scalar: int, mod: int | None = None) -> list[int]: - """Multiply each vector element by a scalar.""" - return [(x * scalar) % mod if mod else x * scalar for x in vec] - - -def vect_add(a: Sequence[int], b: Sequence[int], prime: int) -> list[int]: - if len(a) != len(b): - raise ValueError("Vector lengths must match") - return [(int(x) + int(y)) % prime for x, y in zip(a, b, strict=True)] - - def poly_add(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: """Add two polynomials in a prime field.""" result_len = max(len(poly1), len(poly2)) @@ -76,25 +65,6 @@ def poly_add(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int return result -def poly_subtract(poly1: Sequence[int], poly2: Sequence[int], prime: int) -> list[int]: - """Subtract poly2 from poly1 in a prime field.""" - result_len = max(len(poly1), len(poly2)) - result = [0] * result_len - - for i in range(len(poly1)): - result[i] = poly1[i] - - for i in range(len(poly2)): - val1 = result[i] - val2 = poly2[i] - if val1 >= val2: - result[i] = val1 - val2 - else: - result[i] = prime - (val2 - val1) - - return result - - def poly_scalar_mul(poly: Sequence[int], scalar: int, prime: int) -> list[int]: """Multiply a polynomial by a scalar in a prime field.""" n = len(poly) @@ -206,18 +176,6 @@ def poly_evaluate_single(poly: Sequence[int], x: int, prime: int) -> int: return result -def poly_evaluate_domain(poly: Sequence[int], domain: Sequence[int], prime: int) -> list[int]: - """Evaluate a polynomial over a point domain. - - Roots-of-unity domains use FFT. Arbitrary point lists use Horner per point. - """ - coeffs = list(poly) - omega = _root_of_unity_domain_omega(domain, prime) - if omega is not None: - return evaluate_poly_fft(coeffs, len(domain), omega, prime) - return [poly_evaluate_single(coeffs, x, prime) for x in domain] - - def lagrange_basis_polynomial(domain: Sequence[int], i: int, prime: int) -> list[int]: """Return the i-th Lagrange basis polynomial over the given domain.""" omega = _root_of_unity_domain_omega(domain, prime) @@ -246,9 +204,8 @@ def lagrange_basis_polynomial(domain: Sequence[int], i: int, prime: int) -> list return poly_scalar_mul(numerator, pow(denominator, -1, prime), prime) -def poly_divide_by_vanishing(poly: Sequence[int], domain_size: int, prime: int) -> list[int]: +def poly_divide_by_vanishing(poly: Sequence[int], domain_size: int) -> list[int]: """Return the quotient of poly divided by x^domain_size - 1.""" - del prime if domain_size <= 0: raise ValueError("domain_size must be positive") diff --git a/dot_ring/ring_proof/proof/__init__.py b/dot_ring/ring_proof/proof/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/dot_ring/ring_proof/proof/aggregation_poly.py b/dot_ring/ring_proof/proof/aggregation_poly.py deleted file mode 100644 index a753e4b..0000000 --- a/dot_ring/ring_proof/proof/aggregation_poly.py +++ /dev/null @@ -1,47 +0,0 @@ -from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.pcs.kzg import KZG -from dot_ring.ring_proof.pcs.protocol import PCS -from dot_ring.ring_proof.polynomial.ops import poly_add, poly_scalar_mul - - -class AggPoly: - # get the aggregated poly - @classmethod - def aggregated_poly(cls, fixed_cols: list, witness_cols: list, Q_p: list[int], cf_vectors: list[int], prime: int = S_PRIME) -> list[int]: - poly_I = [ - fixed_cols[0].coeffs, - fixed_cols[1].coeffs, - fixed_cols[2].coeffs, - witness_cols[0].coeffs, - witness_cols[3].coeffs, - witness_cols[1].coeffs, - witness_cols[2].coeffs, - Q_p, - ] - agg_poly = [0] - for poly, scalar in zip(poly_I, cf_vectors, strict=True): - agg_poly = poly_add(agg_poly, poly_scalar_mul(poly, scalar, prime), prime) - return agg_poly - - # two proof openings - @classmethod - def proof_contents_phi( - cls, - zeta: int, - zeta_omega: int, - l_agg: list[int], - fixed_cols: list, - witness_cols: list, - Q_p: list[int], - cf_vectors: list[int], - prime: int = S_PRIME, - pcs: type[PCS] = KZG, - ) -> tuple: - """ - input:agg_poly, liner_poly, zeta, zeta_omega - output: Phi_zeta, phi_zeta_omega - """ - agg_p = cls.aggregated_poly(fixed_cols, witness_cols, Q_p, cf_vectors, prime) - phi_z_opening = pcs.open(agg_p, zeta) # take only proof - phi_zw_opening = pcs.open(l_agg, zeta_omega) # take only proof - return phi_z_opening, phi_zw_opening, phi_z_opening, phi_zw_opening diff --git a/dot_ring/ring_proof/proof/linearization_poly.py b/dot_ring/ring_proof/proof/linearization_poly.py deleted file mode 100644 index 93dd12c..0000000 --- a/dot_ring/ring_proof/proof/linearization_poly.py +++ /dev/null @@ -1,106 +0,0 @@ -from dot_ring.ring_proof.columns.columns import Column -from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.polynomial.ops import poly_add, poly_evaluate_single, poly_scalar_mul -from dot_ring.ring_proof.transcript.phases import phase2_eval_point -from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript - - -class LAggPoly: - def __init__( - self, - cur_t: FiatShamirTranscript, - C_q: list[int], - fixed_cols: list[Column], - witness_res: list[Column], - alphas: list[int], - domain: list[int], - omega: int, - prime: int = S_PRIME, - padding_rows: int = 4, - edwards_a: int = -5, - ) -> None: - self.t, self.zeta = phase2_eval_point(cur_t, C_q) - self.prime = prime - self.zeta_omega = (self.zeta * omega) % self.prime - last_index = len(domain) - padding_rows - self.scalar_term = (self.zeta - domain[last_index]) % self.prime - self.fs = fixed_cols - self.wts = witness_res - self.alphas = alphas - self.a = edwards_a - - def evaluate_polys_at_zeta(self) -> None: - if self.fs[0].coeffs is None or self.fs[1].coeffs is None or self.fs[2].coeffs is None: - raise ValueError("Fixed columns not interpolated") - if self.wts[0].coeffs is None or self.wts[1].coeffs is None or self.wts[2].coeffs is None or self.wts[3].coeffs is None: - raise ValueError("Witness columns not interpolated") - - self.P_x_zeta = poly_evaluate_single(self.fs[0].coeffs, self.zeta, self.prime) - self.P_y_zeta = poly_evaluate_single(self.fs[1].coeffs, self.zeta, self.prime) - self.s_zeta = poly_evaluate_single(self.fs[2].coeffs, self.zeta, self.prime) - self.b_zeta = poly_evaluate_single(self.wts[0].coeffs, self.zeta, self.prime) - self.acc_ip_zeta = poly_evaluate_single(self.wts[3].coeffs, self.zeta, self.prime) - self.acc_x_zeta = poly_evaluate_single(self.wts[1].coeffs, self.zeta, self.prime) - self.acc_y_zeta = poly_evaluate_single(self.wts[2].coeffs, self.zeta, self.prime) - - def compute_l1(self) -> list[int]: - if self.wts[3].coeffs is None: - raise ValueError("Witness column 3 not interpolated") - return poly_scalar_mul(self.wts[3].coeffs, self.scalar_term, self.prime) - - def compute_l2(self) -> list[int]: - x1, y1 = self.acc_x_zeta, self.acc_y_zeta - x2, y2 = self.P_x_zeta, self.P_y_zeta - b = self.b_zeta - coeff_a = self.a - - C_acc_x = (b * (y1 * y2 + (coeff_a * x1 * x2)) % self.prime + (1 - b) % self.prime) % self.prime - C_acc_x_f = C_acc_x * self.scalar_term - - if self.wts[1].coeffs is None or self.wts[2].coeffs is None: - raise ValueError("Witness columns not interpolated") - return poly_scalar_mul(self.wts[1].coeffs, C_acc_x_f, self.prime) - - def compute_l3(self) -> list[int]: - b = self.b_zeta - x1, y1 = self.acc_x_zeta, self.acc_y_zeta - x2, y2 = self.P_x_zeta, self.P_y_zeta - C_acc_y = ((b * (x1 * y2 - x2 * y1)) % self.prime + (1 - b) % self.prime) % self.prime - C_acc_y *= self.scalar_term - - if self.wts[1].coeffs is None or self.wts[2].coeffs is None: - raise ValueError("Witness columns not interpolated") - return poly_scalar_mul(self.wts[2].coeffs, C_acc_y, self.prime) - - def linearize(self, l1: list[int], l2: list[int], l3: list[int]) -> list[int]: - l_agg = [0] - for poly, scalar in zip([l1, l2, l3], self.alphas[:3], strict=True): - l_agg = poly_add(l_agg, poly_scalar_mul(poly, scalar, self.prime), self.prime) - return l_agg - - def l_agg_poly(self) -> tuple[FiatShamirTranscript, int, dict[str, int], list[int], int, int]: - self.evaluate_polys_at_zeta() # fills P_x_zeta … acc_y_zeta - l1 = self.compute_l1() - l2 = self.compute_l2() - l3 = self.compute_l3() - l_agg = self.linearize(l1, l2, l3) - - l_agg_zeta_omega = poly_evaluate_single(l_agg, self.zeta_omega, self.prime) - return ( - self.t, - self.zeta, - { - # "Zeta":self.zeta, - "P_x_zeta": self.P_x_zeta, - "P_y_zeta": self.P_y_zeta, - "s_zeta": self.s_zeta, - "b_zeta": self.b_zeta, - "acc_ip_zeta": self.acc_ip_zeta, - "acc_x_zeta": self.acc_x_zeta, - "acc_y_zeta": self.acc_y_zeta, - # "l_agg_zeta_omega": l_agg_zeta_omega, - }, - l_agg, - self.zeta_omega, - l_agg_zeta_omega, - ) diff --git a/dot_ring/ring_proof/proof/quotient_poly.py b/dot_ring/ring_proof/proof/quotient_poly.py deleted file mode 100644 index 3ad9556..0000000 --- a/dot_ring/ring_proof/proof/quotient_poly.py +++ /dev/null @@ -1,35 +0,0 @@ -from dot_ring.ring_proof.pcs.kzg import KZG -from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment -from dot_ring.ring_proof.polynomial.ops import poly_divide_by_vanishing - - -class QuotientPoly: - def __init__(self, domain_size: int, pcs: type[PCS] = KZG, prime: int | None = None) -> None: - self.domain_size = domain_size - self.pcs = pcs - self.prime = prime - - @staticmethod - def poly_vector_xn_minus_1(n: int) -> list[int]: - vec = [0] * (n + 1) - vec[0] = -1 - vec[n] = 1 - # print("vect:", vec) - return vec - - def quotient_poly_commitment(self, q_x: list[int]) -> G1Commitment: - """ - input: quotient polynomial - output: commitment to quotient polynomial - """ - c_q = self.pcs.commit(q_x) - return c_q - - def quotient_poly(self, C_agg: list[int]) -> tuple[list[int], G1Commitment]: - if self.prime is None: - raise ValueError("quotient polynomial division requires a prime field modulus") - qnt_poly = poly_divide_by_vanishing(C_agg, self.domain_size, self.prime) - # print("q_p:", qnt_poly) - C_qp = self.quotient_poly_commitment(qnt_poly) - # C_qp_nm=nm(C_qp) - return qnt_poly, C_qp diff --git a/dot_ring/vrf/pedersen/vrf.py b/dot_ring/vrf/pedersen/vrf.py index 7ac5a2a..270ee5f 100644 --- a/dot_ring/vrf/pedersen/vrf.py +++ b/dot_ring/vrf/pedersen/vrf.py @@ -189,11 +189,6 @@ def blinding_scalar(cls, secret_scalar: int, transcript: Any) -> int: t.absorb(bytes([DomSep.PEDERSEN_BLINDING])) return nonce(cls.cv, secret_scalar, t) - @classmethod - def blinding(cls, secret: bytes, input_point: bytes, add: bytes) -> int: - del input_point, add - return cls.blinding_scalar(scalar_decode(cls.cv, secret), vrf_transcript(cls.cv, DomSep.PEDERSEN_VRF, [], b"")[0]) - @classmethod def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: if mul_cofactor: diff --git a/dot_ring/vrf/protocol.py b/dot_ring/vrf/protocol.py deleted file mode 100644 index 337dff5..0000000 --- a/dot_ring/vrf/protocol.py +++ /dev/null @@ -1,34 +0,0 @@ -from __future__ import annotations - -from abc import abstractmethod -from typing import Protocol, TypeVar - -from ..curve.curve import CurveVariant -from ..curve.point import CurvePoint - -C = TypeVar("C", bound=CurveVariant) -P = TypeVar("P", bound=CurvePoint) - - -class VRFProtocol(Protocol[C, P]): - """Protocol defining the interface for VRF implementations.""" - - curve: C - point_type: type[P] - - @abstractmethod - def proof(self, alpha: bytes, secret_key: int, additional_data: bytes) -> tuple[P, tuple[int, int]]: - """Generate VRF proof.""" - ... - - @abstractmethod - def verify( - self, - public_key: P, - input_point: P, - additional_data: bytes, - output_point: P, - proof: tuple[int, int], - ) -> bool: - """Verify VRF proof.""" - ... diff --git a/dot_ring/vrf/ring/__init__.py b/dot_ring/vrf/ring/__init__.py index 2725890..8108c79 100644 --- a/dot_ring/vrf/ring/__init__.py +++ b/dot_ring/vrf/ring/__init__.py @@ -1,5 +1,5 @@ from .batch_verifier import RingBatchContext, RingBatchItem, RingBatchVerifier -from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .context import RingContext, RingRootBuilder from .members import Ring from .root import RingRoot from .vrf import RingVRF @@ -11,7 +11,6 @@ "RingBatchVerifier", "RingContext", "RingRoot", - "RingSetup", + "RingRootBuilder", "RingVRF", - "RingVerifierKeyBuilder", ] diff --git a/dot_ring/vrf/ring/context.py b/dot_ring/vrf/ring/context.py index 6239c1c..5621351 100644 --- a/dot_ring/vrf/ring/context.py +++ b/dot_ring/vrf/ring/context.py @@ -9,45 +9,41 @@ from .members import Ring, parse_concatenated_keys from .root import RingRoot -RingSetup = RingProofParams - @dataclass(frozen=True) class RingContext: - setup: RingSetup + params: RingProofParams @classmethod def from_ring_size(cls, ring_size: int, *, test_vectors: bool = False, cv: CurveVariant | None = None) -> RingContext: - return cls(RingSetup.from_ring_size(ring_size, test_vectors=test_vectors, cv=cv or RingSetup().cv)) + default_params = RingProofParams() + return cls(RingProofParams.from_ring_size(ring_size, test_vectors=test_vectors, cv=cv or default_params.cv)) @property def max_ring_size(self) -> int: - return self.setup.max_ring_size + return self.params.max_ring_size def ring(self, keys: Sequence[bytes | str] | bytes) -> Ring: if isinstance(keys, bytes): - return Ring(parse_concatenated_keys(keys, self.setup.cv), self.setup) - return Ring(keys, self.setup) + return Ring(parse_concatenated_keys(keys, self.params.cv), self.params) + return Ring(keys, self.params) def ring_root(self, ring: Ring | Sequence[bytes | str] | bytes) -> RingRoot: if not isinstance(ring, Ring): ring = self.ring(ring) - return RingRoot.from_ring(ring, self.setup) - - def verifier_key(self, ring: Ring | Sequence[bytes | str] | bytes) -> RingRoot: - return self.ring_root(ring) + return RingRoot.from_ring(ring, self.params) - def verifier_key_from_commitment(self, commitment: RingRoot | bytes) -> RingRoot: + def ring_root_from_bytes(self, commitment: RingRoot | bytes) -> RingRoot: if isinstance(commitment, RingRoot): return commitment - return RingRoot.from_bytes(commitment, self.setup) + return RingRoot.from_bytes(commitment, self.params) - def verifier_key_builder(self) -> RingVerifierKeyBuilder: - return RingVerifierKeyBuilder(self) + def ring_root_builder(self) -> RingRootBuilder: + return RingRootBuilder(self) @dataclass -class RingVerifierKeyBuilder: +class RingRootBuilder: context: RingContext keys: list[bytes] = field(default_factory=list) _root: RingRoot | None = field(default=None, init=False, repr=False) @@ -60,15 +56,15 @@ def append(self, keys: list[bytes] | bytes) -> None: def extend(self, keys: list[bytes] | bytes) -> None: if isinstance(keys, bytes): - keys = parse_concatenated_keys(keys, self.context.setup.cv) + keys = parse_concatenated_keys(keys, self.context.params.cv) if len(self.keys) + len(keys) > self.context.max_ring_size: - raise ValueError("too many keys for ring verifier-key builder") + raise ValueError("too many keys for ring root builder") self.keys.extend(keys) self._root = None if len(self.keys) == self.context.max_ring_size: - self._root = self.context.verifier_key(self.keys) + self._root = self.context.ring_root(self.keys) def finalize(self) -> RingRoot: if self._root is None: - self._root = self.context.verifier_key(self.keys) + self._root = self.context.ring_root(self.keys) return self._root diff --git a/dot_ring/vrf/ring/members.py b/dot_ring/vrf/ring/members.py index 840887f..a9ccd0a 100644 --- a/dot_ring/vrf/ring/members.py +++ b/dot_ring/vrf/ring/members.py @@ -1,9 +1,9 @@ from __future__ import annotations from collections.abc import Sequence +from functools import lru_cache from dot_ring.curve.curve import CurveVariant -from dot_ring.ring_proof.constants import DEFAULT_SIZE from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.transcript import point_len @@ -15,16 +15,34 @@ def parse_concatenated_keys(keys: bytes, cv: CurveVariant) -> list[bytes]: return [keys[encoded_point_len * i : encoded_point_len * (i + 1)] for i in range(len(keys) // encoded_point_len)] -def _h_vector(params: RingProofParams, size: int = DEFAULT_SIZE) -> list[tuple[int, int]]: +def blinding_base_powers(params: RingProofParams, count: int) -> tuple[tuple[int, int], ...]: + def _bb_cache_key(params: RingProofParams) -> tuple[str, int, int, int, tuple[int, int]]: + return ( + params.ring_curve_variant().name, + params.prime, + params.ring_edwards_a, + params.ring_edwards_d, + params.blinding_base, + ) + + return _blinding_base_powers(params, _bb_cache_key(params), count) + + +@lru_cache(maxsize=16) +def _blinding_base_powers( + params: RingProofParams, + _cache_key: tuple[str, int, int, int, tuple[int, int]], + count: int, +) -> tuple[tuple[int, int], ...]: """Return `[2^0 * H, 2^1 * H, ...]` in ring coordinates.""" point = params.blinding_base points = [] - for _ in range(size): + for _ in range(count): points.append(point) point_obj = params.ring_point(point) result = point_obj + point_obj point = int(result.x), int(result.y) - return points + return tuple(points) class Ring: @@ -60,8 +78,7 @@ def __init__(self, keys: Sequence[bytes | str], params: RingProofParams | None = fill_count = params.domain_size - params.padding_rows - len(nm_points) if fill_count > 0: - h_vec = _h_vector(params, params.domain_size) - nm_points.extend(h_vec[:fill_count]) + nm_points.extend(blinding_base_powers(params, fill_count)) if params.padding_rows > 0: nm_points.extend([(0, 0)] * params.padding_rows) diff --git a/dot_ring/vrf/ring/proof_builder.py b/dot_ring/vrf/ring/proof_builder.py index 2437baf..0fba6a0 100644 --- a/dot_ring/vrf/ring/proof_builder.py +++ b/dot_ring/vrf/ring/proof_builder.py @@ -6,12 +6,9 @@ from dot_ring.curve.curve import CurveVariant from dot_ring.ring_proof.columns.columns import Column, WitnessColumnBuilder, require_commitment from dot_ring.ring_proof.constraints.constraints import RingConstraintBuilder -from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft -from dot_ring.ring_proof.polynomial.ops import poly_multiply, vect_add, vect_scalar_mul -from dot_ring.ring_proof.proof.aggregation_poly import AggPoly -from dot_ring.ring_proof.proof.linearization_poly import LAggPoly -from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly -from dot_ring.ring_proof.transcript.phases import phase1_alphas_after_vk, phase3_nu_vector +from dot_ring.ring_proof.polynomial.fft import inverse_fft +from dot_ring.ring_proof.polynomial.ops import poly_add, poly_divide_by_vanishing, poly_evaluate_single, poly_multiply, poly_scalar_mul +from dot_ring.ring_proof.transcript.phases import phase1_alphas_after_vk, phase2_eval_point, phase3_nu_vector from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript from .members import Ring @@ -68,33 +65,31 @@ def build(self) -> RingProofPayload: constraint_polys = list(constraints.compute().values()) c_agg = self._aggregate_constraints(constraint_polys, alpha) - quotient_poly = QuotientPoly(params.domain_size, params.pcs, params.prime) - q_poly, c_q = quotient_poly.quotient_poly(c_agg) + q_poly = poly_divide_by_vanishing(c_agg, params.domain_size) + c_q = params.pcs.commit(q_poly) c_q_column = Column(name="C_q", evals=[], commitment=c_q) - l_agg = LAggPoly( + fixed_columns = (ring_root.px, ring_root.py, ring_root.s) + + current_transcript, zeta, relation_evals, l_agg_poly, zeta_omega, l_zeta_omega = self._linearization_poly( transcript, - params.pcs.serialize_g1_uncompressed(c_q), - [ring_root.px, ring_root.py, ring_root.s], - list(witness_columns), + c_q, + fixed_columns, + witness_columns, alpha, - domain=params.domain, - omega=params.omega, - prime=params.prime, - padding_rows=params.padding_rows, - edwards_a=params.ring_edwards_a, ) - current_transcript, zeta, relation_evals, l_agg_poly, zeta_omega, l_zeta_omega = l_agg.l_agg_poly() - _, _, phi_zeta, phi_zeta_omega = AggPoly.proof_contents_phi( + phi_zeta, phi_zeta_omega = self._opening_proofs( zeta, zeta_omega, l_agg_poly, - [ring_root.px, ring_root.py, ring_root.s], - list(witness_columns), + fixed_columns, + witness_columns, q_poly, - phase3_nu_vector(current_transcript, list(relation_evals.values()), l_zeta_omega), - prime=params.prime, - pcs=params.pcs, + phase3_nu_vector( + current_transcript, + list(relation_evals.values()), + l_zeta_omega, + ), ) ( px_zeta, @@ -155,12 +150,17 @@ def _aggregate_constraints( alphas: Sequence[int], ) -> list[int]: params = self.ring.params + if not constraint_evals: + return [] + aggregated_evals = [0] * len(constraint_evals[0]) for constraint, alpha in zip(constraint_evals, alphas, strict=False): - weighted_constraint = vect_scalar_mul(constraint, alpha, params.prime) - aggregated_evals = vect_add(aggregated_evals, weighted_constraint, params.prime) + if len(constraint) != len(aggregated_evals): + raise ValueError("constraint evaluation lengths must match") + for index, value in enumerate(constraint): + aggregated_evals[index] = (aggregated_evals[index] + value * alpha) % params.prime - aggregated_poly = poly_interpolate_fft(aggregated_evals, params.radix_omega, params.prime) + aggregated_poly = inverse_fft(aggregated_evals, params.radix_omega, params.prime) c_agg = poly_multiply(aggregated_poly, self._tail_vanishing_polynomial(), params.prime) last_nonzero = len(c_agg) - 1 @@ -174,3 +174,127 @@ def _tail_vanishing_polynomial(self, k: int = 3) -> list[int]: for offset in range(1, k + 1): vanishing = poly_multiply(vanishing, [-params.domain[-offset], 1], params.prime) return vanishing + + def _linearization_poly( + self, + transcript: FiatShamirTranscript, + c_q_commitment: object, + fixed_columns: Sequence[Column], + witness_columns: tuple[Column, Column, Column, Column], + alphas: Sequence[int], + ) -> tuple[FiatShamirTranscript, int, dict[str, int], list[int], int, int]: + params = self.ring.params + current_transcript, zeta = phase2_eval_point( + transcript, + params.pcs.serialize_g1_uncompressed(c_q_commitment), + ) + zeta_omega = (zeta * params.omega) % params.prime + last_index = params.domain_size - params.padding_rows + scalar_term = (zeta - params.domain[last_index]) % params.prime + + relation_evals = self._relation_evals_at_zeta(fixed_columns, witness_columns, zeta) + l1 = poly_scalar_mul(self._coeffs(witness_columns[3]), scalar_term, params.prime) + l2 = poly_scalar_mul( + self._coeffs(witness_columns[1]), + self._acc_x_constraint_factor(relation_evals, scalar_term), + params.prime, + ) + l3 = poly_scalar_mul( + self._coeffs(witness_columns[2]), + self._acc_y_constraint_factor(relation_evals, scalar_term), + params.prime, + ) + + l_agg = [0] + for poly, scalar in zip((l1, l2, l3), alphas[:3], strict=True): + l_agg = poly_add(l_agg, poly_scalar_mul(poly, scalar, params.prime), params.prime) + + l_zeta_omega = poly_evaluate_single(l_agg, zeta_omega, params.prime) + return current_transcript, zeta, relation_evals, l_agg, zeta_omega, l_zeta_omega + + def _relation_evals_at_zeta( + self, + fixed_columns: Sequence[Column], + witness_columns: tuple[Column, Column, Column, Column], + zeta: int, + ) -> dict[str, int]: + params = self.ring.params + c_b, c_accx, c_accy, c_accip = witness_columns + return { + "P_x_zeta": poly_evaluate_single(self._coeffs(fixed_columns[0]), zeta, params.prime), + "P_y_zeta": poly_evaluate_single(self._coeffs(fixed_columns[1]), zeta, params.prime), + "s_zeta": poly_evaluate_single(self._coeffs(fixed_columns[2]), zeta, params.prime), + "b_zeta": poly_evaluate_single(self._coeffs(c_b), zeta, params.prime), + "acc_ip_zeta": poly_evaluate_single(self._coeffs(c_accip), zeta, params.prime), + "acc_x_zeta": poly_evaluate_single(self._coeffs(c_accx), zeta, params.prime), + "acc_y_zeta": poly_evaluate_single(self._coeffs(c_accy), zeta, params.prime), + } + + def _acc_x_constraint_factor(self, evals: dict[str, int], scalar_term: int) -> int: + params = self.ring.params + b = evals["b_zeta"] + x1, y1 = evals["acc_x_zeta"], evals["acc_y_zeta"] + x2, y2 = evals["P_x_zeta"], evals["P_y_zeta"] + point_relation = y1 * y2 + params.ring_edwards_a * x1 * x2 + return (b * point_relation + (1 - b)) * scalar_term % params.prime + + def _acc_y_constraint_factor(self, evals: dict[str, int], scalar_term: int) -> int: + params = self.ring.params + b = evals["b_zeta"] + x1, y1 = evals["acc_x_zeta"], evals["acc_y_zeta"] + x2, y2 = evals["P_x_zeta"], evals["P_y_zeta"] + point_relation = x1 * y2 - x2 * y1 + return (b * point_relation + (1 - b)) * scalar_term % params.prime + + def _opening_proofs( + self, + zeta: int, + zeta_omega: int, + l_agg: list[int], + fixed_columns: Sequence[Column], + witness_columns: tuple[Column, Column, Column, Column], + q_poly: list[int], + opening_challenges: Sequence[int], + ): + params = self.ring.params + aggregated_poly = self._aggregated_opening_poly( + fixed_columns, + witness_columns, + q_poly, + opening_challenges, + ) + return params.pcs.open(aggregated_poly, zeta), params.pcs.open(l_agg, zeta_omega) + + def _aggregated_opening_poly( + self, + fixed_columns: Sequence[Column], + witness_columns: tuple[Column, Column, Column, Column], + q_poly: list[int], + opening_challenges: Sequence[int], + ) -> list[int]: + params = self.ring.params + c_b, c_accx, c_accy, c_accip = witness_columns + polynomials = [ + self._coeffs(fixed_columns[0]), + self._coeffs(fixed_columns[1]), + self._coeffs(fixed_columns[2]), + self._coeffs(c_b), + self._coeffs(c_accip), + self._coeffs(c_accx), + self._coeffs(c_accy), + q_poly, + ] + aggregated_poly = [0] + for poly, scalar in zip(polynomials, opening_challenges, strict=True): + aggregated_poly = poly_add( + aggregated_poly, + poly_scalar_mul(poly, scalar, params.prime), + params.prime, + ) + return aggregated_poly + + @staticmethod + def _coeffs(column: Column) -> list[int]: + if column.coeffs is None: + raise ValueError(f"{column.name} column is not interpolated") + return column.coeffs diff --git a/dot_ring/vrf/ring/vrf.py b/dot_ring/vrf/ring/vrf.py index 63a20ee..5bca602 100644 --- a/dot_ring/vrf/ring/vrf.py +++ b/dot_ring/vrf/ring/vrf.py @@ -13,7 +13,7 @@ from ..vrf import VRF from .batch_verifier import RingBatchItem, RingBatchVerifier, _proof_relation_points, _proof_transcript_commitments -from .context import RingContext, RingSetup, RingVerifierKeyBuilder +from .context import RingContext, RingRootBuilder from .members import Ring from .members import parse_concatenated_keys as _parse_concatenated_keys from .proof_builder import RingProofBuilder @@ -302,7 +302,6 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: "RingBatchItem", "RingBatchVerifier", "RingContext", - "RingSetup", "RingVRF", - "RingVerifierKeyBuilder", + "RingRootBuilder", ] diff --git a/pyproject.toml b/pyproject.toml index 07c47a7..99fbbd1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,9 @@ [project] name = "dot-ring" dynamic = ["version"] -description = "Serves as a library to generate and verify signatures using Tiny, Thin, Pedersen and Ring VRF-AD schemes" +description = "Library to generate and verify signatures using Tiny, Thin, Pedersen and Ring VRF-AD schemes" authors = [ - {name = "prasad-kumkar", email = "prasad@chainscore.finance"} + {name = "prasad-kumkar", email = "prasad@chainscorelabs.com"} ] readme = "README.md" license = {text = "MIT"} @@ -16,7 +16,8 @@ keywords = [ "Interpolation","Ring Root", "Commitment", "Constraints", "fflonk", "powers of tau", "string to point", "point to string", "encode to curve","public key", "elliptic curve cryptography", "proof to hash", - "bls12_381", "Bandersnatch", "fiat shamir", "pairings" + "bls12_381", "Bandersnatch", "fiat shamir", "pairings", + "hash to curve", "hash to field" ] classifiers = [ "Development Status :: 4 - Beta", @@ -53,7 +54,6 @@ exclude = ["tests*", "blst*", "perf*", "testenv*"] dot_ring = [ "py.typed", "vrf/data/*.bin", - "ring_proof/columns/*.json", "blst/*.so", "blst/*.dylib", "blst/*.dll", diff --git a/setup.py b/setup.py index e749480..87a7539 100644 --- a/setup.py +++ b/setup.py @@ -186,7 +186,6 @@ def _clean_blst_artifacts(self, blst_dir: Path) -> None: "dot_ring": ["py.typed"], "dot_ring.blst": ["*.so", "*.dylib", "*.dll", "*.pyd"], "dot_ring.vrf": ["data/*.bin"], - "dot_ring.ring_proof": ["columns/*.json"], }, include_package_data=True, ) diff --git a/tests/test_ark_vrf.py b/tests/test_ark_vrf.py index bfd3265..5cb2bb5 100644 --- a/tests/test_ark_vrf.py +++ b/tests/test_ark_vrf.py @@ -260,13 +260,13 @@ def test_ring_context_helpers() -> None: context = RingContext(RingProofParams(test_vectors=True)) ring = context.ring(keys_blob) - ring_root = context.verifier_key(ring) - builder = context.verifier_key_builder() + ring_root = context.ring_root(ring) + builder = context.ring_root_builder() builder.extend(keys_blob) assert ring_root.to_bytes().hex() == vector["ring_pks_com"] assert context.ring_root(keys_blob).to_bytes() == ring_root.to_bytes() - assert context.verifier_key_from_commitment(ring_root.to_bytes()).to_bytes() == ring_root.to_bytes() + assert context.ring_root_from_bytes(ring_root.to_bytes()).to_bytes() == ring_root.to_bytes() assert builder.finalize().to_bytes() == ring_root.to_bytes() diff --git a/tests/test_coverage/test_fft.py b/tests/test_coverage/test_fft.py index f052efd..02fe218 100644 --- a/tests/test_coverage/test_fft.py +++ b/tests/test_coverage/test_fft.py @@ -5,10 +5,8 @@ from dot_ring.ring_proof.polynomial.fft import ( _fft_in_place, _get_bit_reverse, - _get_roots, _get_twiddle_factors, evaluate_poly_fft, - evaluate_poly_over_domain, inverse_fft, ) @@ -71,25 +69,6 @@ def test_get_twiddle_factors_basic(self): # twiddles[1] = [1, 4] assert twiddles[1] == [1, 4] - def test_get_roots(self): - """Test roots of unity computation.""" - n = 8 - omega = 4 - prime = 17 - - # 2 is an 8th root of unity mod 17. - omega = 2 - - roots = _get_roots(n, omega, prime) - - # Should have n/2 = 4 roots - assert len(roots) == 4 - # roots[i] = omega^i - assert roots[0] == 1 - assert roots[1] == 2 - assert roots[2] == 4 - assert roots[3] == 8 - def test_fft_in_place_trivial(self): """Test FFT with single coefficient.""" coeffs = [5] @@ -127,51 +106,6 @@ def test_inverse_fft(self): assert recovered == original - def test_evaluate_poly_over_domain_basic(self): - """Test polynomial evaluation over domain.""" - # poly = 1 + 2x (coefficients [1, 2]) - poly = [1, 2] - n = 4 - omega = 4 # 4th root of unity mod 17 - prime = 17 - domain = [pow(omega, i, prime) for i in range(n)] # [1, 4, 16, 13] - - result = evaluate_poly_over_domain(poly, domain, omega, prime) - - # Should evaluate at each domain point - # At x=1: 1 + 2*1 = 3 - # At x=4: 1 + 2*4 = 9 - # At x=16: 1 + 2*16 = 33 mod 17 = 16 - # At x=13: 1 + 2*13 = 27 mod 17 = 10 - assert len(result) == 4 - - def test_evaluate_poly_over_domain_with_padding(self): - """Test polynomial evaluation when poly is smaller than domain.""" - poly = [5] # constant polynomial - n = 4 - omega = 4 - prime = 17 - domain = [pow(omega, i, prime) for i in range(n)] - - result = evaluate_poly_over_domain(poly, domain, omega, prime) - - # Constant polynomial evaluates to same value everywhere - assert len(result) == 4 - - def test_evaluate_poly_over_domain_with_folding(self): - """Test polynomial evaluation with folding (poly larger than domain).""" - # poly of degree > n requires folding - poly = [1, 2, 3, 4, 5] # degree 4 - n = 4 - omega = 4 - prime = 17 - domain = [pow(omega, i, prime) for i in range(n)] - - result = evaluate_poly_over_domain(poly, domain, omega, prime) - - # Result should fold coefficients - assert len(result) == 4 - def test_evaluate_poly_fft_standard(self): """Test FFT polynomial evaluation with coset_offset=1.""" poly = [1, 2, 3, 4] @@ -254,11 +188,3 @@ def test_caching_twiddle_factors(self): assert result1 == result2 assert result1 is result2 # Same cached object - - def test_caching_roots(self): - """Test that roots caching works correctly.""" - result1 = _get_roots(16, OMEGA, S_PRIME) - result2 = _get_roots(16, OMEGA, S_PRIME) - - assert result1 == result2 - assert result1 is result2 diff --git a/tests/test_coverage/test_interpolation.py b/tests/test_coverage/test_interpolation.py deleted file mode 100644 index d0afe33..0000000 --- a/tests/test_coverage/test_interpolation.py +++ /dev/null @@ -1,37 +0,0 @@ -"""Additional tests for interpolation module to improve coverage.""" - -from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA -from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.polynomial.interpolation import poly_interpolate_fft - - -class TestInterpolation: - """Test polynomial interpolation functions.""" - - def test_poly_interpolate_fft_power_of_two(self): - """Test FFT interpolation with power of 2 size.""" - # 8 values - y_values = [1, 2, 3, 4, 5, 6, 7, 8] - - # Use real constants - coeffs = poly_interpolate_fft(y_values, OMEGA, S_PRIME) - - assert coeffs is not None - - def test_poly_interpolate_fft_single_value(self): - """Test FFT interpolation with single value.""" - y_values = [42] - - coeffs = poly_interpolate_fft(y_values, OMEGA, S_PRIME) - - assert coeffs is not None - - def test_poly_interpolate_fft_zeros(self): - """Test FFT interpolation with all zeros.""" - y_values = [0, 0, 0, 0] - - coeffs = poly_interpolate_fft(y_values, OMEGA, S_PRIME) - - assert coeffs is not None - # All zero input should give all zero coeffs - assert all(c == 0 for c in coeffs[:4]) diff --git a/tests/test_coverage/test_ops.py b/tests/test_coverage/test_ops.py index 0852763..e9348df 100644 --- a/tests/test_coverage/test_ops.py +++ b/tests/test_coverage/test_ops.py @@ -6,17 +6,12 @@ lagrange_basis_polynomial, poly_add, poly_divide_by_vanishing, - poly_evaluate_domain, poly_evaluate_single, poly_mul_linear, poly_multiply, poly_scalar_mul, - poly_subtract, - vect_scalar_mul, ) -poly_scalar = poly_scalar_mul - class TestPolynomialOps: """Test cases for polynomial operations.""" @@ -44,29 +39,6 @@ def test_poly_add_with_modulo(self): result = poly_add(p1, p2, 17) assert result == [1, 3] # (18 mod 17, 20 mod 17) - def test_poly_subtract_same_length(self): - """Test polynomial subtraction with same length.""" - p1 = [5, 7, 9] - p2 = [1, 2, 3] - result = poly_subtract(p1, p2, 17) - assert result == [4, 5, 6] - - def test_poly_subtract_different_length(self): - """Test polynomial subtraction with different lengths.""" - p1 = [5, 7] - p2 = [1, 2, 3, 4] - result = poly_subtract(p1, p2, 17) - # [5-1, 7-2, 0-3, 0-4] = [4, 5, -3, -4] mod 17 = [4, 5, 14, 13] - assert result == [4, 5, 14, 13] - - def test_poly_subtract_with_negative_result(self): - """Test polynomial subtraction resulting in negative (modular).""" - p1 = [1, 2] - p2 = [5, 8] - result = poly_subtract(p1, p2, 17) - # [1-5, 2-8] = [-4, -6] mod 17 = [13, 11] - assert result == [13, 11] - def test_poly_multiply_small(self): """Test polynomial multiplication with small polynomials.""" p1 = [1, 2] # 1 + 2x @@ -96,13 +68,13 @@ def test_poly_multiply_single_coefficient(self): def test_poly_scalar_basic(self): """Test polynomial scalar multiplication.""" poly = [1, 2, 3] - result = poly_scalar(poly, 5, 17) + result = poly_scalar_mul(poly, 5, 17) assert result == [5, 10, 15] def test_poly_scalar_with_modulo(self): """Test polynomial scalar multiplication with modular reduction.""" poly = [3, 4, 5] - result = poly_scalar(poly, 6, 17) + result = poly_scalar_mul(poly, 6, 17) # [3*6, 4*6, 5*6] = [18, 24, 30] mod 17 = [1, 7, 13] assert result == [1, 7, 13] @@ -126,31 +98,11 @@ def test_poly_evaluate_single_quadratic(self): # 1 + 2*2 + 3*4 = 1 + 4 + 12 = 17 mod 17 = 0 assert result == 0 - def test_poly_evaluate_domain_arbitrary_points(self): - """Test domain evaluation with multiple arbitrary points.""" - poly = [1, 2] # 1 + 2x - points = [0, 1, 2, 3] - result = poly_evaluate_domain(poly, points, 17) - # At x=0: 1, x=1: 3, x=2: 5, x=3: 7 - assert result == [1, 3, 5, 7] - - def test_poly_evaluate_domain_d512(self): - """Test domain evaluation with D_512 uses FFT.""" - poly = [1, 2, 3, 4] - result = poly_evaluate_domain(poly, D_512, S_PRIME) - assert len(result) == 512 - - def test_poly_evaluate_domain_d2048(self): - """Test domain evaluation with D_2048 uses FFT.""" - poly = [1, 2, 3, 4] - result = poly_evaluate_domain(poly, D_2048, S_PRIME) - assert len(result) == 2048 - def test_poly_divide_by_vanishing_quotient_zero(self): """Test division when deg(f) < domain_size (quotient is 0).""" coeffs = [1, 2, 3] # degree 2 domain_size = 8 - result = poly_divide_by_vanishing(coeffs, domain_size, 17) + result = poly_divide_by_vanishing(coeffs, domain_size) assert result == [0] def test_poly_divide_by_vanishing_basic(self): @@ -160,7 +112,7 @@ def test_poly_divide_by_vanishing_basic(self): domain_size = 4 # Dividing by x^4 - 1 # quotient is coefficient of x^4, which is 1 - result = poly_divide_by_vanishing(coeffs, domain_size, 17) + result = poly_divide_by_vanishing(coeffs, domain_size) assert result == [1] def test_poly_divide_by_vanishing_larger(self): @@ -168,7 +120,7 @@ def test_poly_divide_by_vanishing_larger(self): # f(x) with degree 7, domain_size 4 coeffs = [1, 2, 3, 4, 5, 6, 7, 8] # coeffs[4:] = [5,6,7,8] is initial quotient domain_size = 4 - result = poly_divide_by_vanishing(coeffs, domain_size, 17) + result = poly_divide_by_vanishing(coeffs, domain_size) # quotient = coeffs[n:] = [5, 6, 7, 8] assert len(result) == 4 @@ -176,7 +128,7 @@ def test_poly_divide_by_vanishing_strips_zeros(self): """Test that division strips trailing zeros from quotient.""" coeffs = [1, 2, 3, 4, 5, 0, 0, 0] # trailing zeros in quotient part domain_size = 4 - result = poly_divide_by_vanishing(coeffs, domain_size, 17) + result = poly_divide_by_vanishing(coeffs, domain_size) # quotient should have trailing zeros stripped assert result[-1] != 0 or result == [0] @@ -259,18 +211,6 @@ def test_get_root_of_unity_various_sizes(self): if n > 1: assert pow(root, n // 2, S_PRIME) != 1 - def test_vect_scalar_mul_basic(self): - """Test vector scalar multiplication.""" - vec = [1, 2, 3, 4] - result = vect_scalar_mul(vec, 5, 17) - assert result == [5, 10, 15, 3] # 20 mod 17 = 3 - - def test_vect_scalar_mul_no_modulo(self): - """Test vector scalar multiplication without modulo.""" - vec = [1, 2, 3] - result = vect_scalar_mul(vec, 5, None) - assert result == [5, 10, 15] - class TestPolynomialOpsEdgeCases: """Edge case tests for polynomial operations.""" @@ -280,11 +220,6 @@ def test_poly_add_empty(self): result = poly_add([], [], 17) assert result == [] - def test_poly_subtract_empty(self): - """Test subtracting empty polynomials.""" - result = poly_subtract([], [], 17) - assert result == [] - def test_poly_multiply_by_zero(self): """Test multiplying by zero polynomial.""" p1 = [1, 2, 3] @@ -295,7 +230,7 @@ def test_poly_multiply_by_zero(self): def test_poly_scalar_zero(self): """Test scalar multiplication by zero.""" poly = [1, 2, 3] - result = poly_scalar(poly, 0, 17) + result = poly_scalar_mul(poly, 0, 17) assert result == [0, 0, 0] def test_poly_evaluate_empty(self): diff --git a/tests/test_coverage/test_pcs_utils.py b/tests/test_coverage/test_pcs_utils.py index 7e3aed0..cabd1cd 100644 --- a/tests/test_coverage/test_pcs_utils.py +++ b/tests/test_coverage/test_pcs_utils.py @@ -1,59 +1,12 @@ """Additional tests for utils module to improve coverage.""" from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.pcs.utils import synthetic_div, synthetic_div_with_eval +from dot_ring.ring_proof.pcs.utils import synthetic_div_with_eval class TestPCSUtils: """Test PCS utility functions.""" - def test_synthetic_div_basic(self): - """Test synthetic division basic case.""" - # Polynomial coefficients (x^2 + 2x + 1) - poly = [1, 2, 1] # 1 + 2x + x^2 - x = 1 - y = 4 # f(1) = 1 + 2 + 1 = 4 - - quotient = synthetic_div(poly, x, y) - - assert quotient is not None - - def test_synthetic_div_with_modulo(self): - """Test synthetic division with modulo.""" - poly = [1, 2, 3, 4] - x = 5 - # Compute y = f(x) - y = sum(c * pow(x, i, S_PRIME) for i, c in enumerate(poly)) % S_PRIME - - quotient = synthetic_div(poly, x, y) - - assert quotient is not None - - def test_synthetic_div_simple_linear(self): - """Test synthetic division of linear polynomial.""" - # f(x) = 2 + 3x divided by (x - 2) - # f(2) = 2 + 6 = 8 - poly = [2, 3] # 2 + 3x - x = 2 - y = 8 - - quotient = synthetic_div(poly, x, y) - - assert quotient is not None - # Quotient should be just [3] since f(x) = 8 + 3(x - 2) = 8 + 3x - 6 = 2 + 3x - - def test_synthetic_div_larger_poly(self): - """Test synthetic division with larger polynomial.""" - poly = [1, 1, 1, 1, 1] # 1 + x + x^2 + x^3 + x^4 - x = 3 - # f(3) = 1 + 3 + 9 + 27 + 81 = 121 - y = 121 - - quotient = synthetic_div(poly, x, y) - - assert quotient is not None - assert len(quotient) == len(poly) - 1 - def test_synthetic_div_with_eval_matches_checked_division(self): poly = [3, 5, 7, 11, 13] x = 17 @@ -62,4 +15,4 @@ def test_synthetic_div_with_eval_matches_checked_division(self): quotient, value = synthetic_div_with_eval(poly, x) assert value == y - assert quotient == synthetic_div(poly, x, y) + assert len(quotient) == len(poly) - 1 diff --git a/tests/test_curve_ops/test_curve.py b/tests/test_curve_ops/test_curve.py index bf1fd0a..996e064 100644 --- a/tests/test_curve_ops/test_curve.py +++ b/tests/test_curve_ops/test_curve.py @@ -12,7 +12,6 @@ TwistedEdwardsCurveParams, ) from dot_ring.curve.twisted_edwards.te_affine_point import TEAffinePoint -from dot_ring.ring_proof.proof.quotient_poly import QuotientPoly from dot_ring.ring_proof.transcript.serialize import serialize @@ -71,15 +70,6 @@ def test_te_affine_double_result_identity(self): # Alternatively, we can test the denominator check by mocking. pass - def test_quotient_poly_vector_xn_minus_1(self): - """Test QuotientPoly.poly_vector_xn_minus_1.""" - n = 4 - vec = QuotientPoly.poly_vector_xn_minus_1(n) - assert len(vec) == n + 1 - assert vec[0] == -1 - assert vec[n] == 1 - assert vec[1] == 0 - def test_serialize_large_int(self): """Test serialize with integer too large.""" large_int = 1 << (48 * 8 + 1) diff --git a/tests/test_ring_vrf/test_audit_regressions.py b/tests/test_ring_vrf/test_audit_regressions.py index 85f0fb6..6ecb64c 100644 --- a/tests/test_ring_vrf/test_audit_regressions.py +++ b/tests/test_ring_vrf/test_audit_regressions.py @@ -104,15 +104,15 @@ def test_prove_rejects_producer_key_that_does_not_match_secret(): RingVRF[Bandersnatch].prove(b"audit-input", b"audit-ad", sk1, pk2, ring, ring_root) -def test_verifier_key_builder_caches_root_when_full(): +def test_ring_root_builder_caches_root_when_full(): keys = _keys(8) params = RingProofParams(test_vectors=True, max_ring_size=8) context = RingContext(params) - builder = context.verifier_key_builder() + builder = context.ring_root_builder() builder.append(keys) built_root = builder.finalize() - direct_root = context.verifier_key(keys) + direct_root = context.ring_root(keys) assert built_root.to_bytes() == direct_root.to_bytes() assert builder.finalize() is built_root From 65c23b54ef40cded79691083d675d322bc314825 Mon Sep 17 00:00:00 2001 From: Prasad Kumkar Date: Fri, 19 Jun 2026 04:46:51 +0530 Subject: [PATCH 33/33] clean proof plumbing --- dot_ring/curve/curve.py | 15 - dot_ring/ring_proof/columns/columns.py | 3 +- .../ring_proof/constraints/constraints.py | 18 +- dot_ring/ring_proof/helpers.py | 199 ---------- dot_ring/ring_proof/transcript/phases.py | 91 +++-- dot_ring/ring_proof/transcript/serialize.py | 70 ---- dot_ring/ring_proof/transcript/transcript.py | 82 +--- dot_ring/ring_proof/verify.py | 373 ++---------------- dot_ring/vrf/ietf/tiny.py | 38 +- dot_ring/vrf/ring/batch_verifier.py | 8 +- dot_ring/vrf/ring/proof_payload.py | 35 +- dot_ring/vrf/ring/root.py | 65 ++- dot_ring/vrf/ring/vrf.py | 6 +- dot_ring/vrf/vrf.py | 194 +-------- tests/test_coverage/test_helpers.py | 295 -------------- tests/test_coverage/test_pairing.py | 2 +- tests/test_coverage/test_serialize.py | 61 --- tests/test_coverage/test_transcript.py | 110 ------ tests/test_coverage/test_verify.py | 250 ------------ tests/test_curve_ops/test_curve.py | 20 - tests/test_curve_ops/test_gaps.py | 30 +- tests/test_vectors.py | 9 +- tests/test_verify_ring_sig.py | 19 +- 23 files changed, 200 insertions(+), 1793 deletions(-) delete mode 100644 dot_ring/ring_proof/helpers.py delete mode 100644 dot_ring/ring_proof/transcript/serialize.py delete mode 100644 tests/test_coverage/test_helpers.py delete mode 100644 tests/test_coverage/test_serialize.py delete mode 100644 tests/test_coverage/test_transcript.py delete mode 100644 tests/test_coverage/test_verify.py diff --git a/dot_ring/curve/curve.py b/dot_ring/curve/curve.py index 8a7fa13..e8114b5 100644 --- a/dot_ring/curve/curve.py +++ b/dot_ring/curve/curve.py @@ -247,19 +247,11 @@ def mod_inverse(self, val: int) -> int: raise ValueError("No inverse exists") return int(_invert(_mpz(val), modulus)) - @staticmethod - def CMOV(a: int, b: int, cond: int) -> int: - """Constant-time conditional move: if cond is True, return b; else return a.""" - return b if cond else a - @staticmethod def sgn0(x: int) -> int: """Return the sign of x: 1 if odd, 0 if even.""" return x % 2 - def find_z_ell2(self) -> int: - return 5 # 5 is only for bandersnatch - def is_square(self, val: int) -> bool: """Check if val is a quadratic residue mod p using gmpy2 if available.""" if val == 0: @@ -329,9 +321,6 @@ def inv(self, x: int) -> int: # modular inverse in GF(p) return pow(x, self.params.field_modulus - 2, self.params.field_modulus) - def legendre_symbol(self, x: int) -> int: - return pow(x, (self.params.field_modulus - 1) // 2, self.params.field_modulus) - @staticmethod def sha512(data: bytes) -> bytes: """Calculate SHA-512 hash""" @@ -343,10 +332,6 @@ def I2OSP(value: int, length: int) -> bytes: raise ValueError("integer too large") return value.to_bytes(length, "big") - @staticmethod - def OS2IP(octets: bytearray) -> int: - return int.from_bytes(octets, "big") - @staticmethod def strxor(s1: bytes, s2: bytes) -> bytes: n = min(len(s1), len(s2)) diff --git a/dot_ring/ring_proof/columns/columns.py b/dot_ring/ring_proof/columns/columns.py index 6814e0d..4eca673 100644 --- a/dot_ring/ring_proof/columns/columns.py +++ b/dot_ring/ring_proof/columns/columns.py @@ -6,7 +6,6 @@ from typing import cast from dot_ring.ring_proof.constants import DEFAULT_SIZE, MAX_RING_SIZE, OMEGAS, S_PRIME, ZK_ROWS, SeedPoint -from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS, G1Commitment @@ -139,7 +138,7 @@ def _conditional_sum_accumulator(self, b_vector: list[int]) -> tuple[list[int], for i in range(1, acc_len): next_pt = acc[i - 1] if b_vector[i - 1] == 0 else self._add(acc[i - 1], self.ring_pk[i - 1]) acc.append(next_pt) - return cast(tuple[list[int], list[int]], H.unzip(acc)) + return [point[0] for point in acc], [point[1] for point in acc] def _inner_product_accumulator(self, b_vector: list[int]) -> list[int]: acc = [0] diff --git a/dot_ring/ring_proof/constraints/constraints.py b/dot_ring/ring_proof/constraints/constraints.py index 619720d..0aefea4 100644 --- a/dot_ring/ring_proof/constraints/constraints.py +++ b/dot_ring/ring_proof/constraints/constraints.py @@ -1,6 +1,6 @@ from __future__ import annotations -from collections.abc import Mapping, Sequence +from collections.abc import Sequence from dataclasses import dataclass, field from typing import Any, cast @@ -64,20 +64,6 @@ def __post_init__(self) -> None: self._accy4 = _to_radix(self.acc_y, len(radix_domain), self.params.radix_omega, self.params.prime) self._accip4 = _to_radix(self.acc_ip, len(radix_domain), self.params.radix_omega, self.params.prime) - # convenient classmethod for Column builders - @classmethod - def from_columns(cls, columns: Mapping[str, Sequence[int]], Result_plus_Seed: Any) -> RingConstraintBuilder: - return cls( - Result_plus_Seed=Result_plus_Seed, - acc_ip=columns["accip"], - b=columns["b"], - s=columns["s"], - acc_x=columns["accx"], - acc_y=columns["accy"], - px=columns["Px"], - py=columns["Py"], - ) - def compute(self) -> dict[str, list[int]]: c2x, c3x = self._c2_c3() c5x, c6x = self._c5_c6() @@ -91,8 +77,6 @@ def compute(self) -> dict[str, list[int]]: "c7x": self._c7(), } - compute_all = compute # alias - def _shifted_index(self, index: int) -> int: shifted = index + self._ctx["shift"] if shifted >= len(self._px4): diff --git a/dot_ring/ring_proof/helpers.py b/dot_ring/ring_proof/helpers.py deleted file mode 100644 index 9911f85..0000000 --- a/dot_ring/ring_proof/helpers.py +++ /dev/null @@ -1,199 +0,0 @@ -from typing import Any, Literal, TypeVar, cast - -from py_ecc.bls import point_compression -from py_ecc.optimized_bls12_381 import FQ, FQ2, is_on_curve - - -class Helpers: - @staticmethod - def knocker_delta(i: int, j: int) -> int: - """ " - input:i,j - output: return 1 if i==j 0 otherwise - """ - return 1 if i == j else 0 - - T = TypeVar("T") - - @staticmethod - def unzip(points: list[tuple[T, T]]) -> tuple[list[T], list[T]]: - """ " - input:Gets a list of points as input - output:Splits the points into x,y co-ordinates - """ - x_c = [pt[0] for pt in points] - y_c = [pt[1] for pt in points] - return x_c, y_c - - @staticmethod - # bls point to string - def bls_g1_compress(bls_point: Any) -> str: - try: - from dot_ring import blst - - if isinstance(bls_point, blst.P1): - return bls_point.compress().hex() - if isinstance(bls_point, blst.P1_Affine): - return blst.P1(bls_point).compress().hex() - except (ImportError, AttributeError): - pass - - if len(bls_point) == 2: - point = (FQ(bls_point[0]), FQ(bls_point[1]), FQ(1)) - else: - point = (FQ(bls_point[0]), FQ(bls_point[1]), FQ(bls_point[2])) - - # Compress the point - compressed = point_compression.compress_G1(point) - hex_rep = compressed.to_bytes(48, "big").hex() - return str(hex_rep) - - @staticmethod - def bls_g1_decompress(byte_array: bytes | str) -> tuple[FQ, FQ, FQ]: - """ - Decompress a BLS G1 point from its byte representation. - - Args: - byte_array (bytes | str): Byte array or hex string representing the compressed point. - - Returns: - Tuple[FQ, FQ, FQ]: Decompressed point in Jacobian coordinates. - """ - # Fast path: use BLST directly if available - try: - from dot_ring import blst - - if isinstance(byte_array, str): - byte_array = bytes.fromhex(byte_array) - p1_affine = blst.P1_Affine(byte_array) - affine_bytes = p1_affine.serialize() - # TODO: Alt we could recover x,y from compressed bytes directly - x = int.from_bytes(affine_bytes[:48], "big") - y = int.from_bytes(affine_bytes[48:], "big") - return (FQ(x), FQ(y), FQ(1)) - except (ImportError, Exception): - # Fallback to py_ecc - if isinstance(byte_array, bytes): - byte_array = byte_array.hex() - dcp_scalar = int(byte_array, 16) - else: - dcp_scalar = int(byte_array, 16) - decompressed = point_compression.decompress_G1(dcp_scalar) - if not is_on_curve(decompressed, 4): - raise ValueError("Invalid BLS12-381 G1 point") from None - return cast(tuple[FQ, FQ, FQ], decompressed) - - @staticmethod - def bls_g2_compress(g2_point: tuple) -> str: - if len(g2_point) == 3: - x, y, z = g2_point - point = ( - FQ2([x.coeffs[0], x.coeffs[1]]) if hasattr(x, "coeffs") else x, - FQ2([y.coeffs[0], y.coeffs[1]]) if hasattr(y, "coeffs") else y, - FQ2([z.coeffs[0], z.coeffs[1]]) if hasattr(z, "coeffs") else z, - ) - else: - x, y = g2_point - point = ( - FQ2([x.coeffs[0], x.coeffs[1]]) if hasattr(x, "coeffs") else x, - FQ2([y.coeffs[0], y.coeffs[1]]) if hasattr(y, "coeffs") else y, - FQ2([1, 0]), - ) - - # compress the point - # compress the point - compressed = point_compression.compress_G2(point) - return str(compressed[0].to_bytes(48, "big").hex() + compressed[1].to_bytes(48, "big").hex()) - - @staticmethod - # for fiat_shamir - def to_int(tup: tuple) -> tuple[int, int]: - x, y = tup - res = int(x), int(y) - return res - - @staticmethod - # int to hex_string - def to_bytes(val: int) -> str: - res = val.to_bytes(32, "little") - return res.hex() - - @staticmethod - def altered_points(g2_points: Any) -> list: - res = [(b, a) for pair in g2_points for point in pair for a, b in [point]] - return res - - @staticmethod - def to_scalar_int(string: str | bytes) -> int: - if isinstance(string, bytes): - return int.from_bytes(string, "little") - byts = bytes.fromhex(string) - return int.from_bytes(byts, "little") - - @staticmethod - def require_length(data: bytes, expected: int, name: str) -> None: - if len(data) != expected: - raise ValueError(f"{name} must be exactly {expected} bytes, got {len(data)}") - - @staticmethod - def canonical_scalar_from_bytes( - data: bytes, - modulus: int, - name: str = "scalar", - order: Literal["little", "big"] = "little", - length: int = 32, - ) -> int: - Helpers.require_length(data, length, name) - value = int.from_bytes(data, order) - if value >= modulus: - raise ValueError(f"{name} is not canonical") - return value - - @staticmethod - def bls_projective_2_affine(points_3d: list[tuple[Any, Any, Any]]) -> list[tuple[Any, Any]]: - """ - Convert a list of 3D coordinate points to 2D by removing the z-coordinate. - """ - return [(x, y) for x, y, z in points_3d] - - @staticmethod - def to_fq(point: tuple[int, int, int]) -> tuple[FQ, FQ, FQ]: - """convert a int type point cords to FQ type""" - x, y, z = point - res = (FQ(x), FQ(y), FQ(z)) - return res - - @staticmethod - def l_endian_2_int(byte_array: bytes) -> int: - if isinstance(byte_array, str): - return int.from_bytes(bytes.fromhex(byte_array), "little") - return int.from_bytes(byte_array, "little") - - @staticmethod - def str_to_int(byte_array: bytes, order: Literal["little", "big"]) -> int: - if isinstance(byte_array, str): - return int.from_bytes(bytes.fromhex(byte_array), order) - return int.from_bytes(byte_array, order) - - @staticmethod - def int_to_str(val: int, order: Literal["little", "big"], n_bytes: int = 32) -> bytes: - return val.to_bytes(n_bytes, order) - - @staticmethod - def b_endian_2_int(byte_array: bytes) -> int: - if isinstance(byte_array, str): - return int.from_bytes(bytes.fromhex(byte_array), "big") - return int.from_bytes(byte_array, "big") - - @staticmethod - def to_b_endian(val: int, n_bytes: int = 32) -> bytes: - return val.to_bytes(n_bytes, "big") - - @staticmethod - def to_l_endian(val: int, n_bytes: int = 32) -> bytes: - return val.to_bytes(n_bytes, "little") - - @staticmethod - def pt_len(prime_field: int) -> int: - coord_size = (prime_field.bit_length() + 7) // 8 - return coord_size diff --git a/dot_ring/ring_proof/transcript/phases.py b/dot_ring/ring_proof/transcript/phases.py index e9fed1e..259f758 100644 --- a/dot_ring/ring_proof/transcript/phases.py +++ b/dot_ring/ring_proof/transcript/phases.py @@ -1,49 +1,34 @@ -# phases.py from __future__ import annotations from collections.abc import Sequence from typing import Any -from dot_ring.ring_proof.transcript.serialize import serialize from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript __all__ = [ "derive_challenges_after_vk", - "phase1_alphas", "phase1_alphas_after_vk", "phase2_eval_point", "phase3_nu_vector", + "serialize_verifier_key", ] -def phase1_alphas( - t: FiatShamirTranscript, - vk: Any, - result_point: Any, - witness_commitments: Sequence[Any], -) -> Any: - """Append phase‑1 data and return 7 constraint‑aggregation coefficients.""" - t.add_serialized_bytes(b"vk", serialize(vk)) - t.add_serialized_bytes(b"instance", serialize(result_point)) - t.add_serialized_bytes(b"committed_cols", serialize(witness_commitments)) - return t, t.get_constraints_aggregation_coeffs(7) - - def phase1_alphas_after_vk( t: FiatShamirTranscript, result_point: Any, witness_commitments: Sequence[Any], ) -> Any: """Append phase-1 data after the verifier key has already been absorbed.""" - t.add_serialized_bytes(b"instance", _serialize_instance(result_point)) - t.add_serialized_bytes(b"committed_cols", _serialize_commitments(witness_commitments)) - return t, t.get_constraints_aggregation_coeffs(7) + t.absorb_labeled(b"instance", _serialize_instance(result_point)) + t.absorb_labeled(b"committed_cols", _serialize_commitments(witness_commitments)) + return t, t.challenges(b"constraints_aggregation", 7) def phase2_eval_point(t: FiatShamirTranscript, C_q_commitment: Any) -> Any: - """Append quotient commitment and derive evaluation point ζ.""" - t.add_serialized_bytes(b"quotient", _serialize_bytes_or_fallback(C_q_commitment)) - return t, t.get_evaluation_point(1)[0] + """Append quotient commitment and derive the evaluation point.""" + t.absorb_labeled(b"quotient", _serialize_bytes(C_q_commitment)) + return t, t.challenge(b"evaluation_point") def phase3_nu_vector( @@ -51,10 +36,10 @@ def phase3_nu_vector( rel_poly_evals: list[int] | bytes, agg_poly_eval: int, ) -> list[int]: - """Append evaluation bundle and linearisation eval, return 8 ν‑challenges.""" - t.add_serialized_bytes(b"register_evaluations", _serialize_ints_or_bytes(rel_poly_evals)) - t.add_serialized_bytes(b"shifted_linearization_evaluation", _serialize_int_or_bytes(agg_poly_eval)) - return t.get_kzg_aggregation_challenges(8) + """Append evaluation bundle and linearization eval, then return 8 challenges.""" + t.absorb_labeled(b"register_evaluations", _serialize_ints_or_bytes(rel_poly_evals)) + t.absorb_labeled(b"shifted_linearization_evaluation", _serialize_int_or_bytes(agg_poly_eval)) + return t.challenges(b"kzg_aggregation", 8) def derive_challenges_after_vk( @@ -69,25 +54,30 @@ def derive_challenges_after_vk( t = t.copy() instance_bytes = _serialize_instance(result_point) commitments_bytes = _serialize_commitments(witness_commitments) - quotient_bytes = _serialize_bytes_or_fallback(quotient_commitment) + quotient_bytes = _serialize_bytes(quotient_commitment) evals_bytes = _serialize_ints_or_bytes(evals) lin_eval_bytes = _serialize_int_or_bytes(lin_eval) - t.add_serialized_bytes(b"instance", instance_bytes) - t.add_serialized_bytes(b"committed_cols", commitments_bytes) - alpha_list = t.get_constraints_aggregation_coeffs(7) - t.add_serialized_bytes(b"quotient", quotient_bytes) - zeta = t.get_evaluation_point(1)[0] - t.add_serialized_bytes(b"register_evaluations", evals_bytes) - t.add_serialized_bytes(b"shifted_linearization_evaluation", lin_eval_bytes) - return t, alpha_list, zeta, t.get_kzg_aggregation_challenges(8) + t.absorb_labeled(b"instance", instance_bytes) + t.absorb_labeled(b"committed_cols", commitments_bytes) + alpha_list = t.challenges(b"constraints_aggregation", 7) + t.absorb_labeled(b"quotient", quotient_bytes) + zeta = t.challenge(b"evaluation_point") + t.absorb_labeled(b"register_evaluations", evals_bytes) + t.absorb_labeled(b"shifted_linearization_evaluation", lin_eval_bytes) + return t, alpha_list, zeta, t.challenges(b"kzg_aggregation", 8) + + +def serialize_verifier_key(g1: Any, g2_points: Sequence[Any], commitments: Sequence[Any]) -> bytes: + """Serialize the fixed verifier key exactly as the ring transcript expects.""" + return _serialize_bls_pair(g1) + b"".join(_serialize_bls_pair(point) for point in g2_points) + _serialize_commitments(commitments) def _serialize_instance(result_point: Any) -> bytes: if isinstance(result_point, tuple) and len(result_point) == 2: x, y = result_point return int(x).to_bytes(32, "little") + int(y).to_bytes(32, "little") - return serialize(result_point) + return _serialize_bytes(result_point) def _serialize_commitments(commitments: Sequence[Any]) -> bytes: @@ -97,15 +87,17 @@ def _serialize_commitments(commitments: Sequence[Any]) -> bytes: return bytes(commitments) if all(isinstance(commitment, bytes | bytearray) for commitment in commitments): return b"".join(bytes(commitment) for commitment in commitments) - return serialize(commitments) + return b"".join(_serialize_bls_pair(commitment) for commitment in commitments) -def _serialize_bytes_or_fallback(value: Any) -> bytes: +def _serialize_bytes(value: Any) -> bytes: if isinstance(value, bytes): return value if isinstance(value, bytearray): return bytes(value) - return serialize(value) + if isinstance(value, tuple) and len(value) == 2: + return _serialize_bls_pair(value) + raise TypeError(f"expected bytes or a BLS point, got {type(value).__name__}") def _serialize_ints_or_bytes(values: list[int] | bytes) -> bytes: @@ -113,11 +105,7 @@ def _serialize_ints_or_bytes(values: list[int] | bytes) -> bytes: return values if isinstance(values, bytearray): return bytes(values) - try: - chunks = [value.to_bytes(32, "little") for value in values] - except AttributeError: - return serialize(values) - return b"".join(chunks) + return b"".join(int(value).to_bytes(32, "little") for value in values) def _serialize_int_or_bytes(value: Any) -> bytes: @@ -125,6 +113,15 @@ def _serialize_int_or_bytes(value: Any) -> bytes: return value if isinstance(value, bytearray): return bytes(value) - if isinstance(value, int): - return value.to_bytes(32, "little") - return serialize(value) + return int(value).to_bytes(32, "little") + + +def _serialize_bls_pair(point: Any) -> bytes: + if not isinstance(point, tuple | list) or len(point) != 2: + raise TypeError(f"expected a two-coordinate BLS point, got {type(point).__name__}") + x, y = point + return _serialize_bls_coord(x) + _serialize_bls_coord(y) + + +def _serialize_bls_coord(value: Any) -> bytes: + return int(value).to_bytes(48, "big") diff --git a/dot_ring/ring_proof/transcript/serialize.py b/dot_ring/ring_proof/transcript/serialize.py deleted file mode 100644 index 4499dba..0000000 --- a/dot_ring/ring_proof/transcript/serialize.py +++ /dev/null @@ -1,70 +0,0 @@ -# serializer.py -from __future__ import annotations - -from typing import Any - -from dot_ring.ring_proof.constants import S_PRIME - -__all__ = ["serialize"] - - -def serialize(obj: Any) -> bytes: - """Serialize objects into bytes format exactly as in the original implementation.""" - # Handle Scalar types by converting to int - if type(obj).__name__ == "Scalar": - return serialize(int(obj)) - - if isinstance(obj, int): - byte_len = (obj.bit_length() + 7) // 8 - if byte_len <= 32: - return obj.to_bytes(32, "little") - elif byte_len <= 48: - return obj.to_bytes(48, "big") - else: - raise ValueError("Integer too large to serialize in 48 bytes") - - elif isinstance(obj, tuple): - if len(obj) == 2 and (obj[0].bit_length() // 8 + obj[1].bit_length() // 8) > 64: - # G1 point (x, y) where both are integers - x, y = obj - return serialize(x) + serialize(y) - else: - x, y = obj - # Uncompressed: serialize x, y, and flag - b"\x01" if x > (S_PRIME - 1) // 2 else b"" - return serialize(x) + serialize(y) - - elif isinstance(obj, list): - # List of items - result = b"" - for item in obj: - result += serialize(item) - return result - - elif isinstance(obj, dict): - # Handle dictionary (like verifier key) - result = b"" - if "g1" in obj and "g2" in obj: - # Serialize g1 point - result += serialize(obj["g1"]) - # Handle g2 as a list - g2_list = obj["g2"] - for g2_item in g2_list: - result += serialize(g2_item) - - # Add commitments if present - if "commitments" in obj: - commitments = obj["commitments"] - for commitment in commitments: - result += serialize(commitment) - - return result - - elif isinstance(obj, bytes): - return obj - - elif isinstance(obj, bytearray): - return bytes(obj) - - else: - raise TypeError(f"Unsupported object type for serialization: {type(obj)}") diff --git a/dot_ring/ring_proof/transcript/transcript.py b/dot_ring/ring_proof/transcript/transcript.py index 1774c9c..7b2d656 100644 --- a/dot_ring/ring_proof/transcript/transcript.py +++ b/dot_ring/ring_proof/transcript/transcript.py @@ -1,4 +1,3 @@ -# transcript.py from __future__ import annotations import hashlib @@ -38,93 +37,66 @@ def copy(self) -> FiatShamirTranscript: clone._challenge_bytes = self._challenge_bytes return clone - def separate(self) -> None: - """Write length footer if needed and reset counter.""" + def _separate(self) -> None: if self._length is not None: self._shake.update(struct.pack(">I", self._length)) self._length = None - def write(self, data: bytes) -> None: - """Write data to the transcript.""" + def _write(self, data: bytes) -> None: if self._length is None: self._length = 0 self._shake.update(data) self._length += len(data) - def write_bytes(self, data: bytes) -> None: - """ - Write `data` into the transcript, breaking into <=2^31-1 chunks - and inserting length-footers between them. - """ + def _write_bytes(self, data: bytes) -> None: HIGH = 1 << 31 idx = 0 total_len = len(data) while idx < total_len: - # Initialize or fetch the byte-counter since last separator if self._length is None: self._length = 0 - # How many bytes can we consume before hitting (2^31-1) remaining_allowed = (HIGH - 1) - self._length to_take = min(remaining_allowed, total_len - idx) - # Absorb that slice chunk = data[idx : idx + to_take] - self.write(chunk) + self._write(chunk) - # Advance idx += to_take - # If we're done, exit if idx >= total_len: return - # Otherwise we hit the chunk-size limit: set the MSB flag if self._length is not None: self._length |= HIGH - # Emit a separator and loop for the rest - self.separate() + self._separate() def label(self, lbl: bytes) -> None: """Add a domain separation label.""" - self.separate() + self._separate() self._shake.update(_label_prefix(lbl)) self._length = None - def challenge(self, label: bytes) -> int: - """Generate a challenge value with the given label.""" - return self._challenge_direct(label) - - def _read_xof(self, n: int) -> bytes: - """Read n bytes from the sponge.""" - return self._shake.digest(n) - - def append(self, data: bytes) -> None: - """Append data with domain separation.""" - self.separate() - self.write_bytes(data) - self.separate() - - def add_serialized(self, label: bytes, data: bytes) -> None: - """Add labeled serialized object to transcript.""" - self.label(label) - self.append(data) - - def add_serialized_bytes(self, label: bytes, data: bytes) -> None: - """Add already-serialized small bytes without generic write dispatch.""" + def absorb_labeled(self, label: bytes, data: bytes) -> None: + """Absorb one explicitly serialized value under a transcript label.""" if len(data) >= 1 << 31: - self.add_serialized(label, data) + self.label(label) + self._separate() + self._write_bytes(data) + self._separate() return - self.separate() + self._separate() self._shake.update(_label_prefix(label) + data + struct.pack(">I", len(data))) self._length = None - def read_reduce(self) -> int: - """Read a value and reduce it modulo the field size.""" - rnd = self._read_xof(self._challenge_bytes) - val = int.from_bytes(rnd, "big") - return val % self.modulus + def challenge(self, label: bytes) -> int: + """Generate a challenge value with the given label.""" + return self._challenge_direct(label) + + def challenges(self, label: bytes, n: int) -> list[int]: + """Generate n challenge values with the given label.""" + return self._repeated_challenges(label, n) def _repeated_challenges(self, label: bytes, n: int) -> list[int]: if n <= 0: @@ -162,17 +134,3 @@ def _challenge_direct(self, label: bytes) -> int: update(_CHALLENGE_FOOTER) self._length = None return value - - def get_constraints_aggregation_coeffs(self, n: int) -> list[int]: - """Get n constraint aggregation coefficients.""" - return self._repeated_challenges(b"constraints_aggregation", n) - - def get_evaluation_point(self, n: int = 1) -> list[int]: - """Get n evaluation points.""" - out = [] - out.append(self.challenge(b"evaluation_point")) - return out - - def get_kzg_aggregation_challenges(self, n: int) -> list[int]: - """Get n KZG aggregation challenges.""" - return self._repeated_challenges(b"kzg_aggregation", n) diff --git a/dot_ring/ring_proof/verify.py b/dot_ring/ring_proof/verify.py index 07608b3..936bd56 100644 --- a/dot_ring/ring_proof/verify.py +++ b/dot_ring/ring_proof/verify.py @@ -1,29 +1,17 @@ -from typing import Any, NamedTuple, cast +from typing import Any, NamedTuple -from dot_ring import blst as _blst -from dot_ring.curve.native_field.scalar import Scalar from dot_ring.ring_proof.constants import OMEGA_2048, S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.protocol import PCS from dot_ring.ring_proof.pcs.utils import ( LinearPcsVerification, - PcsVerification, g1_to_blst, ) from dot_ring.ring_proof.transcript.phases import ( derive_challenges_after_vk, - phase1_alphas, - phase2_eval_point, - phase3_nu_vector, ) from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript -blst = cast(Any, _blst) - -# Pre-compute Scalar constants -ONE_S = Scalar(1) -ZERO_S = Scalar(0) - class RingProofFields(NamedTuple): c_b: Any @@ -65,17 +53,6 @@ def _ring_proof_fields(proof: RingProofFields | tuple[Any, ...]) -> RingProofFie return RingProofFields(*proof) -def blst_msm(points: list, scalars: list) -> Any: - """ - Multi-scalar multiplication using Pippenger's algorithm via blst. - Much faster than individual scalar_mul + add operations. - """ - if not points: - return blst.P1() - - return blst.P1_Affines.mult_pippenger(blst.P1_Affines.as_memory(points), scalars) - - def _compute_quotient_and_linearization_terms( alpha_list: list[int], v_list: list[int], @@ -97,11 +74,10 @@ def _compute_quotient_and_linearization_terms( domain_neg2_value: int, domain_neg3_value: int, domain_size: int, - domain_size_inv_value: int | None = None, - edwards_a_value: int | Scalar | None = None, - omega_value: int | None = None, - include_linearization: bool = False, -) -> int | tuple[int, int, int, int, int, int]: + domain_size_inv_value: int, + edwards_a_value: int, + omega_value: int, +) -> tuple[int, int, int, int, int, int]: if len(alpha_list) < 7 or len(v_list) < 8: raise ValueError("expected at least 7 alpha values and 8 aggregation values") @@ -125,7 +101,7 @@ def _compute_quotient_and_linearization_terms( domain_neg1 = int(domain_neg1_value) % prime domain_neg2 = int(domain_neg2_value) % prime domain_neg3 = int(domain_neg3_value) % prime - inv_size = pow(domain_size, -1, prime) if domain_size_inv_value is None else int(domain_size_inv_value) % prime + inv_size = int(domain_size_inv_value) % prime zeta_n_minus_1 = (pow(zeta, domain_size, prime) - 1) % prime zeta_minus_d4 = (zeta - domain_last) % prime @@ -184,11 +160,6 @@ def _compute_quotient_and_linearization_terms( terms = (px, py, s, b, accip, accx, accy, q_zeta) agg_zeta = sum((v * term) % prime for v, term in zip(v_values, terms, strict=True)) % prime - if not include_linearization: - return agg_zeta - - if edwards_a_value is None or omega_value is None: - raise ValueError("edwards_a_value and omega_value are required when include_linearization=True") edwards_a = int(edwards_a_value) % prime omega = int(omega_value) % prime @@ -256,12 +227,8 @@ def prepare_linear_pcs_verifications_fast( domain_size_inv, edwards_a, omega, - True, - ) - Agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega_out = cast( - tuple[int, int, int, int, int, int], - linear_eval_bundle, ) + agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega_out = linear_eval_bundle Cpx_blst, Cpy_blst, Cs_blst = fixed_cols_blst Cb_blst = g1_to_blst(proof_fields.c_b) @@ -288,76 +255,34 @@ def prepare_linear_pcs_verifications_fast( (Caccy_blst, scalar_accy), ) return ( - LinearPcsVerification(quotient_terms, Phi_zeta_blst, zeta_p, Agg_zeta), + LinearPcsVerification(quotient_terms, Phi_zeta_blst, zeta_p, agg_zeta), LinearPcsVerification(linearization_terms, Phi_zeta_omega_blst, zeta_omega, l_zeta_omega_out), ) -def lagrange_at_zeta(domain_size: int, index: int, zeta: int, omega: int, prime: int) -> Scalar: - """ - Compute L_i(zeta) using closed-form formula for roots of unity domain. - - L_i(zeta) = (omega^i / n) * (zeta^n - 1) / (zeta - omega^i) - - This is O(1) instead of O(n) polynomial evaluation! - """ - # Use Scalar for optimized arithmetic - zeta_s = Scalar(zeta) - - omega_s = Scalar(omega) - # omega^i - if index == 0: - omega_i = ONE_S - else: - omega_i = omega_s**index - - # zeta - omega^i - zeta_minus_omega_i = zeta_s - omega_i - - # Handle special case when zeta == omega^i - if zeta_minus_omega_i == ZERO_S: - return ONE_S # L_i(omega^i) = 1 - - # zeta^n - 1 - zeta_n_minus_1 = (zeta_s**domain_size) - ONE_S - - # omega^i / n - inv_size = Scalar(domain_size) ** -1 - omega_i_over_n = omega_i * inv_size - - # Final: (omega^i / n) * (zeta^n - 1) / (zeta - omega^i) - result = omega_i_over_n * zeta_n_minus_1 * (zeta_minus_omega_i**-1) - - return result - - class Verify: prime = S_PRIME pcs: type[PCS] = KZG omega: int | None = None - edwards_a = Scalar(-5) + edwards_a = -5 def __init__( self, proof: RingProofFields | tuple[Any, ...], - vk: dict | bytes, fixed_cols: list, - rl_to_proove: tuple | bytes | None, - rps: tuple, + relation_to_prove: tuple | bytes, + result_plus_seed: tuple, seed_point: tuple, - Domain: list, - raw_proof_bytes: dict | None = None, - transcript_challenge: bytes = b"Bandersnatch-SHA512-ELL2-v1", + domain: list, + transcript_prefix: FiatShamirTranscript, padding_rows: int = 4, edwards_a: int = -5, prime: int = S_PRIME, omega: int | None = None, pcs: type[PCS] = KZG, - transcript_prefix: FiatShamirTranscript | None = None, domain_size_inv: int | None = None, - transcript_witness_commitments: tuple[Any, ...] | None = None, + transcript_witness_commitments: tuple[Any, ...] | bytes | None = None, transcript_quotient_commitment: Any | None = None, - relation_to_prove: tuple | bytes | None = None, ) -> None: proof_fields = _ring_proof_fields(proof) self.Cb = proof_fields.c_b @@ -376,24 +301,15 @@ def __init__( self.Phi_zeta = proof_fields.phi_zeta self.Phi_zeta_omega = proof_fields.phi_zeta_omega - self.proof_ptr = proof_fields - self.verifier_key = vk self.Cpx, self.Cpy, self.Cs = fixed_cols - if relation_to_prove is None: - if rl_to_proove is None: - raise TypeError("relation_to_prove is required") - relation_to_prove = rl_to_proove - elif rl_to_proove is not None and rl_to_proove != relation_to_prove: - raise ValueError("relation_to_prove and rl_to_proove differ") self.relation_to_prove = relation_to_prove - self.relation_to_proove = relation_to_prove - self.Result_plus_Seed, self.sp, self.D = rps, seed_point, Domain + self.Result_plus_Seed, self.sp, self.D = result_plus_seed, seed_point, domain self.padding_rows = padding_rows self.prime = prime self.omega = omega self.pcs = pcs self.domain_size_inv = domain_size_inv if domain_size_inv is not None else pow(len(self.D), -1, self.prime) - self.edwards_a = Scalar(edwards_a) + self.edwards_a = edwards_a if self.padding_rows < 1 or self.padding_rows >= len(self.D): raise ValueError("padding_rows must be >= 1 and less than domain size") self.last_index = len(self.D) - self.padding_rows @@ -410,9 +326,8 @@ def __init__( self.Cpy_blst = g1_to_blst(self.Cpy) self.Cs_blst = g1_to_blst(self.Cs) - self.t = transcript_prefix if transcript_prefix is not None else FiatShamirTranscript(self.prime, transcript_challenge) + self.t = transcript_prefix - # Absorb into transcript witness_commitments = ( transcript_witness_commitments if transcript_witness_commitments is not None @@ -422,29 +337,14 @@ def __init__( quotient_commitment = ( transcript_quotient_commitment if transcript_quotient_commitment is not None else self.pcs.serialize_g1_uncompressed(proof_fields.c_q) ) - if transcript_prefix is not None: - self.t, self.alpha_list, self.zeta_p, self.V_list = derive_challenges_after_vk( - self.t, - self.relation_to_prove, - witness_commitments, - quotient_commitment, - proof_fields.evaluations, - proof_fields.l_zeta_omega, - ) - elif transcript_prefix is None: - evals_bytes = b"".join(v.to_bytes(32, "little") for v in proof_fields.evaluations) - lin_eval_bytes = proof_fields.l_zeta_omega.to_bytes(32, "little") - self.t, self.alpha_list = phase1_alphas( - self.t, - self.verifier_key, - self.relation_to_prove, - witness_commitments, - ) - self.t, self.zeta_p = phase2_eval_point(self.t, quotient_commitment) - self.V_list = phase3_nu_vector(self.t, evals_bytes, lin_eval_bytes) - - # Save transcript - self.cur_t = self.t + self.t, self.alpha_list, self.zeta_p, self.V_list = derive_challenges_after_vk( + self.t, + self.relation_to_prove, + witness_commitments, + quotient_commitment, + proof_fields.evaluations, + proof_fields.l_zeta_omega, + ) def _domain_omega(self) -> int: if self.omega is not None: @@ -454,182 +354,15 @@ def _domain_omega(self) -> int: raise ValueError(f"omega must be supplied for domain size {domain_size}") return pow(OMEGA_2048, 2048 // domain_size, self.prime) - def contributions_to_constraints_eval_at_zeta(self) -> tuple[Any, Any, Any, Any, Any, Any, Any]: - # Convert to Scalar for optimized arithmetic - zeta = Scalar(self.zeta_p) - sx, sy = Scalar(self.sp[0]), Scalar(self.sp[1]) - - # Precompute common values - zeta_minus_d4 = zeta - Scalar(self.D[self.last_index]) - - # Inline lagrange_at_zeta for index=0 and index=SIZE-4 - # L_i(zeta) = (omega^i / n) * (zeta^n - 1) / (zeta - omega^i) - - # Shared term: zeta^n - 1 - domain_size = len(self.D) - zeta_n_minus_1 = (zeta**domain_size) - ONE_S - - # L_0: index=0, omega^0 = 1 - # omega^0 / n = 1/n = INV_SIZE_S - # zeta - omega^0 = zeta - 1 - inv_size = Scalar(domain_size) ** -1 - zeta_minus_1 = zeta - ONE_S - if zeta_minus_1 == ZERO_S: - L_0_zeta = ONE_S - else: - L_0_zeta = inv_size * zeta_n_minus_1 * (zeta_minus_1**-1) - - # L_N_4: index=SIZE-4, omega^(SIZE-4) from the domain - # omega^(SIZE-4) / n - omega_i_N_4 = Scalar(self.D[self.last_index]) - omega_i_over_n_N_4 = omega_i_N_4 * inv_size - zeta_minus_omega_i_N_4 = zeta - omega_i_N_4 - if zeta_minus_omega_i_N_4 == ZERO_S: - L_N_4_zeta = ONE_S - else: - L_N_4_zeta = omega_i_over_n_N_4 * zeta_n_minus_1 * (zeta_minus_omega_i_N_4**-1) - - # Pre-fetch instance variables and convert to Scalar - b = Scalar(self.b_zeta) - accx = Scalar(self.accx_zeta) - accy = Scalar(self.accy_zeta) - accip = Scalar(self.accip_zeta) - px = Scalar(self.px_zeta) - py = Scalar(self.py_zeta) - s = Scalar(self.s_zeta) - rps0 = Scalar(self.Result_plus_Seed[0]) - rps1 = Scalar(self.Result_plus_Seed[1]) - - # Constraint 1 - combined operations - c1_zeta = -(accip + b * s) * zeta_minus_d4 - - # Constraints 2 and 3 - reuse calculations - x1_y1 = accx * accy - x2_y2 = px * py - one_minus_b = ONE_S - b - - c2 = b * -(x1_y1 + x2_y2) + one_minus_b * -accx - c2_zeta = c2 * zeta_minus_d4 - - c3 = b * -(x1_y1 - x2_y2) + one_minus_b * -accy - c3_zeta = c3 * zeta_minus_d4 - - # Constraint 4 - simplified - c4_zeta = b * (ONE_S - b) - - # Constraints 5-7 - combined operations - c5_zeta = (accx - sx) * L_0_zeta + (accx - rps0) * L_N_4_zeta - c6_zeta = (accy - sy) * L_0_zeta + (accy - rps1) * L_N_4_zeta - c7_zeta = accip * L_0_zeta + (accip - ONE_S) * L_N_4_zeta - - return ( - c1_zeta, - c2_zeta, - c3_zeta, - c4_zeta, - c5_zeta, - c6_zeta, - c7_zeta, - ) - - def divide(self, numr: int, denom: int) -> int: - # Inlined modular division - use built-in pow (faster than external call) - return (numr * pow(denom, -1, self.prime)) % self.prime - def is_valid(self) -> bool: """If both the verifications are true then sign is valid""" return bool(self.pcs.batch_verify_linear_preconverted(list(self._prepare_linear_pcs_verifications()))) - def _prepare_quotient_poly_verification(self) -> PcsVerification: - """Prepare KZG verification data for quotient polynomial""" - alphas_list = [Scalar(a) for a in self.alpha_list] - zeta = Scalar(self.zeta_p) - v_list = [Scalar(v) for v in self.V_list] - - # cs are now Scalars - cs = self.contributions_to_constraints_eval_at_zeta() - - # Precompute vanishing polynomial evaluation - combine pow operations - prod_sum = ONE_S - for k in range(1, 4): - prod_sum = prod_sum * (zeta - Scalar(self.D[-k])) - - # Calculate numerator efficiently - linear_combination = ZERO_S - for alpha, c in zip(alphas_list, cs, strict=False): - linear_combination = linear_combination + alpha * c - - # Re-calculating based on original quotient definition, but with optimized s_sum - s_sum = linear_combination + Scalar(self.l_zeta_omega) - domain_size = len(self.D) - zeta_pow_size_minus_1 = (zeta**domain_size) - ONE_S - - # q_zeta = (s_sum * prod_sum) / zeta_pow_size_minus_1 - q_zeta = (s_sum * prod_sum) * (zeta_pow_size_minus_1**-1) - - C_a_blst = [ - self.Cpx_blst, - self.Cpy_blst, - self.Cs_blst, - self.Cb_blst, - self.Caccip_blst, - self.Caccx_blst, - self.Caccy_blst, - self.Cq_blst, - ] - - # Use Pippenger MSM instead of loop (much faster) - # Use self.V_list (ints) directly for blst_msm - C_agg = blst_msm(C_a_blst, self.V_list) - - # Use Scalars for terms calculation - px = Scalar(self.px_zeta) - py = Scalar(self.py_zeta) - s = Scalar(self.s_zeta) - b = Scalar(self.b_zeta) - accip = Scalar(self.accip_zeta) - accx = Scalar(self.accx_zeta) - accy = Scalar(self.accy_zeta) - q = q_zeta # Scalar - - terms = [ - v_list[0] * px, - v_list[1] * py, - v_list[2] * s, - v_list[3] * b, - v_list[4] * accip, - v_list[5] * accx, - v_list[6] * accy, - v_list[7] * q, - ] - - Agg_zeta = Scalar(0) - for t in terms: - Agg_zeta = Agg_zeta + t - - return PcsVerification(C_agg, self.Phi_zeta_blst, int(zeta), int(Agg_zeta)) - - def _prepare_quotient_poly_verification_linear(self) -> LinearPcsVerification: - """Prepare quotient KZG verification with an unmaterialized commitment.""" - Agg_zeta = self._native_linear_eval_bundle()[0] - - commitment_terms = ( - (self.Cpx_blst, self.V_list[0]), - (self.Cpy_blst, self.V_list[1]), - (self.Cs_blst, self.V_list[2]), - (self.Cb_blst, self.V_list[3]), - (self.Caccip_blst, self.V_list[4]), - (self.Caccx_blst, self.V_list[5]), - (self.Caccy_blst, self.V_list[6]), - (self.Cq_blst, self.V_list[7]), - ) - return LinearPcsVerification(commitment_terms, self.Phi_zeta_blst, self.zeta_p, Agg_zeta) - def _prepare_linear_pcs_verifications( self, ) -> tuple[LinearPcsVerification, LinearPcsVerification]: """Prepare deferred linear KZG equations from one native scalar pass.""" - Agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega = self._native_linear_eval_bundle() + agg_zeta, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega = self._native_linear_eval_bundle() quotient_terms = ( (self.Cpx_blst, self.V_list[0]), (self.Cpy_blst, self.V_list[1]), @@ -646,7 +379,7 @@ def _prepare_linear_pcs_verifications( (self.Caccy_blst, scalar_accy), ) return ( - LinearPcsVerification(quotient_terms, self.Phi_zeta_blst, self.zeta_p, Agg_zeta), + LinearPcsVerification(quotient_terms, self.Phi_zeta_blst, self.zeta_p, agg_zeta), LinearPcsVerification(linearization_terms, self.Phi_zeta_omega_blst, zeta_omega, l_zeta_omega), ) @@ -676,55 +409,5 @@ def _native_linear_eval_bundle(self) -> tuple[int, int, int, int, int, int]: self.domain_size_inv, self.edwards_a, omega, - True, - ) - return cast(tuple[int, int, int, int, int, int], bundle) - - def _prepare_linearization_poly_verification(self) -> PcsVerification: - """Prepare KZG verification data for linearization polynomial""" - alphas_list = [Scalar(a) for a in self.alpha_list] - zeta = Scalar(self.zeta_p) - - zeta_minus_d4 = zeta - Scalar(self.D[self.last_index]) - - # Cl1 scalar - scalar_cl1 = zeta_minus_d4 - - # Cl2 scalars - x1, y1 = Scalar(self.accx_zeta), Scalar(self.accy_zeta) - x2, y2 = Scalar(self.px_zeta), Scalar(self.py_zeta) - b = Scalar(self.b_zeta) - coeff_a = self.edwards_a - - # S_PRIME is scalar field modulus, which Scalar handles implicitly - - C_acc_x_cl2 = b * (y1 * y2 + (coeff_a * x1 * x2)) + (ONE_S - b) - C_acc_x_f_cl2 = C_acc_x_cl2 * zeta_minus_d4 - - # Cl3 scalars - C_acc_y_cl3 = (b * (x1 * y2 - x2 * y1)) + (ONE_S - b) - C_acc_y_f_cl3 = C_acc_y_cl3 * zeta_minus_d4 - - # Combined scalars - scalar_accip = alphas_list[0] * scalar_cl1 - scalar_accx = alphas_list[1] * C_acc_x_f_cl2 - scalar_accy = alphas_list[2] * C_acc_y_f_cl3 - - # Use MSM for the final combination - points = [self.Caccip_blst, self.Caccx_blst, self.Caccy_blst] - scalars = [int(scalar_accip), int(scalar_accx), int(scalar_accy)] - Cl = blst_msm(points, scalars) - - zeta_omega = zeta * Scalar(self._domain_omega()) - - return PcsVerification(Cl, self.Phi_zeta_omega_blst, int(zeta_omega), int(Scalar(self.l_zeta_omega))) - - def _prepare_linearization_poly_verification_linear(self) -> LinearPcsVerification: - """Prepare linearization KZG verification with an unmaterialized commitment.""" - _, scalar_accip, scalar_accx, scalar_accy, zeta_omega, l_zeta_omega = self._native_linear_eval_bundle() - commitment_terms = ( - (self.Caccip_blst, scalar_accip), - (self.Caccx_blst, scalar_accx), - (self.Caccy_blst, scalar_accy), ) - return LinearPcsVerification(commitment_terms, self.Phi_zeta_omega_blst, zeta_omega, l_zeta_omega) + return bundle diff --git a/dot_ring/vrf/ietf/tiny.py b/dot_ring/vrf/ietf/tiny.py index 212f669..9bb0d82 100644 --- a/dot_ring/vrf/ietf/tiny.py +++ b/dot_ring/vrf/ietf/tiny.py @@ -109,7 +109,7 @@ def prove_ios( ) z0 = scalars.next() z1 = scalars.next() - k = cls.generate_nonce(secret_scalar, transcript) + k = nonce(cls.cv, secret_scalar, transcript) generator = cls.cv.generator_point() order = cls.cv.curve.params.subgroup_order r = cls.cv.msm( @@ -121,19 +121,13 @@ def prove_ios( return cls(ios[0].output, c, s) transcript, merged = vrf_transcript(cls.cv, DomSep.TINY_VRF, schnorr_ios(cls.cv, public_key, ios), additional_data) - k = cls.generate_nonce(secret_scalar, transcript) + k = nonce(cls.cv, secret_scalar, transcript) r = merged.input * k c = challenge(cls.cv, [r], transcript) s = (k + c * secret_scalar) % cls.cv.curve.params.subgroup_order output_point = ios[0].output if len(ios) == 1 else merged.output return cls(output_point, c, s) - @classmethod - def generate_nonce(cls, secret_scalar: int, transcript: Any) -> int: - if not hasattr(transcript, "copy"): - return super().generate_nonce(secret_scalar, transcript) - return nonce(cls.cv, secret_scalar, transcript) - def verify(self, public_key: bytes, input: bytes, additional_data: bytes, salt: bytes = b"") -> bool: input_point = self.cv.encode_to_curve(input, salt) try: @@ -177,3 +171,31 @@ def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: if mul_cofactor: gamma = gamma * cls.cv.curve.params.cofactor return point_to_hash(cls.cv, gamma) + + @classmethod + def ecvrf_decode_proof(cls, proof: bytes | str) -> tuple[CurvePoint, int, int]: + if not isinstance(proof, bytes): + proof = bytes.fromhex(proof) + + encoded_point_len = point_len(cls.cv) + scalar_size = scalar_len(cls.cv) + expected = encoded_point_len + CHALLENGE_LEN + scalar_size + if len(proof) != expected: + raise ValueError(f"invalid Tiny VRF proof length: expected {expected}, got {len(proof)}") + + try: + output_point = cls.cv.string_to_point(proof[:encoded_point_len]) + except ValueError as exc: + raise ValueError("Invalid gamma point") from exc + + order = cls.cv.curve.params.subgroup_order + c = int.from_bytes(proof[encoded_point_len : encoded_point_len + CHALLENGE_LEN], "little") % order + s = scalar_decode(cls.cv, proof[encoded_point_len + CHALLENGE_LEN :]) + if s >= order: + raise ValueError("Response scalar S is not less than the curve order") + return output_point, c, s + + @classmethod + def ecvrf_proof_to_hash(cls, proof: bytes | str) -> bytes: + output_point, _, _ = cls.ecvrf_decode_proof(proof) + return cls.proof_to_hash(output_point) diff --git a/dot_ring/vrf/ring/batch_verifier.py b/dot_ring/vrf/ring/batch_verifier.py index 2982018..856750a 100644 --- a/dot_ring/vrf/ring/batch_verifier.py +++ b/dot_ring/vrf/ring/batch_verifier.py @@ -33,7 +33,6 @@ class RingBatchContext: ring_root: RingRoot fixed_cols_cmts: list[Any] fixed_cols_blst: tuple[Any, Any, Any] - verifier_key: dict[str, Any] transcript_prefix: FiatShamirTranscript seed_point: tuple[int, int] domain: list[int] @@ -46,7 +45,7 @@ class RingBatchContext: def from_ring(cls, ring: Ring, ring_root: RingRoot, *, validate_ring_root: bool = True) -> RingBatchContext: if validate_ring_root and not ring_root.matches_ring(ring): raise ValueError("ring root does not match ring") - fixed_cols_cmts, verifier_key = ring_root.verifier_key(ring.params) + fixed_cols_cmts = ring_root.fixed_commitments(ring.params) transcript_prefix = ring_root.verifier_transcript_prefix(ring.params) domain = ring.params.domain return cls( @@ -54,7 +53,6 @@ def from_ring(cls, ring: Ring, ring_root: RingRoot, *, validate_ring_root: bool ring_root=ring_root, fixed_cols_cmts=fixed_cols_cmts, fixed_cols_blst=tuple(g1_to_blst(commitment) for commitment in fixed_cols_cmts), - verifier_key=verifier_key, transcript_prefix=transcript_prefix, seed_point=ring.params.seed_point, domain=domain, @@ -85,19 +83,17 @@ def ring_proof_verifier(self, proof: RingVRF, message: Any) -> Verify: return Verify( proof.ring_proof_tuple(), - self.verifier_key, self.fixed_cols_cmts, rltn, res_plus_seed, self.seed_point, self.domain, - transcript_challenge=proof.cv.curve.params.suite_id, + self.transcript_prefix, padding_rows=self.padding_rows, edwards_a=self.ring_edwards_a, prime=ring.params.prime, omega=self.omega, pcs=ring.params.pcs, - transcript_prefix=self.transcript_prefix, domain_size_inv=self.domain_size_inv, transcript_witness_commitments=witness_commitments, transcript_quotient_commitment=quotient_commitment, diff --git a/dot_ring/vrf/ring/proof_payload.py b/dot_ring/vrf/ring/proof_payload.py index 5a4e7e4..93b4019 100644 --- a/dot_ring/vrf/ring/proof_payload.py +++ b/dot_ring/vrf/ring/proof_payload.py @@ -1,10 +1,8 @@ from __future__ import annotations from dataclasses import dataclass -from typing import cast from dot_ring.ring_proof.columns.columns import Column, require_commitment -from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.opening import Opening from dot_ring.ring_proof.pcs.protocol import G1Commitment @@ -78,15 +76,15 @@ def to_bytes(self, params: RingProofParams) -> bytes: params.pcs.compress_g1(require_commitment(self.c_accip)), params.pcs.compress_g1(require_commitment(self.c_accx)), params.pcs.compress_g1(require_commitment(self.c_accy)), - H.to_l_endian(self.px_zeta), - H.to_l_endian(self.py_zeta), - H.to_l_endian(self.s_zeta), - H.to_l_endian(self.b_zeta), - H.to_l_endian(self.accip_zeta), - H.to_l_endian(self.accx_zeta), - H.to_l_endian(self.accy_zeta), + _scalar_to_bytes(self.px_zeta), + _scalar_to_bytes(self.py_zeta), + _scalar_to_bytes(self.s_zeta), + _scalar_to_bytes(self.b_zeta), + _scalar_to_bytes(self.accip_zeta), + _scalar_to_bytes(self.accx_zeta), + _scalar_to_bytes(self.accy_zeta), params.pcs.compress_g1(require_commitment(self.c_q)), - H.to_l_endian(self.l_zeta_omega), + _scalar_to_bytes(self.l_zeta_omega), params.pcs.compress_g1(self.open_agg_zeta.proof), params.pcs.compress_g1(self.open_l_zeta_omega.proof), ) @@ -133,6 +131,19 @@ def commitment(self) -> G1Commitment: def scalar(self) -> int: end = self.offset + 32 - value = H.canonical_scalar_from_bytes(self.data[self.offset : end], self.params.prime) + value = _scalar_from_bytes(self.data[self.offset : end], self.params.prime) self.offset = end - return cast(int, value) + return value + + +def _scalar_to_bytes(value: int) -> bytes: + return value.to_bytes(RING_SCALAR_LEN, "little") + + +def _scalar_from_bytes(data: bytes, modulus: int) -> int: + if len(data) != RING_SCALAR_LEN: + raise ValueError(f"scalar must be exactly {RING_SCALAR_LEN} bytes, got {len(data)}") + value = int.from_bytes(data, "little") + if value >= modulus: + raise ValueError("scalar is not canonical") + return value diff --git a/dot_ring/vrf/ring/root.py b/dot_ring/vrf/ring/root.py index 20747ce..4d29691 100644 --- a/dot_ring/vrf/ring/root.py +++ b/dot_ring/vrf/ring/root.py @@ -1,11 +1,12 @@ from dataclasses import dataclass from functools import lru_cache -from typing import Any, cast +from typing import Any from dot_ring.ring_proof.columns.columns import Column, require_commitment -from dot_ring.ring_proof.helpers import Helpers as H from dot_ring.ring_proof.params import RingProofParams from dot_ring.ring_proof.pcs.kzg import KZG +from dot_ring.ring_proof.transcript.phases import serialize_verifier_key +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript from .members import Ring @@ -43,6 +44,10 @@ def _copy_commitment(commitment: Any) -> Any: return commitment.dup() if hasattr(commitment, "dup") else commitment +def _transcript_g2_points(g2_points: Any) -> list[tuple[int, int]]: + return [(b, a) for pair in g2_points for point in pair for a, b in [point]] + + @dataclass class RingRoot: px: Column @@ -54,9 +59,8 @@ class RingRoot: def from_ring(cls, ring: Ring, params: RingProofParams | None = None): if params is None: params = ring.params - # Px, Py, s points - px, py = H.unzip(ring.nm_points) - # Columns + px = [point[0] for point in ring.nm_points] + py = [point[1] for point in ring.nm_points] px_col = Column("Px", px, size=params.domain_size) py_col = Column("Py", py, size=params.domain_size) s_col = _selector_column(params) @@ -65,26 +69,19 @@ def from_ring(cls, ring: Ring, params: RingProofParams | None = None): col.commit(params.pcs) return cls(px=px_col, py=py_col, s=s_col, params=params) - def verifier_key(self, params: RingProofParams | None = None) -> tuple[list[Any], dict[str, Any]]: - """Return fixed-column commitments and their transcript verifier key.""" + def fixed_commitments(self, params: RingProofParams | None = None) -> list[Any]: + """Return fixed-column commitments for the ring root.""" if params is None: params = self.params if params is None: - raise ValueError("Ring root verifier key requires ring proof parameters") + raise ValueError("Ring root commitments require ring proof parameters") - commitments = [ + return [ require_commitment(self.px), require_commitment(self.py), require_commitment(self.s), ] - verifier_key = { - "g1": params.pcs.srs.g1_points[0], - "g2": H.altered_points(params.pcs.srs.g2_points), - "commitments": [params.pcs.serialize_g1_uncompressed(commitment) for commitment in commitments], - } - return commitments, verifier_key - def verifier_transcript_prefix(self, params: RingProofParams | None = None, transcript_challenge: bytes | None = None): """Return a transcript state after absorbing the fixed verifier key.""" if params is None: @@ -92,37 +89,29 @@ def verifier_transcript_prefix(self, params: RingProofParams | None = None, tran if params is None: raise ValueError("Ring root verifier transcript requires ring proof parameters") - _, verifier_key = self.verifier_key(params) + commitments = self.fixed_commitments(params) + commitment_bytes = [params.pcs.serialize_g1_uncompressed(commitment) for commitment in commitments] + verifier_key_bytes = serialize_verifier_key( + params.pcs.srs.g1_points[0], + _transcript_g2_points(params.pcs.srs.g2_points), + commitment_bytes, + ) if transcript_challenge is None: transcript_challenge = params.cv.curve.params.suite_id - from dot_ring.ring_proof.transcript.serialize import serialize - from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript - transcript = FiatShamirTranscript(params.prime, transcript_challenge) - # Verifier-key transcript prefix is reused before proof-local data is absorbed. - transcript.add_serialized(b"vk", serialize(verifier_key)) + transcript.absorb_labeled(b"vk", verifier_key_bytes) return transcript def to_bytes(self) -> bytes: - pcs = self.params.pcs if self.params is not None else None - if pcs is not None: - return b"".join( - ( - pcs.compress_g1(require_commitment(self.px)), - pcs.compress_g1(require_commitment(self.py)), - pcs.compress_g1(require_commitment(self.s)), - ) + pcs = self.params.pcs if self.params is not None else KZG + return b"".join( + ( + pcs.compress_g1(require_commitment(self.px)), + pcs.compress_g1(require_commitment(self.py)), + pcs.compress_g1(require_commitment(self.s)), ) - for col in (self.px, self.py, self.s): - if col.commitment is None: - raise ValueError("Ring root is missing commitments") - comm_keys = ( - H.bls_g1_compress(cast(Any, require_commitment(self.px))), - H.bls_g1_compress(cast(Any, require_commitment(self.py))), - H.bls_g1_compress(cast(Any, require_commitment(self.s))), ) - return bytes.fromhex(comm_keys[0]) + bytes.fromhex(comm_keys[1]) + bytes.fromhex(comm_keys[2]) @classmethod def from_bytes(cls, data: bytes, params: RingProofParams | None = None) -> "RingRoot": diff --git a/dot_ring/vrf/ring/vrf.py b/dot_ring/vrf/ring/vrf.py index 5bca602..b84e8b1 100644 --- a/dot_ring/vrf/ring/vrf.py +++ b/dot_ring/vrf/ring/vrf.py @@ -169,7 +169,7 @@ def _ring_proof_verifier( ring: Ring, ring_root: RingRoot, ) -> Verify: - fixed_cols_cmts, verifier_key = ring_root.verifier_key(ring.params) + fixed_cols_cmts = ring_root.fixed_commitments(ring.params) transcript_prefix = ring_root.verifier_transcript_prefix(ring.params) if isinstance(message, bytes): @@ -183,19 +183,17 @@ def _ring_proof_verifier( return Verify( self.ring_proof_tuple(), - verifier_key, fixed_cols_cmts, rltn, res_plus_seed, ring.params.seed_point, ring.params.domain, - transcript_challenge=self.cv.curve.params.suite_id, + transcript_prefix, padding_rows=ring.params.padding_rows, edwards_a=ring.params.ring_edwards_a, prime=ring.params.prime, omega=ring.params.omega, pcs=ring.params.pcs, - transcript_prefix=transcript_prefix, domain_size_inv=pow(ring.params.domain_size, -1, ring.params.prime), transcript_witness_commitments=witness_commitments, transcript_quotient_commitment=quotient_commitment, diff --git a/dot_ring/vrf/vrf.py b/dot_ring/vrf/vrf.py index 79e194e..0fc7501 100644 --- a/dot_ring/vrf/vrf.py +++ b/dot_ring/vrf/vrf.py @@ -1,14 +1,10 @@ from __future__ import annotations -import hashlib -import hmac from dataclasses import dataclass -from typing import Any, ClassVar, Generic, Literal, Self, TypeVar, cast +from typing import Any, ClassVar, Generic, Self, TypeVar, cast from ..curve.curve import CurveVariant from ..curve.point import CurvePoint -from ..ring_proof.helpers import Helpers -from .transcript import point_len, scalar_len C = TypeVar("C", bound=CurveVariant) @@ -53,194 +49,6 @@ def __class_getitem__(cls, curve_variant: CurveVariant | Any) -> type[Self] | An new_class = type(f"{cls.__name__}[{curve_variant.name}]", (cls,), {"cv": curve_variant}) return new_class - @classmethod - def generate_nonce(cls, secret_key: int, input_point: CurvePoint) -> int: - """ - Generate a deterministic nonce for VRF proof. - - Args: - secret_key: The secret key - input_point: The input point - - Returns: - int: Generated nonce - """ - # Hash secret key (little-endian) - params = cls.cv.curve.params - scalr_len = scalar_len(cls.cv) - sk_encoded = Helpers.int_to_str( - secret_key % params.subgroup_order, - cast(Literal["little", "big"], cls.cv.curve.encoding_endian()), - scalr_len, - ) - hash_len = 2 * scalr_len - hashed_sk = cls.cv.curve.hash(sk_encoded, hash_len) - sk_hash = hashed_sk[scalr_len:hash_len] - # Concatenate with input point encoding - if isinstance(input_point, int): - raise ValueError("Input point must be a CurvePoint") - input_point_octet = input_point.point_to_string() - data = sk_hash + input_point_octet - # Generate final nonce - nonce_hash = cls.cv.curve.hash(data, hash_len) - nonce = Helpers.str_to_int(nonce_hash, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) - - # Calculate k = nonce % order - k = nonce % params.subgroup_order - return k - - @classmethod - def challenge(cls, points: list[CurvePoint], additional_data: bytes) -> int: - """ - Generate VRF challenge according to RFC 9381. - - The challenge length is determined by the curve params encoding challenge length, - which is typically set based on the curve's security level. - - Args: - points: List of points to include in the challenge - additional_data: Additional data to include in challenge - - Returns: - int: Generated challenge in the range [0, curve.params.subgroup_order) - """ - # Create challenge string with domain separator (0x02) - params = cls.cv.curve.params - challenge_string = params.suite_id + bytes([0x02]) - - # Add point encodings for each point in the challenge - for point in points: - challenge_string += point.point_to_string() - - # Add additional data and finalize with 0x00 - hash_input = challenge_string + additional_data + bytes([0x00]) - - # Generate hash output - challenge_len = params.encoding.challenge_len - hash_output = cls.cv.curve.hash(hash_input, challenge_len) - - # Truncate to the curve's specified challenge length - challenge_hash = hash_output[:challenge_len] - - # Convert to integer and reduce modulo curve order - return Helpers.b_endian_2_int(challenge_hash) % params.subgroup_order - - @classmethod - def ecvrf_nonce_rfc6979(cls, secret_scalar: int, h_string: bytes, hash_func: str = "sha256") -> int: - """ - Deterministically derive a nonce from secret scalar and input bytes. - - This follows the RFC 6979 HMAC construction but samples once instead of - looping until the candidate is in range. - """ - hasher = getattr(hashlib, hash_func) - hlen = hasher().digest_size - q = cls.cv.curve.params.subgroup_order - # Convert inputs - x_bytes = secret_scalar.to_bytes((q.bit_length() + 7) // 8, "big") - h1 = hasher(h_string).digest() - # Initialize V, K - V = b"\x01" * hlen - K = b"\x00" * hlen - # Step 1: K = HMAC_K(V || 0x00 || x || h1) - K = hmac.new(K, V + b"\x00" + x_bytes + h1, hasher).digest() - # Step 2: V = HMAC_K(V) - V = hmac.new(K, V, hasher).digest() - # Step 3: K = HMAC_K(V || 0x01 || x || h1) - K = hmac.new(K, V + b"\x01" + x_bytes + h1, hasher).digest() - # Step 4: V = HMAC_K(V) - V = hmac.new(K, V, hasher).digest() - # Step 5: one more HMAC_K(V) - V = hmac.new(K, V, hasher).digest() - # Interpret V as integer and mod q - k = int.from_bytes(V, "big") % q - if k == 0: - k = 1 - return k - - @classmethod - def ecvrf_decode_proof(cls, pi_string: bytes | str) -> tuple[CurvePoint, int, int]: - """Decode VRF proof. - - Args: - pi_string: VRF proof - - Returns: - Tuple[CurvePoint, int, int]: (gamma, C, S) - """ - - if not isinstance(pi_string, bytes): - pi_string = bytes.fromhex(pi_string) - - # Get lengths from curve parameters - challenge_len = cls.cv.curve.params.encoding.challenge_len - scalar_size = scalar_len(cls.cv) - - # Calculate positions in the proof - gamma_end = point_len(cls.cv) - - c_end = gamma_end + challenge_len - s_end = c_end + scalar_size - - # Extract components - gamma_string = pi_string[:gamma_end] - c_string = pi_string[gamma_end:c_end] - s_string = pi_string[c_end:s_end] - - # Convert to appropriate types] - try: - gamma = cls.cv.string_to_point(gamma_string) - except ValueError as exc: - raise ValueError("Invalid gamma point") from exc - C = Helpers.str_to_int(c_string, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) - S = Helpers.str_to_int(s_string, cast(Literal["little", "big"], cls.cv.curve.encoding_endian())) - - if S >= cls.cv.curve.params.subgroup_order: - raise ValueError("Response scalar S is not less than the curve order") - - return gamma, C, S - - @classmethod - def ecvrf_proof_to_hash(cls, pi_string: bytes | str) -> bytes: - """Convert VRF proof to hash. - - Args: - pi_string: VRF proof - - Returns: - bytes: Hash of VRF proof - """ - if not isinstance(pi_string, bytes): - pi_string = bytes.fromhex(pi_string) - gamma, C, S = cls.ecvrf_decode_proof(pi_string) - return cls.proof_to_hash(gamma) - - @classmethod - def proof_to_hash(cls, gamma: CurvePoint, mul_cofactor: bool = False) -> bytes: - """Convert VRF proof to hash. - - Args: - gamma: VRF output point - - Returns: - bytes: Hash of VRF proof - """ - proof_to_hash_domain_separator_front = b"\x03" - proof_to_hash_domain_separator_back = b"\x00" - beta_string = cls.cv.curve.hash( - cls.cv.curve.params.suite_id - + proof_to_hash_domain_separator_front - + ( - gamma - # In some cases, we don't want to multiply by the cofactor. - # https://github.com/davxy/ark-ec-vrfs/issues/52 - if not mul_cofactor - else gamma * cls.cv.curve.params.cofactor - ).point_to_string() - + proof_to_hash_domain_separator_back, - ) - return beta_string - @classmethod def get_public_key(cls, secret_key: bytes) -> bytes: """Take the Secret_Key and return Public Key""" diff --git a/tests/test_coverage/test_helpers.py b/tests/test_coverage/test_helpers.py deleted file mode 100644 index 8c103eb..0000000 --- a/tests/test_coverage/test_helpers.py +++ /dev/null @@ -1,295 +0,0 @@ -"""Tests for helpers module to improve coverage.""" - -from py_ecc.optimized_bls12_381 import FQ - -from dot_ring.ring_proof.helpers import Helpers as H - - -class TestHelpers: - """Test cases for Helpers class.""" - - def test_knocker_delta_equal(self): - """Test Kronecker delta for equal indices.""" - assert H.knocker_delta(5, 5) == 1 - assert H.knocker_delta(0, 0) == 1 - - def test_knocker_delta_not_equal(self): - """Test Kronecker delta for unequal indices.""" - assert H.knocker_delta(3, 5) == 0 - assert H.knocker_delta(0, 1) == 0 - - def test_unzip_basic(self): - """Test unzip of point list.""" - points = [(1, 2), (3, 4), (5, 6)] - x_coords, y_coords = H.unzip(points) - assert x_coords == [1, 3, 5] - assert y_coords == [2, 4, 6] - - def test_unzip_empty(self): - """Test unzip of empty list.""" - x_coords, y_coords = H.unzip([]) - assert x_coords == [] - assert y_coords == [] - - def test_unzip_single(self): - """Test unzip of single point.""" - points = [(10, 20)] - x_coords, y_coords = H.unzip(points) - assert x_coords == [10] - assert y_coords == [20] - - def test_to_int(self): - """Test conversion to int tuple.""" - tup = (FQ(123), FQ(456)) - result = H.to_int(tup) - assert result == (123, 456) - - def test_to_bytes(self): - """Test int to hex string conversion.""" - val = 0x12345 - result = H.to_bytes(val) - # Should be 32 bytes little-endian hex - assert len(result) == 64 # 32 bytes * 2 hex chars - assert isinstance(result, str) - - def test_to_scalar_int_bytes(self): - """Test bytes to scalar int conversion.""" - data = b"\x01\x02\x03\x04" - result = H.to_scalar_int(data) - expected = int.from_bytes(data, "little") - assert result == expected - - def test_to_scalar_int_hex_string(self): - """Test hex string to scalar int conversion.""" - hex_str = "01020304" - result = H.to_scalar_int(hex_str) - expected = int.from_bytes(bytes.fromhex(hex_str), "little") - assert result == expected - - def test_l_endian_2_int_bytes(self): - """Test little-endian bytes to int.""" - data = b"\x01\x02\x03\x04" - result = H.l_endian_2_int(data) - expected = int.from_bytes(data, "little") - assert result == expected - - def test_l_endian_2_int_hex_string(self): - """Test little-endian hex string to int.""" - hex_str = "01020304" - result = H.l_endian_2_int(hex_str) - expected = int.from_bytes(bytes.fromhex(hex_str), "little") - assert result == expected - - def test_b_endian_2_int_bytes(self): - """Test big-endian bytes to int.""" - data = b"\x01\x02\x03\x04" - result = H.b_endian_2_int(data) - expected = int.from_bytes(data, "big") - assert result == expected - - def test_b_endian_2_int_hex_string(self): - """Test big-endian hex string to int.""" - hex_str = "01020304" - result = H.b_endian_2_int(hex_str) - expected = int.from_bytes(bytes.fromhex(hex_str), "big") - assert result == expected - - def test_str_to_int_little(self): - """Test str_to_int with little endian.""" - data = b"\x01\x02\x03\x04" - result = H.str_to_int(data, "little") - expected = int.from_bytes(data, "little") - assert result == expected - - def test_str_to_int_big(self): - """Test str_to_int with big endian.""" - data = b"\x01\x02\x03\x04" - result = H.str_to_int(data, "big") - expected = int.from_bytes(data, "big") - assert result == expected - - def test_str_to_int_hex_string_little(self): - """Test str_to_int with hex string and little endian.""" - hex_str = "01020304" - result = H.str_to_int(hex_str, "little") # type: ignore - expected = int.from_bytes(bytes.fromhex(hex_str), "little") - assert result == expected - - def test_str_to_int_hex_string_big(self): - """Test str_to_int with hex string and big endian.""" - hex_str = "01020304" - result = H.str_to_int(hex_str, "big") # type: ignore - expected = int.from_bytes(bytes.fromhex(hex_str), "big") - assert result == expected - - def test_int_to_str_little(self): - """Test int_to_str with little endian.""" - val = 0x12345678 - result = H.int_to_str(val, "little", 4) - expected = val.to_bytes(4, "little") - assert result == expected - - def test_int_to_str_big(self): - """Test int_to_str with big endian.""" - val = 0x12345678 - result = H.int_to_str(val, "big", 4) - expected = val.to_bytes(4, "big") - assert result == expected - - def test_int_to_str_default_length(self): - """Test int_to_str with default 32-byte length.""" - val = 0x12345 - result = H.int_to_str(val, "little") - assert len(result) == 32 - assert int.from_bytes(result, "little") == val - - def test_to_b_endian(self): - """Test to_b_endian conversion.""" - val = 0x12345678 - result = H.to_b_endian(val, 4) - expected = val.to_bytes(4, "big") - assert result == expected - - def test_to_b_endian_default_length(self): - """Test to_b_endian with default length.""" - val = 0x12345 - result = H.to_b_endian(val) - assert len(result) == 32 - - def test_to_l_endian(self): - """Test to_l_endian conversion.""" - val = 0x12345678 - result = H.to_l_endian(val, 4) - expected = val.to_bytes(4, "little") - assert result == expected - - def test_to_l_endian_default_length(self): - """Test to_l_endian with default length.""" - val = 0x12345 - result = H.to_l_endian(val) - assert len(result) == 32 - - def test_pt_len(self): - """Test pt_len calculation.""" - # For a prime with bit_length n, coord_size = (n + 7) // 8 - prime = 0xFFFF # 16 bits -> 2 bytes - result = H.pt_len(prime) - assert result == 2 - - # Larger prime - prime = (1 << 256) - 1 # 256 bits -> 32 bytes - result = H.pt_len(prime) - assert result == 32 - - def test_to_fq(self): - """Test conversion to FQ tuple.""" - point = (123, 456, 1) - result = H.to_fq(point) - assert isinstance(result[0], FQ) - assert isinstance(result[1], FQ) - assert isinstance(result[2], FQ) - assert int(result[0]) == 123 - assert int(result[1]) == 456 - assert int(result[2]) == 1 - - def test_bls_projective_2_affine(self): - """Test projective to affine conversion.""" - points_3d = [(1, 2, 3), (4, 5, 6), (7, 8, 9)] - result = H.bls_projective_2_affine(points_3d) - assert result == [(1, 2), (4, 5), (7, 8)] - - def test_bls_projective_2_affine_empty(self): - """Test projective to affine with empty list.""" - result = H.bls_projective_2_affine([]) - assert result == [] - - def test_altered_points(self): - """Test altered_points for G2 points.""" - # G2 points structure: list of pairs where each pair has points with (a, b) coords - g2_points = [[((1, 2), (3, 4))]] - result = H.altered_points(g2_points) - # Should swap coordinates - assert len(result) > 0 - - -class TestBLSCompression: - """Test BLS point compression/decompression.""" - - def test_bls_g1_compress_affine(self): - """Test G1 point compression with 2D affine coordinates.""" - # Use a known valid BLS12-381 G1 point (the generator) - # This is the generator point of BLS12-381 G1 - x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 - y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 - - point = (x, y) - result = H.bls_g1_compress(point) - - assert isinstance(result, str) - assert len(result) == 96 # 48 bytes in hex - - def test_bls_g1_compress_projective(self): - """Test G1 point compression with 3D projective coordinates.""" - x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 - y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 - z = 1 - - point = (x, y, z) - result = H.bls_g1_compress(point) - - assert isinstance(result, str) - assert len(result) == 96 - - def test_bls_g1_decompress_bytes(self): - """Test G1 point decompression from bytes.""" - # Compress a point first, then decompress - x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 - y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 - - point = (x, y) - compressed_hex = H.bls_g1_compress(point) - compressed_bytes = bytes.fromhex(compressed_hex) - - result = H.bls_g1_decompress(compressed_bytes) - - assert isinstance(result, tuple) - assert len(result) == 3 # (FQ, FQ, FQ) - - def test_bls_g1_decompress_hex_string(self): - """Test G1 point decompression from hex string.""" - x = 3685416753713387016781088315183077757961620795782546409894578378688607592378376318836054947676345821548104185464507 - y = 1339506544944476473020471379941921221584933875938349620426543736416511423956333506472724655353366534992391756441569 - - point = (x, y) - compressed_hex = H.bls_g1_compress(point) - - result = H.bls_g1_decompress(compressed_hex) - - assert isinstance(result, tuple) - assert len(result) == 3 - - def test_bls_g2_compress_projective(self): - """Test G2 point compression with projective coordinates.""" - # Use a simple G2 point structure - # G2 points have FQ2 coordinates - from py_ecc.optimized_bls12_381 import G2 - - result = H.bls_g2_compress(G2) - - assert isinstance(result, str) - assert len(result) == 192 # 96 bytes in hex (2x 48 bytes) - - def test_bls_g2_compress_affine(self): - """Test G2 point compression with affine coordinates.""" - from py_ecc.optimized_bls12_381 import G2, normalize - - # Normalize to get affine coordinates - g2_affine = normalize(G2) - x, y = g2_affine - - # Create 2D point - point = (x, y) - result = H.bls_g2_compress(point) - - assert isinstance(result, str) - assert len(result) == 192 diff --git a/tests/test_coverage/test_pairing.py b/tests/test_coverage/test_pairing.py index 13a98f0..f1ddf64 100644 --- a/tests/test_coverage/test_pairing.py +++ b/tests/test_coverage/test_pairing.py @@ -12,7 +12,7 @@ from dot_ring.ring_proof.pcs.srs import srs -class TestPairingHelpers: +class TestPairing: """Test pairing helper functions.""" def test_ensure_p1_affine_from_p1(self): diff --git a/tests/test_coverage/test_serialize.py b/tests/test_coverage/test_serialize.py deleted file mode 100644 index b8b6ae5..0000000 --- a/tests/test_coverage/test_serialize.py +++ /dev/null @@ -1,61 +0,0 @@ -"""Additional tests for serialize module to improve coverage.""" - -from dot_ring.ring_proof.transcript.serialize import serialize - - -class TestSerialize: - """Test serialization functions.""" - - def test_serialize_int(self): - """Test serializing an integer.""" - scalar = 12345 - - result = serialize(scalar) - - assert result is not None - assert isinstance(result, bytes) - - def test_serialize_large_int(self): - """Test serializing a large integer.""" - scalar = 2**200 - - result = serialize(scalar) - - assert result is not None - assert isinstance(result, bytes) - - def test_serialize_zero(self): - """Test serializing zero.""" - scalar = 0 - - result = serialize(scalar) - - assert result is not None - assert isinstance(result, bytes) - - def test_serialize_list_of_ints(self): - """Test serializing a list of ints.""" - data = [1, 2, 3] - - result = serialize(data) - - assert result is not None - assert isinstance(result, bytes) - - def test_serialize_nested_list(self): - """Test serializing a nested list.""" - data = [[1, 2], [3, 4]] - - result = serialize(data) - - assert result is not None - assert isinstance(result, bytes) - - def test_serialize_tuple_of_two(self): - """Test serializing a tuple of two integers (like coordinates).""" - data = (1, 2) - - result = serialize(data) - - assert result is not None - assert isinstance(result, bytes) diff --git a/tests/test_coverage/test_transcript.py b/tests/test_coverage/test_transcript.py deleted file mode 100644 index 1769bc4..0000000 --- a/tests/test_coverage/test_transcript.py +++ /dev/null @@ -1,110 +0,0 @@ -"""Additional tests for transcript module to improve coverage.""" - -from dot_ring.ring_proof.constants import S_PRIME -from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript - - -class TestTranscriptAdditional: - """Additional tests for FiatShamirTranscript class.""" - - def test_transcript_init(self): - """Test FiatShamirTranscript initialization.""" - transcript = FiatShamirTranscript(S_PRIME, b"test_transcript") - assert transcript is not None - - def test_transcript_label(self): - """Test adding label to transcript.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.label(b"new_label") - # Just verify it doesn't error - - def test_transcript_write(self): - """Test writing data to transcript.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.write(b"some_data") - # Just verify it doesn't error - - def test_transcript_write_bytes(self): - """Test writing bytes to transcript.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.write_bytes(b"more_data_here") - # Just verify it doesn't error - - def test_transcript_append(self): - """Test appending data to transcript.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.append(b"appended_data") - # Just verify it doesn't error - - def test_transcript_add_serialized(self): - """Test add_serialized method.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.add_serialized(b"label", b"serialized_data") - # Just verify it doesn't error - - def test_transcript_challenge(self): - """Test getting challenge.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.append(b"some_data") - - challenge = transcript.challenge(b"challenge") - - assert isinstance(challenge, int) - assert 0 <= challenge < S_PRIME - - def test_transcript_read_reduce(self): - """Test read_reduce method.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.write(b"data") - - result = transcript.read_reduce() - - assert isinstance(result, int) - assert 0 <= result < S_PRIME - - def test_transcript_separate(self): - """Test separate method.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - transcript.write(b"data1") - transcript.separate() - transcript.write(b"data2") - # Just verify it doesn't error - - def test_transcript_get_constraints_aggregation_coeffs(self): - """Test getting constraint aggregation coefficients.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - - coeffs = transcript.get_constraints_aggregation_coeffs(3) - - assert len(coeffs) == 3 - for c in coeffs: - assert isinstance(c, int) - assert 0 <= c < S_PRIME - - def test_transcript_get_evaluation_point(self): - """Test getting evaluation point.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - - points = transcript.get_evaluation_point(1) - - assert len(points) == 1 - assert isinstance(points[0], int) - - def test_transcript_get_kzg_aggregation_challenges(self): - """Test getting KZG aggregation challenges.""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - - challenges = transcript.get_kzg_aggregation_challenges(5) - - assert len(challenges) == 5 - for c in challenges: - assert isinstance(c, int) - assert 0 <= c < S_PRIME - - def test_transcript_large_write(self): - """Test writing large data (>2^31 bytes would trigger chunking).""" - transcript = FiatShamirTranscript(S_PRIME, b"test") - # Write 100KB of data - large_data = b"x" * 100000 - transcript.write_bytes(large_data) - # Just verify it doesn't error diff --git a/tests/test_coverage/test_verify.py b/tests/test_coverage/test_verify.py deleted file mode 100644 index 8d083b0..0000000 --- a/tests/test_coverage/test_verify.py +++ /dev/null @@ -1,250 +0,0 @@ -"""Additional tests to improve coverage for verify module.""" - -import pytest -from dot_ring.curve.native_field.scalar import Scalar -from py_ecc.optimized_bls12_381 import curve_order - -from dot_ring.ring_proof.constants import DEFAULT_SIZE as SIZE -from dot_ring.ring_proof.constants import OMEGA_512 as OMEGA -from dot_ring.ring_proof.constants import OMEGA_2048, S_PRIME -from dot_ring.ring_proof.pcs.srs import srs -from dot_ring.ring_proof.verify import Verify, blst_msm, lagrange_at_zeta - - -class TestVerifyHelpers: - """Test helper functions in verify module.""" - - def test_lagrange_at_zeta_basic(self): - """Test Lagrange basis evaluation at a point.""" - # L_i(zeta) = (omega^i / n) * (zeta^n - 1) / (zeta - omega^i) - domain_size = SIZE - index = 0 - zeta = 12345 # arbitrary point - omega = OMEGA - prime = S_PRIME - - result = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - - # Just verify it returns a scalar-like value - assert result is not None - - def test_lagrange_at_zeta_at_omega_power(self): - """Test Lagrange basis at omega^i equals 1.""" - # L_i(omega^i) = 1 - domain_size = SIZE - index = 5 - omega = OMEGA - prime = S_PRIME - - # zeta = omega^index - zeta = pow(omega, index, prime) - - result = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - - # Should be 1 - # The Scalar class has an internal value - assert int(result) == 1 - - def test_lagrange_at_zeta_index_zero(self): - """Test Lagrange basis at index 0.""" - domain_size = SIZE - index = 0 - zeta = 99999 - omega = OMEGA - prime = S_PRIME - - result = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - - assert result is not None - - def test_lagrange_at_zeta_index_size_minus_4(self): - """Test Lagrange basis at index SIZE-4 (uses precomputed omega power).""" - domain_size = SIZE - index = SIZE - 4 - zeta = 12345 - omega = OMEGA - prime = S_PRIME - - result = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - - assert result is not None - - def test_lagrange_at_zeta_caching(self): - """Test that lagrange_at_zeta results are cached.""" - domain_size = SIZE - index = 10 - zeta = 54321 - omega = OMEGA - prime = S_PRIME - - result1 = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - result2 = lagrange_at_zeta(domain_size, index, zeta, omega, prime) - - assert result1 == result2 - - def test_divide_modular_inverse(self): - """Test modular division helper.""" - verifier = Verify.__new__(Verify) - - numerator = 10 - denominator = 3 - expected = (numerator * pow(denominator, -1, curve_order)) % curve_order - - assert verifier.divide(numerator, denominator) == expected - - def test_init_rejects_invalid_padding_rows(self): - """Verify init validates padding_rows against domain size.""" - proof = (0,) * 15 - with pytest.raises(ValueError, match="padding_rows"): - Verify( - proof=proof, - vk={}, - fixed_cols=[0, 0, 0], - rl_to_proove=(0, 0), - rps=(0, 0), - seed_point=(0, 0), - Domain=[1, 2, 3], - padding_rows=3, - ) - - def test_contributions_handles_zeta_equal_domain_point(self): - """Zeta equal to a domain point should hit the zero-difference branches.""" - verifier = Verify.__new__(Verify) - - verifier.zeta_p = 1 - verifier.sp = (2, 3) - verifier.D = [1, 2, 3, 4] - verifier.b_zeta = 1 - verifier.accx_zeta = 5 - verifier.accy_zeta = 7 - verifier.accip_zeta = 11 - verifier.px_zeta = 13 - verifier.py_zeta = 17 - verifier.s_zeta = 19 - verifier.Result_plus_Seed = (23, 29) - - verifier.last_index = len(verifier.D) - 1 - - result = verifier.contributions_to_constraints_eval_at_zeta() - - assert len(result) == 7 - assert all(isinstance(value, Scalar) for value in result) - - def test_linearization_uses_expected_omega(self): - """Verify omega selection for non-512 domains.""" - verifier = Verify.__new__(Verify) - - verifier.alpha_list = [1, 2, 3] - verifier.zeta_p = 7 - verifier.D = list(range(1024)) - verifier.accx_zeta = 2 - verifier.accy_zeta = 3 - verifier.px_zeta = 5 - verifier.py_zeta = 7 - verifier.b_zeta = 11 - verifier.Caccip_blst = srs.blst_g1[0] - verifier.Caccx_blst = srs.blst_g1[1] - verifier.Caccy_blst = srs.blst_g1[2] - verifier.Phi_zeta_omega_blst = srs.blst_g1[3] - verifier.l_zeta_omega = 13 - - verifier.last_index = len(verifier.D) - 1 - - _, _, zeta_omega, _ = verifier._prepare_linearization_poly_verification() - expected_omega = pow(OMEGA_2048, 2048 // 1024, S_PRIME) - expected_zeta_omega = (verifier.zeta_p * expected_omega) % S_PRIME - - assert zeta_omega == expected_zeta_omega - - def test_linearization_uses_fallback_omega(self): - """Verify omega selection for non-standard domain sizes.""" - verifier = Verify.__new__(Verify) - - verifier.alpha_list = [1, 2, 3] - verifier.zeta_p = 7 - verifier.D = list(range(16)) - verifier.accx_zeta = 2 - verifier.accy_zeta = 3 - verifier.px_zeta = 5 - verifier.py_zeta = 7 - verifier.b_zeta = 11 - verifier.Caccip_blst = srs.blst_g1[0] - verifier.Caccx_blst = srs.blst_g1[1] - verifier.Caccy_blst = srs.blst_g1[2] - verifier.Phi_zeta_omega_blst = srs.blst_g1[3] - verifier.l_zeta_omega = 13 - - verifier.last_index = len(verifier.D) - 1 - - _, _, zeta_omega, _ = verifier._prepare_linearization_poly_verification() - expected_omega = pow(OMEGA_2048, 2048 // 16, S_PRIME) - expected_zeta_omega = (verifier.zeta_p * expected_omega) % S_PRIME - - assert zeta_omega == expected_zeta_omega - - def test_prepare_quotient_poly_verification_smoke(self): - """Smoke test for quotient verification preparation.""" - verifier = Verify.__new__(Verify) - - verifier.alpha_list = [1, 2, 3, 4, 5, 6, 7] - verifier.zeta_p = 7 - verifier.V_list = [1, 2, 3, 4, 5, 6, 7, 8] - verifier.D = [1, 2, 3, 4] - verifier.sp = (2, 3) - verifier.Result_plus_Seed = (23, 29) - verifier.b_zeta = 1 - verifier.accx_zeta = 5 - verifier.accy_zeta = 7 - verifier.accip_zeta = 11 - verifier.px_zeta = 13 - verifier.py_zeta = 17 - verifier.s_zeta = 19 - verifier.l_zeta_omega = 31 - - verifier.Cpx_blst = srs.blst_g1[0] - verifier.Cpy_blst = srs.blst_g1[1] - verifier.Cs_blst = srs.blst_g1[2] - verifier.Cb_blst = srs.blst_g1[3] - verifier.Caccip_blst = srs.blst_g1[4] - verifier.Caccx_blst = srs.blst_g1[5] - verifier.Caccy_blst = srs.blst_g1[6] - verifier.Cq_blst = srs.blst_g1[7] - verifier.Phi_zeta_blst = srs.blst_g1[8] - - verifier.last_index = len(verifier.D) - 1 - - _, phi, zeta, agg = verifier._prepare_quotient_poly_verification() - - assert phi is verifier.Phi_zeta_blst - assert zeta == verifier.zeta_p - assert isinstance(agg, int) - - -class TestBLSTMSM: - """Test blst multi-scalar multiplication.""" - - def test_blst_msm_empty(self): - """Test MSM with empty inputs.""" - result = blst_msm([], []) - - # Should return point at infinity - assert result.is_inf() - - def test_blst_msm_single_point(self): - """Test MSM with single point.""" - points = [srs.blst_g1[0]] - scalars = [5] - - result = blst_msm(points, scalars) - - assert result is not None - assert not result.is_inf() - - def test_blst_msm_multiple_points(self): - """Test MSM with multiple points.""" - points = [srs.blst_g1[0], srs.blst_g1[1], srs.blst_g1[2]] - scalars = [1, 2, 3] - - result = blst_msm(points, scalars) - - assert result is not None diff --git a/tests/test_curve_ops/test_curve.py b/tests/test_curve_ops/test_curve.py index 996e064..edf22ba 100644 --- a/tests/test_curve_ops/test_curve.py +++ b/tests/test_curve_ops/test_curve.py @@ -12,7 +12,6 @@ TwistedEdwardsCurveParams, ) from dot_ring.curve.twisted_edwards.te_affine_point import TEAffinePoint -from dot_ring.ring_proof.transcript.serialize import serialize def _mock_hash_params(e2c: E2C_Variant = E2C_Variant.ELL2) -> HashToCurveParams[int]: @@ -70,25 +69,6 @@ def test_te_affine_double_result_identity(self): # Alternatively, we can test the denominator check by mocking. pass - def test_serialize_large_int(self): - """Test serialize with integer too large.""" - large_int = 1 << (48 * 8 + 1) - with pytest.raises(ValueError, match="Integer too large"): - serialize(large_int) - - def test_serialize_bytes_bytearray(self): - """Test serialize with bytes and bytearray.""" - b = b"test" - assert serialize(b) == b - - ba = bytearray(b"test") - assert serialize(ba) == b - - def test_serialize_invalid_type(self): - """Test serialize with invalid type.""" - with pytest.raises(TypeError, match="Unsupported object type"): - serialize(1.5) # type: ignore - def test_te_affine_double_coverage(self): """Test TEAffinePoint.double with a valid point.""" g = Ed25519_RO.generator_point() diff --git a/tests/test_curve_ops/test_gaps.py b/tests/test_curve_ops/test_gaps.py index f73d020..8bf0340 100644 --- a/tests/test_curve_ops/test_gaps.py +++ b/tests/test_curve_ops/test_gaps.py @@ -8,11 +8,6 @@ class TestCoverageGaps: - def test_vrf_generate_nonce_invalid_input(self): - """Test generate_nonce raises ValueError for invalid input point type.""" - with pytest.raises(ValueError, match="Input point must be a CurvePoint"): - TinyVRF[Bandersnatch].generate_nonce(123, 456) # type: ignore - def test_ecvrf_decode_proof_string_input(self): """Test ecvrf_decode_proof handles hex string input.""" # Generate a valid proof first @@ -30,10 +25,7 @@ def test_ecvrf_decode_proof_string_input(self): def test_ecvrf_decode_proof_invalid_gamma(self): """Test ecvrf_decode_proof raises ValueError for invalid gamma point.""" - # Create a proof with invalid gamma point bytes (e.g. all zeros if not valid, or just garbage) - # Bandersnatch point length is 33. - invalid_gamma = b"\xff" * 33 - # Valid C and S lengths + invalid_gamma = b"\xff" * point_len(Bandersnatch) c_len = Bandersnatch.curve.params.encoding.challenge_len s_len = scalar_len(Bandersnatch) dummy_c = b"\x00" * c_len @@ -57,23 +49,13 @@ def test_ecvrf_decode_proof_invalid_s(self): invalid_s = order + 1 s_len = (order.bit_length() + 7) // 8 - # Reconstruct proof with invalid S - # Structure: gamma (33) + c (16 or 32) + s (32) - # Bandersnatch challenge length is 32. - gamma_len = 33 - c_len = 32 + gamma_len = point_len(Bandersnatch) + c_len = Bandersnatch.curve.params.encoding.challenge_len gamma_bytes = proof_bytes[:gamma_len] c_bytes = proof_bytes[gamma_len : gamma_len + c_len] - from dot_ring.ring_proof.helpers import Helpers - - invalid_s_bytes = Helpers.int_to_str(invalid_s, "little", s_len) # Bandersnatch is little endian - - # Note: Helpers.int_to_str might mask the overflow if not careful, but here we want to inject bytes that decode to >= order. - # If s_len is fixed, we might not be able to fit order+1 if order is max for that length. - # Bandersnatch order is ~253 bits, fits in 32 bytes? 253/8 = 31.6. So 32 bytes. - # 2^253 fits in 32 bytes. + invalid_s_bytes = invalid_s.to_bytes(s_len, "little") invalid_proof = gamma_bytes + c_bytes + invalid_s_bytes @@ -123,9 +105,7 @@ def test_ietf_from_bytes_invalid_s(self): gamma_bytes = proof_bytes[:gamma_len] c_bytes = proof_bytes[gamma_len : gamma_len + c_len] - from dot_ring.ring_proof.helpers import Helpers - - invalid_s_bytes = Helpers.int_to_str(invalid_s, "little", s_len) + invalid_s_bytes = invalid_s.to_bytes(s_len, "little") invalid_proof = gamma_bytes + c_bytes + invalid_s_bytes diff --git a/tests/test_vectors.py b/tests/test_vectors.py index a021a7b..51301f3 100644 --- a/tests/test_vectors.py +++ b/tests/test_vectors.py @@ -6,7 +6,6 @@ import pytest from dot_ring import P256, Bandersnatch, Ed25519, PedersenVRF, RingVRF, TinyVRF -from dot_ring.ring_proof.helpers import Helpers from dot_ring.ring_proof.params import RingProofParams from dot_ring.vrf.ring import Ring, RingRoot from dot_ring.vrf.transcript import point_len, scalar_len @@ -67,11 +66,11 @@ def verify_ietf_vector(vector: dict[str, Any], curve) -> None: # Verify proof challenge challenge_len = curve.curve.params.encoding.challenge_len - c_bytes = Helpers.int_to_str(proof.c, curve.curve.encoding_endian(), challenge_len) + c_bytes = proof.c.to_bytes(challenge_len, curve.curve.encoding_endian()) assert c_bytes == expected_c, f"challenge mismatch: expected {expected_c.hex()}, got {c_bytes.hex()}" # Verify proof response - s_bytes = Helpers.int_to_str(proof.s, curve.curve.encoding_endian(), scalar_len(curve)) + s_bytes = proof.s.to_bytes(scalar_len(curve), curve.curve.encoding_endian()) assert s_bytes == expected_s, f"response mismatch: expected {expected_s.hex()}, got {s_bytes.hex()}" # Verify the proof @@ -175,10 +174,10 @@ def verify_pedersen_vector(vector: dict[str, Any], curve) -> None: assert proof.ok.point_to_string() == expected_ok, "Ok mismatch" - s_bytes = Helpers.int_to_str(proof.s, curve.curve.encoding_endian(), scalar_len(curve)) + s_bytes = proof.s.to_bytes(scalar_len(curve), curve.curve.encoding_endian()) assert s_bytes == expected_s, f"s mismatch: expected {expected_s.hex()}, got {s_bytes.hex()}" - sb_bytes = Helpers.int_to_str(proof.sb, curve.curve.encoding_endian(), scalar_len(curve)) + sb_bytes = proof.sb.to_bytes(scalar_len(curve), curve.curve.encoding_endian()) assert sb_bytes == expected_sb, f"sb mismatch: expected {expected_sb.hex()}, got {sb_bytes.hex()}" # Verify the proof diff --git a/tests/test_verify_ring_sig.py b/tests/test_verify_ring_sig.py index 792eead..7e5cebf 100644 --- a/tests/test_verify_ring_sig.py +++ b/tests/test_verify_ring_sig.py @@ -8,9 +8,10 @@ sys.path.insert(0, str(Path(__file__).parent.parent)) from dot_ring import Bandersnatch, blst -from dot_ring.ring_proof.constants import EVAL_DOMAINS +from dot_ring.ring_proof.constants import EVAL_DOMAINS, S_PRIME from dot_ring.ring_proof.pcs.kzg import KZG from dot_ring.ring_proof.pcs.utils import g2_to_blst +from dot_ring.ring_proof.transcript.transcript import FiatShamirTranscript from dot_ring.ring_proof.verify import Verify from tests.utils.rust_serde import ( compressed_g1_to_uncompressed_bytes, @@ -150,22 +151,24 @@ def verify_vector(vector_path: Path) -> None: + compressed_g1_to_uncompressed_bytes(vk_bytes[288:336]) + compressed_g1_to_uncompressed_bytes(vk_bytes[336:384]) ) + witness_uncompressed = b"".join(compressed_g1_to_uncompressed_bytes(commitment) for commitment in raw_bytes["col_commitments"]) quotient_compressed = bytes.fromhex(proof_data["proof"]["quotient_commitment"]) quotient_uncompressed = compressed_g1_to_uncompressed_bytes(quotient_compressed) - raw_bytes["quotient_commitment_uncompressed"] = quotient_uncompressed + transcript_prefix = FiatShamirTranscript(S_PRIME, b"w3f-ring-proof-test") + transcript_prefix.absorb_labeled(b"vk", vk_uncompressed) verifier = Verify( proof=proof_tuple, - vk=vk_uncompressed, fixed_cols=fixed_cols, - rl_to_proove=result_ark_bytes, - rps=result_plus_seed, + relation_to_prove=result_ark_bytes, + result_plus_seed=result_plus_seed, seed_point=seed_point, - Domain=domain, - raw_proof_bytes=raw_bytes, - transcript_challenge=b"w3f-ring-proof-test", + domain=domain, + transcript_prefix=transcript_prefix, padding_rows=padding_rows, + transcript_witness_commitments=witness_uncompressed, + transcript_quotient_commitment=quotient_uncompressed, ) assert verifier.is_valid(), f"Verification failed for {vector_path.name}"