Skip to content

Commit

Permalink
feat: global variables in public noir (#917)
Browse files Browse the repository at this point in the history
* feat: globalvars in public noir

* chore: recompile noir contracts

* chore: noir rebuild

* feat: use globals in block production

* chore: formatting

* chore: lint

* chore: compile noir

* chore: remove comment

* chore: formatting

* fix: use context for l2 -> l1 msgs chainid and version

* fix: test used hardcoded chain_id and version

* chore: tidy

* chore: add comment

* test: cleanup solo block builder
  • Loading branch information
LHerskind authored Jul 3, 2023
1 parent 48babdc commit 145e34e
Show file tree
Hide file tree
Showing 23 changed files with 214 additions and 123 deletions.
7 changes: 2 additions & 5 deletions circuits/cpp/src/aztec3/circuits/kernel/private/common.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,10 @@ void common_update_end_values(DummyBuilder& builder,
std::array<NT::fr, NEW_L2_TO_L1_MSGS_LENGTH> new_l2_to_l1_msgs_to_insert{};
for (size_t i = 0; i < new_l2_to_l1_msgs.size(); ++i) {
if (!new_l2_to_l1_msgs[i].is_zero()) {
// @todo @LHerskind chain-ids and rollup version id should be added here. Right now, just hard coded.
// @todo @LHerskind chain-id is hardcoded for foundry
const auto chain_id = fr(31337);
new_l2_to_l1_msgs_to_insert[i] = compute_l2_to_l1_hash<NT>(storage_contract_address,
fr(1), // rollup version id
private_call_public_inputs.version,
portal_contract_address,
chain_id,
private_call_public_inputs.chain_id,
new_l2_to_l1_msgs[i]);
}
}
Expand Down
26 changes: 15 additions & 11 deletions circuits/cpp/src/aztec3/circuits/kernel/public/.test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -236,19 +236,15 @@ std::array<fr, KERNEL_NEW_NULLIFIERS_LENGTH> new_nullifiers_as_siloed_nullifiers
std::array<NT::fr, KERNEL_NEW_L2_TO_L1_MSGS_LENGTH> new_l2_messages_from_message(
std::array<NT::fr, KERNEL_NEW_L2_TO_L1_MSGS_LENGTH> const& new_messages,
NT::fr const& contract_address,
fr const& portal_contract_address)
fr const& portal_contract_address,
fr const& chain_id,
fr const& version)
{
std::array<NT::fr, KERNEL_NEW_L2_TO_L1_MSGS_LENGTH> formatted_msgs{};
for (size_t i = 0; i < KERNEL_NEW_L2_TO_L1_MSGS_LENGTH; ++i) {
if (!new_messages[i].is_zero()) {
// @todo @LHerskind chain-ids and rollup version id should be added here. Right now, just hard coded.
// @todo @LHerskind chain-id is hardcoded for foundry
const auto chain_id = fr(31337);
formatted_msgs[i] = compute_l2_to_l1_hash<NT>(contract_address,
fr(1), // rollup version id
portal_contract_address,
chain_id,
new_messages[i]);
formatted_msgs[i] = compute_l2_to_l1_hash<NT>(
contract_address, version, portal_contract_address, chain_id, new_messages[i]);
}
}
return formatted_msgs;
Expand Down Expand Up @@ -1120,8 +1116,16 @@ TEST(public_kernel_tests, circuit_outputs_should_be_correctly_populated_with_pre
expected_new_nullifiers,
public_inputs.end.new_nullifiers));

std::array<NT::fr, KERNEL_NEW_L2_TO_L1_MSGS_LENGTH> const expected_new_messages = new_l2_messages_from_message(
inputs.public_call.call_stack_item.public_inputs.new_l2_to_l1_msgs, contract_address, portal_contract_address);
// Reading the chain id and version from the tx context
fr const chain_id = inputs.previous_kernel.public_inputs.constants.tx_context.chain_id;
fr const version = inputs.previous_kernel.public_inputs.constants.tx_context.version;

std::array<NT::fr, KERNEL_NEW_L2_TO_L1_MSGS_LENGTH> const expected_new_messages =
new_l2_messages_from_message(inputs.public_call.call_stack_item.public_inputs.new_l2_to_l1_msgs,
contract_address,
portal_contract_address,
chain_id,
version);

ASSERT_TRUE(source_arrays_are_in_target(inputs.previous_kernel.public_inputs.end.new_l2_to_l1_msgs,
expected_new_messages,
Expand Down
13 changes: 5 additions & 8 deletions circuits/cpp/src/aztec3/circuits/kernel/public/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,14 +348,11 @@ void propagate_new_l2_to_l1_messages(Builder& builder,
std::array<NT::fr, NEW_L2_TO_L1_MSGS_LENGTH> new_l2_to_l1_msgs_to_insert{};
for (size_t i = 0; i < new_l2_to_l1_msgs.size(); ++i) {
if (!new_l2_to_l1_msgs[i].is_zero()) {
// @todo @lherskind chain-ids and rollup version id should be added here. right now, just hard coded.
// @todo @lherskind chain-id is hardcoded for foundry
const auto chain_id = fr(31337);
new_l2_to_l1_msgs_to_insert[i] = compute_l2_to_l1_hash<NT>(storage_contract_address,
fr(1), // rollup version id
portal_contract_address,
chain_id,
new_l2_to_l1_msgs[i]);
const auto chain_id = public_kernel_inputs.previous_kernel.public_inputs.constants.tx_context.chain_id;
const auto version = public_kernel_inputs.previous_kernel.public_inputs.constants.tx_context.version;

new_l2_to_l1_msgs_to_insert[i] = compute_l2_to_l1_hash<NT>(
storage_contract_address, version, portal_contract_address, chain_id, new_l2_to_l1_msgs[i]);
}
}
push_array_to_array(builder, new_l2_to_l1_msgs_to_insert, circuit_outputs.end.new_l2_to_l1_msgs);
Expand Down
37 changes: 28 additions & 9 deletions yarn-project/acir-simulator/src/public/executor.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
import { AztecAddress, CallContext, EthAddress, Fr, FunctionData, PrivateHistoricTreeRoots } from '@aztec/circuits.js';
import {
AztecAddress,
CallContext,
EthAddress,
Fr,
FunctionData,
GlobalVariables,
PrivateHistoricTreeRoots,
} from '@aztec/circuits.js';
import { padArrayEnd } from '@aztec/foundation/collection';
import { createDebugLogger } from '@aztec/foundation/log';
import { FunctionL2Logs } from '@aztec/types';
Expand Down Expand Up @@ -43,17 +51,18 @@ export class PublicExecutor {
/**
* Executes a public execution request.
* @param execution - The execution to run.
* @param globalVariables - The global variables to use.
* @returns The result of the run plus all nested runs.
*/
public async execute(execution: PublicExecution): Promise<PublicExecutionResult> {
public async execute(execution: PublicExecution, globalVariables: GlobalVariables): Promise<PublicExecutionResult> {
const selectorHex = execution.functionData.functionSelectorBuffer.toString('hex');
this.log(`Executing public external function ${execution.contractAddress.toString()}:${selectorHex}`);

const selector = execution.functionData.functionSelectorBuffer;
const acir = await this.contractsDb.getBytecode(execution.contractAddress, selector);
if (!acir) throw new Error(`Bytecode not found for ${execution.contractAddress.toString()}:${selectorHex}`);

const initialWitness = getInitialWitness(execution.args, execution.callContext, this.treeRoots);
const initialWitness = getInitialWitness(execution.args, execution.callContext, this.treeRoots, globalVariables);
const storageActions = new ContractStorageActionsCollector(this.stateDb, execution.contractAddress);
const newCommitments: Fr[] = [];
const newL2ToL1Messages: Fr[] = [];
Expand Down Expand Up @@ -125,6 +134,7 @@ export class PublicExecutor {
frToSelector(fromACVMField(functionSelector)),
args.map(f => fromACVMField(f)),
execution.callContext,
globalVariables,
);

nestedExecutions.push(childExecutionResult);
Expand Down Expand Up @@ -162,6 +172,7 @@ export class PublicExecutor {
targetFunctionSelector: Buffer,
targetArgs: Fr[],
callerContext: CallContext,
globalVariables: GlobalVariables,
) {
const portalAddress = (await this.contractsDb.getPortalContractAddress(targetContractAddress)) ?? EthAddress.ZERO;
const functionData = new FunctionData(targetFunctionSelector, false, false);
Expand All @@ -182,21 +193,24 @@ export class PublicExecutor {
callContext,
};

return this.execute(nestedExecution);
return this.execute(nestedExecution, globalVariables);
}
}

/**
* Generates the initial witness for a public function.
* @param args - The arguments to the function.
* @param callContext - The call context of the function.
* @param historicTreeRoots - The historic tree roots.
* @param globalVariables - The global variables.
* @param witnessStartIndex - The index where to start inserting the parameters.
* @returns The initial witness.
*/
function getInitialWitness(
args: Fr[],
callContext: CallContext,
commitmentTreeRoots: PrivateHistoricTreeRoots,
historicTreeRoots: PrivateHistoricTreeRoots,
globalVariables: GlobalVariables,
witnessStartIndex = 1,
) {
return toACVMWitness(witnessStartIndex, [
Expand All @@ -207,10 +221,15 @@ function getInitialWitness(
callContext.isStaticCall,
callContext.isContractDeployment,

commitmentTreeRoots.privateDataTreeRoot,
commitmentTreeRoots.nullifierTreeRoot,
commitmentTreeRoots.contractTreeRoot,
commitmentTreeRoots.l1ToL2MessagesTreeRoot,
historicTreeRoots.privateDataTreeRoot,
historicTreeRoots.nullifierTreeRoot,
historicTreeRoots.contractTreeRoot,
historicTreeRoots.l1ToL2MessagesTreeRoot,

globalVariables.chainId,
globalVariables.version,
globalVariables.blockNumber,
globalVariables.timestamp,

...args,
]);
Expand Down
30 changes: 20 additions & 10 deletions yarn-project/acir-simulator/src/public/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
CircuitsWasm,
PrivateHistoricTreeRoots,
L1_TO_L2_MESSAGES_TREE_HEIGHT,
GlobalVariables,
} from '@aztec/circuits.js';
import { AztecAddress } from '@aztec/foundation/aztec-address';
import { EthAddress } from '@aztec/foundation/eth-address';
Expand Down Expand Up @@ -84,7 +85,7 @@ describe('ACIR public execution simulator', () => {
publicState.storageRead.mockResolvedValue(previousBalance);

const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

const expectedBalance = new Fr(160n);
expect(result.returnValues).toEqual([expectedBalance]);
Expand Down Expand Up @@ -151,7 +152,7 @@ describe('ACIR public execution simulator', () => {
const recipientBalance = new Fr(20n);
mockStore(senderBalance, recipientBalance);

const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

const expectedRecipientBalance = new Fr(160n);
const expectedSenderBalance = new Fr(60n);
Expand All @@ -174,7 +175,7 @@ describe('ACIR public execution simulator', () => {
const recipientBalance = new Fr(20n);
mockStore(senderBalance, recipientBalance);

const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

expect(result.returnValues).toEqual([recipientBalance]);

Expand Down Expand Up @@ -228,9 +229,18 @@ describe('ACIR public execution simulator', () => {
});

const execution: PublicExecution = { contractAddress: parentContractAddress, functionData, args, callContext };
const result = await executor.execute(execution);

expect(result.returnValues).toEqual([new Fr(42n + initialValue)]);
const globalVariables = new GlobalVariables(new Fr(69), new Fr(420), new Fr(1), new Fr(7));
const result = await executor.execute(execution, globalVariables);

expect(result.returnValues).toEqual([
new Fr(
initialValue +
globalVariables.chainId.value +
globalVariables.version.value +
globalVariables.blockNumber.value +
globalVariables.timestamp.value,
),
]);
});
});

Expand Down Expand Up @@ -265,7 +275,7 @@ describe('ACIR public execution simulator', () => {
publicContracts.getBytecode.mockResolvedValue(Buffer.from(publicToPrivateAbi.bytecode, 'hex'));

const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

// Assert the commitment was created
expect(result.newCommitments.length).toEqual(1);
Expand Down Expand Up @@ -295,7 +305,7 @@ describe('ACIR public execution simulator', () => {
publicContracts.getBytecode.mockResolvedValue(Buffer.from(createL2ToL1MessagePublicAbi.bytecode, 'hex'));

const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

// Assert the l2 to l1 message was created
expect(result.newL2ToL1Messages.length).toEqual(1);
Expand Down Expand Up @@ -359,7 +369,7 @@ describe('ACIR public execution simulator', () => {
});

const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

expect(result.newNullifiers.length).toEqual(1);
});
Expand All @@ -383,7 +393,7 @@ describe('ACIR public execution simulator', () => {
publicContracts.getBytecode.mockResolvedValue(Buffer.from(createNullifierPublicAbi.bytecode, 'hex'));

const execution: PublicExecution = { contractAddress, functionData, args, callContext };
const result = await executor.execute(execution);
const result = await executor.execute(execution, GlobalVariables.empty());

// Assert the l2 to l1 message was created
expect(result.newNullifiers.length).toEqual(1);
Expand Down
4 changes: 4 additions & 0 deletions yarn-project/circuits.js/src/structs/global_variables.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ export class GlobalVariables {
return new GlobalVariables(...GlobalVariables.getFields(fields));
}

static empty(): GlobalVariables {
return new GlobalVariables(Fr.zero(), Fr.zero(), Fr.zero(), Fr.zero());
}

static fromBuffer(buffer: Buffer | BufferReader): GlobalVariables {
const reader = BufferReader.asReader(buffer);
return new GlobalVariables(reader.readFr(), reader.readFr(), reader.readFr(), reader.readFr());
Expand Down
22 changes: 18 additions & 4 deletions yarn-project/end-to-end/src/integration_l1_publisher.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { createMemDown, getConfigEnvVars } from '@aztec/aztec-node';
import {
AztecAddress,
GlobalVariables,
KERNEL_NEW_COMMITMENTS_LENGTH,
KERNEL_NEW_L2_TO_L1_MSGS_LENGTH,
KERNEL_NEW_NULLIFIERS_LENGTH,
Expand Down Expand Up @@ -130,7 +131,7 @@ describe('L1Publisher integration', () => {
const vks = getVerificationKeys();
const simulator = await WasmRollupCircuitSimulator.new();
const prover = new EmptyRollupProver();
builder = new SoloBlockBuilder(builderDb, vks, simulator, prover, new Fr(config.chainId), new Fr(config.version));
builder = new SoloBlockBuilder(builderDb, vks, simulator, prover);

l2Proof = Buffer.alloc(0);

Expand Down Expand Up @@ -182,7 +183,7 @@ describe('L1Publisher integration', () => {
};

const sendToL2 = async (content: Fr, recipientAddress: AztecAddress) => {
// @todo @LHerskind version hardcoded here
// @todo @LHerskind version hardcoded here (update to bigint or field)
const recipient = new L2Actor(recipientAddress, 1);
// Note: using max deadline
const deadline = 2 ** 32 - 1;
Expand Down Expand Up @@ -266,7 +267,14 @@ describe('L1Publisher integration', () => {
await makeBloatedProcessedTx(128 * i + 96),
await makeBloatedProcessedTx(128 * i + 128),
];
const [block] = await builder.buildL2Block(1 + i, txs, l1ToL2Messages);
// @todo @LHerskind fix time.
const globalVariables = new GlobalVariables(
new Fr(config.chainId),
new Fr(config.version),
new Fr(1 + i),
Fr.ZERO,
);
const [block] = await builder.buildL2Block(globalVariables, txs, l1ToL2Messages);

// check that values are in the inbox
for (let j = 0; j < l1ToL2Messages.length; j++) {
Expand Down Expand Up @@ -351,7 +359,13 @@ describe('L1Publisher integration', () => {
await makeEmptyProcessedTx(),
await makeEmptyProcessedTx(),
];
const [block] = await builder.buildL2Block(1 + i, txs, l1ToL2Messages);
const globalVariables = new GlobalVariables(
new Fr(config.chainId),
new Fr(config.version),
new Fr(1 + i),
Fr.ZERO,
);
const [block] = await builder.buildL2Block(globalVariables, txs, l1ToL2Messages);

await publisher.processL2Block(block);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ contract Child {
// @param _padding: Nested public functions always get called with MAX_ARGS, since we don't have the ABI available
// during execution time to know how many args are expected, hence the _padding argument. We should
// be able to remove it when we migrate to brillig.
open fn pubValue(_inputs: PublicContextInputs, base_value: Field, _padding: [Field; abi::MAX_ARGS - 1]) -> pub Field {
base_value + 42
open fn pubValue(inputs: PublicContextInputs, base_value: Field, _padding: [Field; abi::MAX_ARGS - 1]) -> pub Field {
base_value + inputs.public_global_variables.chain_id + inputs.public_global_variables.version + inputs.public_global_variables.block_number + inputs.public_global_variables.timestamp
}

// Increments `current_value` by `new_value` and returns `new_value` + 1.
Expand Down
15 changes: 15 additions & 0 deletions yarn-project/noir-contracts/src/contracts/noir-aztec3/src/abi.nr
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ struct PrivateContextInputs {
struct PublicContextInputs {
call_context: CallContext,
roots: CommitmentTreesRoots,

public_global_variables: PublicGlobalVariables,
}

global CALL_CONTEXT_SIZE: comptime Field = 6;
Expand Down Expand Up @@ -115,6 +117,19 @@ impl PrivateGlobalVariables {
}
}

struct PublicGlobalVariables {
chain_id: Field,
version: Field,
block_number: Field,
timestamp: Field,
}

impl PublicGlobalVariables {
fn serialize(self) -> [Field; 4] {
[self.chain_id, self.version, self.block_number, self.timestamp]
}
}

global FUNCTION_DATA_SIZE: comptime Field = 3;

struct FunctionData {
Expand Down
Loading

0 comments on commit 145e34e

Please sign in to comment.