Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions yarn-project/aztec-node/src/aztec-node/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export function getConfigEnvVars(): AztecNodeConfig {

type ConfigRequiredToBuildKeyStore = TxSenderConfig & SequencerClientConfig & SharedNodeConfig & ValidatorClientConfig;

function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore) {
function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore): KeyStore | undefined {
const validatorKeyStores: ValidatorKeyStore[] = [];

if (
Expand Down Expand Up @@ -122,7 +122,7 @@ function createKeyStoreFromWeb3Signer(config: ConfigRequiredToBuildKeyStore) {
return keyStore;
}

function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore) {
function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore): KeyStore | undefined {
const validatorKeyStores: ValidatorKeyStore[] = [];
const ethPrivateKeys = config.validatorPrivateKeys
? config.validatorPrivateKeys.getValue().map(x => ethPrivateKeySchema.parse(x))
Expand Down Expand Up @@ -156,7 +156,9 @@ function createKeyStoreFromPrivateKeys(config: ConfigRequiredToBuildKeyStore) {
return keyStore;
}

export function createKeyStoreForValidator(config: TxSenderConfig & SequencerClientConfig & SharedNodeConfig) {
export function createKeyStoreForValidator(
config: TxSenderConfig & SequencerClientConfig & SharedNodeConfig,
): KeyStore | undefined {
if (config.web3SignerUrl !== undefined && config.web3SignerUrl.length > 0) {
return createKeyStoreFromWeb3Signer(config);
}
Expand Down
2 changes: 2 additions & 0 deletions yarn-project/aztec-node/src/aztec-node/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ export class AztecNodeService implements AztecNode, AztecNodeAdmin, Traceable {
}
}

await keyStoreManager?.validateSigners();

// If we are a validator, verify our configuration before doing too much more.
if (!config.disableValidator) {
if (keyStoreManager === undefined) {
Expand Down
12 changes: 11 additions & 1 deletion yarn-project/end-to-end/bootstrap.sh
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,17 @@ function test_cmds {
echo "$hash:ONLY_TERM_PARENT=1 $run_test_script compose $test"
done

echo "$hash:ONLY_TERM_PARENT=1 $run_test_script web3signer src/composed/web3signer/integration_remote_signer.test.ts"
tests=(
src/composed/web3signer/*.test.ts
)
for test in "${tests[@]}"; do
# We must set ONLY_TERM_PARENT=1 to allow the script to fully control cleanup process.
echo "$hash:ONLY_TERM_PARENT=1 $run_test_script web3signer $test"
done

#echo "$hash:ONLY_TERM_PARENT=1 $run_test_script simple src/e2e_multi_validator/e2e_multi_validator_node.test.ts"
# echo "$hash:ONLY_TERM_PARENT=1 $run_test_script web3signer src/composed/web3signer/integration_remote_signer.test.ts"
#echo "$hash:ONLY_TERM_PARENT=1 $run_test_script web3signer src/e2e_multi_validator/e2e_multi_validator_node_key_store.test.ts"

# TODO(AD): figure out workaround for mainframe subnet exhaustion
if [ "$CI" -eq 1 ]; then
Expand Down
26 changes: 8 additions & 18 deletions yarn-project/end-to-end/scripts/web3signer/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,25 +1,15 @@
configs:
test_private_key:
content: |
type: file-raw
keyType: SECP256K1
privateKey: 0x1111111111111111111111111111111111111111111111111111111111111111

services:
web3signer:
image: consensys/web3signer:25.6.0
ports:
- "9000:9000"
configs:
- source: test_private_key
target: /keys/test_private_key.yaml
command:
- --http-listen-port=9000
- --http-host-allowlist=*
- --key-store-path=/keys
- --logging=ALL
- eth1
- --chain-id=31337
volumes:
- web3signer_keys:/keys

end-to-end:
image: aztecprotocol/build:3.0
Expand All @@ -29,6 +19,7 @@ services:
volumes:
- ../../../../:/root/aztec-packages
- ${HOME}/.bb-crs:/root/.bb-crs
- web3signer_keys:/keys
tmpfs:
- /tmp:rw,size=1g
- /tmp-jest:rw,size=512m
Expand All @@ -38,6 +29,7 @@ services:
LOG_LEVEL: ${LOG_LEVEL:-verbose}
L1_CHAIN_ID: 31337
WEB3_SIGNER_URL: http://web3signer:9000
WEB3_SIGNER_TEST_KEYSTORE_DIR: /keys
FORCE_COLOR: ${FORCE_COLOR:-1}
# Allow git usage despite different ownership. Relevant for script tests.
GIT_CONFIG_GLOBAL: /root/aztec-packages/build-images/src/home/.gitconfig
Expand All @@ -60,8 +52,6 @@ services:
# There's a lot of doubling of $'s to escape dockers string interpolation.
entrypoint: >
bash -c '
export TEST_PRIVATE_KEY=$(yq .privateKey /keys/test_private_key.yaml)

while ! nc -z web3signer 9000; do sleep 1; done;
setsid ./scripts/test_simple.sh ${TEST:-./src/e2e_deploy_contract.test.ts} &
pid=$$!
Expand All @@ -72,7 +62,7 @@ services:
'
depends_on:
- web3signer
configs:
# mount in the test as well in order to compare remote against local signer
- source: test_private_key
target: /keys/test_private_key.yaml

volumes:
# a shared volume so that tests can load up arbitrary private keys into web3signer
web3signer_keys: {}
Original file line number Diff line number Diff line change
Expand Up @@ -26,31 +26,33 @@ import { NodeKeystoreAdapter, ValidatorClient } from '@aztec/validator-client';

import { jest } from '@jest/globals';
import { mkdtemp, rmdir } from 'fs/promises';
import { createServer } from 'http';
import { tmpdir } from 'os';
import { join } from 'path';
import { privateKeyToAccount } from 'viem/accounts';

import { MNEMONIC } from '../fixtures/fixtures.js';
import { getPrivateKeyFromIndex, setup } from '../fixtures/utils.js';
import {
addressForPrivateKey,
createJSONRPCSigner,
createKeyFile1,
createKeyFile2,
createKeyFile3,
createKeyFile4,
createKeyFile5,
createKeyFile6,
} from './utils.js';
} from '../../e2e_multi_validator/utils.js';
import { MNEMONIC } from '../../fixtures/fixtures.js';
import { getPrivateKeyFromIndex, setup } from '../../fixtures/utils.js';
import {
createWeb3SignerKeystore,
getWeb3SignerTestKeystoreDir,
getWeb3SignerUrl,
refreshWeb3Signer,
} from '../../fixtures/web3signer.js';

const VALIDATOR_COUNT = 7;
const COMMITTEE_SIZE = VALIDATOR_COUNT;
const PUBLISHER_COUNT = 7;
const VALIDATOR_KEY_START_INDEX = 0;
const PUBLISHER_KEY_START_INDEX = VALIDATOR_COUNT + VALIDATOR_KEY_START_INDEX;
const SIGNER_URL_PORT = 15000;
const SIGNER_URL = `http://localhost:${SIGNER_URL_PORT}`;
const PROVER_PUBLISHER_INDEX = PUBLISHER_KEY_START_INDEX + PUBLISHER_COUNT;
const BLOCK_COUNT = 20;

Expand All @@ -66,6 +68,8 @@ const validators = Array.from(

async function createKeyFiles() {
const directory = await mkdtemp(join(tmpdir(), 'foo-'));
const web3signerDir = getWeb3SignerTestKeystoreDir();
const web3signerUrl = getWeb3SignerUrl();
const file1 = join(directory, 'keyfile1.json');
const file2 = join(directory, 'keyfile2.json');
const file3 = join(directory, 'keyfile3.json');
Expand Down Expand Up @@ -106,9 +110,11 @@ async function createKeyFiles() {
publishers[2].key,
publishers[3].key,
coinbaseAddresses[1],
SIGNER_URL,
getWeb3SignerUrl(),
feeRecipientAddresses[2],
);
await createWeb3SignerKeystore(web3signerDir, validators[2]);

await createKeyFile4(
file4,
addressForPrivateKey(validators[3]),
Expand All @@ -119,12 +125,19 @@ async function createKeyFiles() {
publishers[6].key,
coinbaseAddresses[3],
coinbaseAddresses[4],
SIGNER_URL,
web3signerUrl,
feeRecipientAddresses[3],
feeRecipientAddresses[4],
);
await createKeyFile5(file5, addressForPrivateKey(proverPrivateKey), SIGNER_URL);
await createWeb3SignerKeystore(web3signerDir, validators[3], validators[4]);

await createKeyFile5(file5, addressForPrivateKey(proverPrivateKey), web3signerUrl);
await createWeb3SignerKeystore(web3signerDir, proverPrivateKey);

await createKeyFile6(file6, MNEMONIC, 5, coinbaseAddresses[5], feeRecipientAddresses[5]);

await refreshWeb3Signer(web3signerUrl);

return directory;
}

Expand Down Expand Up @@ -157,11 +170,8 @@ describe('e2e_multi_validator_node', () => {
let sequencerClient: SequencerClient | undefined;
let publisherFactory: SequencerPublisherFactory;
let validatorClient: ValidatorClient;
let jsonRpcServer: ReturnType<typeof createServer> | null = null;
const artifact = StatefulTestContractArtifact;
const addressToPrivateKey = new Map<string, EthPrivateKey>();
const remoteSignerStats = new Map<string, number>();
const expectedRemoteSigners = new Set<string>();
const expectedCoinbaseAddresses = new Map<string, string>();
const expectedFeeRecipientAddresses = new Map<string, string>();
const expectedPublishers = new Map<string, string[]>();
Expand All @@ -185,11 +195,6 @@ describe('e2e_multi_validator_node', () => {
};
});

// These validators have remote signing configured
expectedRemoteSigners.add(validatorAddresses[2].toLowerCase());
expectedRemoteSigners.add(validatorAddresses[3].toLowerCase());
expectedRemoteSigners.add(validatorAddresses[4].toLowerCase());

// Setup expected coinbase and fee recipient values per validator
validatorAddresses.forEach((validatorAddress, i) => {
const coinbase = EthAddress.fromNumber(i + 1)
Expand Down Expand Up @@ -268,13 +273,6 @@ describe('e2e_multi_validator_node', () => {
addressToPrivateKey.set(account.toLowerCase(), pk);
}

// Create JSON RPC server for signing transactions
jsonRpcServer = createJSONRPCSigner(addressToPrivateKey, remoteSignerStats);
// Start server on the SIGNER_URL port
await new Promise<void>(resolve => {
jsonRpcServer!.listen(SIGNER_URL_PORT, resolve);
});

const { aztecSlotDuration: _aztecSlotDuration } = getL1ContractsConfigEnvVars();

({
Expand Down Expand Up @@ -322,13 +320,6 @@ describe('e2e_multi_validator_node', () => {
afterEach(async () => {
await teardown();
await rmdir(keyStoreDirectory, { recursive: true });

// Close JSON RPC server
if (jsonRpcServer) {
await new Promise<void>(resolve => {
jsonRpcServer!.close(() => resolve());
});
}
});

const sendTx = async (sender: AztecAddress, contractAddressSalt: Fr) => {
Expand Down Expand Up @@ -411,14 +402,6 @@ describe('e2e_multi_validator_node', () => {
}),
);

const currentBlockNumber = await aztecNode.getBlockNumber();

for (const expectedRemoteSigner of expectedRemoteSigners) {
const remoteSigner = remoteSignerStats.get(expectedRemoteSigner);
expect(remoteSigner).toBeDefined();
expect(remoteSigner).toBeGreaterThanOrEqual(currentBlockNumber);
}

for (const [proposer, coinbase] of requestedCoinbaseAddresses) {
const expectedCoinbase = expectedCoinbaseAddresses.get(proposer);
expect(expectedCoinbase).toBeDefined();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,40 +4,39 @@ import { LocalSigner, RemoteSigner } from '@aztec/node-keystore';

import { jest } from '@jest/globals';
import type { TransactionSerializable, TypedDataDefinition } from 'viem';
import { privateKeyToAddress } from 'viem/accounts';
import { generatePrivateKey, privateKeyToAddress } from 'viem/accounts';

const {
WEB3_SIGNER_URL = 'http://localhost:9000',
L1_CHAIN_ID = '31337',
TEST_PRIVATE_KEY = '0x1111111111111111111111111111111111111111111111111111111111111111',
} = process.env;
import {
createWeb3SignerKeystore,
getWeb3SignerTestKeystoreDir,
getWeb3SignerUrl,
refreshWeb3Signer,
} from '../../fixtures/web3signer.js';

const { L1_CHAIN_ID = '31337' } = process.env;

describe('RemoteSigner integration: Web3Signer (compose)', () => {
jest.setTimeout(180_000);

let chainId: number;
let web3SignerUrl: string;
let chainId: number;

let privateKey: Buffer32;
let address: EthAddress;

let remoteSigner: RemoteSigner;
let localSigner: LocalSigner;

beforeAll(() => {
if (!WEB3_SIGNER_URL) {
throw new Error('Need to set WEB3_SIGNER_URL');
}
beforeAll(async () => {
web3SignerUrl = getWeb3SignerUrl();

if (!TEST_PRIVATE_KEY) {
throw new Error('Need to set WEB3_SIGNER_URL');
}

privateKey = Buffer32.fromString(TEST_PRIVATE_KEY);
privateKey = Buffer32.fromString(generatePrivateKey());
address = EthAddress.fromString(privateKeyToAddress(privateKey.toString()));

chainId = parseInt(L1_CHAIN_ID, 10);
web3SignerUrl = WEB3_SIGNER_URL;

await createWeb3SignerKeystore(getWeb3SignerTestKeystoreDir(), privateKey.toString());
await refreshWeb3Signer(web3SignerUrl);
});

beforeEach(() => {
Expand Down Expand Up @@ -138,4 +137,21 @@ describe('RemoteSigner integration: Web3Signer (compose)', () => {
expect(remoteSig.s.toString()).toBe(localSig.s.toString());
expect([0, 1, 27, 28]).toContain(remoteSig.v);
});

it('validates web3signer accessibility and address availability', async () => {
// Should succeed with the correct address
await expect(RemoteSigner.validateAccess(web3SignerUrl, [address.toString()])).resolves.not.toThrow();

// Should fail with a non-existent address
const nonExistentAddress = EthAddress.random().toString();
await expect(RemoteSigner.validateAccess(web3SignerUrl, [nonExistentAddress])).rejects.toThrow(
`The following addresses are not available in the web3signer: ${nonExistentAddress.toLowerCase()}`,
);

// Should succeed when checking multiple addresses where one exists
await expect(RemoteSigner.validateAccess(web3SignerUrl, [address.toString()])).resolves.not.toThrow();

// Should fail with an invalid URL
await expect(RemoteSigner.validateAccess('http://invalid-url:9999', [address.toString()])).rejects.toThrow();
});
});
46 changes: 46 additions & 0 deletions yarn-project/end-to-end/src/fixtures/web3signer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { sleep } from '@aztec/aztec.js';
import { randomBytes } from '@aztec/foundation/crypto';

import { mkdirSync } from 'node:fs';
import { writeFile } from 'node:fs/promises';
import { join } from 'node:path';

export async function createWeb3SignerKeystore(dir: string, ...privateKeys: string[]) {
const yaml = privateKeys
.map(
pk => `\
type: file-raw
keyType: SECP256K1
privateKey: ${pk}`,
)
.join('\n---\n');

// NOTE: nodejs stdlib can only create temp directories, not temp files!
// this write uses wx (write-exclusive) so it'll throw if the file already exists
const path = join(dir, `keystore-${randomBytes(4).toString('hex')}.yaml`);
await writeFile(path, yaml, { flag: 'wx' });
}

export async function refreshWeb3Signer(url: string) {
await fetch(new URL('reload', url), { method: 'POST' });
// give the service a chance to load up the new files
// 1s might not be enough if there are a lot of files to scan
await sleep(1000);
}

export function getWeb3SignerTestKeystoreDir(): string {
if (process.env.WEB3_SIGNER_TEST_KEYSTORE_DIR) {
mkdirSync(process.env.WEB3_SIGNER_TEST_KEYSTORE_DIR, { recursive: true });
return process.env.WEB3_SIGNER_TEST_KEYSTORE_DIR;
} else {
throw new Error('Web3signer not running');
}
}

export function getWeb3SignerUrl(): string {
if (process.env.WEB3_SIGNER_URL) {
return process.env.WEB3_SIGNER_URL;
} else {
throw new Error('Web3signer not running');
}
}
Loading
Loading