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
32 changes: 30 additions & 2 deletions barretenberg/cpp/src/barretenberg/vm2/common/avm_inputs.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,8 +194,36 @@ struct EnqueuedCallHint {
MSGPACK_FIELDS(msgSender, contractAddress, calldata, isStaticCall);
};

struct AccumulatedData {
// TODO: add as needed.
std::vector<FF> noteHashes;
std::vector<FF> nullifiers;
Comment on lines +197 to +200
Copy link
Contributor

Choose a reason for hiding this comment

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

TODO meaning "add the remaining side effects as needed?"

Copy link
Contributor

Choose a reason for hiding this comment

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

And do we need two variations of AccumulatedData like we have in public inputs?
image

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll respond in the other thread.


bool operator==(const AccumulatedData& other) const = default;

MSGPACK_FIELDS(noteHashes, nullifiers);
};

// We are currently using this structure as the input to TX simulation.
// That's why I'm not calling it TxHint. We can reconsider if the inner types seem to dirty.
struct Tx {
AccumulatedData nonRevertibleAccumulatedData;
AccumulatedData revertibleAccumulatedData;
std::vector<EnqueuedCallHint> setupEnqueuedCalls;
std::vector<EnqueuedCallHint> appLogicEnqueuedCalls;
std::optional<EnqueuedCallHint> teardownEnqueuedCall;

bool operator==(const Tx& other) const = default;

MSGPACK_FIELDS(nonRevertibleAccumulatedData,
revertibleAccumulatedData,
setupEnqueuedCalls,
appLogicEnqueuedCalls,
teardownEnqueuedCall);
};
Comment on lines +207 to +223
Copy link
Contributor

Choose a reason for hiding this comment

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

Will this eventually become a complete mirror of the TS TX? Or this will be some subset including only what is needed for our purposes?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

IDK. It looked like we didn't need everything from the TX object in TS, so I guess this could be a subset? Also, if I wanted to respect the same structure (or if I want to serialize the whole TX) then I have to go in TS and implement (de)serialization/schemas for everything that is in it, even if we don't use it.

