Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: global variables in public noir #917

Merged
merged 14 commits into from
Jul 3, 2023
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,
Copy link
Contributor

Choose a reason for hiding this comment

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

nice!

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 {
Copy link
Member

Choose a reason for hiding this comment

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

ik this is already merged, but should we rename GlobalVariables to GlobalContextVariables?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

GlobalBlockContextVariables or maybe GlobalBlockVariables? Context is not super descriptive around what context it is.

Copy link
Contributor

Choose a reason for hiding this comment

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

"Global" and "Block" seem opposite? Global would mean always true whereas block variables to me would be things that change per block.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

They are "global" from the POV of noir similarly to how they are in solidity. But some of them change depending on the individual block, so naming might be a little weird.

Copy link
Member

Choose a reason for hiding this comment

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

I like global block variables

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Will address in a separate pr as it is used a lot of places and don't want to pollute this one.

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.
Copy link
Contributor

Choose a reason for hiding this comment

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

this stale?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No, its part of a separate pr that is to fix #830, to be fixed in #948

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 {
Copy link
Member

Choose a reason for hiding this comment

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

maybe we should make this a seperate test/

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Were thinking that it was fine as this since we were anyway just returning random values, so might as well return the values from the struct.

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