Skip to content

Commit

Permalink
refac: refactor transcript and organize commitment schemes
Browse files Browse the repository at this point in the history
This commit solves 2 problems.

1. `Transcript` was only able to handle field or curve point.
   Since proof and hash algorithm can take arbitrary types, this limitation is
   overcame.

2. The interfaces of `VectorCommitmentScheme` and
   `UnivariatePolynomialCommitmentScheme` are organized.

   When committing, the cases are divided into 3 cases.

   - non interactive: e.g, SHPlonk, MerkleTree
   - non interactive with random: e.g, Pedersen
   - interactive: e.g, FRI

  When opening, the cases are divided into 2 cases.

  - non interactive: e.g, MerkleTree
  - interactive: e.g, SHPlonk and FRI

  When verifying, the cases are divided into 2 cases.

  - both committing and opening is non interactive: e.g, MerkleTree
  - either committing or opening is interative: e.g, SHPlonk and FRI
  • Loading branch information
chokobole committed Jan 10, 2024
1 parent 5efe19b commit 5b9fadb
Show file tree
Hide file tree
Showing 41 changed files with 800 additions and 720 deletions.
45 changes: 45 additions & 0 deletions tachyon/crypto/commitments/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Commitments

The commitment scheme's interface has been designed to accommodate various commitment schemes.
While it might seem straightforward, it poses challenges, particularly in accessing the oracle.

For instance, in the case of a binary merkle tree, it commits leaves to a merkle root, constructing
the tree as a side effect. To facilitate this, we've implemented the `BinaryMerkleTree`, which holds
storage as a member. This design eliminates the need for an extra argument to obtain
the hash of the merkle tree during opening phase.

On the other hand, with the `KZG` commitment, there are no side effects during the commit phase.
However, during opening phase, it necessitates access to the original polynomials.
We're actively working on resolving this for future iterations.

This documentation provides an overview of each commitment scheme's interface in LaTeX,
offering a concise understanding of their functionalities.

## Vector Commitment Scheme

We've successfully unified `Commit()` to enable commitment production by passing a vector.
However, it's important to note that `Open()` and `Verify()` may have varying implementations
across schemes, leading to ambiguity. Please keep this in mind when working with them!

### Binary Merkle Tree

$$Commit(L) \to R$$
$$Open(i) \to \pi$$
$$Verify(R, \pi) \to true \space or \space false$$
$$L\text{: the set of leaf, }i\text{: index of the leaf }R\text{: merkle root}$$

## Univariate Polynomial Commitment Scheme

### FRI

$$Commit(P) \to C$$
$$Open(i) \to \pi$$
$$Verify(i, \pi) \to true \space or \space false$$
$$P\text{: polynomial, }i\text{: index of the leaf}$$

### SHPlonk

$$Commit(P) \to C$$
$$Open(O) \to \pi$$
$$Verify(O, \pi) \to true \space or \space false$$
$$P\text{: polynomial, }O\text{: polynomial openings }$$
100 changes: 48 additions & 52 deletions tachyon/crypto/commitments/fri/fri.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@

namespace tachyon::crypto {

template <typename F, size_t MaxDegree>
class FRI final
: public UnivariatePolynomialCommitmentScheme<FRI<F, MaxDegree>> {
template <typename F, size_t MaxDegree, typename TranscriptReader,
typename TranscriptWriter>
class FRI final : public UnivariatePolynomialCommitmentScheme<
FRI<F, MaxDegree, TranscriptReader, TranscriptWriter>> {
public:
using Base = UnivariatePolynomialCommitmentScheme<FRI<F, MaxDegree>>;
using Base = UnivariatePolynomialCommitmentScheme<
FRI<F, MaxDegree, TranscriptReader, TranscriptWriter>>;
using Poly = typename Base::Poly;
using Evals = typename Base::Evals;
using Domain = typename Base::Domain;
Expand All @@ -45,14 +47,15 @@ class FRI final
// UnivariatePolynomialCommitmentScheme methods
size_t N() const { return domain_->size(); }

[[nodiscard]] bool Commit(const Poly& poly, Transcript<F>* transcript) const {
[[nodiscard]] bool Commit(const Poly& poly, std::vector<F>* roots,
TranscriptWriter* writer) const {
size_t num_layers = domain_->log_size_of_group();
TranscriptWriter<F>* writer = transcript->ToWriter();
BinaryMerkleTree<F, F, MaxDegree + 1> tree(storage_->GetLayer(0), hasher_);
Evals evals = domain_->FFT(poly);
F root;
if (!tree.Commit(evals.evaluations(), &root)) return false;
if (!writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(root))
roots->resize(num_layers + 1);
if (!tree.Commit(evals.evaluations(), &(*roots)[0])) return false;
if (!writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(
(*roots)[0]))
return false;
const Poly* cur_poly = &poly;

Expand All @@ -67,9 +70,9 @@ class FRI final
BinaryMerkleTree<F, F, MaxDegree + 1> tree(storage_->GetLayer(i),
hasher_);
evals = sub_domains_[i - 1]->FFT(folded_poly);
if (!tree.Commit(evals.evaluations(), &root)) return false;
if (!tree.Commit(evals.evaluations(), &(*roots)[i])) return false;
if (!writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(
root))
(*roots)[i]))
return false;
cur_poly = &folded_poly;
}
Expand All @@ -78,46 +81,39 @@ class FRI final
beta = writer->SqueezeChallenge();
folded_poly = cur_poly->template Fold<false>(beta);
const F* constant = folded_poly[0];
root = constant ? *constant : F::Zero();
(*roots)[num_layers] = constant ? *constant : F::Zero();
return writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(
root);
(*roots)[num_layers]);
}

