Skip to content

Commit

Permalink
feat: Test against Sepolia (#8176)
Browse files Browse the repository at this point in the history
This PR creates an e2e test that can be used against Sepolia. It also
sets up an automated job for the test.
  • Loading branch information
PhilWindle authored Aug 26, 2024
1 parent cd5d2df commit 758c723
Show file tree
Hide file tree
Showing 7 changed files with 237 additions and 18 deletions.
80 changes: 80 additions & 0 deletions .github/workflows/sepolia-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Run public testnet test
on:
schedule:
- cron: '00 08 * * 1-5'

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
GIT_COMMIT: ${{ github.sha }}
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}

# Uncomment the following to run against Sepolia
# SEQ_PUBLISHER_PRIVATE_KEY: ${{ secrets.SEPOLIA_SEQ_PRIVATE_KEY }}
# PROVER_PUBLISHER_PRIVATE_KEY: ${{ secrets.SEPOLIA_PROVER_PRIVATE_KEY }}
# ETHEREUM_HOST: 'https://sepolia.infura.io/v3/${{ secrets.SEPOLIA_API_KEY }}';
# L1_CHAIN_ID: '11155111' # Sepolia Chain ID

jobs:
setup:
uses: ./.github/workflows/setup-runner.yml
with:
username: ${{ github.event.pull_request.user.login || github.actor }}
runner_type: builder-x86
secrets: inherit

build:
needs: setup
runs-on: ${{ github.event.pull_request.user.login || github.actor }}-x86
outputs:
e2e_list: ${{ steps.e2e_list.outputs.list }}
steps:
- uses: actions/checkout@v4
with:
ref: "${{ env.GIT_COMMIT }}"

- uses: ./.github/ci-setup-action
with:
concurrency_key: build-test-artifacts-${{ github.actor }}

- name: "Build E2E Image"
timeout-minutes: 40
run: |
earthly-ci ./yarn-project+export-e2e-test-images
- name: Create list of devnet end-to-end jobs
id: e2e_list
run: echo "list=$(earthly ls ./yarn-project/end-to-end | grep 'public_testnet' | sed 's/+//' | jq -R . | jq -cs .)" >> $GITHUB_OUTPUT

