diff --git a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr index 564221b2e8b..05158e604d5 100644 --- a/yarn-project/noir-contracts/contracts/test_contract/src/main.nr +++ b/yarn-project/noir-contracts/contracts/test_contract/src/main.nr @@ -301,14 +301,27 @@ contract Test { storage.example_constant.initialize(&mut note, false); } - #[aztec(public)] - fn assert_coinbase(coinbase: EthAddress) { - assert(context.coinbase().eq(coinbase), "Invalid coinbase"); + #[aztec(private)] + fn assert_private_global_vars(chain_id: Field, version: Field) { + assert(context.chain_id() == chain_id, "Invalid chain id"); + assert(context.version() == version, "Invalid version"); } #[aztec(public)] - fn assert_fee_recipient(fee_recipient: AztecAddress) { - assert(context.fee_recipient().eq(fee_recipient), "Invalid fee recipient"); + fn assert_public_global_vars( + chain_id: Field, + version: Field, + block_number: Field, + timestamp: Field, + coinbase: EthAddress, + fee_recipient: AztecAddress + ) { + assert(context.chain_id() == chain_id, "Invalid chain id"); + assert(context.version() == version, "Invalid version"); + assert(context.block_number() == block_number, "Invalid block number"); + assert(context.timestamp() == timestamp, "Invalid timestamp"); + assert(context.coinbase() == coinbase, "Invalid coinbase"); + assert(context.fee_recipient() == fee_recipient, "Invalid fee recipient"); } unconstrained fn get_constant() -> pub Field { diff --git a/yarn-project/simulator/src/client/private_execution.test.ts b/yarn-project/simulator/src/client/private_execution.test.ts index 63ae8f7c410..2ea03803d01 100644 --- a/yarn-project/simulator/src/client/private_execution.test.ts +++ b/yarn-project/simulator/src/client/private_execution.test.ts @@ -1129,4 +1129,41 @@ describe('Private Execution test suite', () => { expect(result.returnValues).toEqual(portalContractAddress.toField().value); }); }); + + describe('Private global variables', () => { + let chainId: Fr; + let version: Fr; + let args: any[]; + let artifact: FunctionArtifact; + + beforeAll(() => { + chainId = Fr.random(); + version = Fr.random(); + args = [chainId, version]; + + artifact = getFunctionArtifact(TestContractArtifact, 'assert_private_global_vars'); + oracle.getFunctionArtifact.mockImplementation(() => Promise.resolve(artifact)); + }); + + it('Private global vars are correctly set', () => { + // Chain id and version set in tx context is the same as the ones we pass via args so this should not throw + expect(() => runSimulator({ artifact, msgSender: owner, args, txContext: { chainId, version } })).not.toThrow(); + }); + + it('Throws when chainId is incorrectly set', async () => { + // We set the chainId in the tx context to a different value than the one we pass via args so the simulator should throw + const unexpectedChainId = Fr.random(); + await expect( + runSimulator({ artifact, msgSender: owner, args, txContext: { chainId: unexpectedChainId, version } }), + ).rejects.toThrowError('Invalid chain id'); + }); + + it('Throws when version is incorrectly set', async () => { + // We set the version in the tx context to a different value than the one we pass via args so the simulator should throw + const unexpectedVersion = Fr.random(); + await expect( + runSimulator({ artifact, msgSender: owner, args, txContext: { chainId, version: unexpectedVersion } }), + ).rejects.toThrowError('Invalid version'); + }); + }); }); diff --git a/yarn-project/simulator/src/public/index.test.ts b/yarn-project/simulator/src/public/index.test.ts index c2c625dda91..1d1a6a7b2e0 100644 --- a/yarn-project/simulator/src/public/index.test.ts +++ b/yarn-project/simulator/src/public/index.test.ts @@ -231,11 +231,7 @@ describe('ACIR public execution simulator', () => { const initialValue = 3n; const functionData = new FunctionData(parentEntryPointFnSelector, isInternal ?? false, false, false); - const args = encodeArguments(parentEntryPointFn, [ - childContractAddress.toField(), - childValueFnSelector.toField(), - initialValue, - ]); + const args = encodeArguments(parentEntryPointFn, [childContractAddress, childValueFnSelector, initialValue]); const callContext = CallContext.from({ msgSender: AztecAddress.random(), @@ -311,7 +307,7 @@ describe('ACIR public execution simulator', () => { const msgSender = AztecAddress.random(); const secretHash = Fr.random(); - const args = encodeArguments(shieldArtifact, [msgSender.toField(), amount, secretHash, Fr.ZERO]); + const args = encodeArguments(shieldArtifact, [msgSender, amount, secretHash, Fr.ZERO]); const callContext = CallContext.from({ msgSender: msgSender, @@ -436,9 +432,9 @@ describe('ACIR public execution simulator', () => { const computeArgs = () => encodeArguments(mintPublicArtifact, [ - tokenRecipient.toField(), + tokenRecipient, bridgedAmount, - canceller.toField(), + canceller, messageKey ?? preimage.hash(), secret, ]); @@ -636,6 +632,14 @@ describe('ACIR public execution simulator', () => { describe('Global variables in public context', () => { let contractAddress: AztecAddress; let callContext: CallContext; + let assertGlobalVarsArtifact: FunctionArtifact; + let functionData: FunctionData; + + const modifyGlobalVariables = (globalVariables: GlobalVariables, propertyIndex: number, value: any) => { + const globalVariablesFields = GlobalVariables.getFields(globalVariables) as unknown as any[]; + globalVariablesFields[propertyIndex] = value; + return GlobalVariables.fromFields(globalVariablesFields); + }; beforeAll(() => { contractAddress = AztecAddress.random(); @@ -649,92 +653,68 @@ describe('ACIR public execution simulator', () => { isStaticCall: false, startSideEffectCounter: 0, }); + assertGlobalVarsArtifact = TestContractArtifact.functions.find(f => f.name === 'assert_public_global_vars')!; + functionData = FunctionData.fromAbi(assertGlobalVarsArtifact); }); - const computeGlobalVariables = (coinbase: EthAddress, feeRecipient: AztecAddress) => - new GlobalVariables(new Fr(1), new Fr(2), Fr.ZERO, Fr.ZERO, coinbase, feeRecipient); - - describe('Coinbase', () => { - let expectedCoinbase: EthAddress; - let globalVariables: GlobalVariables; - let assertCoinbaseArtifact: FunctionArtifact; - let functionData: FunctionData; - - beforeAll(() => { - expectedCoinbase = EthAddress.random(); - globalVariables = computeGlobalVariables(expectedCoinbase, AztecAddress.random()); - - assertCoinbaseArtifact = TestContractArtifact.functions.find(f => f.name === 'assert_coinbase')!; - functionData = FunctionData.fromAbi(assertCoinbaseArtifact); - }); - - beforeEach(() => { - publicContracts.getBytecode.mockResolvedValue(Buffer.from(assertCoinbaseArtifact.bytecode, 'base64')); - }); - - it('Valid', () => { - // We set the coinbase function arg to one we set in global vars - const args = encodeArguments(assertCoinbaseArtifact, [expectedCoinbase]); - - const execution: PublicExecution = { contractAddress, functionData, args, callContext }; - executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); - - // Now we check that the global vars value was correctly set by checking if the assert func throws - expect(() => executor.simulate(execution, globalVariables)).not.toThrow(); - }); - - it('Invalid', async () => { - // We set the coinbase function arg to a random invalid value - const invalidCoinbase = EthAddress.random(); - const args = encodeArguments(assertCoinbaseArtifact, [invalidCoinbase]); - - const execution: PublicExecution = { contractAddress, functionData, args, callContext }; - executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); - - // Now we check that the function throws in case of invalid coinbase - await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid coinbase'); - }); + beforeEach(() => { + publicContracts.getBytecode.mockResolvedValue(Buffer.from(assertGlobalVarsArtifact.bytecode, 'base64')); }); - describe('Fee recipient', () => { - let expectedFeeRecipient: AztecAddress; - let globalVariables: GlobalVariables; - let assertFeeRecipientArtifact: FunctionArtifact; - let functionData: FunctionData; + // Note: Order here has to match the order of the properties in GlobalVariables.getFields(...) function. + const testCases = [ + { value: new Fr(1), invalidValue: Fr.random(), description: 'Chain ID' }, + { value: new Fr(1), invalidValue: Fr.random(), description: 'Version' }, + { value: new Fr(1), invalidValue: Fr.random(), description: 'Block number' }, + { value: new Fr(1), invalidValue: Fr.random(), description: 'Timestamp' }, + { value: EthAddress.random(), invalidValue: EthAddress.random(), description: 'Coinbase' }, + { + value: AztecAddress.random(), + invalidValue: AztecAddress.random(), + description: 'Fee recipient', + }, + ]; - beforeAll(() => { - expectedFeeRecipient = AztecAddress.random(); - globalVariables = computeGlobalVariables(EthAddress.random(), expectedFeeRecipient); + testCases.forEach(({ value, invalidValue, description }, i: number) => { + describe(`${description}`, () => { + let globalVariables: GlobalVariables; - assertFeeRecipientArtifact = TestContractArtifact.functions.find(f => f.name === 'assert_fee_recipient')!; - functionData = FunctionData.fromAbi(assertFeeRecipientArtifact); - }); - - beforeEach(() => { - publicContracts.getBytecode.mockResolvedValue(Buffer.from(assertFeeRecipientArtifact.bytecode, 'base64')); - }); + beforeAll(() => { + // We create a new global variables object containing non-zero value in place of the tested property + globalVariables = modifyGlobalVariables(GlobalVariables.empty(), i, value); + }); - it('Valid', () => { - // We set the fee recipient function arg to one we set in global vars - const args = encodeArguments(assertFeeRecipientArtifact, [expectedFeeRecipient]); + it('Valid', () => { + let args: Fr[]; + { + // We create the args by just serializing the reference global variables object + const rawArgs = GlobalVariables.getFields(globalVariables) as unknown as any[]; + args = encodeArguments(assertGlobalVarsArtifact, rawArgs); + } - const execution: PublicExecution = { contractAddress, functionData, args, callContext }; - executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); - // Now we check that the global vars value was correctly set by checking if the assert func throws - expect(() => executor.simulate(execution, globalVariables)).not.toThrow(); - }); + expect(() => executor.simulate(execution, globalVariables)).not.toThrow(); + }); - it('Invalid', async () => { - // We set the fee recipient function arg to a random invalid value - const invalidFeeRecipient = AztecAddress.random(); - const args = encodeArguments(assertFeeRecipientArtifact, [invalidFeeRecipient]); + it('Invalid', async () => { + let args: Fr[]; + { + // We create the args by modifying the global variables object to contain an invalid value in place of + // the tested property + const modifiedGlobalVariables = modifyGlobalVariables(globalVariables, i, invalidValue); + const rawArgs = GlobalVariables.getFields(modifiedGlobalVariables) as unknown as any[]; + args = encodeArguments(assertGlobalVarsArtifact, rawArgs); + } - const execution: PublicExecution = { contractAddress, functionData, args, callContext }; - executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); + const execution: PublicExecution = { contractAddress, functionData, args, callContext }; + executor = new PublicExecutor(publicState, publicContracts, commitmentsDb, header); - // Now we check that the function throws in case of invalid fee recipient - await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError('Invalid fee recipient'); + await expect(executor.simulate(execution, globalVariables)).rejects.toThrowError( + `Invalid ${description.toLowerCase()}`, + ); + }); }); }); });