[[nodiscard]] bool DoCreateOpeningProof(size_t index,
FRIProof<F>* fri_proof) const {
size_t domain_size = domain_->size();
size_t num_layers = domain_->log_size_of_group();
fri_proof->proof.resize(num_layers);
fri_proof->proof_sym.resize(num_layers);
for (size_t i = 0; i < num_layers; ++i) {
// Merkle proof for Pᵢ(ωʲ) against Cᵢ
size_t leaf_index = index % domain_size;
BinaryMerkleTreeStorage<F>* layer = storage_->GetLayer(i);
BinaryMerkleTreeStorage<F, F>* layer = storage_->GetLayer(i);
BinaryMerkleTree<F, F, MaxDegree + 1> tree(layer, hasher_);
BinaryMerkleProof<F> proof;
if (!tree.CreateOpeningProof(leaf_index, &proof)) return false;
// Merkle proof for Pᵢ(ωʲ) against Cᵢ
fri_proof->paths.push_back(std::move(proof));
// Pᵢ(ωʲ)
fri_proof->evaluations.push_back(
layer->GetHash(domain_size - 1 + leaf_index));
if (!tree.CreateOpeningProof(leaf_index, &fri_proof->proof[i]))
return false;

// Merkle proof for Pᵢ(-ωʲ) against Cᵢ
size_t half_domain_size = domain_size >> 1;
size_t leaf_index_sym = (index + half_domain_size) % domain_size;
BinaryMerkleProof<F> proof_sym;
if (!tree.CreateOpeningProof(leaf_index_sym, &proof_sym)) return false;
// Merkle proof for Pᵢ(-ωʲ) against Cᵢ
fri_proof->paths_sym.push_back(std::move(proof_sym));
// Pᵢ(-ωʲ)
fri_proof->evaluations_sym.push_back(
layer->GetHash(domain_size - 1 + leaf_index_sym));
if (!tree.CreateOpeningProof(leaf_index_sym, &fri_proof->proof_sym[i]))
return false;

domain_size = half_domain_size;
}
return true;
}

