Skip to content
Merged
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
16 changes: 12 additions & 4 deletions barretenberg/cpp/src/barretenberg/transcript/origin_tag.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ OriginTag::OriginTag(const OriginTag& tag_a, const OriginTag& tag_b)
{
// Elements with instant death should not be touched
if (tag_a.instant_death || tag_b.instant_death) {
throw_or_abort("Touched an element that should not have been touched");
throw_or_abort("Touched an element that should not have been touched. tag_a id: " +
std::to_string(tag_a.tag_id) + ", tag_b id: " + std::to_string(tag_b.tag_id));
}
// If one of the tags is a constant, just use the other tag
if (tag_a.transcript_index == CONSTANT) {
Expand All @@ -107,7 +108,9 @@ OriginTag::OriginTag(const OriginTag& tag_a, const OriginTag& tag_b)
// A free witness element should not interact with an element that has an origin
if (tag_a.is_free_witness()) {
if (!tag_b.is_free_witness() && !tag_b.is_empty()) {
throw_or_abort("A free witness element should not interact with an element that has an origin");
throw_or_abort(
"A free witness element (id: " + std::to_string(tag_a.tag_id) +
") should not interact with an element that has an origin (id: " + std::to_string(tag_b.tag_id) + ")");
} else {
// If both are free witnesses or one of them is empty, just use tag_a
*this = tag_a;
Expand All @@ -116,7 +119,9 @@ OriginTag::OriginTag(const OriginTag& tag_a, const OriginTag& tag_b)
}
if (tag_b.is_free_witness()) {
if (!tag_a.is_free_witness() && !tag_a.is_empty()) {
throw_or_abort("A free witness element should not interact with an element that has an origin");
throw_or_abort(
"A free witness element (id: " + std::to_string(tag_b.tag_id) +
") should not interact with an element that has an origin (id: " + std::to_string(tag_a.tag_id) + ")");
} else {
// If both are free witnesses or one of them is empty, just use tag_b
*this = tag_b;
Expand All @@ -125,7 +130,10 @@ OriginTag::OriginTag(const OriginTag& tag_a, const OriginTag& tag_b)
}
// Elements from different transcripts shouldn't interact
if (tag_a.transcript_index != tag_b.transcript_index) {
throw_or_abort("Tags from different transcripts were involved in the same computation");
throw_or_abort("Tags from different transcripts were involved in the same computation. tag_a: { id: " +
std::to_string(tag_a.tag_id) + ", transcript: " + std::to_string(tag_a.transcript_index) +
" } tag_b: { id: " + std::to_string(tag_b.tag_id) +
", transcript: " + std::to_string(tag_b.transcript_index) + " }");
}
// Check that submitted values from different rounds don't mix without challenges
check_round_provenance(tag_a.round_provenance, tag_b.round_provenance);
Expand Down
12 changes: 9 additions & 3 deletions barretenberg/cpp/src/barretenberg/transcript/origin_tag.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include "barretenberg/common/assert.hpp"
#include "barretenberg/common/throw_or_abort.hpp"
#include "barretenberg/numeric/uint256/uint256.hpp"
#include <atomic>
#include <cstddef>
#include <iterator>
#include <ostream>
Expand Down Expand Up @@ -68,6 +69,11 @@ void check_round_provenance(const uint256_t& provenance_a, const uint256_t& prov
#ifndef AZTEC_NO_ORIGIN_TAGS
struct OriginTag {

// Unique per-object ID for debugging: when a tag merge fails, the error message includes
// the IDs of both tags so you can trace which specific field elements were involved.
static inline std::atomic<uint64_t> next_tag_id{ 0 };
uint64_t tag_id = next_tag_id.fetch_add(1, std::memory_order_relaxed);

static constexpr size_t CONSTANT = static_cast<size_t>(-1);
static constexpr size_t FREE_WITNESS = static_cast<size_t>(-2);
// transcript_index represents the index of a unique transcript object that generated the value. It uses
Expand Down Expand Up @@ -96,7 +102,7 @@ struct OriginTag {
OriginTag& operator=(const OriginTag& other) = default;
OriginTag& operator=(OriginTag&& other) noexcept
{

tag_id = other.tag_id;
transcript_index = other.transcript_index;
round_provenance = other.round_provenance;
instant_death = other.instant_death;
Expand Down Expand Up @@ -204,8 +210,8 @@ struct OriginTag {
};
inline std::ostream& operator<<(std::ostream& os, OriginTag const& v)
{
return os << "{ transcript_idx: " << v.transcript_index << ", round_prov: " << v.round_provenance
<< ", instadeath: " << v.instant_death << " }";
return os << "{ id: " << v.tag_id << ", transcript_idx: " << v.transcript_index
<< ", round_prov: " << v.round_provenance << ", instadeath: " << v.instant_death << " }";
}

#else
Expand Down
19 changes: 19 additions & 0 deletions barretenberg/cpp/src/barretenberg/transcript/origin_tag.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -242,4 +242,23 @@ TEST(OriginTag, RetaggingReflectsProtocolConstraints)
EXPECT_NO_THROW(OriginTag(comm_times_challenge, eval_retagged));
}

// Test that unique per-object IDs are assigned
TEST(OriginTag, UniqueTagIds)
{
auto tag_a = OriginTag(0, 0, true);
auto tag_b = OriginTag(0, 0, true);

// Each constructed tag gets a unique ID
EXPECT_NE(tag_a.tag_id, tag_b.tag_id);

// Copy preserves the ID
auto tag_a_copy = tag_a;
EXPECT_EQ(tag_a_copy.tag_id, tag_a.tag_id);

// Merge produces a new ID
auto merged = OriginTag(tag_a, tag_b);
EXPECT_NE(merged.tag_id, tag_a.tag_id);
EXPECT_NE(merged.tag_id, tag_b.tag_id);
}

#endif // AZTEC_NO_ORIGIN_TAGS
Loading