For the moment I've opted to add what we need, but it can be refactored later if needed. Modifying serialization is really not that difficult now with MP/zod, but it's easier to do it incrementally, I find. Also it lets us look at the structure and see what we already solved and what not (of course not completely true since we don't use note hashes now ;)).


struct ExecutionHints {
std::vector<EnqueuedCallHint> enqueuedCalls;
Tx tx;
// Contracts.
std::vector<ContractInstanceHint> contractInstances;
std::vector<ContractClassHint> contractClasses;
Expand All @@ -213,7 +241,7 @@ struct ExecutionHints {

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

MSGPACK_FIELDS(enqueuedCalls,
MSGPACK_FIELDS(tx,
contractInstances,
contractClasses,
bytecodeCommitments,
Expand Down
Binary file not shown.
47 changes: 36 additions & 11 deletions barretenberg/cpp/src/barretenberg/vm2/simulation/tx_execution.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,43 @@ namespace bb::avm2::simulation {

void TxExecution::simulate(const Tx& tx)
{
// TODO: other inter-enqueued-call stuff will be done here.
for (const auto& call : tx.enqueued_calls) {
info("Executing enqueued call to ", call.contractAddress);
info("Simulating tx with ",
tx.setupEnqueuedCalls.size(),
" setup enqueued calls, ",
tx.appLogicEnqueuedCalls.size(),
" app logic enqueued calls, and ",
tx.teardownEnqueuedCall ? "1 teardown enqueued call" : "no teardown enqueued call");

// TODO: This method is currently wrong. We need to lift the context to this level.

// Insert non-revertibles.
// TODO: We need a context at this level to be able to do the insertions.

// Setup.
for (const auto& call : tx.setupEnqueuedCalls) {
info("[SETUP] Executing enqueued call to ", call.contractAddress);
auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall);
auto result = call_execution.execute(*context);
info("Enqueued call to ",
call.contractAddress,
" was a ",
result.success ? "success" : "failure",
" and it returned ",
context->get_last_rd_size(),
" elements.");
call_execution.execute(*context);
}

// Insert revertibles.
// TODO: We need a context at this level to be able to do the insertions.

// App logic.
for (const auto& call : tx.appLogicEnqueuedCalls) {
info("[APP_LOGIC] Executing enqueued call to ", call.contractAddress);
auto context = make_enqueued_context(call.contractAddress, call.msgSender, call.calldata, call.isStaticCall);
call_execution.execute(*context);
}

// Teardown.
if (tx.teardownEnqueuedCall) {
info("[TEARDOWN] Executing enqueued call to ", tx.teardownEnqueuedCall->contractAddress);
auto context = make_enqueued_context(tx.teardownEnqueuedCall->contractAddress,
tx.teardownEnqueuedCall->msgSender,
tx.teardownEnqueuedCall->calldata,
tx.teardownEnqueuedCall->isStaticCall);
call_execution.execute(*context);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,6 @@

namespace bb::avm2::simulation {

// Temporary.
struct Tx {
std::vector<EnqueuedCallHint> enqueued_calls;
};

// In charge of executing a transaction.
class TxExecution final {
public:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ template <typename S> EventsContainer AvmSimulationHelper::simulate_with_setting
TxExecution tx_execution(execution);
Sha256 sha256(sha256_compression_emitter);

tx_execution.simulate({ .enqueued_calls = inputs.hints.enqueuedCalls });
tx_execution.simulate(inputs.hints.tx);

return { execution_emitter.dump_events(),
alu_emitter.dump_events(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <cstdint>
#include <stdexcept>

#include "barretenberg/common/utils.hpp"
#include "barretenberg/vm2/common/field.hpp"
#include "barretenberg/vm2/common/map.hpp"
#include "barretenberg/vm2/generated/columns.hpp"
Expand Down Expand Up @@ -136,11 +137,8 @@ template <typename LookupSettings> class LookupIntoDynamicTableSequential : publ
template <typename T, size_t SIZE> struct std::hash<std::array<T, SIZE>> {
std::size_t operator()(const std::array<T, SIZE>& arr) const noexcept
{
std::size_t hash = 0;
for (const auto& elem : arr) {
hash = std::rotl(hash, 1);
hash ^= std::hash<T>{}(elem);
}
return hash;
return [&arr]<size_t... Is>(std::index_sequence<Is...>) {
return bb::utils::hash_as_tuple(arr[Is]...);
}(std::make_index_sequence<SIZE>{});
}
};
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,13 @@ import { padArrayEnd } from '@aztec/foundation/collection';
import { Fr } from '@aztec/foundation/fields';
import { type Logger, createLogger } from '@aztec/foundation/log';
import { assertLength } from '@aztec/foundation/serialize';
import { type AvmCircuitPublicInputs, AvmExecutionHints, PublicDataWrite, RevertCode } from '@aztec/stdlib/avm';
import {
type AvmCircuitPublicInputs,
AvmExecutionHints,
AvmTxHint,
PublicDataWrite,
RevertCode,
} from '@aztec/stdlib/avm';
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
import type { SimulationError } from '@aztec/stdlib/errors';
import { computeTransactionFee } from '@aztec/stdlib/fees';
Expand Down Expand Up @@ -105,7 +111,7 @@ export class PublicTxContext {
const firstNullifier = nonRevertibleAccumulatedDataFromPrivate.nullifiers[0];

// We wrap the DB to collect AVM hints.
const hints = new AvmExecutionHints();
const hints = new AvmExecutionHints(AvmTxHint.fromTx(tx));
const hintingContractsDB = new HintingPublicContractsDB(contractsDB, hints);
const hintingTreesDB = new HintingPublicTreesDB(treesDB, hints);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { computeFeePayerBalanceStorageSlot } from '@aztec/protocol-contracts/fee
import {
AvmCircuitInputs,
AvmCircuitPublicInputs,
AvmEnqueuedCallHint,
AvmExecutionHints,
type AvmProvingRequest,
type RevertCode,
Expand Down Expand Up @@ -72,6 +71,7 @@ export class PublicTxSimulator {
const txHash = await this.computeTxHash(tx);

this.log.debug(`Simulating ${tx.publicFunctionCalldata.length} public calls for tx ${txHash}`, { txHash });

const context = await PublicTxContext.create(
this.treesDB,
this.contractsDB,
Expand Down Expand Up @@ -265,18 +265,7 @@ export class PublicTxSimulator {

const allocatedGas = context.getGasLeftAtPhase(phase);

// The reason we need enqueued hints at all (and cannot just use the public inputs) is
// because they don't have the actual calldata, just the hash of it.
// If/when we pass the whole TX to C++, we can remove this class of hints.
stateManager.traceEnqueuedCall(callRequest.request);
Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm... i guess we still need to trace the enqueued call because tracing it means "hint that this enqueued call actually happened"? Otherwise, the TX should include everything

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep tracing is for PIs I guess? Didn't look too much in detail, maybe it can be removed.

context.hints.enqueuedCalls.push(
new AvmEnqueuedCallHint(
callRequest.request.msgSender,
contractAddress,
callRequest.calldata,
callRequest.request.isStaticCall,
),
);

const result = await this.simulateEnqueuedCallInternal(
context.state.getActiveStateManager(),
Expand Down
94 changes: 89 additions & 5 deletions yarn-project/stdlib/src/avm/avm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { AppendOnlyTreeSnapshot } from '../trees/append_only_tree_snapshot.js';
import { MerkleTreeId } from '../trees/merkle_tree_id.js';
import { NullifierLeafPreimage } from '../trees/nullifier_leaf.js';
import { PublicDataTreeLeafPreimage } from '../trees/public_data_leaf.js';
import type { Tx } from '../tx/index.js';
import { AvmCircuitPublicInputs } from './avm_circuit_public_inputs.js';
import { serializeWithMessagePack } from './message_pack.js';

Expand Down Expand Up @@ -287,9 +288,92 @@ export class AvmEnqueuedCallHint {
}
}

export class AvmTxHint {
constructor(
public readonly nonRevertibleAccumulatedData: {
noteHashes: Fr[];
nullifiers: Fr[];
// TODO: add as needed.
},
public readonly revertibleAccumulatedData: {
noteHashes: Fr[];
nullifiers: Fr[];
// TODO: add as needed.
},
public readonly setupEnqueuedCalls: AvmEnqueuedCallHint[],
public readonly appLogicEnqueuedCalls: AvmEnqueuedCallHint[],
// We need this to be null and not undefined because that's what
// MessagePack expects for an std::optional.
public readonly teardownEnqueuedCall: AvmEnqueuedCallHint | null,
) {}

static fromTx(tx: Tx): AvmTxHint {
const setupCallRequests = tx.getNonRevertiblePublicCallRequestsWithCalldata();
const appLogicCallRequests = tx.getRevertiblePublicCallRequestsWithCalldata();
const teardownCallRequest = tx.getTeardownPublicCallRequestWithCalldata();

return new AvmTxHint(
{
noteHashes: tx.data.forPublic!.nonRevertibleAccumulatedData.noteHashes.filter(x => !x.isZero()),
nullifiers: tx.data.forPublic!.nonRevertibleAccumulatedData.nullifiers.filter(x => !x.isZero()),
},
{
noteHashes: tx.data.forPublic!.revertibleAccumulatedData.noteHashes.filter(x => !x.isZero()),
nullifiers: tx.data.forPublic!.revertibleAccumulatedData.nullifiers.filter(x => !x.isZero()),
},
setupCallRequests.map(
call =>
new AvmEnqueuedCallHint(
call.request.msgSender,
call.request.contractAddress,
call.calldata,
call.request.isStaticCall,
),
),
appLogicCallRequests.map(
call =>
new AvmEnqueuedCallHint(
call.request.msgSender,
call.request.contractAddress,
call.calldata,
call.request.isStaticCall,
),
),
teardownCallRequest
? new AvmEnqueuedCallHint(
teardownCallRequest.request.msgSender,
teardownCallRequest.request.contractAddress,
teardownCallRequest.calldata,
teardownCallRequest.request.isStaticCall,
)
: null,
);
}

static empty() {
return new AvmTxHint({ noteHashes: [], nullifiers: [] }, { noteHashes: [], nullifiers: [] }, [], [], null);
}

static get schema() {
return z.object({
nonRevertibleAccumulatedData: z.object({
noteHashes: schemas.Fr.array(),
nullifiers: schemas.Fr.array(),
}),
revertibleAccumulatedData: z.object({
noteHashes: schemas.Fr.array(),
nullifiers: schemas.Fr.array(),
}),
setupEnqueuedCalls: AvmEnqueuedCallHint.schema.array(),
appLogicEnqueuedCalls: AvmEnqueuedCallHint.schema.array(),
teardownEnqueuedCall: AvmEnqueuedCallHint.schema.nullable(),
});
}
}

export class AvmExecutionHints {
constructor(
public readonly enqueuedCalls: AvmEnqueuedCallHint[] = [],
public tx: AvmTxHint,
// Contract hints.
public readonly contractInstances: AvmContractInstanceHint[] = [],
public readonly contractClasses: AvmContractClassHint[] = [],
Expand All @@ -305,13 +389,13 @@ export class AvmExecutionHints {
) {}

static empty() {
return new AvmExecutionHints();
return new AvmExecutionHints(AvmTxHint.empty());
}

static get schema() {
return z
.object({
enqueuedCalls: AvmEnqueuedCallHint.schema.array(),
tx: AvmTxHint.schema,
contractInstances: AvmContractInstanceHint.schema.array(),
contractClasses: AvmContractClassHint.schema.array(),
bytecodeCommitments: AvmBytecodeCommitmentHint.schema.array(),
Expand All @@ -325,7 +409,7 @@ export class AvmExecutionHints {
})
.transform(
({
enqueuedCalls,
tx,
contractInstances,
contractClasses,
bytecodeCommitments,
Expand All @@ -338,7 +422,7 @@ export class AvmExecutionHints {
sequentialInsertHintsNullifierTree,
}) =>
new AvmExecutionHints(
enqueuedCalls,
tx,
contractInstances,
contractClasses,
bytecodeCommitments,
Expand Down
21 changes: 19 additions & 2 deletions yarn-project/stdlib/src/tests/factories.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ import {
AvmGetSiblingPathHint,
AvmSequentialInsertHintNullifierTree,
AvmSequentialInsertHintPublicDataTree,
AvmTxHint,
RevertCode,
} from '../avm/index.js';
import { PublicDataHint } from '../avm/public_data_hint.js';
Expand Down Expand Up @@ -1427,6 +1428,22 @@ export function makeAvmEnqueuedCallHint(seed = 0): AvmEnqueuedCallHint {
);
}

export function makeAvmTxHint(seed = 0): AvmTxHint {
return new AvmTxHint(
{
noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x1000),
nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x2000),
},
{
noteHashes: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x3000),
nullifiers: makeArray((seed % 20) + 4, i => new Fr(i), seed + 0x4000),
},
makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x5000), // setupEnqueuedCalls
makeArray((seed % 20) + 4, i => makeAvmEnqueuedCallHint(i), seed + 0x6000), // appLogicEnqueuedCalls
makeAvmEnqueuedCallHint(seed + 0x7000), // teardownEnqueuedCall
);
}

/**
* Creates arbitrary AvmExecutionHints.
* @param seed - The seed to use for generating the hints.
Expand All @@ -1441,7 +1458,7 @@ export async function makeAvmExecutionHints(
const baseLength = lengthOffset + (seed % lengthSeedMod);

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

return new AvmExecutionHints(
fields.enqueuedCalls,
fields.tx,
fields.contractInstances,
fields.contractClasses,
fields.bytecodeCommitments,
Expand Down