[[nodiscard]] bool DoVerifyOpeningProof(Transcript<F>& transcript,
size_t index,
const FRIProof<F>& proof) const {
TranscriptReader<F>* reader = transcript.ToReader();
[[nodiscard]] bool DoVerifyOpeningProof(size_t index,
const FRIProof<F>& fri_proof,
TranscriptReader& reader) const {
size_t domain_size = domain_->size();
size_t num_layers = domain_->log_size_of_group();
F root;
Expand All @@ -127,18 +123,14 @@ class FRI final
F evaluation_sym;
F two_inv = F(2).Inverse();
for (size_t i = 0; i < num_layers; ++i) {
BinaryMerkleTreeStorage<F>* layer = storage_->GetLayer(i);
BinaryMerkleTreeStorage<F, F>* layer = storage_->GetLayer(i);
BinaryMerkleTree<F, F, MaxDegree + 1> tree(layer, hasher_);

if (!reader->template ReadFromProof</*NeedToWriteToTranscript=*/true>(
if (!reader.template ReadFromProof</*NeedToWriteToTranscript=*/true>(
&root))
return false;
if (!tree.VerifyOpeningProof(root, proof.evaluations[i], proof.paths[i]))
return false;

if (!tree.VerifyOpeningProof(root, proof.evaluations_sym[i],
proof.paths_sym[i]))
return false;
if (!tree.VerifyOpeningProof(root, fri_proof.proof[i])) return false;
if (!tree.VerifyOpeningProof(root, fri_proof.proof_sym[i])) return false;

// Given equations:
// Pᵢ(X) = Pᵢ_even(X²) + X * Pᵢ_odd(X²)
Expand All @@ -163,25 +155,25 @@ class FRI final
// = ((1 + β * ω⁻ʲ) * Pᵢ(ωʲ) + (1 - β * ω⁻ʲ) * Pᵢ(-ωʲ)) / 2
size_t leaf_index = index % domain_size;
if (i == 0) {
evaluation = proof.evaluations[i];
evaluation_sym = proof.evaluations_sym[i];
evaluation = fri_proof.proof[i].value;
evaluation_sym = fri_proof.proof_sym[i].value;
x = domain_->GetElement(leaf_index);
} else {
evaluation *= (F::One() + beta);
evaluation_sym *= (F::One() - beta);
evaluation += evaluation_sym;
evaluation *= two_inv;

if (evaluation != proof.evaluations[i]) {
if (evaluation != fri_proof.proof[i].value) {
LOG(ERROR)
<< "Proof doesn't match with expected evaluation at layer [" << i
<< "]";
return false;
}
evaluation_sym = proof.evaluations_sym[i];
evaluation_sym = fri_proof.proof_sym[i].value;
x = sub_domains_[i - 1]->GetElement(leaf_index);
}
beta = reader->SqueezeChallenge();
beta = reader.SqueezeChallenge();
beta *= x.Inverse();
domain_size = domain_size >> 1;
}
Expand All @@ -191,8 +183,7 @@ class FRI final
evaluation += evaluation_sym;
evaluation *= two_inv;

if (!reader->template ReadFromProof</*NeedToWriteToTranscript=*/true>(
&root))
if (!reader.template ReadFromProof</*NeedToWriteToTranscript=*/true>(&root))
return false;
if (root != evaluation) {
LOG(ERROR) << "Root doesn't match with expected evaluation";
Expand All @@ -208,19 +199,24 @@ class FRI final
mutable FRIStorage<F>* storage_ = nullptr;
// not owned
BinaryMerkleHasher<F, F>* hasher_ = nullptr;
// not owned
Transcript<F>* transcript_ = nullptr;
std::vector<std::unique_ptr<Domain>> sub_domains_;
};

template <typename F, size_t MaxDegree>
struct VectorCommitmentSchemeTraits<FRI<F, MaxDegree>> {
template <typename F, size_t MaxDegree, typename _TranscriptReader,
typename _TranscriptWriter>
struct VectorCommitmentSchemeTraits<
FRI<F, MaxDegree, _TranscriptReader, _TranscriptWriter>> {
public:
constexpr static size_t kMaxSize = MaxDegree + 1;
constexpr static bool kIsTransparent = true;
constexpr static bool kIsCommitInteractive = true;
constexpr static bool kIsOpenInteractive = false;

using Field = F;
using Commitment = Transcript<F>;
using Commitment = std::vector<F>;
using TranscriptReader = _TranscriptReader;
using TranscriptWriter = _TranscriptWriter;
using Proof = FRIProof<F>;
};

} // namespace tachyon::crypto
Expand Down
6 changes: 2 additions & 4 deletions tachyon/crypto/commitments/fri/fri_proof.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,8 @@ namespace tachyon::crypto {

template <typename F>
struct FRIProof {
std::vector<BinaryMerkleProof<F>> paths;
std::vector<BinaryMerkleProof<F>> paths_sym;
std::vector<F> evaluations;
std::vector<F> evaluations_sym;
std::vector<BinaryMerkleProof<F, F>> proof;
std::vector<BinaryMerkleProof<F, F>> proof_sym;
};

} // namespace tachyon::crypto
Expand Down
4 changes: 2 additions & 2 deletions tachyon/crypto/commitments/fri/fri_storage.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

namespace tachyon::crypto {

template <typename Hash>
template <typename F>
class FRIStorage {
public:
virtual ~FRIStorage() = default;

virtual void Allocate(size_t size) = 0;
virtual BinaryMerkleTreeStorage<Hash>* GetLayer(size_t index) = 0;
virtual BinaryMerkleTreeStorage<F, F>* GetLayer(size_t index) = 0;
};

} // namespace tachyon::crypto
Expand Down
34 changes: 23 additions & 11 deletions tachyon/crypto/commitments/fri/fri_unittest.cc
Original file line number Diff line number Diff line change
Expand Up @@ -33,19 +33,25 @@ class SimpleHasher

class SimpleFRIStorage : public FRIStorage<math::Goldilocks> {
public:
const std::vector<SimpleBinaryMerkleTreeStorage<math::Goldilocks>>& layers()
const {
return layers_;
using Layer =
SimpleBinaryMerkleTreeStorage<math::Goldilocks, math::Goldilocks>;

const std::vector<Layer>& layers() const { return layers_; }

std::vector<math::Goldilocks> GetRoots() const {
return base::Map(layers_,
[](const Layer& layer) { return layer.GetRoot(); });
}

// FRIStorage<math::Goldilocks> methods
void Allocate(size_t size) override { layers_.resize(size); }
BinaryMerkleTreeStorage<math::Goldilocks>* GetLayer(size_t index) override {
BinaryMerkleTreeStorage<math::Goldilocks, math::Goldilocks>* GetLayer(
size_t index) override {
return &layers_[index];
}

private:
std::vector<SimpleBinaryMerkleTreeStorage<math::Goldilocks>> layers_;
std::vector<Layer> layers_;
};

class FRITest : public testing::Test {
Expand All @@ -54,11 +60,14 @@ class FRITest : public testing::Test {
constexpr static size_t N = size_t{1} << K;
constexpr static size_t kMaxDegree = N - 1;

using PCS = FRI<math::Goldilocks, kMaxDegree>;
using PCS = FRI<math::Goldilocks, kMaxDegree,
SimpleTranscriptReader<math::Goldilocks>,
SimpleTranscriptWriter<math::Goldilocks>>;
using F = PCS::Field;
using Poly = PCS::Poly;
using Commitment = PCS::Commitment;
using Domain = PCS::Domain;
using TranscriptReader = PCS::TranscriptReader;
using TranscriptWriter = PCS::TranscriptWriter;

static void SetUpTestSuite() { math::Goldilocks::Init(); }

Expand All @@ -79,16 +88,19 @@ class FRITest : public testing::Test {
TEST_F(FRITest, CommitAndVerify) {
Poly poly = Poly::Random(kMaxDegree);
base::Uint8VectorBuffer write_buffer;
SimpleTranscriptWriter<F> writer(std::move(write_buffer));
ASSERT_TRUE(pcs_.Commit(poly, &writer));
TranscriptWriter writer(std::move(write_buffer));
std::vector<F> roots;
ASSERT_TRUE(pcs_.Commit(poly, &roots, &writer));
roots.pop_back();
EXPECT_EQ(roots, storage_.GetRoots());

size_t index = base::Uniform(base::Range<size_t>::Until(kMaxDegree + 1));
FRIProof<math::Goldilocks> proof;
ASSERT_TRUE(pcs_.CreateOpeningProof(index, &proof));

SimpleTranscriptReader<F> reader(std::move(writer).TakeBuffer());
TranscriptReader reader(std::move(writer).TakeBuffer());
reader.buffer().set_buffer_offset(0);
ASSERT_TRUE(pcs_.VerifyOpeningProof(reader, index, proof));
ASSERT_TRUE(pcs_.VerifyOpeningProof(index, proof, reader));
}

} // namespace tachyon::crypto
6 changes: 6 additions & 0 deletions tachyon/crypto/commitments/kzg/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,19 @@ tachyon_cc_library(
hdrs = ["shplonk.h"],
deps = [
":kzg_family",
":shplonk_proof",
"//tachyon/crypto/commitments:polynomial_openings",
"//tachyon/crypto/commitments:univariate_polynomial_commitment_scheme",
"//tachyon/crypto/transcripts:transcript",
"//tachyon/math/elliptic_curves/pairing",
],
)

tachyon_cc_library(
name = "shplonk_proof",
hdrs = ["shplonk_proof.h"],
)

tachyon_cc_unittest(
name = "kzg_unittests",
srcs = [
Expand Down

0 comments on commit 5b9fadb

Please sign in to comment.