Skip to content

Commit 44f6cdb

Browse files
committed
feat(avm): tx hint
1 parent f16b70e commit 44f6cdb

File tree

10 files changed

+188
-46
lines changed

10 files changed

+188
-46
lines changed

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

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,36 @@ struct EnqueuedCallHint {
194194
MSGPACK_FIELDS(msgSender, contractAddress, calldata, isStaticCall);
195195
};
196196

197+
struct AccumulatedData {
198+
// TODO: add as needed.
199+
std::vector<FF> noteHashes;
200+
std::vector<FF> nullifiers;
201+
202+
bool operator==(const AccumulatedData& other) const = default;
203+
204+
MSGPACK_FIELDS(noteHashes, nullifiers);
205+
};
206+
207+
// We are currently using this structure as the input to TX simulation.
208+
// That's why I'm not calling it TxHint. We can reconsider if the inner types seem to dirty.
209+
struct Tx {
210+
AccumulatedData nonRevertibleAccumulatedData;
211+
AccumulatedData revertibleAccumulatedData;
212+
std::vector<EnqueuedCallHint> setupEnqueuedCalls;
213+
std::vector<EnqueuedCallHint> appLogicEnqueuedCalls;
214+
std::optional<EnqueuedCallHint> teardownEnqueuedCall;
215+
216+
bool operator==(const Tx& other) const = default;
217+
218+
MSGPACK_FIELDS(nonRevertibleAccumulatedData,
219+
revertibleAccumulatedData,
220+
setupEnqueuedCalls,
221+
appLogicEnqueuedCalls,
222+
teardownEnqueuedCall);
223+
};
224+
197225
struct ExecutionHints {
198-
std::vector<EnqueuedCallHint> enqueuedCalls;
226+
Tx tx;
199227
// Contracts.
200228
std::vector<ContractInstanceHint> contractInstances;
201229
std::vector<ContractClassHint> contractClasses;
@@ -213,7 +241,7 @@ struct ExecutionHints {
213241

214242
bool operator==(const ExecutionHints& other) const = default;
215243

216-
MSGPACK_FIELDS(enqueuedCalls,
244+
MSGPACK_FIELDS(tx,
217245
contractInstances,
218246
contractClasses,
219247
bytecodeCommitments,
Binary file not shown.

barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,43 @@ namespace bb::avm2::simulation {
55

66
void TxExecution::simulate(const Tx& tx)
77
{
8-
// TODO: other inter-enqueued-call stuff will be done here.
9-
for (const auto& call : tx.enqueued_calls) {
10-
info("Executing enqueued call to ", call.contractAddress);
8+
info("Simulating tx with ",
9+
tx.setupEnqueuedCalls.size(),
10+
" setup enqueued calls, ",
11+
tx.appLogicEnqueuedCalls.size(),
12+
" app logic enqueued calls, and ",
13+
tx.teardownEnqueuedCall ? "1 teardown enqueued call" : "no teardown enqueued call");
14+
15+
// TODO: This method is currently wrong. We need to lift the context to this level.
16+
17+
// Insert non-revertibles.
18+
// TODO: We need a context at this level to be able to do the insertions.
19+
20+
// Setup.
21+
for (const auto& call : tx.setupEnqueuedCalls) {
22+
info("[SETUP] Executing enqueued call to ", call.contractAddress);
1123
auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall);
12-
auto result = call_execution.execute(*context);
13-
info("Enqueued call to ",
14-
call.contractAddress,
15-
" was a ",
16-
result.success ? "success" : "failure",
17-
" and it returned ",
18-
context->get_last_rd_size(),
19-
" elements.");
24+
call_execution.execute(*context);
25+
}
26+
27+
// Insert revertibles.
28+
// TODO: We need a context at this level to be able to do the insertions.
29+
30+
// App logic.
31+
for (const auto& call : tx.appLogicEnqueuedCalls) {
32+
info("[APP_LOGIC] Executing enqueued call to ", call.contractAddress);
33+
auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall);
34+
call_execution.execute(*context);
35+
}
36+
37+
// Teardown.
38+
if (tx.teardownEnqueuedCall) {
39+
info("[TEARDOWN] Executing enqueued call to ", tx.teardownEnqueuedCall->contractAddress);
40+
auto context = make_enqueued_context(tx.teardownEnqueuedCall->contractAddress,
41+
tx.teardownEnqueuedCall->msgSender,
42+
tx.teardownEnqueuedCall->calldata,
43+
tx.teardownEnqueuedCall->isStaticCall);
44+
call_execution.execute(*context);
2045
}
2146
}
2247

barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.hpp

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,6 @@
55

66
namespace bb::avm2::simulation {
77

8-
// Temporary.
9-
struct Tx {
10-
std::vector<EnqueuedCallHint> enqueued_calls;
11-
};
12-
138
// In charge of executing a transaction.
149
class TxExecution final {
1510
public:

barretenberg/cpp/src/barretenberg/vm2/simulation_helper.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ template <typename S> EventsContainer AvmSimulationHelper::simulate_with_setting
118118
TxExecution tx_execution(execution);
119119
Sha256 sha256(sha256_compression_emitter);
120120

121-
tx_execution.simulate({ .enqueued_calls = inputs.hints.enqueuedCalls });
121+
tx_execution.simulate(inputs.hints.tx);
122122

123123
return { execution_emitter.dump_events(),
124124
alu_emitter.dump_events(),

barretenberg/cpp/src/barretenberg/vm2/tracegen/lib/lookup_builder.hpp

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
#include <cstdint>
77
#include <stdexcept>
88

9+
#include "barretenberg/common/utils.hpp"
910
#include "barretenberg/vm2/common/field.hpp"
1011
#include "barretenberg/vm2/common/map.hpp"
1112
#include "barretenberg/vm2/generated/columns.hpp"
@@ -136,11 +137,8 @@ template <typename LookupSettings> class LookupIntoDynamicTableSequential : publ
136137
template <typename T, size_t SIZE> struct std::hash<std::array<T, SIZE>> {
137138
std::size_t operator()(const std::array<T, SIZE>& arr) const noexcept
138139
{
139-
std::size_t hash = 0;
140-
for (const auto& elem : arr) {
141-
hash = std::rotl(hash, 1);
142-
hash ^= std::hash<T>{}(elem);
143-
}
144-
return hash;
140+
return [&arr]<size_t... Is>(std::index_sequence<Is...>) {
141+
return bb::utils::hash_as_tuple(arr[Is]...);
142+
}(std::make_index_sequence<SIZE>{});
145143
}
146144
};

yarn-project/simulator/src/public/public_tx_simulator/public_tx_context.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,13 @@ import { padArrayEnd } from '@aztec/foundation/collection';
1010
import { Fr } from '@aztec/foundation/fields';
1111
import { type Logger, createLogger } from '@aztec/foundation/log';
1212
import { assertLength } from '@aztec/foundation/serialize';
13-
import { type AvmCircuitPublicInputs, AvmExecutionHints, PublicDataWrite, RevertCode } from '@aztec/stdlib/avm';
13+
import {
14+
type AvmCircuitPublicInputs,
15+
AvmExecutionHints,
16+
AvmTxHint,
17+
PublicDataWrite,
18+
RevertCode,
19+
} from '@aztec/stdlib/avm';
1420
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
1521
import type { SimulationError } from '@aztec/stdlib/errors';
1622
import { computeTransactionFee } from '@aztec/stdlib/fees';
@@ -105,7 +111,7 @@ export class PublicTxContext {
105111
const firstNullifier = nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];
106112

107113
// We wrap the DB to collect AVM hints.
108-
const hints = new AvmExecutionHints();
114+
const hints = new AvmExecutionHints(AvmTxHint.fromTx(tx));
109115
const hintingContractsDB = new HintingPublicContractsDB(contractsDB, hints);
110116
const hintingTreesDB = new HintingPublicTreesDB(treesDB, hints);
111117

yarn-project/simulator/src/public/public_tx_simulator/public_tx_simulator.ts

Lines changed: 1 addition & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee
55
import {
66
AvmCircuitInputs,
77
AvmCircuitPublicInputs,
8-
AvmEnqueuedCallHint,
98
AvmExecutionHints,
109
type AvmProvingRequest,
1110
type RevertCode,
@@ -72,6 +71,7 @@ export class PublicTxSimulator {
7271
const txHash = await this.computeTxHash(tx);
7372

7473
this.log.debug(`Simulating ${tx.publicFunctionCalldata.length} public calls for tx ${txHash}`, { txHash });
74+
7575
const context = await PublicTxContext.create(
7676
this.treesDB,
7777
this.contractsDB,
@@ -265,18 +265,7 @@ export class PublicTxSimulator {
265265

266266
const allocatedGas = context.getGasLeftAtPhase(phase);
267267

268-
// The reason we need enqueued hints at all (and cannot just use the public inputs) is
269-
// because they don't have the actual calldata, just the hash of it.
270-
// If/when we pass the whole TX to C++, we can remove this class of hints.
271268
stateManager.traceEnqueuedCall(callRequest.request);
272-
context.hints.enqueuedCalls.push(
273-
new AvmEnqueuedCallHint(
274-
callRequest.request.msgSender,
275-
contractAddress,
276-
callRequest.calldata,
277-
callRequest.request.isStaticCall,
278-
),
279-
);
280269

281270
const result = await this.simulateEnqueuedCallInternal(
282271
context.state.getActiveStateManager(),

yarn-project/stdlib/src/avm/avm.ts

Lines changed: 89 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js';
1010
import { MerkleTreeId } from '../trees/merkle_tree_id.js';
1111
import { NullifierLeafPreimage } from '../trees/nullifier_leaf.js';
1212
import { PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js';
13+
import type { Tx } from '../tx/index.js';
1314
import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js';
1415
import { serializeWithMessagePack } from './message_pack.js';
1516

@@ -287,9 +288,92 @@ export class AvmEnqueuedCallHint {
287288
}
288289
}
289290

291+
export class AvmTxHint {
292+
constructor(
293+
public readonly nonRevertibleAccumulatedData: {
294+
noteHashes: Fr[];
295+
nullifiers: Fr[];
296+
// TODO: add as needed.
297+
},
298+
public readonly revertibleAccumulatedData: {
299+
noteHashes: Fr[];
300+
nullifiers: Fr[];
301+
// TODO: add as needed.
302+
},
303+
public readonly setupEnqueuedCalls: AvmEnqueuedCallHint[],
304+
public readonly appLogicEnqueuedCalls: AvmEnqueuedCallHint[],
305+
// We need this to be null and not undefined because that's what
306+
// MessagePack expects for an std::optional.
307+
public readonly teardownEnqueuedCall: AvmEnqueuedCallHint | null,
308+
) {}
309+
310+
static fromTx(tx: Tx): AvmTxHint {
311+
const setupCallRequests = tx.getNonRevertiblePublicCallRequestsWithCalldata();
312+
const appLogicCallRequests = tx.getRevertiblePublicCallRequestsWithCalldata();
313+
const teardownCallRequest = tx.getTeardownPublicCallRequestWithCalldata();
314+
315+
return new AvmTxHint(
316+
{
317+
noteHashes: tx.data.forPublic!.nonRevertibleAccumulatedData.noteHashes.filter(x => !x.isZero()),
318+
nullifiers: tx.data.forPublic!.nonRevertibleAccumulatedData.nullifiers.filter(x => !x.isZero()),
319+
},
320+
{
321+
noteHashes: tx.data.forPublic!.revertibleAccumulatedData.noteHashes.filter(x => !x.isZero()),
322+
nullifiers: tx.data.forPublic!.revertibleAccumulatedData.nullifiers.filter(x => !x.isZero()),
323+
},
324+
setupCallRequests.map(
325+
call =>
326+
new AvmEnqueuedCallHint(
327+
call.request.msgSender,
328+
call.request.contractAddress,
329+
call.calldata,
330+
call.request.isStaticCall,
331+
),
332+
),
333+
appLogicCallRequests.map(
334+
call =>
335+
new AvmEnqueuedCallHint(
336+
call.request.msgSender,
337+
call.request.contractAddress,
338+
call.calldata,
339+
call.request.isStaticCall,
340+
),
341+
),
342+
teardownCallRequest
343+
? new AvmEnqueuedCallHint(
344+
teardownCallRequest.request.msgSender,
345+
teardownCallRequest.request.contractAddress,
346+
teardownCallRequest.calldata,
347+
teardownCallRequest.request.isStaticCall,
348+
)
349+
: null,
350+
);
351+
}
352+
353+
static empty() {
354+
return new AvmTxHint({ noteHashes: [], nullifiers: [] }, { noteHashes: [], nullifiers: [] }, [], [], null);
355+
}
356+
357+
static get schema() {
358+
return z.object({
359+
nonRevertibleAccumulatedData: z.object({
360+
noteHashes: schemas.Fr.array(),
361+
nullifiers: schemas.Fr.array(),
362+
}),
363+
revertibleAccumulatedData: z.object({
364+
noteHashes: schemas.Fr.array(),
365+
nullifiers: schemas.Fr.array(),
366+
}),
367+
setupEnqueuedCalls: AvmEnqueuedCallHint.schema.array(),
368+
appLogicEnqueuedCalls: AvmEnqueuedCallHint.schema.array(),
369+
teardownEnqueuedCall: AvmEnqueuedCallHint.schema.nullable(),
370+
});
371+
}
372+
}
373+
290374
export class AvmExecutionHints {
291375
constructor(
292-
public readonly enqueuedCalls: AvmEnqueuedCallHint[] = [],
376+
public tx: AvmTxHint,
293377
// Contract hints.
294378
public readonly contractInstances: AvmContractInstanceHint[] = [],
295379
public readonly contractClasses: AvmContractClassHint[] = [],
@@ -305,13 +389,13 @@ export class AvmExecutionHints {
305389
) {}
306390

307391
static empty() {
308-
return new AvmExecutionHints();
392+
return new AvmExecutionHints(AvmTxHint.empty());
309393
}
310394

311395
static get schema() {
312396
return z
313397
.object({
314-
enqueuedCalls: AvmEnqueuedCallHint.schema.array(),
398+
tx: AvmTxHint.schema,
315399
contractInstances: AvmContractInstanceHint.schema.array(),
316400
contractClasses: AvmContractClassHint.schema.array(),
317401
bytecodeCommitments: AvmBytecodeCommitmentHint.schema.array(),
@@ -325,7 +409,7 @@ export class AvmExecutionHints {
325409
})
326410
.transform(
327411
({
328-
enqueuedCalls,
412+
tx,
329413
contractInstances,
330414
contractClasses,
331415
bytecodeCommitments,
@@ -338,7 +422,7 @@ export class AvmExecutionHints {
338422
sequentialInsertHintsNullifierTree,
339423
}) =>
340424
new AvmExecutionHints(
341-
enqueuedCalls,
425+
tx,
342426
contractInstances,
343427
contractClasses,
344428
bytecodeCommitments,

yarn-project/stdlib/src/tests/factories.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import {
6969
AvmGetSiblingPathHint,
7070
AvmSequentialInsertHintNullifierTree,
7171
AvmSequentialInsertHintPublicDataTree,
72+
AvmTxHint,
7273
RevertCode,
7374
} from '../avm/index.js';
7475
import { PublicDataHint } from '../avm/public_data_hint.js';
@@ -1427,6 +1428,22 @@ export function makeAvmEnqueuedCallHint(seed = 0): AvmEnqueuedCallHint {
14271428
);
14281429
}
14291430

1431+
export function makeAvmTxHint(seed = 0): AvmTxHint {
1432+
return new AvmTxHint(
1433+
{
1434+
noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x1000),
1435+
nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x2000),
1436+
},
1437+
{
1438+
noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x3000),
1439+
nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x4000),
1440+
},
1441+
makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x5000), // setupEnqueuedCalls
1442+
makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x6000), // appLogicEnqueuedCalls
1443+
makeAvmEnqueuedCallHint(seed + 0x7000), // teardownEnqueuedCall
1444+
);
1445+
}
1446+
14301447
/**
14311448
* Creates arbitrary AvmExecutionHints.
14321449
* @param seed - The seed to use for generating the hints.
@@ -1441,7 +1458,7 @@ export async function makeAvmExecutionHints(
14411458
const baseLength = lengthOffset + (seed % lengthSeedMod);
14421459

14431460
const fields = {
1444-
enqueuedCalls: makeArray(baseLength, makeAvmEnqueuedCallHint, seed + 0x4100),
1461+
tx: makeAvmTxHint(seed + 0x4100),
14451462
contractInstances: makeArray(baseLength + 2, makeAvmContractInstanceHint, seed + 0x4700),
14461463
contractClasses: makeArray(baseLength + 5, makeAvmContractClassHint, seed + 0x4900),
14471464
bytecodeCommitments: await makeArrayAsync(baseLength + 5, makeAvmBytecodeCommitmentHint, seed + 0x4900),
@@ -1468,7 +1485,7 @@ export async function makeAvmExecutionHints(
14681485
};
14691486

14701487
return new AvmExecutionHints(
1471-
fields.enqueuedCalls,
1488+
fields.tx,
14721489
fields.contractInstances,
14731490
fields.contractClasses,
14741491
fields.bytecodeCommitments,

0 commit comments

Comments
 (0)