Skip to content

Commit 6d20760

Browse files
authored
feat: merge-train/avm (#16991)
BEGIN_COMMIT_OVERRIDE chore(avm): always print bulk bench metrics chore: betas optimization - 2^d multiplications (#16963) docs: Add migration notes for gas costs (#16925) feat(avm)!: protocol contracts (#16949) END_COMMIT_OVERRIDE
2 parents e04bc1d + d8ec40b commit 6d20760

File tree

56 files changed

+995
-198
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+995
-198
lines changed

barretenberg/cpp/.clang-format-ignore

Lines changed: 0 additions & 1 deletion
This file was deleted.

barretenberg/cpp/pil/vm2/bytecode/contract_instance_retrieval.pil

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
include "bc_hashing.pil";
22
include "address_derivation.pil";
33
include "update_check.pil";
4-
include "../precomputed.pil";
4+
include "../ff_gt.pil";
5+
include "../protocol_contract.pil";
56

67
/**
78
* Contract Instance Retrieval gadget.
@@ -84,13 +85,22 @@ namespace contract_instance_retrieval;
8485
pol commit deployer_protocol_contract_address;
8586
sel * (constants.CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS - deployer_protocol_contract_address) = 0;
8687

87-
// Indicates if the instance belongs to a protocol contract. Is hinted by the prover.
88-
// If this is set to true for non-protocol contracts the lookup #[PROTOCOL_CONTRACT_DERIVED_ADDRESS] will fail.
89-
// If this is set to false for protocol contracts #[ADDRESS_DERIVATION] will fail since it will use the canonical address.
88+
// Indicates if the instance belongs to a protocol contract
9089
pol commit is_protocol_contract;
91-
is_protocol_contract * (1 - is_protocol_contract) = 0;
9290

93-
// If is_protocol_contract = 1, then exists = 1. But if have is_protocol_contract = 0, we don't constrain exists here.
91+
// Canonical Addresses can be in the range of 1 <= address <= MAX_PROTOCOL_CONTRACT_ADDRESS
92+
// currently MAX_PROTOCOL_CONTRACT_ADDRESS = ROUTER_ADDRESS (but it is bound to change)
93+
pol commit max_protocol_contract_address;
94+
max_protocol_contract_address = sel * constants.MAX_PROTOCOL_CONTRACT_ADDRESS;
95+
96+
pol commit address_sub_one;
97+
address_sub_one = sel * (address - 1);
98+
#[CHECK_PROTOCOL_ADDRESS_RANGE]
99+
sel { max_protocol_contract_address, address_sub_one, is_protocol_contract }
100+
in
101+
ff_gt.sel { ff_gt.a, ff_gt.b, ff_gt.result};
102+
103+
// If is_protocol_contract = 1, then exists = 1. But if have is_protocol_contract = 0, we don't constrain exists here.
94104
// It is later constrained by the nullifier check
95105
sel * is_protocol_contract * (1 - exists) = 0 ;
96106

@@ -128,13 +138,13 @@ namespace contract_instance_retrieval;
128138

129139
// The address that is checked in address derivation.
130140
pol commit derived_address;
131-
// For protocol contracts we retrieve the derived address from the precomputed trace, else use the input address
141+
// For protocol contracts we retrieve the derived address from the protocol contract trace, else use the input address
132142
#[UNCHANGED_ADDRESS_NON_PROTOCOL]
133143
sel * (1 - is_protocol_contract) * (derived_address - address) = 0;
134144

135145
#[PROTOCOL_CONTRACT_DERIVED_ADDRESS]
136146
is_protocol_contract { address, derived_address } in
137-
precomputed.sel_protocol_contract { precomputed.clk, precomputed.protocol_contract_derived_address };
147+
protocol_contract.sel { protocol_contract.canonical_address, protocol_contract.derived_address };
138148

139149
// Address derivation lookup (only if the nullifier exists or for protocol contract instances)
140150
#[ADDRESS_DERIVATION]

barretenberg/cpp/pil/vm2/constants_gen.pil

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// GENERATED FILE - DO NOT EDIT, RUN yarn remake-constants in yarn-project/constants
22
namespace constants;
3+
pol PROTOCOL_CONTRACT_TREE_HEIGHT = 3;
34
pol NOTE_HASH_TREE_HEIGHT = 40;
45
pol PUBLIC_DATA_TREE_HEIGHT = 40;
56
pol NULLIFIER_TREE_HEIGHT = 40;
@@ -14,8 +15,13 @@ namespace constants;
1415
pol MAX_L2_TO_L1_MSGS_PER_TX = 8;
1516
pol MAX_PUBLIC_LOGS_PER_TX = 8;
1617
pol MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS = 3000;
18+
pol CANONICAL_AUTH_REGISTRY_ADDRESS = 1;
1719
pol CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS = 2;
20+
pol CONTRACT_CLASS_REGISTRY_CONTRACT_ADDRESS = 3;
21+
pol MULTI_CALL_ENTRYPOINT_ADDRESS = 4;
1822
pol FEE_JUICE_ADDRESS = 5;
23+
pol ROUTER_ADDRESS = 6;
24+
pol MAX_PROTOCOL_CONTRACT_ADDRESS = 6;
1925
pol FEE_JUICE_BALANCES_SLOT = 1;
2026
pol UPDATED_CLASS_IDS_SLOT = 1;
2127
pol PUBLIC_LOG_SIZE_IN_FIELDS = 13;
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
include "./trees/merkle_check.pil";
2+
include "precomputed.pil";
3+
include "constants_gen.pil";
4+
include "poseidon2_hash.pil";
5+
6+
namespace protocol_contract;
7+
8+
pol commit sel;
9+
sel * (1 - sel) = 0;
10+
11+
#[skippable_if]
12+
sel = 0;
13+
14+
pol commit canonical_address;
15+
pol commit derived_address;
16+
17+
pol commit root; // todo(ilyas): this needs to be constrained against public inputs
18+
pol commit tree_depth; // todo: while we don't support constants in lookups
19+
sel * (constants.PROTOCOL_CONTRACT_TREE_HEIGHT - tree_depth) = 0;
20+
21+
pol commit next_derived_address; // Is this ok to be purely hinted?
22+
pol commit leaf_hash;
23+
#[LEAF_HASH]
24+
sel { sel, derived_address, next_derived_address, precomputed.zero, leaf_hash }
25+
in
26+
poseidon2_hash.end { poseidon2_hash.start, poseidon2_hash.input_0, poseidon2_hash.input_1, poseidon2_hash.input_2, poseidon2_hash.output };
27+
28+
#[MERKLE_CHECK]
29+
sel { leaf_hash, canonical_address, tree_depth, root }
30+
in
31+
merkle_check.start { merkle_check.read_node, merkle_check.index, merkle_check.path_len, merkle_check.read_root };

barretenberg/cpp/src/barretenberg/polynomials/gate_separator.hpp

Lines changed: 53 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "barretenberg/common/bb_bench.hpp"
99
#include "barretenberg/common/compiler_hints.hpp"
1010
#include "barretenberg/common/thread.hpp"
11+
#include "barretenberg/numeric/bitop/get_msb.hpp"
1112
#include "barretenberg/stdlib/primitives/bool/bool.hpp"
1213

1314
#include <cstddef>
@@ -157,22 +158,62 @@ template <typename FF> struct GateSeparatorPolynomial {
157158
size_t num_threads = std::min(desired_num_threads, max_num_threads); // fewer than max if justified
158159
num_threads = num_threads > 0 ? num_threads : 1; // ensure num threads is >= 1
159160
size_t iterations_per_thread = pow_size / num_threads; // actual iterations per thread
161+
const size_t num_betas_per_thread = numeric::get_msb(iterations_per_thread);
162+
163+
// Explanations of the algorithm:
164+
// The product of the betas at index i (beta_products[i]) contains the multiplicative factor betas[j] if and
165+
// only if the jth bit of i is 1 (j starting with 0 for the least significant bit). For instance, i = 13 = 1101
166+
// in binary, so the product is betas[0] * betas[2] * betas[3]. Observe that if we toggle the kth bit of i (0 to
167+
// 1), i.e., we add 2^k to i, then the product is multiplied by betas[k]: beta_products[i + 2^k] =
168+
// beta_products[i] * betas[k]. If we know the products for the interval of indices [0, 2^k), we can compute all
169+
// the products for the interval of indices [2^k, 2^(k+1)) by multiplying each element by betas[k]. Iteratively,
170+
// starting with beta_products[0] = 1, we can double the number of computed products at each iteration by
171+
// multiplying the previous products by betas[k]. We first multiply beta_products[0] = 1 by betas[0], then we
172+
// multiply beta_products[0] and beta_products[1] by betas[1], etc...
173+
//
174+
// We distribute the computation of the beta_products evenly across threads, i.e., thread number
175+
// thread_idx will handle the interval of indices [thread_idx * iterations_per_thread, (thread_idx + 1) *
176+
// iterations_per_thread). Note that for a given thread, all the processed indices have the same
177+
// prefix in binary. Therefore, each beta_product of the thread is a multiple of this "prefix product". The
178+
// successive products are then populated by the above algorithm whereby we double the interval at each
179+
// iteration and multiply by the new beta to process the suffix bits. The difference is that we initialize the
180+
// first product with this "prefix product" instead of 1.
181+
182+
// Compute the prefix products for each thread
183+
std::vector<FF> thread_prefix_beta_products(num_threads);
184+
thread_prefix_beta_products.at(0) = 1;
185+
186+
// Same algorithm applies for the prefix products. The difference is that we start at a beta which is not the
187+
// first one (index 0), but the one at index num_betas_per_thread. We process the high bits only.
188+
// Example: If num_betas_per_thread = 3, we compute after the first iteration:
189+
// (1, beta_3)
190+
// 2nd iteration: (1, beta_3, beta_4, beta_3 * beta_4)
191+
// 3nd iteration: (1, beta_3, beta_4, beta_3 * beta_4, beta_5, beta_3 * beta_5, beta_4 * beta_5, beta_3 * beta_4
192+
// * beta_5)
193+
// etc ....
194+
for (size_t beta_idx = num_betas_per_thread, window_size = 1; beta_idx < log_num_monomials;
195+
beta_idx++, window_size <<= 1) {
196+
const auto& beta = betas.at(beta_idx);
197+
for (size_t j = 0; j < window_size; j++) {
198+
thread_prefix_beta_products.at(window_size + j) = beta * thread_prefix_beta_products.at(j);
199+
}
200+
}
160201

161-
// TODO(https://github.com/AztecProtocol/barretenberg/issues/864): This computation is asymtotically slow as it
162-
// does pow_size * log(pow_size) work. However, in practice, its super efficient because its trivially
163-
// parallelizable and only takes 45ms for the whole 6 iter IVC benchmark. Its also very readable, so we're
164-
// leaving it unoptimized for now.
165202
parallel_for(num_threads, [&](size_t thread_idx) {
166203
size_t start = thread_idx * iterations_per_thread;
167-
size_t end = (thread_idx + 1) * iterations_per_thread;
168-
for (size_t i = start; i < end; i++) {
169-
auto res = FF(1);
170-
for (size_t j = i, beta_idx = 0; j > 0; j >>= 1, beta_idx++) {
171-
if ((j & 1) == 1) {
172-
res *= betas[beta_idx];
173-
}
204+
beta_products.at(start) = thread_prefix_beta_products.at(thread_idx);
205+
206+
// Compute the suffix products for each thread
207+
// Example: Assume we start with the prefix product = beta_3 * beta_5
208+
// After the first iteration, we get: (beta_3 * beta_5, beta_0 * beta_3 * beta_5)
209+
// 2nd iteration: (beta_3 * beta_5, beta_0 * beta_3 * beta_5, beta_1 * beta_3 * beta_5, beta_0 * beta_1 *
210+
// beta_3 * beta_5)
211+
// etc ...
212+
for (size_t beta_idx = 0, window_size = 1; beta_idx < num_betas_per_thread; beta_idx++, window_size <<= 1) {
213+
const auto& beta = betas.at(beta_idx);
214+
for (size_t j = 0; j < window_size; j++) {
215+
beta_products.at(start + window_size + j) = beta * beta_products.at(start + j);
174216
}
175-
beta_products[i] = res;
176217
}
177218
});
178219

barretenberg/cpp/src/barretenberg/sumcheck/sumcheck.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -519,7 +519,7 @@ template <typename Flavor> class SumcheckProver {
519519
// virtually zeroize any leftover values beyond the limit (in-place computation).
520520
// This is important to zeroize leftover values to not mess up with compute_univariate().
521521
// Note that the virtual size of pep_view[j] remains unchanged.
522-
pep_view[j].shrink_end_index(limit / 2 + limit % 2);
522+
pep_view[j].shrink_end_index((limit / 2) + (limit % 2));
523523
});
524524
};
525525
/**
@@ -544,7 +544,7 @@ template <typename Flavor> class SumcheckProver {
544544
// virtually zeroize any leftover values beyond the limit (in-place computation).
545545
// This is important to zeroize leftover values to not mess up with compute_univariate().
546546
// Note that the virtual size of pep_view[j] remains unchanged.
547-
pep_view[j].shrink_end_index(limit / 2 + limit % 2);
547+
pep_view[j].shrink_end_index((limit / 2) + (limit % 2));
548548
});
549549
};
550550

barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,15 @@ struct BytecodeCommitmentHint {
159159
MSGPACK_FIELDS(classId, commitment);
160160
};
161161

162+
struct ProtocolContractAddressHint {
163+
AztecAddress canonicalAddress;
164+
AztecAddress derivedAddress;
165+
166+
bool operator==(const ProtocolContractAddressHint& other) const = default;
167+
168+
MSGPACK_FIELDS(canonicalAddress, derivedAddress);
169+
};
170+
162171
////////////////////////////////////////////////////////////////////////////
163172
// Hints (merkle db)
164173
////////////////////////////////////////////////////////////////////////////
@@ -328,6 +337,8 @@ struct Tx {
328337
struct ExecutionHints {
329338
GlobalVariables globalVariables;
330339
Tx tx;
340+
// Protocol Contract Hints
341+
std::vector<ProtocolContractAddressHint> protocolContractDerivedAddresses;
331342
// Contracts.
332343
std::vector<ContractInstanceHint> contractInstances;
333344
std::vector<ContractClassHint> contractClasses;
@@ -352,6 +363,7 @@ struct ExecutionHints {
352363

353364
MSGPACK_FIELDS(globalVariables,
354365
tx,
366+
protocolContractDerivedAddresses,
355367
contractInstances,
356368
contractClasses,
357369
bytecodeCommitments,

barretenberg/cpp/src/barretenberg/vm2/common/aztec_constants.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// GENERATED FILE - DO NOT EDIT, RUN yarn remake-constants in yarn-project/constants
22
#pragma once
33

4+
#define PROTOCOL_CONTRACT_TREE_HEIGHT 3
45
#define NOTE_HASH_TREE_HEIGHT 40
56
#define PUBLIC_DATA_TREE_HEIGHT 40
67
#define NULLIFIER_TREE_HEIGHT 40
@@ -17,12 +18,14 @@
1718
#define GENESIS_BLOCK_HEADER_HASH "0x0e40440ea6abb9b58877d3aea6628cc6b14b6bc2148ce2be6f46db23360a6aba"
1819
#define GENESIS_ARCHIVE_ROOT "0x1f9c798be7975bb34c3e605a4c92c75796eae7b9a08644bc9a6a55354ed470be"
1920
#define MAX_PACKED_PUBLIC_BYTECODE_SIZE_IN_FIELDS 3000
21+
#define MAX_PROTOCOL_CONTRACTS 7
2022
#define CANONICAL_AUTH_REGISTRY_ADDRESS 1
2123
#define CONTRACT_INSTANCE_REGISTRY_CONTRACT_ADDRESS 2
2224
#define CONTRACT_CLASS_REGISTRY_CONTRACT_ADDRESS 3
2325
#define MULTI_CALL_ENTRYPOINT_ADDRESS 4
2426
#define FEE_JUICE_ADDRESS 5
2527
#define ROUTER_ADDRESS 6
28+
#define MAX_PROTOCOL_CONTRACT_ADDRESS 6
2629
#define FEE_JUICE_BALANCES_SLOT 1
2730
#define UPDATED_CLASS_IDS_SLOT 1
2831
#define PUBLIC_LOG_SIZE_IN_FIELDS 13

barretenberg/cpp/src/barretenberg/vm2/common/protocol_contract_data.cpp

Lines changed: 0 additions & 15 deletions
This file was deleted.

barretenberg/cpp/src/barretenberg/vm2/common/protocol_contracts.hpp

Lines changed: 0 additions & 21 deletions
This file was deleted.

0 commit comments

Comments
 (0)