mirror of
https://github.com/logos-storage/constantine.git
synced 2026-01-05 22:53:12 +00:00
KZG followup - Batch verification (#272)
* KZG: add batch verification
* workaround Clang and empty {.goto.} branches
* Apply suggestions from code review
This commit is contained in:
parent
153b37b77f
commit
7b64f85a29
@ -9,7 +9,7 @@
|
|||||||
import
|
import
|
||||||
../math/config/curves,
|
../math/config/curves,
|
||||||
../math/[ec_shortweierstrass, arithmetic, extension_fields],
|
../math/[ec_shortweierstrass, arithmetic, extension_fields],
|
||||||
../math/elliptic/[ec_scalar_mul, ec_multi_scalar_mul],
|
../math/elliptic/[ec_multi_scalar_mul, ec_shortweierstrass_batch_ops],
|
||||||
../math/pairings/pairings_generic,
|
../math/pairings/pairings_generic,
|
||||||
../math/constants/zoo_generators,
|
../math/constants/zoo_generators,
|
||||||
../math/polynomials/polynomials,
|
../math/polynomials/polynomials,
|
||||||
@ -306,4 +306,117 @@ func kzg_verify*[F2; C: static Curve](
|
|||||||
var gt {.noInit.}: C.getGT()
|
var gt {.noInit.}: C.getGT()
|
||||||
gt.pairing([proof, cmyG1], [tmzG2, negG2])
|
gt.pairing([proof, cmyG1], [tmzG2, negG2])
|
||||||
|
|
||||||
|
return gt.isOne().bool()
|
||||||
|
|
||||||
|
func kzg_verify_batch*[bits: static int, F2; C: static Curve](
|
||||||
|
commitments: ptr UncheckedArray[ECP_ShortW_Aff[Fp[C], G1]],
|
||||||
|
challenges: ptr UncheckedArray[Fr[C]],
|
||||||
|
evals_at_challenges: ptr UncheckedArray[BigInt[bits]],
|
||||||
|
proofs: ptr UncheckedArray[ECP_ShortW_Aff[Fp[C], G1]],
|
||||||
|
linearIndepRandNumbers: ptr UncheckedArray[Fr[C]],
|
||||||
|
n: int,
|
||||||
|
tauG2: ECP_ShortW_Aff[F2, G2]): bool {.tags:[HeapAlloc, Alloca, Vartime].} =
|
||||||
|
## Verify multiple KZG proofs efficiently
|
||||||
|
##
|
||||||
|
## Parameters
|
||||||
|
##
|
||||||
|
## `n` verification sets
|
||||||
|
## A verification set i (commitmentᵢ, challengeᵢ, eval_at_challengeᵢ, proofᵢ)
|
||||||
|
## is passed in a "struct-of-arrays" fashion.
|
||||||
|
##
|
||||||
|
## Notation:
|
||||||
|
## i ∈ [0, n), a verification set with ID i
|
||||||
|
## [a]₁ corresponds to the scalar multiplication [a]G by the generator G of the group 𝔾1
|
||||||
|
##
|
||||||
|
## - `commitments`: `n` commitments [commitmentᵢ]₁
|
||||||
|
## - `challenges`: `n` challenges zᵢ
|
||||||
|
## - `evals_at_challenges`: `n` evaluation yᵢ = pᵢ(zᵢ)
|
||||||
|
## - `proofs`: `n` [proof]₁
|
||||||
|
## - `linearIndepRandNumbers`: `n` linearly independant numbers that are not in control
|
||||||
|
## of a prover (potentially malicious).
|
||||||
|
## - `n`: the number of verification sets
|
||||||
|
##
|
||||||
|
## For all (commitmentᵢ, challengeᵢ, eval_at_challengeᵢ, proofᵢ),
|
||||||
|
## we verify the relation
|
||||||
|
## proofᵢ.(τ - zᵢ) = pᵢ(τ)-pᵢ(zᵢ)
|
||||||
|
##
|
||||||
|
## As τ is the secret from the trusted setup, boxed in [τ]₁ and [τ]₂,
|
||||||
|
## we rewrite the equality check using pairings
|
||||||
|
##
|
||||||
|
## e([proofᵢ]₁, [τ]₂ - [challengeᵢ]₂) . e([commitmentᵢ]₁ - [eval_at_challengeᵢ]₁, [-1]₂) = 1
|
||||||
|
##
|
||||||
|
## Or batched using Feist-Khovratovich method
|
||||||
|
##
|
||||||
|
## e(∑ [rᵢ][proofᵢ]₁, [τ]₂) . e(∑[rᵢ]([commitmentᵢ]₁ - [eval_at_challengeᵢ]₁) + ∑[rᵢ][zᵢ][proofᵢ]₁, [-1]₂) = 1
|
||||||
|
#
|
||||||
|
# Described in:
|
||||||
|
# - https://github.com/ethereum/consensus-specs/blob/v1.4.0-beta.1/specs/deneb/polynomial-commitments.md#verify_kzg_proof_batch
|
||||||
|
# - https://dankradfeist.de/ethereum/2021/06/18/pcs-multiproofs.html]\
|
||||||
|
# - Fast amortized KZG proofs
|
||||||
|
# Feist, Khovratovich
|
||||||
|
# https://eprint.iacr.org/2023/033
|
||||||
|
# - https://alinush.github.io/2021/06/17/Feist-Khovratovich-technique-for-computing-KZG-proofs-fast.html
|
||||||
|
|
||||||
|
static: doAssert BigInt[bits] is matchingOrderBigInt(C)
|
||||||
|
|
||||||
|
var sums_jac {.noInit.}: array[2, ECP_ShortW_Jac[Fp[C], G1]]
|
||||||
|
template sum_rand_proofs: untyped = sums_jac[0]
|
||||||
|
template sum_commit_minus_evals_G1: untyped = sums_jac[1]
|
||||||
|
var sum_rand_challenge_proofs {.noInit.}: ECP_ShortW_Jac[Fp[C], G1]
|
||||||
|
|
||||||
|
# ∑ [rᵢ][proofᵢ]₁
|
||||||
|
# ---------------
|
||||||
|
let coefs = allocHeapArrayAligned(matchingOrderBigInt(C), n, alignment = 64)
|
||||||
|
for i in 0 ..< n:
|
||||||
|
coefs[i].fromField(linearIndepRandNumbers[i])
|
||||||
|
|
||||||
|
sum_rand_proofs.multiScalarMul_vartime(coefs, proofs, n)
|
||||||
|
|
||||||
|
# ∑[rᵢ]([commitmentᵢ]₁ - [eval_at_challengeᵢ]₁)
|
||||||
|
# ---------------------------------------------
|
||||||
|
#
|
||||||
|
# We interleave allocation and deallocation, which hurts cache reuse
|
||||||
|
# i.e. when alloc is being done, it's better to do all allocs as the metadata will already be in cache
|
||||||
|
#
|
||||||
|
# but it's more important to minimize memory usage especially if we want to commit with 2^26+ points
|
||||||
|
#
|
||||||
|
# We dealloc in reverse alloc order, to avoid leaving holes in the allocator pages.
|
||||||
|
let commits_min_evals = allocHeapArrayAligned(ECP_ShortW_Aff[Fp[C], G1], n, alignment = 64)
|
||||||
|
let commits_min_evals_jac = allocHeapArrayAligned(ECP_ShortW_Jac[Fp[C], G1], n, alignment = 64)
|
||||||
|
|
||||||
|
for i in 0 ..< n:
|
||||||
|
commits_min_evals_jac[i].fromAffine(commitments[i])
|
||||||
|
var boxed_eval {.noInit.}: ECP_ShortW_Jac[Fp[C], G1]
|
||||||
|
boxed_eval.fromAffine(C.getGenerator("G1"))
|
||||||
|
boxed_eval.scalarMul_vartime(evals_at_challenges[i])
|
||||||
|
commits_min_evals_jac[i].diff_vartime(commits_min_evals_jac[i], boxed_eval)
|
||||||
|
|
||||||
|
commits_min_evals.batchAffine(commits_min_evals_jac, n)
|
||||||
|
freeHeapAligned(commits_min_evals_jac)
|
||||||
|
sum_commit_minus_evals_G1.multiScalarMul_vartime(coefs, commits_min_evals, n)
|
||||||
|
freeHeapAligned(commits_min_evals)
|
||||||
|
|
||||||
|
# ∑[rᵢ][zᵢ][proofᵢ]₁
|
||||||
|
var tmp {.noInit.}: Fr[C]
|
||||||
|
for i in 0 ..< n:
|
||||||
|
tmp.prod(linearIndepRandNumbers[i], challenges[i])
|
||||||
|
coefs[i].fromField(tmp)
|
||||||
|
|
||||||
|
sum_rand_challenge_proofs.multiScalarMul_vartime(coefs, proofs, n)
|
||||||
|
freeHeapAligned(coefs)
|
||||||
|
|
||||||
|
# e(∑ [rᵢ][proofᵢ]₁, [τ]₂) . e(∑[rᵢ]([commitmentᵢ]₁ - [eval_at_challengeᵢ]₁) + ∑[rᵢ][zᵢ][proofᵢ]₁, [-1]₂) = 1
|
||||||
|
template sum_of_sums: untyped = sums_jac[1]
|
||||||
|
|
||||||
|
sum_of_sums.sum_vartime(sum_commit_minus_evals_G1, sum_rand_challenge_proofs)
|
||||||
|
|
||||||
|
var sums {.noInit.}: array[2, ECP_ShortW_Aff[Fp[C], G1]]
|
||||||
|
sums.batchAffine(sums_jac)
|
||||||
|
|
||||||
|
var negG2 {.noInit.}: ECP_ShortW_Aff[F2, G2]
|
||||||
|
negG2.neg(C.getGenerator("G2"))
|
||||||
|
|
||||||
|
var gt {.noInit.}: C.getGT()
|
||||||
|
gt.pairing(sums, [tauG2, negG2])
|
||||||
|
|
||||||
return gt.isOne().bool()
|
return gt.isOne().bool()
|
||||||
@ -7,6 +7,8 @@
|
|||||||
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
import
|
import
|
||||||
|
std/typetraits,
|
||||||
|
|
||||||
./math/config/curves,
|
./math/config/curves,
|
||||||
./math/io/[io_bigints, io_fields],
|
./math/io/[io_bigints, io_fields],
|
||||||
./math/[ec_shortweierstrass, arithmetic, extension_fields],
|
./math/[ec_shortweierstrass, arithmetic, extension_fields],
|
||||||
@ -15,7 +17,7 @@ import
|
|||||||
./math/polynomials/polynomials,
|
./math/polynomials/polynomials,
|
||||||
./commitments/kzg_polynomial_commitments,
|
./commitments/kzg_polynomial_commitments,
|
||||||
./hashes,
|
./hashes,
|
||||||
./platforms/[abstractions, views, allocs],
|
./platforms/[abstractions, allocs],
|
||||||
./serialization/[codecs_bls12_381, endians],
|
./serialization/[codecs_bls12_381, endians],
|
||||||
./trusted_setups/ethereum_kzg_srs
|
./trusted_setups/ethereum_kzg_srs
|
||||||
|
|
||||||
@ -67,11 +69,9 @@ const BYTES_PER_BLOB = BYTES_PER_FIELD_ELEMENT*FIELD_ELEMENTS_PER_BLOB
|
|||||||
type
|
type
|
||||||
Blob* = array[BYTES_PER_BLOB, byte]
|
Blob* = array[BYTES_PER_BLOB, byte]
|
||||||
|
|
||||||
KZGCommitment* = object
|
KZGCommitment* = distinct ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
||||||
raw: ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
|
||||||
|
|
||||||
KZGProof* = object
|
KZGProof* = distinct ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
||||||
raw: ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
|
||||||
|
|
||||||
CttEthKzgStatus* = enum
|
CttEthKzgStatus* = enum
|
||||||
cttEthKZG_Success
|
cttEthKZG_Success
|
||||||
@ -121,21 +121,20 @@ func fiatShamirChallenge(dst: var Fr[BLS12_381], blob: Blob, commitmentBytes: ar
|
|||||||
transcript.finish(challenge)
|
transcript.finish(challenge)
|
||||||
dst.fromDigest(challenge)
|
dst.fromDigest(challenge)
|
||||||
|
|
||||||
func computePowers(dst: MutableView[Fr[BLS12_381]], base: Fr[BLS12_381]) =
|
func computePowers(dst: ptr UncheckedArray[Fr[BLS12_381]], len: int, base: Fr[BLS12_381]) =
|
||||||
## We need linearly independent random numbers
|
## We need linearly independent random numbers
|
||||||
## for batch proof sampling.
|
## for batch proof sampling.
|
||||||
## Powers are linearly independent.
|
## Powers are linearly independent.
|
||||||
## It's also likely faster than calling a fast RNG + modular reduction
|
## It's also likely faster than calling a fast RNG + modular reduction
|
||||||
## to be in 0 < number < curve_order
|
## to be in 0 < number < curve_order
|
||||||
## since modular reduction needs modular multiplication anyway.
|
## since modular reduction needs modular multiplication anyway.
|
||||||
let N = dst.len
|
let N = len
|
||||||
if N >= 1:
|
if N >= 1:
|
||||||
dst[0].setOne()
|
dst[0].setOne()
|
||||||
if N >= 2:
|
if N >= 2:
|
||||||
dst[1] = base
|
dst[1] = base
|
||||||
if N >= 3:
|
for i in 2 ..< N:
|
||||||
for i in 2 ..< N:
|
dst[i].prod(dst[i-1], base)
|
||||||
dst[i].prod(dst[i-1], base)
|
|
||||||
|
|
||||||
# Conversion
|
# Conversion
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
@ -160,7 +159,7 @@ func bytes_to_bls_field(dst: var Fr[BLS12_381], src: array[32, byte]): CttCodecS
|
|||||||
|
|
||||||
func bytes_to_kzg_commitment(dst: var KZGCommitment, src: array[48, byte]): CttCodecEccStatus =
|
func bytes_to_kzg_commitment(dst: var KZGCommitment, src: array[48, byte]): CttCodecEccStatus =
|
||||||
## Convert untrusted bytes into a trusted and validated KZGCommitment.
|
## Convert untrusted bytes into a trusted and validated KZGCommitment.
|
||||||
let status = dst.raw.deserialize_g1_compressed(src)
|
let status = dst.distinctBase().deserialize_g1_compressed(src)
|
||||||
if status == cttCodecEcc_PointAtInfinity:
|
if status == cttCodecEcc_PointAtInfinity:
|
||||||
# Point at infinity is allowed
|
# Point at infinity is allowed
|
||||||
return cttCodecEcc_Success
|
return cttCodecEcc_Success
|
||||||
@ -168,7 +167,7 @@ func bytes_to_kzg_commitment(dst: var KZGCommitment, src: array[48, byte]): CttC
|
|||||||
|
|
||||||
func bytes_to_kzg_proof(dst: var KZGProof, src: array[48, byte]): CttCodecEccStatus =
|
func bytes_to_kzg_proof(dst: var KZGProof, src: array[48, byte]): CttCodecEccStatus =
|
||||||
## Convert untrusted bytes into a trusted and validated KZGProof.
|
## Convert untrusted bytes into a trusted and validated KZGProof.
|
||||||
let status = dst.raw.deserialize_g1_compressed(src)
|
let status = dst.distinctBase().deserialize_g1_compressed(src)
|
||||||
if status == cttCodecEcc_PointAtInfinity:
|
if status == cttCodecEcc_PointAtInfinity:
|
||||||
# Point at infinity is allowed
|
# Point at infinity is allowed
|
||||||
return cttCodecEcc_Success
|
return cttCodecEcc_Success
|
||||||
@ -212,8 +211,13 @@ func blob_to_field_polynomial(
|
|||||||
|
|
||||||
# Ethereum KZG public API
|
# Ethereum KZG public API
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
#
|
||||||
|
# We use a simple goto state machine to handle errors and cleanup (if allocs were done)
|
||||||
|
# and have 2 different checks:
|
||||||
|
# - Either we are in "HappyPath" section that shortcuts to resource cleanup on error
|
||||||
|
# - or there are no resources to clean and we can early return from a function.
|
||||||
|
|
||||||
template check(evalExpr: CttCodecScalarStatus): untyped =
|
template check(evalExpr: CttCodecScalarStatus): untyped {.dirty.} =
|
||||||
# Translate codec status code to KZG status code
|
# Translate codec status code to KZG status code
|
||||||
# Beware of resource cleanup like heap allocation, this can early exit the caller.
|
# Beware of resource cleanup like heap allocation, this can early exit the caller.
|
||||||
block:
|
block:
|
||||||
@ -223,7 +227,7 @@ template check(evalExpr: CttCodecScalarStatus): untyped =
|
|||||||
of cttCodecScalar_Zero: discard
|
of cttCodecScalar_Zero: discard
|
||||||
of cttCodecScalar_ScalarLargerThanCurveOrder: return cttEthKZG_ScalarLargerThanCurveOrder
|
of cttCodecScalar_ScalarLargerThanCurveOrder: return cttEthKZG_ScalarLargerThanCurveOrder
|
||||||
|
|
||||||
template check(evalExpr: CttCodecEccStatus): untyped =
|
template check(evalExpr: CttCodecEccStatus): untyped {.dirty.} =
|
||||||
# Translate codec status code to KZG status code
|
# Translate codec status code to KZG status code
|
||||||
# Beware of resource cleanup like heap allocation, this can early exit the caller.
|
# Beware of resource cleanup like heap allocation, this can early exit the caller.
|
||||||
block:
|
block:
|
||||||
@ -236,6 +240,29 @@ template check(evalExpr: CttCodecEccStatus): untyped =
|
|||||||
of cttCodecEcc_PointNotInSubgroup: return cttEthKZG_EccPointNotInSubGroup
|
of cttCodecEcc_PointNotInSubgroup: return cttEthKZG_EccPointNotInSubGroup
|
||||||
of cttCodecEcc_PointAtInfinity: discard
|
of cttCodecEcc_PointAtInfinity: discard
|
||||||
|
|
||||||
|
template check(Section: untyped, evalExpr: CttCodecScalarStatus): untyped {.dirty.} =
|
||||||
|
# Translate codec status code to KZG status code
|
||||||
|
# Exit current code block
|
||||||
|
block:
|
||||||
|
let status = evalExpr # Ensure single evaluation
|
||||||
|
case status
|
||||||
|
of cttCodecScalar_Success: discard
|
||||||
|
of cttCodecScalar_Zero: discard
|
||||||
|
of cttCodecScalar_ScalarLargerThanCurveOrder: result = cttEthKZG_EccPointNotInSubGroup; break Section
|
||||||
|
|
||||||
|
template check(Section: untyped, evalExpr: CttCodecEccStatus): untyped {.dirty.} =
|
||||||
|
# Translate codec status code to KZG status code
|
||||||
|
# Exit current code block
|
||||||
|
block:
|
||||||
|
let status = evalExpr # Ensure single evaluation
|
||||||
|
case status
|
||||||
|
of cttCodecEcc_Success: discard
|
||||||
|
of cttCodecEcc_InvalidEncoding: result = cttEthKZG_EccInvalidEncoding; break Section
|
||||||
|
of cttCodecEcc_CoordinateGreaterThanOrEqualModulus: result = cttEthKZG_EccCoordinateGreaterThanOrEqualModulus; break Section
|
||||||
|
of cttCodecEcc_PointNotOnCurve: result = cttEthKZG_EccPointNotOnCurve; break Section
|
||||||
|
of cttCodecEcc_PointNotInSubgroup: result = cttEthKZG_EccPointNotInSubGroup; break Section
|
||||||
|
of cttCodecEcc_PointAtInfinity: discard
|
||||||
|
|
||||||
func blob_to_kzg_commitment*(
|
func blob_to_kzg_commitment*(
|
||||||
ctx: ptr EthereumKZGContext,
|
ctx: ptr EthereumKZGContext,
|
||||||
dst: var array[48, byte],
|
dst: var array[48, byte],
|
||||||
@ -256,22 +283,20 @@ func blob_to_kzg_commitment*(
|
|||||||
## - and at the verification challenge z.
|
## - and at the verification challenge z.
|
||||||
##
|
##
|
||||||
## with proof = [(p(τ) - p(z)) / (τ-z)]₁
|
## with proof = [(p(τ) - p(z)) / (τ-z)]₁
|
||||||
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, matchingOrderBigInt(BLS12_381)], 64)
|
|
||||||
let status = poly.blob_to_bigint_polynomial(blob)
|
|
||||||
if status == cttCodecScalar_ScalarLargerThanCurveOrder:
|
|
||||||
freeHeapAligned(poly)
|
|
||||||
return cttEthKZG_ScalarLargerThanCurveOrder
|
|
||||||
elif status != cttCodecScalar_Success:
|
|
||||||
debugEcho "Unreachable status in blob_to_kzg_commitment: ", status
|
|
||||||
debugEcho "Panicking ..."
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
var r {.noinit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, matchingOrderBigInt(BLS12_381)], 64)
|
||||||
kzg_commit(r, poly.evals, ctx.srs_lagrange_g1) # symbol resolution need explicit generics
|
|
||||||
discard dst.serialize_g1_compressed(r)
|
block HappyPath:
|
||||||
|
check HappyPath, poly.blob_to_bigint_polynomial(blob)
|
||||||
|
|
||||||
|
var r {.noinit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1]
|
||||||
|
kzg_commit(r, poly.evals, ctx.srs_lagrange_g1)
|
||||||
|
discard dst.serialize_g1_compressed(r)
|
||||||
|
|
||||||
|
result = cttEthKZG_Success
|
||||||
|
|
||||||
freeHeapAligned(poly)
|
freeHeapAligned(poly)
|
||||||
return cttEthKZG_Success
|
return result
|
||||||
|
|
||||||
func compute_kzg_proof*(
|
func compute_kzg_proof*(
|
||||||
ctx: ptr EthereumKZGContext,
|
ctx: ptr EthereumKZGContext,
|
||||||
@ -295,36 +320,30 @@ func compute_kzg_proof*(
|
|||||||
|
|
||||||
# Random or Fiat-Shamir challenge
|
# Random or Fiat-Shamir challenge
|
||||||
var z {.noInit.}: Fr[BLS12_381]
|
var z {.noInit.}: Fr[BLS12_381]
|
||||||
var status = bytes_to_bls_field(z, z_bytes)
|
check z.bytes_to_bls_field(z_bytes)
|
||||||
if status != cttCodecScalar_Success:
|
|
||||||
# cttCodecScalar_Zero is not possible
|
|
||||||
return cttEthKZG_ScalarLargerThanCurveOrder
|
|
||||||
|
|
||||||
# Blob -> Polynomial
|
|
||||||
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
||||||
status = poly.blob_to_field_polynomial(blob)
|
|
||||||
if status == cttCodecScalar_ScalarLargerThanCurveOrder:
|
|
||||||
freeHeapAligned(poly)
|
|
||||||
return cttEthKZG_ScalarLargerThanCurveOrder
|
|
||||||
elif status != cttCodecScalar_Success:
|
|
||||||
debugEcho "Unreachable status in compute_kzg_proof: ", status
|
|
||||||
debugEcho "Panicking ..."
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
var y {.noInit.}: Fr[BLS12_381] # y = p(z), eval at challenge z
|
block HappyPath:
|
||||||
var proof {.noInit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1] # [proof]₁ = [(p(τ) - p(z)) / (τ-z)]₁
|
# Blob -> Polynomial
|
||||||
|
check HappyPath, poly.blob_to_field_polynomial(blob)
|
||||||
|
|
||||||
kzg_prove(
|
# KZG Prove
|
||||||
proof, y,
|
var y {.noInit.}: Fr[BLS12_381] # y = p(z), eval at challenge z
|
||||||
poly[], ctx.domain,
|
var proof {.noInit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1] # [proof]₁ = [(p(τ) - p(z)) / (τ-z)]₁
|
||||||
z, ctx.srs_lagrange_g1,
|
|
||||||
isBitReversedDomain = true)
|
|
||||||
|
|
||||||
discard proof_bytes.serialize_g1_compressed(proof) # cannot fail
|
kzg_prove(
|
||||||
y_bytes.marshal(y, bigEndian) # cannot fail
|
proof, y,
|
||||||
|
poly[], ctx.domain,
|
||||||
|
z, ctx.srs_lagrange_g1,
|
||||||
|
isBitReversedDomain = true)
|
||||||
|
|
||||||
|
discard proof_bytes.serialize_g1_compressed(proof) # cannot fail
|
||||||
|
y_bytes.marshal(y, bigEndian) # cannot fail
|
||||||
|
result = cttEthKZG_Success
|
||||||
|
|
||||||
freeHeapAligned(poly)
|
freeHeapAligned(poly)
|
||||||
return cttEthKZG_Success
|
return result
|
||||||
|
|
||||||
func verify_kzg_proof*(
|
func verify_kzg_proof*(
|
||||||
ctx: ptr EthereumKZGContext,
|
ctx: ptr EthereumKZGContext,
|
||||||
@ -346,7 +365,10 @@ func verify_kzg_proof*(
|
|||||||
var proof {.noInit.}: KZGProof
|
var proof {.noInit.}: KZGProof
|
||||||
check proof.bytes_to_kzg_proof(proof_bytes)
|
check proof.bytes_to_kzg_proof(proof_bytes)
|
||||||
|
|
||||||
let verif = kzg_verify(commitment.raw, challenge, eval_at_challenge, proof.raw, ctx.srs_monomial_g2.coefs[1])
|
let verif = kzg_verify(ECP_ShortW_Aff[Fp[BLS12_381], G1](commitment),
|
||||||
|
challenge, eval_at_challenge,
|
||||||
|
ECP_ShortW_Aff[Fp[BLS12_381], G1](proof),
|
||||||
|
ctx.srs_monomial_g2.coefs[1])
|
||||||
if verif:
|
if verif:
|
||||||
return cttEthKZG_Success
|
return cttEthKZG_Success
|
||||||
else:
|
else:
|
||||||
@ -365,31 +387,31 @@ func compute_blob_kzg_proof*(
|
|||||||
|
|
||||||
# Blob -> Polynomial
|
# Blob -> Polynomial
|
||||||
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
||||||
var status = poly.blob_to_field_polynomial(blob)
|
|
||||||
if status == cttCodecScalar_ScalarLargerThanCurveOrder:
|
|
||||||
freeHeapAligned(poly)
|
|
||||||
return cttEthKZG_ScalarLargerThanCurveOrder
|
|
||||||
elif status != cttCodecScalar_Success:
|
|
||||||
debugEcho "Unreachable status in compute_kzg_proof: ", status
|
|
||||||
debugEcho "Panicking ..."
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
var challenge {.noInit.}: Fr[BLS12_381]
|
block HappyPath:
|
||||||
challenge.fiatShamirChallenge(blob[], commitment_bytes)
|
# Blob -> Polynomial
|
||||||
|
check HappyPath, poly.blob_to_field_polynomial(blob)
|
||||||
|
|
||||||
var y {.noInit.}: Fr[BLS12_381] # y = p(z), eval at challenge z
|
# Fiat-Shamir challenge
|
||||||
var proof {.noInit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1] # [proof]₁ = [(p(τ) - p(z)) / (τ-z)]₁
|
var challenge {.noInit.}: Fr[BLS12_381]
|
||||||
|
challenge.fiatShamirChallenge(blob[], commitment_bytes)
|
||||||
|
|
||||||
kzg_prove(
|
# KZG Prove
|
||||||
proof, y,
|
var y {.noInit.}: Fr[BLS12_381] # y = p(z), eval at challenge z
|
||||||
poly[], ctx.domain,
|
var proof {.noInit.}: ECP_ShortW_Aff[Fp[BLS12_381], G1] # [proof]₁ = [(p(τ) - p(z)) / (τ-z)]₁
|
||||||
challenge, ctx.srs_lagrange_g1,
|
|
||||||
isBitReversedDomain = true)
|
|
||||||
|
|
||||||
discard proof_bytes.serialize_g1_compressed(proof) # cannot fail
|
kzg_prove(
|
||||||
|
proof, y,
|
||||||
|
poly[], ctx.domain,
|
||||||
|
challenge, ctx.srs_lagrange_g1,
|
||||||
|
isBitReversedDomain = true)
|
||||||
|
|
||||||
|
discard proof_bytes.serialize_g1_compressed(proof) # cannot fail
|
||||||
|
|
||||||
|
result = cttEthKZG_Success
|
||||||
|
|
||||||
freeHeapAligned(poly)
|
freeHeapAligned(poly)
|
||||||
return cttEthKZG_Success
|
return result
|
||||||
|
|
||||||
func verify_blob_kzg_proof*(
|
func verify_blob_kzg_proof*(
|
||||||
ctx: ptr EthereumKZGContext,
|
ctx: ptr EthereumKZGContext,
|
||||||
@ -404,49 +426,156 @@ func verify_blob_kzg_proof*(
|
|||||||
var proof {.noInit.}: KZGProof
|
var proof {.noInit.}: KZGProof
|
||||||
check proof.bytes_to_kzg_proof(proof_bytes)
|
check proof.bytes_to_kzg_proof(proof_bytes)
|
||||||
|
|
||||||
# Blob -> Polynomial
|
|
||||||
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], 64)
|
||||||
var status = poly.blob_to_field_polynomial(blob)
|
|
||||||
if status == cttCodecScalar_ScalarLargerThanCurveOrder:
|
|
||||||
freeHeapAligned(poly)
|
|
||||||
return cttEthKZG_ScalarLargerThanCurveOrder
|
|
||||||
elif status != cttCodecScalar_Success:
|
|
||||||
debugEcho "Unreachable status in compute_kzg_proof: ", status
|
|
||||||
debugEcho "Panicking ..."
|
|
||||||
quit 1
|
|
||||||
|
|
||||||
var challengeFr {.noInit.}: Fr[BLS12_381]
|
|
||||||
challengeFr.fiatShamirChallenge(blob[], commitment_bytes)
|
|
||||||
|
|
||||||
var challenge, eval_at_challenge {.noInit.}: matchingOrderBigInt(BLS12_381)
|
|
||||||
challenge.fromField(challengeFr)
|
|
||||||
|
|
||||||
let invRootsMinusZ = allocHeapAligned(array[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], alignment = 64)
|
let invRootsMinusZ = allocHeapAligned(array[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], alignment = 64)
|
||||||
|
|
||||||
# Compute 1/(ωⁱ - z) with ω a root of unity, i in [0, N).
|
block HappyPath:
|
||||||
# zIndex = i if ωⁱ - z == 0 (it is the i-th root of unity) and -1 otherwise.
|
# Blob -> Polynomial
|
||||||
let zIndex = invRootsMinusZ[].inverseRootsMinusZ_vartime(
|
check HappyPath, poly.blob_to_field_polynomial(blob)
|
||||||
ctx.domain, challengeFr,
|
|
||||||
earlyReturnOnZero = true)
|
|
||||||
|
|
||||||
if zIndex == -1:
|
# Fiat-Shamir challenge
|
||||||
var eval_at_challenge_fr{.noInit.}: Fr[BLS12_381]
|
var challengeFr {.noInit.}: Fr[BLS12_381]
|
||||||
eval_at_challenge_fr.evalPolyAt(
|
challengeFr.fiatShamirChallenge(blob[], commitment_bytes)
|
||||||
poly[], challengeFr,
|
|
||||||
invRootsMinusZ[],
|
var challenge, eval_at_challenge {.noInit.}: matchingOrderBigInt(BLS12_381)
|
||||||
ctx.domain)
|
challenge.fromField(challengeFr)
|
||||||
eval_at_challenge.fromField(eval_at_challenge_fr)
|
|
||||||
else:
|
# Lagrange Polynomial evaluation
|
||||||
eval_at_challenge.fromField(poly.evals[zIndex])
|
# ------------------------------
|
||||||
|
# 1. Compute 1/(ωⁱ - z) with ω a root of unity, i in [0, N).
|
||||||
|
# zIndex = i if ωⁱ - z == 0 (it is the i-th root of unity) and -1 otherwise.
|
||||||
|
let zIndex = invRootsMinusZ[].inverseRootsMinusZ_vartime(
|
||||||
|
ctx.domain, challengeFr,
|
||||||
|
earlyReturnOnZero = true)
|
||||||
|
|
||||||
|
# 2. Actual evaluation
|
||||||
|
if zIndex == -1:
|
||||||
|
var eval_at_challenge_fr{.noInit.}: Fr[BLS12_381]
|
||||||
|
eval_at_challenge_fr.evalPolyAt(
|
||||||
|
poly[], challengeFr,
|
||||||
|
invRootsMinusZ[],
|
||||||
|
ctx.domain)
|
||||||
|
eval_at_challenge.fromField(eval_at_challenge_fr)
|
||||||
|
else:
|
||||||
|
eval_at_challenge.fromField(poly.evals[zIndex])
|
||||||
|
|
||||||
|
# KZG verification
|
||||||
|
let verif = kzg_verify(ECP_ShortW_Aff[Fp[BLS12_381], G1](commitment),
|
||||||
|
challenge, eval_at_challenge,
|
||||||
|
ECP_ShortW_Aff[Fp[BLS12_381], G1](proof),
|
||||||
|
ctx.srs_monomial_g2.coefs[1])
|
||||||
|
if verif:
|
||||||
|
result = cttEthKZG_Success
|
||||||
|
else:
|
||||||
|
result = cttEthKZG_VerificationFailure
|
||||||
|
|
||||||
freeHeapAligned(invRootsMinusZ)
|
freeHeapAligned(invRootsMinusZ)
|
||||||
freeHeapAligned(poly)
|
freeHeapAligned(poly)
|
||||||
|
return result
|
||||||
|
|
||||||
let verif = kzg_verify(commitment.raw, challenge, eval_at_challenge, proof.raw, ctx.srs_monomial_g2.coefs[1])
|
func verify_blob_kzg_proof_batch*(
|
||||||
if verif:
|
ctx: ptr EthereumKZGContext,
|
||||||
return cttEthKZG_Success
|
blobs: ptr UncheckedArray[Blob],
|
||||||
else:
|
commitments_bytes: ptr UncheckedArray[array[48, byte]],
|
||||||
|
proof_bytes: ptr UncheckedArray[array[48, byte]],
|
||||||
|
n: int,
|
||||||
|
secureRandomBytes: array[32, byte]): CttEthKzgStatus {.tags:[Alloca, HeapAlloc, Vartime].} =
|
||||||
|
## Verify `n` (blob, commitment, proof) sets efficiently
|
||||||
|
##
|
||||||
|
## `n` is the number of verifications set
|
||||||
|
## - if n is negative, this procedure returns verification failure
|
||||||
|
## - if n is zero, this procedure returns verification success
|
||||||
|
##
|
||||||
|
## `secureRandomBytes` random byte must come from a cryptographically secure RNG
|
||||||
|
## or computed through the Fiat-Shamir heuristic.
|
||||||
|
## It serves as a random number
|
||||||
|
## that is not in the control of a potential attacker to prevent potential
|
||||||
|
## rogue commitments attacks due to homomorphic properties of pairings,
|
||||||
|
## i.e. commitments that are linear combination of others and sum would be zero.
|
||||||
|
|
||||||
|
if n < 0:
|
||||||
return cttEthKZG_VerificationFailure
|
return cttEthKZG_VerificationFailure
|
||||||
|
if n == 0:
|
||||||
|
return cttEthKZG_Success
|
||||||
|
|
||||||
|
let commitments = allocHeapArrayAligned(KZGCommitment, n, alignment = 64)
|
||||||
|
let challenges = allocHeapArrayAligned(Fr[BLS12_381], n, alignment = 64)
|
||||||
|
let evals_at_challenges = allocHeapArrayAligned(matchingOrderBigInt(BLS12_381), n, alignment = 64)
|
||||||
|
let proofs = allocHeapArrayAligned(KZGProof, n, alignment = 64)
|
||||||
|
|
||||||
|
let poly = allocHeapAligned(PolynomialEval[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], alignment = 64)
|
||||||
|
let invRootsMinusZ = allocHeapAligned(array[FIELD_ELEMENTS_PER_BLOB, Fr[BLS12_381]], alignment = 64)
|
||||||
|
|
||||||
|
block HappyPath:
|
||||||
|
for i in 0 ..< n:
|
||||||
|
check HappyPath, commitments[i].bytes_to_kzg_commitment(commitments_bytes[i])
|
||||||
|
check HappyPath, poly.blob_to_field_polynomial(blobs[i].addr)
|
||||||
|
challenges[i].fiatShamirChallenge(blobs[i], commitments_bytes[i])
|
||||||
|
|
||||||
|
# Lagrange Polynomial evaluation
|
||||||
|
# ------------------------------
|
||||||
|
# 1. Compute 1/(ωⁱ - z) with ω a root of unity, i in [0, N).
|
||||||
|
# zIndex = i if ωⁱ - z == 0 (it is the i-th root of unity) and -1 otherwise.
|
||||||
|
let zIndex = invRootsMinusZ[].inverseRootsMinusZ_vartime(
|
||||||
|
ctx.domain, challenges[i],
|
||||||
|
earlyReturnOnZero = true)
|
||||||
|
# 2. Actual evaluation
|
||||||
|
if zIndex == -1:
|
||||||
|
var eval_at_challenge_fr{.noInit.}: Fr[BLS12_381]
|
||||||
|
eval_at_challenge_fr.evalPolyAt(
|
||||||
|
poly[], challenges[i],
|
||||||
|
invRootsMinusZ[],
|
||||||
|
ctx.domain)
|
||||||
|
evals_at_challenges[i].fromField(eval_at_challenge_fr)
|
||||||
|
else:
|
||||||
|
evals_at_challenges[i].fromField(poly.evals[zIndex])
|
||||||
|
|
||||||
|
check HappyPath, proofs[i].bytes_to_kzg_proof(proof_bytes[i])
|
||||||
|
|
||||||
|
var randomBlindingFr {.noInit.}: Fr[BLS12_381]
|
||||||
|
block blinding: # Ensure we don't multiply by 0 for blinding
|
||||||
|
# 1. Try with the random number supplied
|
||||||
|
for i in 0 ..< secureRandomBytes.len:
|
||||||
|
if secureRandomBytes[i] != byte 0:
|
||||||
|
randomBlindingFr.fromDigest(secureRandomBytes)
|
||||||
|
break blinding
|
||||||
|
# 2. If it's 0 (how?!), we just hash all the Fiat-Shamir challenges
|
||||||
|
var transcript: sha256
|
||||||
|
transcript.init()
|
||||||
|
transcript.update(RANDOM_CHALLENGE_KZG_BATCH_DOMAIN)
|
||||||
|
transcript.update(cast[ptr UncheckedArray[byte]](challenges).toOpenArray(0, n*sizeof(Fr[BLS12_381])-1))
|
||||||
|
|
||||||
|
var blindingBytes {.noInit.}: array[32, byte]
|
||||||
|
transcript.finish(blindingBytes)
|
||||||
|
randomBlindingFr.fromDigest(blindingBytes)
|
||||||
|
|
||||||
|
let linearIndepRandNumbers = allocHeapArrayAligned(Fr[BLS12_381], n, alignment = 64)
|
||||||
|
linearIndepRandNumbers.computePowers(n, randomBlindingFr)
|
||||||
|
|
||||||
|
type EcAffArray = ptr UncheckedArray[ECP_ShortW_Aff[Fp[BLS12_381], G1]]
|
||||||
|
let verif = kzg_verify_batch(
|
||||||
|
cast[EcAffArray](commitments),
|
||||||
|
challenges,
|
||||||
|
evals_at_challenges,
|
||||||
|
cast[EcAffArray](proofs),
|
||||||
|
linearIndepRandNumbers,
|
||||||
|
n,
|
||||||
|
ctx.srs_monomial_g2.coefs[1])
|
||||||
|
if verif:
|
||||||
|
result = cttEthKZG_Success
|
||||||
|
else:
|
||||||
|
result = cttEthKZG_VerificationFailure
|
||||||
|
|
||||||
|
freeHeapAligned(linearIndepRandNumbers)
|
||||||
|
|
||||||
|
freeHeapAligned(invRootsMinusZ)
|
||||||
|
freeHeapAligned(poly)
|
||||||
|
freeHeapAligned(proofs)
|
||||||
|
freeHeapAligned(evals_at_challenges)
|
||||||
|
freeHeapAligned(challenges)
|
||||||
|
freeHeapAligned(commitments)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
# Ethereum Trusted Setup
|
# Ethereum Trusted Setup
|
||||||
# ------------------------------------------------------------
|
# ------------------------------------------------------------
|
||||||
|
|||||||
@ -446,9 +446,9 @@ func multiScalarMul_vartime*[bits: static int, F, G](
|
|||||||
r: var ECP_ShortW[F, G],
|
r: var ECP_ShortW[F, G],
|
||||||
coefs: ptr UncheckedArray[BigInt[bits]],
|
coefs: ptr UncheckedArray[BigInt[bits]],
|
||||||
points: ptr UncheckedArray[ECP_ShortW_Aff[F, G]],
|
points: ptr UncheckedArray[ECP_ShortW_Aff[F, G]],
|
||||||
N: int) {.tags:[VarTime, Alloca, HeapAlloc], meter.} =
|
len: int) {.tags:[VarTime, Alloca, HeapAlloc], meter.} =
|
||||||
## Multiscalar multiplication:
|
## Multiscalar multiplication:
|
||||||
## r <- [a₀]P₀ + [a₁]P₁ + ... + [aₙ]Pₙ
|
## r <- [a₀]P₀ + [a₁]P₁ + ... + [aₙ₋₁]Pₙ₋₁
|
||||||
|
|
||||||
multiScalarMul_dispatch_vartime(r, coefs, points, len)
|
multiScalarMul_dispatch_vartime(r, coefs, points, len)
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ func multiScalarMul_vartime*[bits: static int, F, G](
|
|||||||
coefs: openArray[BigInt[bits]],
|
coefs: openArray[BigInt[bits]],
|
||||||
points: openArray[ECP_ShortW_Aff[F, G]]) {.tags:[VarTime, Alloca, HeapAlloc], meter.} =
|
points: openArray[ECP_ShortW_Aff[F, G]]) {.tags:[VarTime, Alloca, HeapAlloc], meter.} =
|
||||||
## Multiscalar multiplication:
|
## Multiscalar multiplication:
|
||||||
## r <- [a₀]P₀ + [a₁]P₁ + ... + [aₙ]Pₙ
|
## r <- [a₀]P₀ + [a₁]P₁ + ... + [aₙ₋₁]Pₙ₋₁
|
||||||
|
|
||||||
debug: doAssert coefs.len == points.len
|
debug: doAssert coefs.len == points.len
|
||||||
let N = points.len
|
let N = points.len
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import
|
|||||||
# 3rd party
|
# 3rd party
|
||||||
pkg/yaml,
|
pkg/yaml,
|
||||||
# Internals
|
# Internals
|
||||||
|
../constantine/hashes,
|
||||||
../constantine/serialization/codecs,
|
../constantine/serialization/codecs,
|
||||||
../constantine/ethereum_eip4844_kzg_polynomial_commitments
|
../constantine/ethereum_eip4844_kzg_polynomial_commitments
|
||||||
|
|
||||||
@ -87,6 +88,35 @@ template parseAssign(dstVariable: untyped, size: static int, hexInput: string) =
|
|||||||
var dstVariable{.inject.} = new(array[size, byte])
|
var dstVariable{.inject.} = new(array[size, byte])
|
||||||
dstVariable[].fromHex(hexInput)
|
dstVariable[].fromHex(hexInput)
|
||||||
|
|
||||||
|
template parseAssignList(dstVariable: untyped, elemSize: static int, hexListInput: YamlNode) =
|
||||||
|
|
||||||
|
var dstVariable{.inject.} = newSeq[array[elemSize, byte]]()
|
||||||
|
|
||||||
|
block exitHappyPath:
|
||||||
|
block exitException:
|
||||||
|
for elem in hexListInput:
|
||||||
|
let hexInput = elem.content
|
||||||
|
|
||||||
|
let prefixBytes = 2*int(hexInput.startsWith("0x"))
|
||||||
|
let expectedLength = elemSize*2 + prefixBytes
|
||||||
|
if hexInput.len != expectedLength:
|
||||||
|
let encodedBytes = (hexInput.len - prefixBytes) div 2
|
||||||
|
stdout.write "[ Incorrect input length for '" &
|
||||||
|
astToStr(dstVariable) &
|
||||||
|
"': encoding " & $encodedBytes & " bytes" &
|
||||||
|
" instead of expected " & $elemSize & " ]\n"
|
||||||
|
|
||||||
|
doAssert testVector["output"].content == "null"
|
||||||
|
break exitException
|
||||||
|
else:
|
||||||
|
dstVariable.setLen(dstVariable.len + 1)
|
||||||
|
dstVariable[^1].fromHex(hexInput)
|
||||||
|
|
||||||
|
break exitHappyPath
|
||||||
|
|
||||||
|
# We're in a template, this shortcuts the caller `walkTests`
|
||||||
|
continue
|
||||||
|
|
||||||
testGen(blob_to_kzg_commitment, testVector):
|
testGen(blob_to_kzg_commitment, testVector):
|
||||||
parseAssign(blob, 32*4096, testVector["input"]["blob"].content)
|
parseAssign(blob, 32*4096, testVector["input"]["blob"].content)
|
||||||
|
|
||||||
@ -175,6 +205,46 @@ testGen(verify_blob_kzg_proof, testVector):
|
|||||||
else:
|
else:
|
||||||
doAssert testVector["output"].content == "null"
|
doAssert testVector["output"].content == "null"
|
||||||
|
|
||||||
|
testGen(verify_blob_kzg_proof_batch, testVector):
|
||||||
|
parseAssignList(blobs, 32*4096, testVector["input"]["blobs"])
|
||||||
|
parseAssignList(commitments, 48, testVector["input"]["commitments"])
|
||||||
|
parseAssignList(proofs, 48, testVector["input"]["proofs"])
|
||||||
|
|
||||||
|
if blobs.len != commitments.len:
|
||||||
|
stdout.write "[ Length mismatch between blobs and commitments ]\n"
|
||||||
|
doAssert testVector["output"].content == "null"
|
||||||
|
continue
|
||||||
|
if blobs.len != proofs.len:
|
||||||
|
stdout.write "[ Length mismatch between blobs and proofs ]\n"
|
||||||
|
doAssert testVector["output"].content == "null"
|
||||||
|
continue
|
||||||
|
|
||||||
|
# For reproducibility/debugging we don't use the CSPRNG here
|
||||||
|
var randomBlinding {.noInit.}: array[32, byte]
|
||||||
|
sha256.hash(randomBlinding, "The wizard quickly jinxed the gnomes before they vaporized.")
|
||||||
|
|
||||||
|
template asUnchecked[T](a: openArray[T]): ptr UncheckedArray[T] =
|
||||||
|
if a.len > 0:
|
||||||
|
cast[ptr UncheckedArray[T]](a[0].unsafeAddr)
|
||||||
|
else:
|
||||||
|
nil
|
||||||
|
|
||||||
|
let status = verify_blob_kzg_proof_batch(
|
||||||
|
ctx,
|
||||||
|
blobs.asUnchecked(),
|
||||||
|
commitments.asUnchecked(),
|
||||||
|
proofs.asUnchecked(),
|
||||||
|
blobs.len,
|
||||||
|
randomBlinding)
|
||||||
|
stdout.write "[" & $status & "]\n"
|
||||||
|
|
||||||
|
if status == cttEthKZG_Success:
|
||||||
|
doAssert testVector["output"].content == "true"
|
||||||
|
elif status == cttEthKZG_VerificationFailure:
|
||||||
|
doAssert testVector["output"].content == "false"
|
||||||
|
else:
|
||||||
|
doAssert testVector["output"].content == "null"
|
||||||
|
|
||||||
block:
|
block:
|
||||||
suite "Ethereum Deneb Hardfork / EIP-4844 / Proto-Danksharding / KZG Polynomial Commitments":
|
suite "Ethereum Deneb Hardfork / EIP-4844 / Proto-Danksharding / KZG Polynomial Commitments":
|
||||||
let ctx = load_ethereum_kzg_test_trusted_setup_mainnet()
|
let ctx = load_ethereum_kzg_test_trusted_setup_mainnet()
|
||||||
@ -191,7 +261,10 @@ block:
|
|||||||
test "compute_blob_kzg_proof(proof: var array[48, byte], blob: ptr array[4096, byte], commitment: array[48, byte])":
|
test "compute_blob_kzg_proof(proof: var array[48, byte], blob: ptr array[4096, byte], commitment: array[48, byte])":
|
||||||
ctx.test_compute_blob_kzg_proof()
|
ctx.test_compute_blob_kzg_proof()
|
||||||
|
|
||||||
test "verify_blob_kzg_proof(blob: ptr array[4096, byte], commitment: array[48, byte], proof: var array[48, byte])":
|
test "verify_blob_kzg_proof(blob: ptr array[4096, byte], commitment, proof: array[48, byte])":
|
||||||
ctx.test_verify_blob_kzg_proof()
|
ctx.test_verify_blob_kzg_proof()
|
||||||
|
|
||||||
|
test "verify_blob_kzg_proof_batch(blobs: ptr UncheckedArray[array[4096, byte]], commitments, proofs: ptr UncheckedArray[array[48, byte]], n: int, secureRandomBytes: array[32, byte])":
|
||||||
|
ctx.test_verify_blob_kzg_proof_batch()
|
||||||
|
|
||||||
ctx.delete()
|
ctx.delete()
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user