e2e:
needs: build
runs-on: ubuntu-20.04
strategy:
fail-fast: false
matrix:
test: ${{ fromJson( needs.build.outputs.e2e_list )}}
steps:
- uses: actions/checkout@v4
with: { ref: "${{ env.GIT_COMMIT }}" }
- uses: ./.github/ci-setup-action
- name: Setup and Test
timeout-minutes: 40
uses: ./.github/ensure-tester-with-images
with:
# big machine since we're doing proving
runner_type: "64core-tester-x86"
builder_type: builder-x86
# these are copied to the tester and expected by the earthly command below
# if they fail to copy, it will try to build them on the tester and fail
builder_images_to_copy: aztecprotocol/end-to-end:${{ env.GIT_COMMIT }}
# command to produce the images in case they don't exist
builder_command: scripts/earthly-ci ./yarn-project+export-e2e-test-images
run: |
set -eux
cd ./yarn-project/end-to-end/
export FORCE_COLOR=1
../../scripts/earthly-ci -P --no-output +${{ matrix.test }}
4 changes: 3 additions & 1 deletion yarn-project/accounts/src/testing/create_account.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { type WaitOpts } from '@aztec/aztec.js';
import { type AccountWalletWithSecretKey } from '@aztec/aztec.js/wallet';
import { type PXE } from '@aztec/circuit-types';
import { Fr, deriveSigningKey } from '@aztec/circuits.js';
Expand Down Expand Up @@ -27,6 +28,7 @@ export async function createAccounts(
pxe: PXE,
numberOfAccounts = 1,
secrets: Fr[] = [],
waitOpts: WaitOpts = { interval: 0.1 },
): Promise<AccountWalletWithSecretKey[]> {
const accounts = [];

Expand Down Expand Up @@ -56,6 +58,6 @@ export async function createAccounts(

// Send them and await them to be mined
const txs = await Promise.all(accounts.map(account => account.deploy()));
await Promise.all(txs.map(tx => tx.wait({ interval: 0.1 })));
await Promise.all(txs.map(tx => tx.wait(waitOpts)));
return Promise.all(accounts.map(account => account.getWallet()));
}
3 changes: 3 additions & 0 deletions yarn-project/end-to-end/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,9 @@ e2e-static-calls:
e2e-token-contract:
DO +E2E_TEST --test=./src/e2e_token_contract

e2e-public-testnet:
DO +E2E_TEST --test=./src/public-testnet

flakey-e2e-tests:
DO +E2E_TEST --test=./src/flakey --allow_fail=true

Expand Down
8 changes: 4 additions & 4 deletions yarn-project/end-to-end/src/fixtures/snapshot_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ async function teardown(context: SubsystemsContext | undefined) {

export async function createAndSyncProverNode(
rollupAddress: EthAddress,
proverNodePrivateKey: Buffer,
proverNodePrivateKey: `0x${string}`,
aztecNodeConfig: AztecNodeConfig,
aztecNode: AztecNode,
) {
Expand All @@ -255,7 +255,7 @@ export async function createAndSyncProverNode(
proverId: new Fr(42),
realProofs: false,
proverAgentConcurrency: 2,
publisherPrivateKey: `0x${proverNodePrivateKey.toString('hex')}`,
publisherPrivateKey: proverNodePrivateKey,
proverNodeMaxPendingJobs: 100,
};
const proverNode = await createProverNode(proverConfig, {
Expand Down Expand Up @@ -332,7 +332,7 @@ async function setupFromFresh(
logger.verbose('Creating and syncing a simulated prover node...');
const proverNode = await createAndSyncProverNode(
deployL1ContractsValues.l1ContractAddresses.rollupAddress,
proverNodePrivateKey!,
`0x${proverNodePrivateKey!.toString('hex')}`,
aztecNodeConfig,
aztecNode,
);
Expand Down Expand Up @@ -416,7 +416,7 @@ async function setupFromState(statePath: string, logger: Logger): Promise<Subsys
logger.verbose('Creating and syncing a simulated prover node...');
const proverNode = await createAndSyncProverNode(
aztecNodeConfig.l1Contracts.rollupAddress,
proverNodePrivateKey!,
`0x${proverNodePrivateKey!}`,
aztecNodeConfig,
aztecNode,
);
Expand Down
34 changes: 26 additions & 8 deletions yarn-project/end-to-end/src/fixtures/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
computeContractAddressFromInstance,
getContractClassFromArtifact,
} from '@aztec/circuits.js';
import { NULL_KEY } from '@aztec/ethereum';
import { bufferAsFields } from '@aztec/foundation/abi';
import { makeBackoff, retry, retryUntil } from '@aztec/foundation/retry';
import {
Expand Down Expand Up @@ -78,7 +79,7 @@ import {
createWalletClient,
http,
} from 'viem';
import { mnemonicToAccount } from 'viem/accounts';
import { mnemonicToAccount, privateKeyToAccount } from 'viem/accounts';
import { foundry } from 'viem/chains';

import { MNEMONIC } from './fixtures.js';
Expand Down Expand Up @@ -113,6 +114,7 @@ export const setupL1Contracts = async (
account: HDAccount | PrivateKeyAccount,
logger: DebugLogger,
args: { salt?: number } = {},
chain: Chain = foundry,
) => {
const l1Artifacts: L1ContractArtifactsForDeployment = {
registry: {
Expand Down Expand Up @@ -145,7 +147,7 @@ export const setupL1Contracts = async (
},
};

const l1Data = await deployL1Contracts(l1RpcUrl, account, foundry, logger, l1Artifacts, {
const l1Data = await deployL1Contracts(l1RpcUrl, account, chain, logger, l1Artifacts, {
l2FeeJuiceAddress: FeeJuiceAddress,
vkTreeRoot: getVKTreeRoot(),
salt: args.salt,
Expand Down Expand Up @@ -291,6 +293,8 @@ type SetupOptions = {
deployL1ContractsValues?: DeployL1Contracts;
/** Whether to skip deployment of protocol contracts (auth registry, etc) */
skipProtocolContracts?: boolean;
/** Salt to use in L1 contract deployment */
salt?: number;
} & Partial<AztecNodeConfig>;

/** Context for an end-to-end test as returned by the `setup` function */
Expand Down Expand Up @@ -329,13 +333,17 @@ export async function setup(
pxeOpts: Partial<PXEServiceConfig> = {},
enableGas = false,
enableValidators = false,
chain: Chain = foundry,
): Promise<EndToEndContext> {
const config = { ...getConfigEnvVars(), ...opts };
const logger = getLogger();

let anvil: Anvil | undefined;

if (!config.l1RpcUrl) {
if (chain.id != foundry.id) {
throw new Error(`No ETHEREUM_HOST set but non anvil chain requested`);
}
if (PXE_URL) {
throw new Error(
`PXE_URL provided but no ETHEREUM_HOST set. Refusing to run, please set both variables so tests can deploy L1 contracts to the same Anvil instance`,
Expand All @@ -359,19 +367,29 @@ export async function setup(
await ethCheatCodes.loadChainState(opts.stateLoad);
}

const publisherHdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 });
const publisherPrivKeyRaw = publisherHdAccount.getHdKey().privateKey;
const publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw);
let publisherPrivKey = undefined;
let publisherHdAccount = undefined;

if (config.publisherPrivateKey && config.publisherPrivateKey != NULL_KEY) {
publisherHdAccount = privateKeyToAccount(config.publisherPrivateKey);
} else if (!MNEMONIC) {
throw new Error(`Mnemonic not provided and no publisher private key`);
} else {
publisherHdAccount = mnemonicToAccount(MNEMONIC, { addressIndex: 0 });
const publisherPrivKeyRaw = publisherHdAccount.getHdKey().privateKey;
publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw);
config.publisherPrivateKey = `0x${publisherPrivKey!.toString('hex')}`;
}

if (PXE_URL) {
// we are setting up against a remote environment, l1 contracts are assumed to already be deployed
return await setupWithRemoteEnvironment(publisherHdAccount, config, logger, numberOfAccounts, enableGas);
return await setupWithRemoteEnvironment(publisherHdAccount!, config, logger, numberOfAccounts, enableGas);
}

const deployL1ContractsValues =
opts.deployL1ContractsValues ?? (await setupL1Contracts(config.l1RpcUrl, publisherHdAccount, logger));
opts.deployL1ContractsValues ??
(await setupL1Contracts(config.l1RpcUrl, publisherHdAccount!, logger, { salt: opts.salt }, chain));

config.publisherPrivateKey = `0x${publisherPrivKey!.toString('hex')}`;
config.l1Contracts = deployL1ContractsValues.l1ContractAddresses;

// Run the test with validators enabled
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import { createAccounts } from '@aztec/accounts/testing';
import { type AztecNodeConfig } from '@aztec/aztec-node';
import { type AztecNode, type DebugLogger, Fr, type PXE } from '@aztec/aztec.js';
import { NULL_KEY } from '@aztec/ethereum';
import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js';
import { type ProverNode, type ProverNodeConfig, getProverNodeConfigFromEnv } from '@aztec/prover-node';

import { foundry, sepolia } from 'viem/chains';

import { createAndSyncProverNode } from '../fixtures/snapshot_manager.js';
import { getPrivateKeyFromIndex, setup } from '../fixtures/utils.js';

// process.env.SEQ_PUBLISHER_PRIVATE_KEY = '<PRIVATE_KEY_WITH_SEPOLIA_ETH>';
// process.env.PROVER_PUBLISHER_PRIVATE_KEY = '<PRIVATE_KEY_WITH_SEPOLIA_ETH>';
// process.env.ETHEREUM_HOST= 'https://sepolia.infura.io/v3/<API_KEY>';
// process.env.L1_CHAIN_ID = '11155111';

describe(`deploys and transfers a private only token`, () => {
let secretKey1: Fr;
let secretKey2: Fr;
let proverConfig: ProverNodeConfig;
let config: AztecNodeConfig;
let aztecNode: AztecNode;
let proverNode: ProverNode;

let pxe: PXE;
let logger: DebugLogger;
let teardown: () => Promise<void>;

beforeEach(async () => {
const chainId = !process.env.L1_CHAIN_ID ? foundry.id : +process.env.L1_CHAIN_ID;
const chain = chainId == sepolia.id ? sepolia : foundry; // Not the best way of doing this.
({ logger, pxe, teardown, config, aztecNode } = await setup(
0,
{ skipProtocolContracts: true, stateLoad: undefined },
{},
false,
false,
chain,
));
proverConfig = getProverNodeConfigFromEnv();
const proverNodePrivateKey = getPrivateKeyFromIndex(2);
proverConfig.publisherPrivateKey =
proverConfig.publisherPrivateKey === NULL_KEY
? `0x${proverNodePrivateKey?.toString('hex')}`
: proverConfig.publisherPrivateKey;

proverNode = await createAndSyncProverNode(
config.l1Contracts.rollupAddress,
proverConfig.publisherPrivateKey,
config,
aztecNode,
);
}, 600_000);

afterEach(async () => {
await proverNode.stop();
await teardown();
});

it('calls a private function', async () => {
const initialBalance = 100000000000n;
const transferValue = 5n;
secretKey1 = Fr.random();
secretKey2 = Fr.random();

logger.info(`Deploying accounts.`);

const accounts = await createAccounts(pxe, 2, [secretKey1, secretKey2], {
interval: 0.1,
proven: true,
provenTimeout: 600,
});

logger.info(`Accounts deployed, deploying token.`);

const [deployerWallet, recipientWallet] = accounts;

const token = await EasyPrivateTokenContract.deploy(
deployerWallet,
initialBalance,
deployerWallet.getAddress(),
deployerWallet.getAddress(),
)
.send({
universalDeploy: true,
skipPublicDeployment: true,
skipClassRegistration: true,
skipInitialization: false,
skipPublicSimulation: true,
})
.deployed({
proven: true,
provenTimeout: 600,
});

logger.info(`Performing transfer.`);

await token.methods
.transfer(transferValue, deployerWallet.getAddress(), recipientWallet.getAddress(), deployerWallet.getAddress())
.send()
.wait({ proven: true, provenTimeout: 600 });

logger.info(`Transfer completed`);

const balanceDeployer = await token.methods.get_balance(deployerWallet.getAddress()).simulate();
const balanceRecipient = await token.methods.get_balance(recipientWallet.getAddress()).simulate();

logger.info(`Deployer balance: ${balanceDeployer}, Recipient balance: ${balanceRecipient}`);

expect(balanceDeployer).toBe(initialBalance - transferValue);
expect(balanceRecipient).toBe(transferValue);
}, 600_000);
});
12 changes: 7 additions & 5 deletions yarn-project/ethereum/src/deploy_l1_contracts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,12 +161,14 @@ export const deployL1Contracts = async (
};
return await (await fetch(rpcUrl, content)).json();
};
const interval = 12;
const res = await rpcCall(rpcUrl, 'anvil_setBlockTimestampInterval', [interval]);
if (res.error) {
throw new Error(`Error setting block interval: ${res.error.message}`);
if (chain.id == foundry.id) {
const interval = 12;
const res = await rpcCall(rpcUrl, 'anvil_setBlockTimestampInterval', [interval]);
if (res.error) {
throw new Error(`Error setting block interval: ${res.error.message}`);
}
logger.info(`Set block interval to ${interval}`);
}
logger.info(`Set block interval to ${interval}`);

logger.info(`Deploying contracts from ${account.address.toString()}...`);

Expand Down

0 comments on commit 758c723

Please sign in to comment.