16 std::span<
const std::array<unsigned char, 33>> ordered_public_keys,
19 "accountable:aggregate_roster");
20 if (ordered_public_keys.empty()) {
23 std::size_t encoded_size = 0;
28 for (
const auto& key : ordered_public_keys) {
29 if ((key[0] != 0x02 && key[0] != 0x03) ||
35 auto sorted = std::vector(ordered_public_keys.begin(), ordered_public_keys.end());
36 std::sort(sorted.begin(), sorted.end());
37 if (std::adjacent_find(sorted.begin(), sorted.end()) != sorted.end()) {
42 encoded.reserve(encoded_size);
43 for (
const auto& key : ordered_public_keys) {
44 encoded.insert(encoded.end(), key.begin(), key.end());
46 static const TaggedHash list_hash(
"KeyAgg list");
47 static const TaggedHash coefficient_hash(
"KeyAgg coefficient");
49 out.key_list_hash_ = list_hash.
digest(encoded);
50 out.members_.reserve(ordered_public_keys.size());
51 std::optional<std::array<unsigned char, 33>> sum;
52 for (std::size_t i = 0; i < ordered_public_keys.size(); ++i) {
53 const auto& key = ordered_public_keys[i];
57 const auto digest = coefficient_hash.
digest_many<2>({out.key_list_hash_, key});
62 if (coefficient.is_zero()) {
65 const auto scalar = coefficient.to_bytes_be();
66 std::array<unsigned char, 33> term{};
70 out.members_.push_back({key, coefficient});
73 }
else if ((*sum)[0] != term[0] && std::equal(sum->begin() + 1, sum->end(), term.begin() + 1)) {
77 std::array<unsigned char, 33> next{};
78 if (
purify_point_add(secp_context, sum->data(), term.data(), next.data()) == 0) {
87 out.aggregate_was_negated_ = (*sum)[0] == 0x03;
88 std::copy(sum->begin() + 1, sum->end(), out.signing_key_.begin());
89 if (out.aggregate_was_negated_) {
90 for (
auto& member : out.members_) member.coefficient = member.coefficient.negate();
std::vector< unsigned char > Bytes
Dynamically sized byte string used for messages, serialized witnesses, and proofs.