From 7298fdca0f42d5ea5ef4ee641036bb0452453f0d Mon Sep 17 00:00:00 2001 From: Mitch Date: Sat, 1 Feb 2025 16:56:57 -0500 Subject: [PATCH] add test to get error codes out. --- .../scripts/native-network/test-transfer.sh | 1 + .../composed/integration_l1_publisher.test.ts | 30 +++- .../src/e2e_sequencer/gov_proposal.test.ts | 2 +- .../ethereum/src/contracts/forwarder.test.ts | 146 ++++++++++++++++++ .../ethereum/src/contracts/forwarder.ts | 2 +- yarn-project/ethereum/src/l1_tx_utils.ts | 2 - .../src/publisher/sequencer-publisher.ts | 3 +- 7 files changed, 175 insertions(+), 11 deletions(-) create mode 100644 yarn-project/ethereum/src/contracts/forwarder.test.ts diff --git a/yarn-project/end-to-end/scripts/native-network/test-transfer.sh b/yarn-project/end-to-end/scripts/native-network/test-transfer.sh index a58483c3fc06..1b91130664b4 100755 --- a/yarn-project/end-to-end/scripts/native-network/test-transfer.sh +++ b/yarn-project/end-to-end/scripts/native-network/test-transfer.sh @@ -9,6 +9,7 @@ SCRIPT_NAME=$(basename "$0" .sh) exec > >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log") 2> >(tee -a "$(dirname $0)/logs/${SCRIPT_NAME}.log" >&2) export BOOTNODE_URL=${BOOTNODE_URL:-http://127.0.0.1:8080} +export NODE_URL=${NODE_URL:-${BOOTNODE_URL:-http://127.0.0.1:8080}} export PXE_URL=${PXE_URL:-http://127.0.0.1:8079} export ETHEREUM_HOST=${ETHEREUM_HOST:-http://127.0.0.1:8545} export K8S=${K8S:-false} diff --git a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts index 918a81fcdf71..1045c5737812 100644 --- a/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts +++ b/yarn-project/end-to-end/src/composed/integration_l1_publisher.test.ts @@ -583,26 +583,44 @@ describe('L1Publisher integration', () => { await expect(publisher.enqueueProposeL2Block(block)).resolves.toEqual(true); await expect(publisher.sendRequests()).resolves.toMatchObject({ - errorMsg: expect.stringContaining('Rollup__InvalidBlobHash'), + errorMsg: expect.stringContaining('Rollup__InvalidInHash'), }); // Test for both calls // NOTE: First error is from the simulate fn, which isn't supported by anvil - expect(loggerErrorSpy).toHaveBeenCalledTimes(2); - - expect(loggerErrorSpy).toHaveBeenNthCalledWith(1, 'Bundled [propose] transaction [failed]'); + expect(loggerErrorSpy).toHaveBeenCalledTimes(3); + expect(loggerErrorSpy).toHaveBeenNthCalledWith( + 1, + 'Forwarder transaction failed', + undefined, + expect.objectContaining({ + receipt: expect.objectContaining({ + type: 'eip4844', + blockHash: expect.any(String), + blockNumber: expect.any(BigInt), + transactionHash: expect.any(String), + }), + }), + ); expect(loggerErrorSpy).toHaveBeenNthCalledWith( 2, + expect.stringContaining('Bundled [propose] transaction [failed]'), + ); + + expect(loggerErrorSpy).toHaveBeenNthCalledWith( + 3, expect.stringMatching( - /^Rollup process tx reverted\. The contract function "forward" reverted\. Error: Rollup__InvalidBlobHash/i, + /^Rollup process tx reverted\. The contract function "forward" reverted\. Error: Rollup__InvalidInHash/i, ), undefined, expect.objectContaining({ - blockHash: expect.any(String), + blockHash: expect.any(Fr), blockNumber: expect.any(Number), slotNumber: expect.any(BigInt), txHash: expect.any(String), + txCount: expect.any(Number), + blockTimestamp: expect.any(Number), }), ); }); diff --git a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts index 204468aa36ba..5240ae926aa1 100644 --- a/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts +++ b/yarn-project/end-to-end/src/e2e_sequencer/gov_proposal.test.ts @@ -94,6 +94,6 @@ describe('e2e_gov_proposal', () => { ); expect(votes).toEqual(roundDuration); }, - 5 * 60000, + 1000 * 60 * 5, ); }); diff --git a/yarn-project/ethereum/src/contracts/forwarder.test.ts b/yarn-project/ethereum/src/contracts/forwarder.test.ts new file mode 100644 index 000000000000..87aa602f0760 --- /dev/null +++ b/yarn-project/ethereum/src/contracts/forwarder.test.ts @@ -0,0 +1,146 @@ +import { AztecAddress } from '@aztec/foundation/aztec-address'; +import { EthAddress } from '@aztec/foundation/eth-address'; +import { Fr } from '@aztec/foundation/fields'; +import { type Logger, createLogger } from '@aztec/foundation/log'; +import { GovernanceProposerAbi } from '@aztec/l1-artifacts/GovernanceProposerAbi'; +import { TestERC20Abi } from '@aztec/l1-artifacts/TestERC20Abi'; +import { TestERC20Bytecode } from '@aztec/l1-artifacts/TestERC20Bytecode'; + +import { type Anvil } from '@viem/anvil'; +import { + type Chain, + type GetContractReturnType, + type HttpTransport, + type PublicClient, + encodeFunctionData, + getContract, +} from 'viem'; +import { type PrivateKeyAccount, privateKeyToAccount } from 'viem/accounts'; +import { foundry } from 'viem/chains'; + +import { DefaultL1ContractsConfig } from '../config.js'; +import { type L1Clients, createL1Clients, deployL1Contract, deployL1Contracts } from '../deploy_l1_contracts.js'; +import { L1TxUtils } from '../l1_tx_utils.js'; +import { startAnvil } from '../test/start_anvil.js'; +import { FormattedViemError } from '../utils.js'; +import { ForwarderContract } from './forwarder.js'; + +describe('Forwarder', () => { + let anvil: Anvil; + let rpcUrl: string; + let privateKey: PrivateKeyAccount; + let logger: Logger; + + let vkTreeRoot: Fr; + let protocolContractTreeRoot: Fr; + // let initialValidators: EthAddress[]; + let l2FeeJuiceAddress: AztecAddress; + let walletClient: L1Clients['walletClient']; + let publicClient: L1Clients['publicClient']; + let forwarder: ForwarderContract; + let l1TxUtils: L1TxUtils; + // let rollupAddress: EthAddress; + let govProposerAddress: EthAddress; + let tokenAddress: EthAddress; + let tokenContract: GetContractReturnType>; + beforeAll(async () => { + logger = createLogger('ethereum:test:forwarder'); + // this is the 6th address that gets funded by the junk mnemonic + privateKey = privateKeyToAccount('0x8b3a350cf5c34c9194ca85829a2df0ec3153be0318b5e2d3348e872092edffba'); + vkTreeRoot = Fr.random(); + protocolContractTreeRoot = Fr.random(); + // initialValidators = times(3, EthAddress.random); + l2FeeJuiceAddress = await AztecAddress.random(); + + ({ anvil, rpcUrl } = await startAnvil()); + + ({ walletClient, publicClient } = createL1Clients(rpcUrl, privateKey)); + + const deployed = await deployL1Contracts(rpcUrl, privateKey, foundry, logger, { + ...DefaultL1ContractsConfig, + salt: undefined, + vkTreeRoot, + protocolContractTreeRoot, + l2FeeJuiceAddress, + }); + + govProposerAddress = deployed.l1ContractAddresses.governanceProposerAddress; + // rollupAddress = deployed.l1ContractAddresses.rollupAddress; + + forwarder = await ForwarderContract.create( + privateKey.address, + walletClient, + publicClient, + logger, + deployed.l1ContractAddresses.rollupAddress.toString(), + ); + + l1TxUtils = new L1TxUtils(publicClient, walletClient, logger); + + const { address: erc20Address, txHash: erc20TxHash } = await deployL1Contract( + walletClient, + publicClient, + TestERC20Abi, + TestERC20Bytecode, + ['test', 'TST', privateKey.address], + '0x42', + undefined, + logger, + ); + expect(erc20TxHash).toBeDefined(); + await publicClient.waitForTransactionReceipt({ hash: erc20TxHash! }); + tokenAddress = erc20Address; + tokenContract = getContract({ + address: tokenAddress.toString(), + abi: TestERC20Abi, + client: publicClient, + }); + + const freeForAllHash = await tokenContract.write.setFreeForAll([true], { account: privateKey }); + await publicClient.waitForTransactionReceipt({ hash: freeForAllHash }); + + logger.info(`Token address: ${tokenAddress}`); + }); + + afterAll(async () => { + await anvil.stop(); + }); + + it('gets good error messages', async () => { + expect(forwarder).toBeDefined(); + const initialBalance = await tokenContract.read.balanceOf([privateKey.address]); + expect(initialBalance).toBe(0n); + const err = await forwarder + .forward( + [ + // This one passes + { + to: tokenAddress.toString(), + data: encodeFunctionData({ + abi: TestERC20Abi, + functionName: 'mint', + args: [privateKey.address, 100n], + }), + }, + + // This one fails + { + to: govProposerAddress.toString(), + data: encodeFunctionData({ + abi: GovernanceProposerAbi, + functionName: 'vote', + args: [EthAddress.random().toString()], + }), + }, + ], + l1TxUtils, + undefined, + undefined, + logger, + ) + .catch(err => err); + expect(err).toBeDefined(); + expect(err).toBeInstanceOf(FormattedViemError); + expect(err.message).toMatch(/GovernanceProposer__OnlyProposerCanVote/); + }); +}); diff --git a/yarn-project/ethereum/src/contracts/forwarder.ts b/yarn-project/ethereum/src/contracts/forwarder.ts index f10f40cc9cad..20f282596127 100644 --- a/yarn-project/ethereum/src/contracts/forwarder.ts +++ b/yarn-project/ethereum/src/contracts/forwarder.ts @@ -89,7 +89,7 @@ export class ForwarderContract { const stats = await l1TxUtils.getTransactionStats(receipt.transactionHash); return { receipt, gasPrice, stats }; } else { - logger.error('Forwarder transaction failed', { receipt }); + logger.error('Forwarder transaction failed', undefined, { receipt }); const args = { ...forwarderFunctionData, diff --git a/yarn-project/ethereum/src/l1_tx_utils.ts b/yarn-project/ethereum/src/l1_tx_utils.ts index 23fe03531768..a052c37a35f7 100644 --- a/yarn-project/ethereum/src/l1_tx_utils.ts +++ b/yarn-project/ethereum/src/l1_tx_utils.ts @@ -672,8 +672,6 @@ export class L1TxUtils { }[] = [], ) { try { - // NB: If this fn starts unexpectedly giving incorrect blob hash errors, it may be because the checkBlob - // bool is no longer at the slot below. To find the slot, run: forge inspect src/core/Rollup.sol:Rollup storage await this.publicClient.simulateContract({ ...args, account: this.walletClient.account, diff --git a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts index 9ab7eec37ab4..f0848d070689 100644 --- a/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts +++ b/yarn-project/sequencer-client/src/publisher/sequencer-publisher.ts @@ -636,6 +636,7 @@ export class SequencerPublisher { const kzg = Blob.getViemKzgInstance(); const { rollupData, simulationResult, blobEvaluationGas } = await this.prepareProposeTx(encodedData, timestamp); const startBlock = await this.l1TxUtils.getBlockNumber(); + const blockHash = await block.hash(); return this.addRequest({ action: 'propose', @@ -687,7 +688,7 @@ export class SequencerPublisher { this.log.error(`Rollup process tx reverted. ${errorMsg ?? 'No error message'}`, undefined, { ...block.getStats(), txHash: receipt.transactionHash, - blockHash: block.hash().toString(), + blockHash, slotNumber: block.header.globalVariables.slotNumber.toBigInt(), }); }