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: Test against Sepolia #8176

Merged
merged 16 commits into from
Aug 26, 2024
81 changes: 81 additions & 0 deletions .github/workflows/sepolia-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
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
Comment on lines +16 to +20
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we uncomment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not just yet. Once I have seen a successful Anvil run I will uncomment, and will have to load some ETH.


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 }}"
ref: pw/new-test-2
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does the cron run if it is defined in a PR? If not, let's just change this to env.GIT_COMMIT now, since it won't need this branch, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good catch, this was a oversight.


- 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 }}
Copy link
Collaborator

Choose a reason for hiding this comment

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

e2e also pulls aztecprotocol/aztec in addition to aztecprotocol/end-to-end. Is it needed here?

# 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 @@ -184,6 +184,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';
Copy link
Collaborator

Choose a reason for hiding this comment

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

Where is this getting set for the nightly run?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Currently it's not as leaving everything defaulted causes the test to run against a local Anvil. Once I have seen it run successfully against Anvil I will put in another PR to set these values in the sepolia-test.yml


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, salt: 1 },
Copy link
Collaborator

Choose a reason for hiding this comment

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

The salt: 1 means that we'll consistently reuse the same set of L1 rollup contracts, which means each test run won't be independent from the previous one. Are we sure about this? I'm worried that we could have breaking changes that screw up the node or prover-node syncing if we keep using the same rollup.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Ahh yeah. You make a good point. I was hoping to save on ETH by not re-deploying everytime but that's probably not desirable.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I had the same concern.

{},
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 () => {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I've feel we've written variations of this test a dozen times already

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Possibly. The difference here really is that this uses the EasyPrivateTokenContract which isn't used anywhere else. It has no public bytecode that needs deploying. Once we are able to deploy public bytecode, I'll probably just use the standard token contract and an existing test.

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
Loading