Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: implement poseidon transcript #107

Merged
merged 7 commits into from
Oct 25, 2023
Merged
1 change: 1 addition & 0 deletions tachyon/crypto/hashes/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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",
],
)

Expand Down
24 changes: 24 additions & 0 deletions tachyon/crypto/hashes/prime_field_serializable.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,11 @@
#ifndef TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_
#define TACHYON_CRYPTO_HASHES_PRIME_FIELD_SERIALIZABLE_H_

#include <array>
#include <type_traits>
#include <vector>

#include "absl/container/inlined_vector.h"
#include "absl/types/span.h"

#include "tachyon/math/base/big_int.h"
Expand Down Expand Up @@ -69,6 +71,28 @@ class PrimeFieldSerializable<std::vector<T>> {
}
};

template <typename T, size_t N>
class PrimeFieldSerializable<absl::InlinedVector<T, N>> {
dongchangYoo marked this conversation as resolved.
Show resolved Hide resolved
public:
template <typename PrimeFieldTy>
constexpr static bool ToPrimeField(const absl::InlinedVector<T, N>& values,
std::vector<PrimeFieldTy>* fields) {
return PrimeFieldSerializable<T>::BatchToPrimeField(
absl::MakeConstSpan(values), fields);
}
};

template <typename T, size_t N>
class PrimeFieldSerializable<std::array<T, N>> {
public:
template <typename PrimeFieldTy>
constexpr static bool ToPrimeField(const std::array<T, N>& values,
std::vector<PrimeFieldTy>* fields) {
return PrimeFieldSerializable<T>::BatchToPrimeField(
absl::MakeConstSpan(values), fields);
}
};

template <typename T, typename PrimeFieldTy>
constexpr bool SerializeToFieldElements(const T& value,
std::vector<PrimeFieldTy>* fields) {
Expand Down
30 changes: 30 additions & 0 deletions tachyon/crypto/hashes/prime_field_serializable_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,36 @@ TEST_F(PrimeFieldSerializableTest, SerializeSingleValueToField) {
EXPECT_EQ(fields, expected);
}

TEST_F(PrimeFieldSerializableTest, SerializeVectorToField) {
std::vector<int64_t> values = {1, 2, 3, 4, 5};
std::vector<math::GF7> fields;
ASSERT_TRUE(SerializeToFieldElements(values, &fields));
std::vector<math::GF7> 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<int64_t, 5> values = {1, 2, 3, 4, 5};
std::vector<math::GF7> fields;
ASSERT_TRUE(SerializeToFieldElements(values, &fields));
std::vector<math::GF7> 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<int64_t, 5> values = {1, 2, 3, 4, 5};
std::vector<math::GF7> fields;
ASSERT_TRUE(SerializeToFieldElements(values, &fields));
std::vector<math::GF7> 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<int64_t> values = {1, 2, 3, 4, 5};
std::vector<math::GF7> fields;
Expand Down
9 changes: 9 additions & 0 deletions tachyon/crypto/hashes/sponge/poseidon/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
59 changes: 59 additions & 0 deletions tachyon/crypto/hashes/sponge/poseidon/halo2_poseidon.h
Original file line number Diff line number Diff line change
@@ -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_
chokobole marked this conversation as resolved.
Show resolved Hide resolved
#define TACHYON_CRYPTO_HASHES_SPONGE_POSEIDON_HALO2_POSEIDON_H_

#include <vector>

#include "third_party/gmp/include/gmpxx.h"

#include "tachyon/crypto/hashes/sponge/poseidon/poseidon.h"

namespace tachyon::crypto {

template <typename PrimeFieldTy>
struct Halo2PoseidonSponge : public PoseidonSponge<PrimeFieldTy> {
using F = PrimeFieldTy;

Halo2PoseidonSponge() = default;
explicit Halo2PoseidonSponge(const PoseidonConfig<F>& config)
: PoseidonSponge<PrimeFieldTy>(config) {
this->state.elements[0] = F::FromMpzClass(mpz_class(1) << 64);
}

// FieldBasedCryptographicSponge methods
std::vector<F> SqueezeNativeFieldElements(size_t num_elements) {
chokobole marked this conversation as resolved.
Show resolved Hide resolved
std::vector<F> 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_
2 changes: 2 additions & 0 deletions tachyon/crypto/hashes/sponge/poseidon/poseidon.h
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ struct PoseidonSponge
}

// FieldBasedCryptographicSponge methods
// NOTE(TomTaehoonKim): If you ever update this, please update
// |Halo2PoseidonSponge| for consistency.
std::vector<F> SqueezeNativeFieldElements(size_t num_elements) {
std::vector<F> ret =
base::CreateVector(num_elements, []() { return F::Zero(); });
Expand Down
30 changes: 30 additions & 0 deletions tachyon/zk/transcript/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -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",
],
)
112 changes: 112 additions & 0 deletions tachyon/zk/transcript/poseidon_transcript.h
Original file line number Diff line number Diff line change
@@ -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_
chokobole marked this conversation as resolved.
Show resolved Hide resolved
#define TACHYON_ZK_TRANSCRIPT_POSEIDON_TRANSCRIPT_H_

#include <array>
#include <utility>

#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 <typename AffinePointTy>
class PoseidonRead : public TranscriptRead<AffinePointTy> {
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 buffer)
: state_(
crypto::PoseidonConfig<ScalarField>::CreateCustom(8, 5, 8, 63, 0)),
buffer_(std::move(buffer)) {}

base::Buffer& buffer() { return buffer_; }
const base::Buffer& buffer() const { return buffer_; }

// Transcript methods
Challenge255<AffinePointTy> SqueezeChallenge() override {
return Challenge255<AffinePointTy>(state_.SqueezeNativeFieldElements(1)[0]);
}

bool WriteToTranscript(const AffinePointTy& point) override {
std::array<ScalarField, 2> 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<ScalarField> state_;
base::Buffer buffer_;
};

template <typename AffinePointTy>
class PoseidonWrite : public TranscriptWrite<AffinePointTy> {
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 buffer)
: state_(
crypto::PoseidonConfig<ScalarField>::CreateCustom(8, 5, 8, 63, 0)),
buffer_(std::move(buffer)) {}

base::VectorBuffer& buffer() { return buffer_; }
const base::VectorBuffer& buffer() const { return buffer_; }

// Transcript methods
Challenge255<AffinePointTy> SqueezeChallenge() override {
return Challenge255<AffinePointTy>(state_.SqueezeNativeFieldElements(1)[0]);
}

bool WriteToTranscript(const AffinePointTy& point) override {
std::array<ScalarField, 2> 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<ScalarField> state_;
base::VectorBuffer buffer_;
};

} // namespace tachyon::zk

#endif // TACHYON_ZK_TRANSCRIPT_POSEIDON_TRANSCRIPT_H_
81 changes: 81 additions & 0 deletions tachyon/zk/transcript/poseidon_transcript_unittest.cc
Original file line number Diff line number Diff line change
@@ -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 <utility>
#include <vector>

#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<AffinePoint> 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<AffinePoint> 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<AffinePoint> 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<AffinePoint> 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<AffinePoint> writer(std::move(write_buf));
AffinePoint generator = AffinePoint::Generator();
ASSERT_TRUE(writer.WriteToProof(generator));

std::vector<uint8_t> 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
Loading