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

refac: improve pcs interface #225

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}$$
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
$$L\text{: the set of leaf, }i\text{: index of the leaf }R\text{: merkle root}$$
$$L\text{: the set of leaf, }i\text{: index of the leaf }R\text{: the 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 }$$
104 changes: 54 additions & 50 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,16 @@ 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->WriteToProof(root)) return false;
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;

F beta;
Expand All @@ -66,54 +70,50 @@ 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 (!writer->WriteToProof(root)) return false;
if (!tree.Commit(evals.evaluations(), &(*roots)[i])) return false;
if (!writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(
(*roots)[i]))
return false;
cur_poly = &folded_poly;
}
}

beta = writer->SqueezeChallenge();
folded_poly = cur_poly->template Fold<false>(beta);
const F* constant = folded_poly[0];
root = constant ? *constant : F::Zero();
return writer->WriteToProof(root);
(*roots)[num_layers] = constant ? *constant : F::Zero();
return writer->template WriteToProof</*NeedToWriteToTranscript=*/true>(
(*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 @@ -123,16 +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->ReadFromProof(&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]))
if (!reader.template ReadFromProof</*NeedToWriteToTranscript=*/true>(
&root))
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 @@ -157,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 @@ -185,7 +183,8 @@ class FRI final
evaluation += evaluation_sym;
evaluation *= two_inv;

if (!reader->ReadFromProof(&root)) return false;
if (!reader.template ReadFromProof</*NeedToWriteToTranscript=*/true>(&root))
return false;
if (root != evaluation) {
LOG(ERROR) << "Root doesn't match with expected evaluation";
return false;
Expand All @@ -200,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
Loading