diff --git a/constantine/eth_verkle_ipa/ipa_prover.nim b/constantine/eth_verkle_ipa/ipa_prover.nim index be40105e..27f13221 100644 --- a/constantine/eth_verkle_ipa/ipa_prover.nim +++ b/constantine/eth_verkle_ipa/ipa_prover.nim @@ -38,7 +38,7 @@ import # Further reference refer to this https://dankradfeist.de/ethereum/2021/07/27/inner-product-arguments.html -func genIPAConfig*(res: var IPASettings, ipaTranscript: var IpaTranscript[sha256, 32]) : bool {.inline.} = +func genIPAConfig*(res: var IPASettings, ipaTranscript: var IpaTranscript[CryptoHash, 32]) : bool {.inline.} = # Initiates a new IPASettings # IPASettings has all the necessary information related to create an IPA proof # such as SRS, precomputed weights for Barycentric formula @@ -54,7 +54,7 @@ func genIPAConfig*(res: var IPASettings, ipaTranscript: var IpaTranscript[sha256 res.numRounds.computeNumRounds(uint64(VerkleDomain)) return true -func createIPAProof*[IPAProof] (res: var IPAProof, transcript: var sha256, ic: IPASettings, commitment: EC_P, a: var openArray[Fr[Banderwagon]], evalPoint: Fr[Banderwagon]) : bool {.inline.} = +func createIPAProof*[IPAProof] (res: var IPAProof, transcript: var CryptoHash, ic: IPASettings, commitment: EC_P, a: var openArray[Fr[Banderwagon]], evalPoint: Fr[Banderwagon]) : bool {.inline.} = ## createIPAProof creates an IPA proof for a committed polynomial in evaluation form. ## `a` vectors are the evaluation points in the domain, and `evalPoint` represents the evaluation point. transcript.domain_separator(asBytes"ipa") diff --git a/constantine/eth_verkle_ipa/ipa_verifier.nim b/constantine/eth_verkle_ipa/ipa_verifier.nim index 1548bae8..429261a0 100644 --- a/constantine/eth_verkle_ipa/ipa_verifier.nim +++ b/constantine/eth_verkle_ipa/ipa_verifier.nim @@ -24,13 +24,13 @@ import # # ############################################################ -func generateChallengesForIPA*(res: var openArray[matchingOrderBigInt(Banderwagon)], transcript: var sha256, proof: IPAProof) = +func generateChallengesForIPA*(res: var openArray[matchingOrderBigInt(Banderwagon)], transcript: var CryptoHash, proof: IPAProof) = for i in 0 ..< 8: transcript.pointAppend( asBytes"L", proof.L_vector[i]) transcript.pointAppend( asBytes"R", proof.R_vector[i]) res[i].generateChallengeScalar(transcript,asBytes"x") -func checkIPAProof* (ic: IPASettings, transcript: var sha256, commitment: var EC_P, proof: IPAProof, evalPoint: Fr[Banderwagon], res: Fr[Banderwagon]) : bool = +func checkIPAProof* (ic: IPASettings, transcript: var CryptoHash, commitment: var EC_P, proof: IPAProof, evalPoint: Fr[Banderwagon], res: Fr[Banderwagon]) : bool = # Check IPA proof verifier a IPA proof for a committed polynomial in evaluation form # It verifies whether the proof is valid for the given polynomial at the evaluation `evalPoint` # and cross-checking it with `result` diff --git a/constantine/eth_verkle_ipa/multiproof.nim b/constantine/eth_verkle_ipa/multiproof.nim index 0c0ab888..931f2d00 100644 --- a/constantine/eth_verkle_ipa/multiproof.nim +++ b/constantine/eth_verkle_ipa/multiproof.nim @@ -59,7 +59,7 @@ func computePowersOfElem*(res: var openArray[Fr], x: Fr, degree: SomeSignedInt)= # ############################################################ -func createMultiProof* [MultiProof] (res: var MultiProof, transcript: var sha256, ipaSetting: IPASettings, Cs: openArray[EC_P], Fs: array[VERKLE_DOMAIN, array[VERKLE_DOMAIN, Fr[Banderwagon]]], Zs: openArray[uint8], precomp: PrecomputedWeights, basis: array[VERKLE_DOMAIN, EC_P]) : bool = +func createMultiProof* [MultiProof] (res: var MultiProof, transcript: var CryptoHash, ipaSetting: IPASettings, Cs: openArray[EC_P], Fs: array[VERKLE_DOMAIN, array[VERKLE_DOMAIN, Fr[Banderwagon]]], Zs: openArray[uint8], precomp: PrecomputedWeights, basis: array[VERKLE_DOMAIN, EC_P]) : bool = # createMultiProof creates a multi-proof for several polynomials in the evaluation form # The list of triplets are as follows: (C, Fs, Z) represents each polynomial commitment # and their evaluation in the domain, and the evaluating point respectively @@ -223,7 +223,7 @@ func createMultiProof* [MultiProof] (res: var MultiProof, transcript: var sha256 # ############################################################ -func verifyMultiproof*(multiProof: var MultiProof, transcript : var sha256, ipaSettings: IPASettings, Cs: openArray[EC_P], Ys: openArray[Fr[Banderwagon]], Zs: openArray[uint8]) : bool = +func verifyMultiproof*(multiProof: var MultiProof, transcript : var CryptoHash, ipaSettings: IPASettings, Cs: openArray[EC_P], Ys: openArray[Fr[Banderwagon]], Zs: openArray[uint8]) : bool = # Multiproof verifier verifies the multiproof for several polynomials in the evaluation form # The list of triplets (C,Y,Z) represents each polynomial commitment, evaluation # result, and evaluation point in the domain diff --git a/constantine/eth_verkle_ipa/transcript_gen.nim b/constantine/eth_verkle_ipa/transcript_gen.nim index 76a679cd..b582eeef 100644 --- a/constantine/eth_verkle_ipa/transcript_gen.nim +++ b/constantine/eth_verkle_ipa/transcript_gen.nim @@ -8,7 +8,7 @@ # ############################################################ # -# CryptoHash Generator for Challenge Scalars +# Generator for Challenge Scalars # # ############################################################ @@ -17,6 +17,8 @@ import ../platforms/[primitives,abstractions], ../serialization/endians, ../math/config/[type_ff, curves], + ../math/io/io_bigints, + ../math/arithmetic/limbs_montgomery, ../math/[extension_fields, arithmetic], ../math/elliptic/ec_twistededwards_projective, ../hashes, @@ -27,37 +29,48 @@ func newTranscriptGen*(res: var CryptoHash, label: openArray[byte]) = res.update(label) func messageAppend*(res: var CryptoHash, message: openArray[byte], label: openArray[byte]) = - res.init() res.update(label) res.update(message) func messageAppend_u64*(res: var CryptoHash, label: openArray[byte], num_value: uint64) = - res.init() res.update(label) res.update(num_value.toBytes(bigEndian)) func domainSeparator*(res: var CryptoHash, label: openArray[byte]) = - var state {.noInit.} : CryptoHash - state.update(label) + res.update(label) func pointAppend*(res: var CryptoHash, label: openArray[byte], point: EC_P) = var bytes {.noInit.}: array[32, byte] - if bytes.serialize(point) == cttCodecEcc_Success: - res.messageAppend(bytes, label) + + let status {.used.} = bytes.serialize(point) + debug: doAssert status == cttCodecEcc_Success, "transcript_gen.pointAppend: Serialization Failure!" + res.messageAppend(bytes, label) func scalarAppend*(res: var CryptoHash, label: openArray[byte], scalar: matchingOrderBigInt(Banderwagon)) = var bytes {.noInit.}: array[32, byte] - if bytes.serialize_scalar(scalar) == cttCodecScalar_Success: - res.messageAppend(bytes, label) + let status {.used.} = bytes.serialize_scalar(scalar, littleEndian) -func generateChallengeScalar*(gen: var matchingOrderBigInt(Banderwagon), transcript: var CryptoHash, label: openArray[byte]) = + debug: doAssert status == cttCodecScalar_Success, "transcript_gen.scalarAppend: Serialization Failure!" + res.messageAppend(bytes, label) + +func generateChallengeScalar*(challenge: var matchingOrderBigInt(Banderwagon), transcript: var CryptoHash, label: openArray[byte]) = # Generating Challenge Scalars based on the Fiat Shamir method transcript.domainSeparator(label) - var hash: array[32, byte] + var hash {.noInit.} : array[32, byte] + # Finalise the transcript state into a hash transcript.finish(hash) - if gen.deserialize_scalar(hash) == cttCodecScalar_Success: - transcript.clear() - transcript.scalarAppend(label, gen) + var interim_challenge {.noInit.}: Fr[Banderwagon] + # Safely deserialize into the Montgomery residue form + let stat {.used.} = interim_challenge.make_scalar_mod_order(hash, littleEndian) + debug: doAssert stat, "transcript_gen.generateChallengeScalar: Unexpected failure" + + # Reset the Transcript state + transcript.clear() + challenge = interim_challenge.toBig() + + # Append the challenge into the resetted transcript + transcript.scalarAppend(label, challenge) + \ No newline at end of file diff --git a/constantine/serialization/codecs_banderwagon.nim b/constantine/serialization/codecs_banderwagon.nim index a915a841..8c5445da 100644 --- a/constantine/serialization/codecs_banderwagon.nim +++ b/constantine/serialization/codecs_banderwagon.nim @@ -20,7 +20,9 @@ import ec_twistededwards_projective, ec_twistededwards_batch_ops ], + ../math/arithmetic/limbs_montgomery, ../math/[ + arithmetic/bigints, extension_fields, arithmetic, constants/banderwagon_subgroups @@ -44,6 +46,27 @@ func validate_scalar*(scalar: matchingOrderBigInt(Banderwagon)): CttCodecScalarS return cttCodecScalar_ScalarLargerThanCurveOrder return cttCodecScalar_Success +func make_scalar_mod_order*(reduced_scalar: var Fr[Banderwagon], src: array[32, byte], order: static Endianness = bigEndian): bool = + ## Convert a 32-byte array to a field element, reducing it modulo Banderwagon's curve order if necessary. + + # Which can be safely stored in a 256 BigInt + # Now incase of the scalar overflowing the last 3-bits + # it is converted from its natural representation + # to the Montgomery residue form + var res: bool = false + var scalar {.noInit.}: BigInt[256] + scalar.unmarshal(src, order) + + getMont(reduced_scalar.mres.limbs, scalar.limbs, + Fr[Banderwagon].fieldMod().limbs, + Fr[Banderwagon].getR2modP().limbs, + Fr[Banderwagon].getNegInvModWord(), + Fr[Banderwagon].getSpareBits()) + res = true + return res + + + func serialize*(dst: var array[32, byte], P: EC_Prj): CttCodecEccStatus = ## Serialize a Banderwagon point(x, y) in the format ## @@ -161,6 +184,17 @@ func deserialize_scalar*(dst: var matchingOrderBigInt(Banderwagon), src: array[3 return status return cttCodecScalar_Success +func deserialize_scalar_mod_order* (dst: var Fr[Banderwagon], src: array[32, byte], order: static Endianness = bigEndian): CttCodecScalarStatus = + ## Deserialize a scalar + ## Take mod value of the scalar (MOD CurveOrder) + ## If the scalar values goes out of range + let stat {.used.} = dst.make_scalar_mod_order(src, order) + debug: doAssert stat, "transcript_gen.deserialize_scalar_mod_order: Unexpected failure" + + return cttCodecScalar_Success + +## ############################################################ +## ## Banderwagon Batch Serialization ## ## ############################################################ diff --git a/tests/t_ethereum_verkle_ipa_primitives.nim b/tests/t_ethereum_verkle_ipa_primitives.nim index 26299a62..d9e8d35f 100644 --- a/tests/t_ethereum_verkle_ipa_primitives.nim +++ b/tests/t_ethereum_verkle_ipa_primitives.nim @@ -31,7 +31,8 @@ import ../constantine/math/arithmetic, ../constantine/math/constants/zoo_generators, ../tests/math_elliptic_curves/t_ec_template, - ../constantine/ethereum_verkle_primitives + ../constantine/ethereum_verkle_primitives, + ../constantine/platforms/abstractions # ############################################################ @@ -276,16 +277,21 @@ suite "Transcript Tests": proc testVec()= + # Initializing a new transcript state var tr {.noInit.}: sha256 + # Generating with a new label tr.newTranscriptGen(asBytes"simple_protocol") + # Generating Challenge Scalar var challenge1 {.noInit.}: matchingOrderBigInt(Banderwagon) challenge1.generateChallengeScalar(tr,asBytes"simple_challenge") - var challenge2 {.noInit.}: matchingOrderBigInt(Banderwagon) - challenge2.generateChallengeScalar(tr,asBytes"simple_challenge") + var b1 {.noInit.} : array[32, byte] + let stat = b1.serialize_scalar(challenge1, littleEndian) + doAssert stat == cttCodecScalar_Success, "Serialization Failure" - doAssert (challenge1 == challenge2).bool() == false , "calling ChallengeScalar twice should yield two different challenges" + # Comparing with Go-IPA implementation + doAssert b1.toHex() == "0xc2aa02607cbdf5595f00ee0dd94a2bbff0bed6a2bf8452ada9011eadb538d003", "Incorrect Value!" testVec() @@ -293,29 +299,99 @@ suite "Transcript Tests": proc testVec1()= + # Initializing 2 new transcript states var tr {.noInit.}: sha256 var tr2 {.noInit.}: sha256 + + # Generating 2 new labels into 2 separate transcripts tr.newTranscriptGen(asBytes"simple_protocol") tr2.newTranscriptGen(asBytes"simple_protocol") - + # Generating Challenge Scalar for Transcript 1 var challenge1 {.noInit.}: matchingOrderBigInt(Banderwagon) challenge1.generateChallengeScalar(tr,asBytes"ethereum_challenge") + # Generating Challenge Scalar for Transcript 2 var challenge2 {.noInit.}: matchingOrderBigInt(Banderwagon) challenge2.generateChallengeScalar(tr2,asBytes"ethereum_challenge") + # Challenge 1 should be equal to Challenge 2 as both are coming from different transcript + # states that are being handled similarly doAssert (challenge1 == challenge2).bool() == true , "calling ChallengeScalar twice should yield the same challenge" testVec1() + test "Transcript testing with repititive append of scalars, thereby a compound challenge scalar": + proc testVec2()= + + # Initializing a new transcript state + var tr {.noInit.}: sha256 + + # Generating with a new label + tr.newTranscriptGen(asBytes"simple_protocol") + + var five {.noInit.} : matchingOrderBigInt(Banderwagon) + five.fromUint(uint64(5)) + + # Appending some scalars to the transcript state + tr.scalarAppend(asBytes"five", five) + tr.scalarAppend(asBytes"five again", five) + + var challenge {.noInit.}: matchingOrderBigInt(Banderwagon) + challenge.generateChallengeScalar(tr, asBytes"simple_challenge") + + var c_bytes {.noInit.}: array[32, byte] + discard c_bytes.serialize_scalar(challenge, littleEndian) + + # Comparing with Go-IPA Implmentation + doAssert c_bytes.toHex() == "0x498732b694a8ae1622d4a9347535be589e4aee6999ffc0181d13fe9e4d037b0b", "Some issue in Challenge Scalar" + + testVec2() + + test "Transcript testing with +1 and -1, appending them to be a compound challenge scalar": + proc testVec3() = + + # Initializing a new transcript state + var tr {.noInit.}: sha256 + + # Generating with a new label + tr.newTranscriptGen(asBytes"simple_protocol") + + var one {.noInit.}: matchingOrderBigInt(Banderwagon) + var minus_one {.noInit.}: Fr[Banderwagon] + # As scalar append and generating challenge scalars mainly deal with BigInts + # and BigInts usually store unsigned values, this test checks if the Transcript state + # generates the correct challenge scalar, even when a signed BigInt such as -1 is + # appended to the transcript state. + minus_one.setMinusOne() + + # Here first `minus_one` is set to -1 MOD (Banderwagon Curve Order) + # and then in-place converted to BigInt while append to the transcript state. + one.setOne() + + # Constructing a Compound Challenge Scalar + tr.scalarAppend(asBytes"-1", minus_one.toBig()) + tr.domainSeparator(asBytes"separate me") + tr.scalarAppend(asBytes"-1 again", minus_one.toBig()) + tr.domainSeparator(asBytes"separate me again") + tr.scalarAppend(asBytes"now 1", one) + + var challenge {.noInit.}: matchingOrderBigInt(Banderwagon) + challenge.generateChallengeScalar(tr, asBytes"simple_challenge") + + var c_bytes {.noInit.}: array[32, byte] + discard c_bytes.serialize_scalar(challenge, littleEndian) + + doAssert c_bytes.toHex() == "0x14f59938e9e9b1389e74311a464f45d3d88d8ac96adf1c1129ac466de088d618", "Computed challenge is incorrect!" + + testVec3() + # ############################################################ # # Test for IPA Proofs # # ############################################################ - suite "IPA proof tests": test "Test for initiating IPA proof configuration": proc testMain()= @@ -330,7 +406,7 @@ suite "IPA proof tests": var point: Fr[Banderwagon] var ipaConfig: IPASettings var ipaTranscript: IpaTranscript[sha256, 32] - let stat1 = ipaConfig.genIPAConfig(ipaTranscript) + discard ipaConfig.genIPAConfig(ipaTranscript) var testGeneratedPoints: array[256, EC_P] testGeneratedPoints.generate_random_points(ipaTranscript, 256) @@ -370,7 +446,7 @@ suite "IPA proof tests": var point : Fr[Banderwagon] var ipaConfig: IPASettings var ipaTranscript: IpaTranscript[sha256, 32] - let stat1 = ipaConfig.genIPAConfig(ipaTranscript) + discard ipaConfig.genIPAConfig(ipaTranscript) var testGeneratedPoints: array[256,EC_P] testGeneratedPoints.generate_random_points(ipaTranscript,256)