From 8825d637b66d100ca7794e5526b61e9976d55baf Mon Sep 17 00:00:00 2001 From: TomTaehoonKim Date: Fri, 13 Oct 2023 17:27:48 +0900 Subject: [PATCH] feat: implement poseidon transcript See [PoseidonRead](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/transcript/poseidon.rs#L12-L100), [PoseidonWrite](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/transcript/poseidon.rs#L102-L174), [Transcript](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/transcript.rs#L43-L63), [TranscriptRead](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/transcript.rs#L65-L73) and [TranscriptWirte](https://github.com/kroma-network/halo2/blob/7d0a36990452c8e7ebd600de258420781a9b7917/halo2_proofs/src/transcript.rs#L75-L83). --- tachyon/crypto/hashes/BUILD.bazel | 1 + .../crypto/hashes/prime_field_serializable.h | 24 ++++ .../prime_field_serializable_unittest.cc | 30 +++++ .../crypto/hashes/sponge/poseidon/BUILD.bazel | 9 ++ .../hashes/sponge/poseidon/halo2_poseidon.h | 59 +++++++++ .../crypto/hashes/sponge/poseidon/poseidon.h | 2 + tachyon/zk/transcript/BUILD.bazel | 30 +++++ tachyon/zk/transcript/poseidon_transcript.h | 112 ++++++++++++++++++ .../poseidon_transcript_unittest.cc | 81 +++++++++++++ tachyon/zk/transcript/transcript.h | 89 ++++++++++++++ 10 files changed, 437 insertions(+) create mode 100644 tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h create mode 100644 tachyon/zk/transcript/BUILD.bazel create mode 100644 tachyon/zk/transcript/poseidon_transcript.h create mode 100644 tachyon/zk/transcript/poseidon_transcript_unittest.cc create mode 100644 tachyon/zk/transcript/transcript.h diff --git a/tachyon/crypto/hashes/BUILD.bazel b/tachyon/crypto/hashes/BUILD.bazel index f14f5d211d..3e6c39b68c 100644 --- a/tachyon/crypto/hashes/BUILD.bazel +++ b/tachyon/crypto/hashes/BUILD.bazel @@ -9,6 +9,7 @@ tachyon_cc_library( "//tachyon/base/buffer", "//tachyon/math/base:big_int", "//tachyon/math/finite_fields:finite_field_traits", + "@com_google_absl//absl/container:inlined_vector", ], ) diff --git a/tachyon/crypto/hashes/prime_field_serializable.h b/tachyon/crypto/hashes/prime_field_serializable.h index 930ffcff6c..9388f35191 100644 --- a/tachyon/crypto/hashes/prime_field_serializable.h +++ b/tachyon/crypto/hashes/prime_field_serializable.h @@ -6,9 +6,11 @@ #ifndef TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_ #define TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_ +#include #include #include +#include "absl/container/inlined_vector.h" #include "absl/types/span.h" #include "tachyon/math/base/big_int.h" @@ -69,6 +71,28 @@ class PrimeFieldSerializable> { } }; +template +class PrimeFieldSerializable> { + public: + template + constexpr static bool ToPrimeField(const absl::InlinedVector& values, + std::vector* fields) { + return PrimeFieldSerializable::BatchToPrimeField( + absl::MakeConstSpan(values), fields); + } +}; + +template +class PrimeFieldSerializable> { + public: + template + constexpr static bool ToPrimeField(const std::array& values, + std::vector* fields) { + return PrimeFieldSerializable::BatchToPrimeField( + absl::MakeConstSpan(values), fields); + } +}; + template constexpr bool SerializeToFieldElements(const T& value, std::vector* fields) { diff --git a/tachyon/crypto/hashes/prime_field_serializable_unittest.cc b/tachyon/crypto/hashes/prime_field_serializable_unittest.cc index 32841452a0..fd204ca8b3 100644 --- a/tachyon/crypto/hashes/prime_field_serializable_unittest.cc +++ b/tachyon/crypto/hashes/prime_field_serializable_unittest.cc @@ -23,6 +23,36 @@ TEST_F(PrimeFieldSerializableTest, SerializeSingleValueToField) { EXPECT_EQ(fields, expected); } +TEST_F(PrimeFieldSerializableTest, SerializeVectorToField) { + std::vector values = {1, 2, 3, 4, 5}; + std::vector fields; + ASSERT_TRUE(SerializeToFieldElements(values, &fields)); + std::vector expected = { + math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5), + }; + EXPECT_EQ(fields, expected); +} + +TEST_F(PrimeFieldSerializableTest, SerializeArrayToField) { + std::array values = {1, 2, 3, 4, 5}; + std::vector fields; + ASSERT_TRUE(SerializeToFieldElements(values, &fields)); + std::vector expected = { + math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5), + }; + EXPECT_EQ(fields, expected); +} + +TEST_F(PrimeFieldSerializableTest, SerializeInlinedVectorToField) { + absl::InlinedVector values = {1, 2, 3, 4, 5}; + std::vector fields; + ASSERT_TRUE(SerializeToFieldElements(values, &fields)); + std::vector expected = { + math::GF7(1), math::GF7(2), math::GF7(3), math::GF7(4), math::GF7(5), + }; + EXPECT_EQ(fields, expected); +} + TEST_F(PrimeFieldSerializableTest, SerializeBatchToField) { std::vector values = {1, 2, 3, 4, 5}; std::vector fields; diff --git a/tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel b/tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel index 4dc35a56e0..ae95ed6cac 100644 --- a/tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel +++ b/tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel @@ -12,6 +12,15 @@ tachyon_cc_library( ], ) +tachyon_cc_library( + name = "halo2_poseidon", + hdrs = ["halo2_poseidon.h"], + deps = [ + ":poseidon", + "@local_config_gmp//:gmp", + ], +) + tachyon_cc_library( name = "poseidon_config", hdrs = ["poseidon_config.h"], diff --git a/tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h b/tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h new file mode 100644 index 0000000000..a8b280d99a --- /dev/null +++ b/tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h @@ -0,0 +1,59 @@ +// Copyright 2020-2022 The Electric Coin Company +// Copyright 2022 The Halo2 developers +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2 +// file. + +#ifndef TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_HALO2_POSEIDON_H_ +#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_HALO2_POSEIDON_H_ + +#include + +#include "third_party/gmp/include/gmpxx.h" + +#include "tachyon/crypto/hashes/sponge/poseidon/poseidon.h" + +namespace tachyon::crypto { + +template +struct Halo2PoseidonSponge : public PoseidonSponge { + using F = PrimeFieldTy; + + Halo2PoseidonSponge() = default; + explicit Halo2PoseidonSponge(const PoseidonConfig& config) + : PoseidonSponge(config) { + this->state.elements[0] = F::FromMpzClass(mpz_class(1) << 64); + } + + // FieldBasedCryptographicSponge methods + std::vector SqueezeNativeFieldElements(size_t num_elements) { + std::vector ret = base::CreateVector(num_elements, F::Zero()); + size_t squeeze_index = this->state.mode.next_index; + if (squeeze_index == this->config.rate) { + squeeze_index = 0; + } + this->state[squeeze_index + 1] = F::One(); + switch (this->state.mode.type) { + case DuplexSpongeMode::Type::kAbsorbing: { + this->Permute(); + this->SqueezeInternal(0, &ret); + return ret; + } + case DuplexSpongeMode::Type::kSqueezing: { + size_t squeeze_index = this->state.mode.next_index; + if (squeeze_index == this->config.rate) { + this->Permute(); + squeeze_index = 0; + } + this->SqueezeInternal(squeeze_index, &ret); + return ret; + } + } + NOTREACHED(); + return {}; + } +}; + +} // namespace tachyon::crypto + +#endif // TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_HALO2_POSEIDON_H_ diff --git a/tachyon/crypto/hashes/sponge/poseidon/poseidon.h b/tachyon/crypto/hashes/sponge/poseidon/poseidon.h index e87f8a858e..438c3dd94d 100644 --- a/tachyon/crypto/hashes/sponge/poseidon/poseidon.h +++ b/tachyon/crypto/hashes/sponge/poseidon/poseidon.h @@ -242,6 +242,8 @@ struct PoseidonSponge } // FieldBasedCryptographicSponge methods + // NOTE(TomTaehoonKim): If you ever update this, please update + // |Halo2PoseidonSponge| for consistency. std::vector SqueezeNativeFieldElements(size_t num_elements) { std::vector ret = base::CreateVector(num_elements, []() { return F::Zero(); }); diff --git a/tachyon/zk/transcript/BUILD.bazel b/tachyon/zk/transcript/BUILD.bazel new file mode 100644 index 0000000000..6ec1129014 --- /dev/null +++ b/tachyon/zk/transcript/BUILD.bazel @@ -0,0 +1,30 @@ +load("//bazel:tachyon_cc.bzl", "tachyon_cc_library", "tachyon_cc_unittest") + +package(default_visibility = ["//visibility:public"]) + +tachyon_cc_library( + name = "transcript", + hdrs = ["transcript.h"], + deps = ["//tachyon/math/base:big_int"], +) + +tachyon_cc_library( + name = "poseidon_transcript", + hdrs = ["poseidon_transcript.h"], + deps = [ + ":transcript", + "//tachyon/base/buffer:vector_buffer", + "//tachyon/crypto/hashes/sponge/poseidon:halo2_poseidon", + ], +) + +tachyon_cc_unittest( + name = "transcript_unittests", + srcs = [ + "poseidon_transcript_unittest.cc", + ], + deps = [ + ":poseidon_transcript", + "//tachyon/math/elliptic_curves/bn/bn254:g1", + ], +) diff --git a/tachyon/zk/transcript/poseidon_transcript.h b/tachyon/zk/transcript/poseidon_transcript.h new file mode 100644 index 0000000000..1e683b8770 --- /dev/null +++ b/tachyon/zk/transcript/poseidon_transcript.h @@ -0,0 +1,112 @@ +// Copyright 2020-2022 The Electric Coin Company +// Copyright 2022 The Halo2 developers +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2 +// file. + +#ifndef TACHYON_ZK_TRANSCRIPT_POSEIDON_TRANSCRIPT_H_ +#define TACHYON_ZK_TRANSCRIPT_POSEIDON_TRANSCRIPT_H_ + +#include +#include + +#include "tachyon/base/buffer/buffer.h" +#include "tachyon/base/buffer/vector_buffer.h" +#include "tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h" +#include "tachyon/zk/transcript/transcript.h" + +namespace tachyon::zk { + +template +class PoseidonRead : public TranscriptRead { + public: + using ScalarField = typename AffinePointTy::ScalarField; + using CurveConfig = typename AffinePointTy::Curve::Config; + + PoseidonRead() = default; + // Initialize a transcript given an input buffer. + explicit PoseidonRead(base::Buffer read_buf) + : state_( + crypto::PoseidonConfig::CreateCustom(8, 5, 8, 63, 0)), + buffer_(std::move(read_buf)) {} + + base::Buffer& buffer() { return buffer_; } + const base::Buffer& buffer() const { return buffer_; } + + // Transcript methods + Challenge255 SqueezeChallenge() override { + return Challenge255(state_.SqueezeNativeFieldElements(1)[0]); + } + + bool WriteToTranscript(const AffinePointTy& point) override { + std::array coords = {CurveConfig::BaseToScalar(point.x()), + CurveConfig::BaseToScalar(point.y())}; + return state_.Absorb(coords); + } + + bool WriteToTranscript(const ScalarField& scalar) override { + return state_.Absorb(scalar); + } + + // TranscriptRead methods + bool ReadPoint(AffinePointTy* point) override { + return buffer_.Read(point) && WriteToTranscript(*point); + } + + bool ReadScalar(ScalarField* scalar) override { + return buffer_.Read(scalar) && WriteToTranscript(*scalar); + } + + private: + crypto::Halo2PoseidonSponge state_; + base::Buffer buffer_; +}; + +template +class PoseidonWrite : public TranscriptWrite { + public: + using ScalarField = typename AffinePointTy::ScalarField; + using CurveConfig = typename AffinePointTy::Curve::Config; + + PoseidonWrite() = default; + // Initialize a transcript given an output buffer. + explicit PoseidonWrite(base::VectorBuffer write_buf) + : state_( + crypto::PoseidonConfig::CreateCustom(8, 5, 8, 63, 0)), + buffer_(std::move(write_buf)) {} + + base::VectorBuffer& buffer() { return buffer_; } + const base::VectorBuffer& buffer() const { return buffer_; } + + // Transcript methods + Challenge255 SqueezeChallenge() override { + return Challenge255(state_.SqueezeNativeFieldElements(1)[0]); + } + + bool WriteToTranscript(const AffinePointTy& point) override { + std::array coords = {CurveConfig::BaseToScalar(point.x()), + CurveConfig::BaseToScalar(point.y())}; + return state_.Absorb(coords); + } + + bool WriteToTranscript(const ScalarField& scalar) override { + return state_.Absorb(scalar); + } + + // TranscriptWrite methods + bool WriteToProof(const AffinePointTy& point) override { + return WriteToTranscript(point) && buffer_.Write(point); + } + + bool WriteToProof(const ScalarField& scalar) override { + return WriteToTranscript(scalar) && buffer_.Write(scalar); + } + + private: + crypto::Halo2PoseidonSponge state_; + base::VectorBuffer buffer_; +}; + +} // namespace tachyon::zk + +#endif // TACHYON_ZK_TRANSCRIPT_POSEIDON_TRANSCRIPT_H_ diff --git a/tachyon/zk/transcript/poseidon_transcript_unittest.cc b/tachyon/zk/transcript/poseidon_transcript_unittest.cc new file mode 100644 index 0000000000..448c572725 --- /dev/null +++ b/tachyon/zk/transcript/poseidon_transcript_unittest.cc @@ -0,0 +1,81 @@ +// Copyright 2020-2022 The Electric Coin Company +// Copyright 2022 The Halo2 developers +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2 +// file. + +#include "tachyon/zk/transcript/poseidon_transcript.h" + +#include +#include + +#include "gtest/gtest.h" + +#include "tachyon/math/elliptic_curves/bn/bn254/g1.h" + +namespace tachyon::zk { + +namespace { + +class PoseidonTranscriptTest : public testing::Test { + public: + static void SetUpTestSuite() { math::bn254::G1Curve::Init(); } +}; + +} // namespace + +TEST_F(PoseidonTranscriptTest, WritePoint) { + using AffinePoint = math::bn254::G1AffinePoint; + + base::VectorBuffer write_buf; + PoseidonWrite writer(std::move(write_buf)); + AffinePoint expected = AffinePoint::Random(); + ASSERT_TRUE(writer.WriteToProof(expected)); + + base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len()); + PoseidonRead reader(std::move(read_buf)); + AffinePoint actual; + ASSERT_TRUE(reader.ReadPoint(&actual)); + + EXPECT_EQ(expected, actual); +} + +TEST_F(PoseidonTranscriptTest, WriteScalar) { + using AffinePoint = math::bn254::G1AffinePoint; + using ScalarField = AffinePoint::ScalarField; + + base::VectorBuffer write_buf; + PoseidonWrite writer(std::move(write_buf)); + ScalarField expected = ScalarField::Random(); + ASSERT_TRUE(writer.WriteToProof(expected)); + + base::Buffer read_buf(writer.buffer().buffer(), writer.buffer().buffer_len()); + PoseidonRead reader(std::move(read_buf)); + ScalarField actual; + ASSERT_TRUE(reader.ReadScalar(&actual)); + + EXPECT_EQ(expected, actual); +} + +TEST_F(PoseidonTranscriptTest, SqueezeChallenge) { + using AffinePoint = math::bn254::G1AffinePoint; + using ScalarField = AffinePoint::ScalarField; + + base::VectorBuffer write_buf; + PoseidonWrite writer(std::move(write_buf)); + AffinePoint generator = AffinePoint::Generator(); + ASSERT_TRUE(writer.WriteToProof(generator)); + + std::vector expected_bytes = {25, 86, 205, 219, 59, 135, 187, 231, + 192, 54, 23, 138, 114, 176, 9, 157, + 1, 97, 110, 174, 67, 9, 89, 85, + 126, 129, 216, 121, 53, 99, 227, 26}; + ScalarField expected = + ScalarField::FromBigInt(math::BigInt<4>::FromBytesLE(expected_bytes)); + + ScalarField actual = writer.SqueezeChallenge().ChallengeAsScalar(); + + EXPECT_EQ(expected, actual); +} + +} // namespace tachyon::zk diff --git a/tachyon/zk/transcript/transcript.h b/tachyon/zk/transcript/transcript.h new file mode 100644 index 0000000000..424b27a300 --- /dev/null +++ b/tachyon/zk/transcript/transcript.h @@ -0,0 +1,89 @@ +// Copyright 2020-2022 The Electric Coin Company +// Copyright 2022 The Halo2 developers +// Use of this source code is governed by a MIT/Apache-2.0 style license that +// can be found in the LICENSE-MIT.halo2 and the LICENCE-APACHE.halo2 +// file. + +#ifndef TACHYON_ZK_TRANSCRIPT_TRANSCRIPT_H_ +#define TACHYON_ZK_TRANSCRIPT_TRANSCRIPT_H_ + +#include "tachyon/math/base/big_int.h" + +namespace tachyon::zk { + +// A 255-bit challenge. +template +class Challenge255 { + public: + using ChallengeTy = math::BigInt<4>; + using ScalarField = typename AffinePointTy::ScalarField; + static_assert(ScalarField::N <= 4); + + constexpr Challenge255() = default; + constexpr explicit Challenge255(const ScalarField& challenge_input) + : challenge_( + ChallengeTy::FromBytesLE(challenge_input.ToBigInt().ToBytesLE())) {} + + constexpr const ChallengeTy& challenge() const { return challenge_; } + + constexpr ScalarField ChallengeAsScalar() const { + return ScalarField::FromBigInt(challenge_); + } + + private: + ChallengeTy challenge_; +}; + +// Generic transcript view (from either the prover or verifier's perspective) +template +class Transcript { + public: + using ScalarField = typename AffinePointTy::ScalarField; + + virtual ~Transcript() = default; + + // Squeeze an encoded verifier challenge from the transcript. + virtual Challenge255 SqueezeChallenge() = 0; + + // Write a curve |point| to the transcript without writing it to the proof, + // treating it as a common input. + virtual bool WriteToTranscript(const AffinePointTy& point) = 0; + + // Write a curve |scalar| to the transcript without writing it to the proof, + // treating it as a common input. + virtual bool WriteToTranscript(const ScalarField& scalar) = 0; +}; + +// Transcript view from the perspective of a verifier that has access to an +// input stream of data from the prover to the verifier. +template +class TranscriptRead : public Transcript { + using ScalarField = typename AffinePointTy::ScalarField; + + // Read a curve |point| from the prover. Note that it also writes the + // |point| to the transcript by calling |WriteToTranscript()| internally. + virtual bool ReadPoint(AffinePointTy* point) = 0; + + // Read a curve |scalar| from the prover. Note that it also writes the + // |scalar| to the transcript by calling |WriteToTranscript()| internally. + virtual bool ReadScalar(ScalarField* scalar) = 0; +}; + +// Transcript view from the perspective of a prover that has access to an output +// stream of messages from the prover to the verifier. +template +class TranscriptWrite : public Transcript { + using ScalarField = typename AffinePointTy::ScalarField; + + // Write a curve |point| to the proof. Note that it also writes the + // |point| to the transcript by calling |WriteToTranscript()| internally. + virtual bool WriteToProof(const AffinePointTy& point) = 0; + + // Write a curve |scalar| to the proof. Note that it also writes the + // |scalar| to the transcript by calling |WriteToTranscript()| internally. + virtual bool WriteToProof(const ScalarField& scalar) = 0; +}; + +} // namespace tachyon::zk + +#endif // TACHYON_ZK_TRANSCRIPT_TRANSCRIPT_H_