@aztec/end-to-end 2.0.0-nightly.20250826 → 2.0.0-nightly.20250828
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/e2e_epochs/epochs_test.d.ts +1 -1
- package/dest/e2e_epochs/epochs_test.d.ts.map +1 -1
- package/dest/e2e_epochs/epochs_test.js +8 -3
- package/dest/e2e_fees/fees_test.d.ts.map +1 -1
- package/dest/e2e_fees/fees_test.js +2 -5
- package/dest/e2e_multi_validator/utils.d.ts +12 -0
- package/dest/e2e_multi_validator/utils.d.ts.map +1 -0
- package/dest/e2e_multi_validator/utils.js +220 -0
- package/dest/e2e_p2p/p2p_network.d.ts +3 -3
- package/dest/e2e_p2p/p2p_network.d.ts.map +1 -1
- package/dest/e2e_p2p/p2p_network.js +5 -8
- package/dest/e2e_p2p/shared.d.ts +13 -7
- package/dest/e2e_p2p/shared.d.ts.map +1 -1
- package/dest/e2e_p2p/shared.js +76 -38
- package/dest/fixtures/e2e_prover_test.js +4 -2
- package/dest/fixtures/setup_p2p_test.js +3 -1
- package/dest/fixtures/snapshot_manager.d.ts.map +1 -1
- package/dest/fixtures/snapshot_manager.js +9 -6
- package/dest/fixtures/utils.d.ts +6 -1
- package/dest/fixtures/utils.d.ts.map +1 -1
- package/dest/fixtures/utils.js +27 -9
- package/package.json +36 -35
- package/src/e2e_epochs/epochs_test.ts +11 -4
- package/src/e2e_fees/fees_test.ts +2 -2
- package/src/e2e_multi_validator/utils.ts +264 -0
- package/src/e2e_p2p/p2p_network.ts +9 -9
- package/src/e2e_p2p/shared.ts +101 -58
- package/src/fixtures/e2e_prover_test.ts +2 -2
- package/src/fixtures/setup_p2p_test.ts +1 -1
- package/src/fixtures/snapshot_manager.ts +6 -3
- package/src/fixtures/utils.ts +39 -9
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { AztecAddress } from '@aztec/aztec.js';
|
|
2
|
+
import { getAddressFromPrivateKey } from '@aztec/ethereum';
|
|
3
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
4
|
+
import type { EthPrivateKey } from '@aztec/node-keystore';
|
|
5
|
+
|
|
6
|
+
import { writeFile } from 'fs/promises';
|
|
7
|
+
import { createServer } from 'http';
|
|
8
|
+
import { signMessage, signTypedData } from 'viem/accounts';
|
|
9
|
+
|
|
10
|
+
// Create a mock JSON RPC signer
|
|
11
|
+
// Only supports signing messages and type data
|
|
12
|
+
|
|
13
|
+
const SUPPORTED_METHODS = ['eth_sign', 'eth_signTypedData_v4'];
|
|
14
|
+
|
|
15
|
+
export function createJSONRPCSigner(keyLookup: Map<string, EthPrivateKey>, stats: Map<string, number>) {
|
|
16
|
+
return createServer((req, res) => {
|
|
17
|
+
if (req.method === 'POST') {
|
|
18
|
+
let body = '';
|
|
19
|
+
req.on('data', chunk => {
|
|
20
|
+
body += chunk.toString();
|
|
21
|
+
});
|
|
22
|
+
req.on('end', () => {
|
|
23
|
+
try {
|
|
24
|
+
const jsonRequest = JSON.parse(body);
|
|
25
|
+
res.writeHead(200, { 'Content-Type': 'application/json' });
|
|
26
|
+
|
|
27
|
+
if (!SUPPORTED_METHODS.includes(jsonRequest.method)) {
|
|
28
|
+
res.end(
|
|
29
|
+
JSON.stringify({
|
|
30
|
+
jsonrpc: '2.0',
|
|
31
|
+
id: jsonRequest.id,
|
|
32
|
+
error: { code: -32601, message: 'Method not supported' },
|
|
33
|
+
}),
|
|
34
|
+
);
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Get the address sending the transaction
|
|
39
|
+
const [address, data] = jsonRequest.params;
|
|
40
|
+
|
|
41
|
+
const lowerCaseAddress = address.toLowerCase();
|
|
42
|
+
stats.set(lowerCaseAddress, (stats.get(lowerCaseAddress) ?? 0) + 1);
|
|
43
|
+
|
|
44
|
+
// Find the private key for the address
|
|
45
|
+
const privateKey = keyLookup.get(address.toLowerCase());
|
|
46
|
+
if (!privateKey) {
|
|
47
|
+
res.end(
|
|
48
|
+
JSON.stringify({
|
|
49
|
+
jsonrpc: '2.0',
|
|
50
|
+
id: jsonRequest.id,
|
|
51
|
+
error: { code: -32602, message: `No private key found for address ${address}` },
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const promise =
|
|
58
|
+
jsonRequest.method === 'eth_sign'
|
|
59
|
+
? signMessage({ message: { raw: data as `0x${string}` }, privateKey })
|
|
60
|
+
: signTypedData({
|
|
61
|
+
privateKey,
|
|
62
|
+
...data,
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
void promise.then(signature => {
|
|
66
|
+
res.end(
|
|
67
|
+
JSON.stringify({
|
|
68
|
+
jsonrpc: '2.0',
|
|
69
|
+
id: jsonRequest.id,
|
|
70
|
+
result: signature,
|
|
71
|
+
}),
|
|
72
|
+
);
|
|
73
|
+
});
|
|
74
|
+
} catch (err) {
|
|
75
|
+
res.end(
|
|
76
|
+
JSON.stringify({
|
|
77
|
+
jsonrpc: '2.0',
|
|
78
|
+
id: 1,
|
|
79
|
+
error: { code: -32603, message: `${err}` },
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
res.writeHead(405);
|
|
86
|
+
res.end('Method not allowed');
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Functions for creating file based key stores for the e2e_multi_validator_node_key_store test
|
|
92
|
+
export async function createKeyFile1(
|
|
93
|
+
fileName: string,
|
|
94
|
+
mnemonic: string,
|
|
95
|
+
validatorIndex: number,
|
|
96
|
+
publisher1Key: EthPrivateKey,
|
|
97
|
+
publisher2Key: EthPrivateKey,
|
|
98
|
+
coinbase: EthAddress,
|
|
99
|
+
feeRecipient: AztecAddress,
|
|
100
|
+
) {
|
|
101
|
+
const obj = {
|
|
102
|
+
schemaVersion: 1,
|
|
103
|
+
validators: [
|
|
104
|
+
{
|
|
105
|
+
attester: {
|
|
106
|
+
mnemonic: mnemonic,
|
|
107
|
+
accountIndex: 0,
|
|
108
|
+
addressIndex: validatorIndex,
|
|
109
|
+
addressCount: 1,
|
|
110
|
+
},
|
|
111
|
+
coinbase: coinbase.toChecksumString(),
|
|
112
|
+
publisher: [publisher1Key, publisher2Key],
|
|
113
|
+
feeRecipient: feeRecipient.toString(),
|
|
114
|
+
},
|
|
115
|
+
],
|
|
116
|
+
};
|
|
117
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function createKeyFile2(
|
|
121
|
+
fileName: string,
|
|
122
|
+
validatorKey: EthPrivateKey,
|
|
123
|
+
publisherMnemonic: string,
|
|
124
|
+
publisher1Index: number,
|
|
125
|
+
coinbase: EthAddress,
|
|
126
|
+
feeRecipient: AztecAddress,
|
|
127
|
+
) {
|
|
128
|
+
const obj = {
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
validators: [
|
|
131
|
+
{
|
|
132
|
+
attester: validatorKey,
|
|
133
|
+
coinbase: coinbase.toChecksumString(),
|
|
134
|
+
publisher: {
|
|
135
|
+
mnemonic: publisherMnemonic,
|
|
136
|
+
accountIndex: 0,
|
|
137
|
+
addressIndex: publisher1Index,
|
|
138
|
+
addressCount: 2,
|
|
139
|
+
},
|
|
140
|
+
feeRecipient: feeRecipient.toString(),
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
};
|
|
144
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export async function createKeyFile3(
|
|
148
|
+
fileName: string,
|
|
149
|
+
validatorAddress: EthAddress,
|
|
150
|
+
publisher1Key: EthPrivateKey,
|
|
151
|
+
publisher2Key: EthPrivateKey,
|
|
152
|
+
coinbase: EthAddress,
|
|
153
|
+
remoteSignerUrl: string,
|
|
154
|
+
feeRecipient: AztecAddress,
|
|
155
|
+
) {
|
|
156
|
+
const obj = {
|
|
157
|
+
schemaVersion: 1,
|
|
158
|
+
validators: [
|
|
159
|
+
{
|
|
160
|
+
attester: {
|
|
161
|
+
address: validatorAddress.toChecksumString(),
|
|
162
|
+
},
|
|
163
|
+
coinbase: coinbase.toChecksumString(),
|
|
164
|
+
publisher: [publisher1Key, publisher2Key],
|
|
165
|
+
feeRecipient: feeRecipient.toString(),
|
|
166
|
+
remoteSigner: {
|
|
167
|
+
remoteSignerUrl: remoteSignerUrl,
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
],
|
|
171
|
+
};
|
|
172
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function createKeyFile4(
|
|
176
|
+
fileName: string,
|
|
177
|
+
validator1Address: EthAddress,
|
|
178
|
+
validator2Address: EthAddress,
|
|
179
|
+
publisher1Index: number,
|
|
180
|
+
publisher2Key: EthPrivateKey,
|
|
181
|
+
mnemonic: string,
|
|
182
|
+
publisher3Key: EthPrivateKey,
|
|
183
|
+
coinbase1: EthAddress,
|
|
184
|
+
coinbase2: EthAddress,
|
|
185
|
+
remoteSignerUrl: string,
|
|
186
|
+
feeRecipient1: AztecAddress,
|
|
187
|
+
feeRecipient2: AztecAddress,
|
|
188
|
+
) {
|
|
189
|
+
const obj = {
|
|
190
|
+
schemaVersion: 1,
|
|
191
|
+
remoteSigner: {
|
|
192
|
+
remoteSignerUrl: remoteSignerUrl,
|
|
193
|
+
},
|
|
194
|
+
validators: [
|
|
195
|
+
{
|
|
196
|
+
attester: {
|
|
197
|
+
address: validator1Address.toChecksumString(),
|
|
198
|
+
},
|
|
199
|
+
coinbase: coinbase1.toChecksumString(),
|
|
200
|
+
publisher: {
|
|
201
|
+
mnemonic: mnemonic,
|
|
202
|
+
accountIndex: 0,
|
|
203
|
+
addressIndex: publisher1Index,
|
|
204
|
+
addressCount: 2,
|
|
205
|
+
},
|
|
206
|
+
feeRecipient: feeRecipient1.toString(),
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
attester: {
|
|
210
|
+
address: validator2Address.toChecksumString(),
|
|
211
|
+
},
|
|
212
|
+
coinbase: coinbase2.toChecksumString(),
|
|
213
|
+
publisher: [publisher2Key, publisher3Key],
|
|
214
|
+
feeRecipient: feeRecipient2.toString(),
|
|
215
|
+
},
|
|
216
|
+
],
|
|
217
|
+
};
|
|
218
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
export async function createKeyFile5(fileName: string, proverAddress: EthAddress, remoteSignerUrl: string) {
|
|
222
|
+
const obj = {
|
|
223
|
+
schemaVersion: 1,
|
|
224
|
+
prover: {
|
|
225
|
+
id: '0x1234567890123456789012345678901234567890',
|
|
226
|
+
publisher: [
|
|
227
|
+
{
|
|
228
|
+
address: proverAddress.toChecksumString(),
|
|
229
|
+
remoteSignerUrl: remoteSignerUrl,
|
|
230
|
+
},
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export async function createKeyFile6(
|
|
238
|
+
fileName: string,
|
|
239
|
+
mnemonic: string,
|
|
240
|
+
validator1Index: number,
|
|
241
|
+
coinbase: EthAddress,
|
|
242
|
+
feeRecipient: AztecAddress,
|
|
243
|
+
) {
|
|
244
|
+
const obj = {
|
|
245
|
+
schemaVersion: 1,
|
|
246
|
+
validators: [
|
|
247
|
+
{
|
|
248
|
+
attester: {
|
|
249
|
+
mnemonic: mnemonic,
|
|
250
|
+
accountIndex: 0,
|
|
251
|
+
addressIndex: validator1Index,
|
|
252
|
+
addressCount: 2,
|
|
253
|
+
},
|
|
254
|
+
coinbase: coinbase.toChecksumString(),
|
|
255
|
+
feeRecipient: feeRecipient.toString(),
|
|
256
|
+
},
|
|
257
|
+
],
|
|
258
|
+
};
|
|
259
|
+
await writeFile(fileName, JSON.stringify(obj, null, 2));
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function addressForPrivateKey(privateKey: EthPrivateKey): EthAddress {
|
|
263
|
+
return EthAddress.fromString(getAddressFromPrivateKey(privateKey));
|
|
264
|
+
}
|
|
@@ -3,20 +3,22 @@ import type { InitialAccountData } from '@aztec/accounts/testing';
|
|
|
3
3
|
import type { AztecNodeConfig, AztecNodeService } from '@aztec/aztec-node';
|
|
4
4
|
import { type AccountWalletWithSecretKey, AztecAddress, EthAddress, Fr } from '@aztec/aztec.js';
|
|
5
5
|
import {
|
|
6
|
+
type EmpireSlashingProposerContract,
|
|
6
7
|
type ExtendedViemWalletClient,
|
|
7
8
|
GSEContract,
|
|
8
|
-
L1TxUtils,
|
|
9
9
|
MultiAdderArtifact,
|
|
10
10
|
type Operator,
|
|
11
11
|
RollupContract,
|
|
12
|
+
type TallySlashingProposerContract,
|
|
12
13
|
type ViemClient,
|
|
14
|
+
createL1TxUtilsFromViemWallet,
|
|
13
15
|
deployL1Contract,
|
|
14
16
|
getL1ContractsConfigEnvVars,
|
|
15
17
|
} from '@aztec/ethereum';
|
|
16
18
|
import { ChainMonitor } from '@aztec/ethereum/test';
|
|
17
19
|
import { SecretValue } from '@aztec/foundation/config';
|
|
18
20
|
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
19
|
-
import {
|
|
21
|
+
import { RollupAbi, SlasherAbi, TestERC20Abi } from '@aztec/l1-artifacts';
|
|
20
22
|
import { SpamContract } from '@aztec/noir-test-contracts.js/Spam';
|
|
21
23
|
import type { BootstrapNode } from '@aztec/p2p/bootstrap';
|
|
22
24
|
import { createBootstrapNodeFromPrivateKey, getBootstrapNodeEnr } from '@aztec/p2p/test-helpers';
|
|
@@ -71,6 +73,7 @@ export class P2PNetworkTest {
|
|
|
71
73
|
|
|
72
74
|
public deployedAccounts: InitialAccountData[] = [];
|
|
73
75
|
public prefilledPublicData: PublicDataTreeLeaf[] = [];
|
|
76
|
+
|
|
74
77
|
// The re-execution test needs a wallet and a spam contract
|
|
75
78
|
public wallet?: AccountWalletWithSecretKey;
|
|
76
79
|
public defaultAccountAddress?: AztecAddress;
|
|
@@ -349,7 +352,7 @@ export class P2PNetworkTest {
|
|
|
349
352
|
}
|
|
350
353
|
|
|
351
354
|
private async _sendDummyTx(l1Client: ExtendedViemWalletClient) {
|
|
352
|
-
const l1TxUtils =
|
|
355
|
+
const l1TxUtils = createL1TxUtilsFromViemWallet(l1Client);
|
|
353
356
|
return await l1TxUtils.sendAndMonitorTransaction({
|
|
354
357
|
to: l1Client.account!.address,
|
|
355
358
|
value: 1n,
|
|
@@ -392,7 +395,7 @@ export class P2PNetworkTest {
|
|
|
392
395
|
async getContracts(): Promise<{
|
|
393
396
|
rollup: RollupContract;
|
|
394
397
|
slasherContract: GetContractReturnType<typeof SlasherAbi, ViemClient>;
|
|
395
|
-
slashingProposer:
|
|
398
|
+
slashingProposer: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
396
399
|
slashFactory: SlashFactoryContract;
|
|
397
400
|
}> {
|
|
398
401
|
if (!this.ctx.deployL1ContractsValues) {
|
|
@@ -410,11 +413,8 @@ export class P2PNetworkTest {
|
|
|
410
413
|
client: this.ctx.deployL1ContractsValues.l1Client,
|
|
411
414
|
});
|
|
412
415
|
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
abi: EmpireSlashingProposerAbi,
|
|
416
|
-
client: this.ctx.deployL1ContractsValues.l1Client,
|
|
417
|
-
});
|
|
416
|
+
// Get the actual slashing proposer from rollup (which handles both empire and tally)
|
|
417
|
+
const slashingProposer = await rollup.getSlashingProposer();
|
|
418
418
|
|
|
419
419
|
const slashFactory = new SlashFactoryContract(
|
|
420
420
|
this.ctx.deployL1ContractsValues.l1Client,
|
package/src/e2e_p2p/shared.ts
CHANGED
|
@@ -12,16 +12,16 @@ import {
|
|
|
12
12
|
retryUntil,
|
|
13
13
|
} from '@aztec/aztec.js';
|
|
14
14
|
import type { RollupCheatCodes } from '@aztec/aztec/testing';
|
|
15
|
-
import type { RollupContract,
|
|
15
|
+
import type { EmpireSlashingProposerContract, RollupContract, TallySlashingProposerContract } from '@aztec/ethereum';
|
|
16
16
|
import { timesAsync, unique } from '@aztec/foundation/collection';
|
|
17
|
-
import type {
|
|
17
|
+
import type { TestDateProvider } from '@aztec/foundation/timer';
|
|
18
18
|
import type { SpamContract } from '@aztec/noir-test-contracts.js/Spam';
|
|
19
19
|
import { TestContract, TestContractArtifact } from '@aztec/noir-test-contracts.js/Test';
|
|
20
20
|
import { PXEService, createPXEService, getPXEServiceConfig as getRpcConfig } from '@aztec/pxe/server';
|
|
21
|
+
import { getRoundForOffense } from '@aztec/slasher';
|
|
22
|
+
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
21
23
|
import type { SlashFactoryContract } from '@aztec/stdlib/l1-contracts';
|
|
22
24
|
|
|
23
|
-
import type { GetContractReturnType } from 'viem';
|
|
24
|
-
|
|
25
25
|
import type { NodeContext } from '../fixtures/setup_p2p_test.js';
|
|
26
26
|
import { submitTxsTo } from '../shared/submit-transactions.js';
|
|
27
27
|
|
|
@@ -107,25 +107,36 @@ export async function createPXEServiceAndPrepareTransactions(
|
|
|
107
107
|
return { txs, pxeService: pxe, node };
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
export
|
|
111
|
-
slashingProposer:
|
|
110
|
+
export function awaitProposalExecution(
|
|
111
|
+
slashingProposer: EmpireSlashingProposerContract | TallySlashingProposerContract,
|
|
112
112
|
timeoutSeconds: number,
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
113
|
+
logger: Logger,
|
|
114
|
+
): Promise<bigint> {
|
|
115
|
+
return new Promise<bigint>((resolve, reject) => {
|
|
116
|
+
const timeout = setTimeout(() => {
|
|
117
|
+
logger.warn(`Timed out waiting for proposal execution`);
|
|
118
|
+
reject(new Error(`Timeout waiting for proposal execution after ${timeoutSeconds}s`));
|
|
119
|
+
}, timeoutSeconds * 1000);
|
|
120
|
+
|
|
121
|
+
if (slashingProposer.type === 'empire') {
|
|
122
|
+
const unwatch = slashingProposer.listenToPayloadSubmitted(args => {
|
|
123
|
+
logger.warn(`Proposal ${args.payload} from round ${args.round} executed`);
|
|
124
|
+
clearTimeout(timeout);
|
|
125
|
+
unwatch();
|
|
126
|
+
resolve(args.round);
|
|
127
|
+
});
|
|
128
|
+
} else if (slashingProposer.type === 'tally') {
|
|
129
|
+
const unwatch = slashingProposer.listenToRoundExecuted(args => {
|
|
130
|
+
logger.warn(`Slash from round ${args.round} executed`);
|
|
131
|
+
clearTimeout(timeout);
|
|
132
|
+
unwatch();
|
|
133
|
+
resolve(args.round);
|
|
134
|
+
});
|
|
135
|
+
} else {
|
|
136
|
+
clearTimeout(timeout);
|
|
137
|
+
reject(new Error(`Unknown slashing proposer type: ${(slashingProposer as any).type}`));
|
|
138
|
+
}
|
|
139
|
+
});
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
export async function awaitCommitteeExists({
|
|
@@ -148,6 +159,35 @@ export async function awaitCommitteeExists({
|
|
|
148
159
|
return committee!;
|
|
149
160
|
}
|
|
150
161
|
|
|
162
|
+
export async function awaitOffenseDetected({
|
|
163
|
+
logger,
|
|
164
|
+
nodeAdmin,
|
|
165
|
+
slashingRoundSize,
|
|
166
|
+
epochDuration,
|
|
167
|
+
}: {
|
|
168
|
+
nodeAdmin: AztecNodeAdmin;
|
|
169
|
+
logger: Logger;
|
|
170
|
+
slashingRoundSize: number;
|
|
171
|
+
epochDuration: number;
|
|
172
|
+
}) {
|
|
173
|
+
logger.info(`Waiting for an offense to be detected`);
|
|
174
|
+
const offenses = await retryUntil(
|
|
175
|
+
async () => {
|
|
176
|
+
const offenses = await nodeAdmin.getSlashOffenses('all');
|
|
177
|
+
if (offenses.length > 0) {
|
|
178
|
+
return offenses;
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
'non-empty offenses',
|
|
182
|
+
60,
|
|
183
|
+
);
|
|
184
|
+
logger.info(
|
|
185
|
+
`Hit ${offenses.length} offenses on rounds ${unique(offenses.map(o => getRoundForOffense(o, { slashingRoundSize, epochDuration })))}`,
|
|
186
|
+
offenses,
|
|
187
|
+
);
|
|
188
|
+
return offenses;
|
|
189
|
+
}
|
|
190
|
+
|
|
151
191
|
/**
|
|
152
192
|
* Await the committee to be slashed out of the validator set.
|
|
153
193
|
* Currently assumes that the committee is the same size as the validator set.
|
|
@@ -161,52 +201,58 @@ export async function awaitCommitteeKicked({
|
|
|
161
201
|
slashingRoundSize,
|
|
162
202
|
aztecSlotDuration,
|
|
163
203
|
logger,
|
|
164
|
-
|
|
204
|
+
dateProvider,
|
|
165
205
|
}: {
|
|
166
206
|
rollup: RollupContract;
|
|
167
207
|
cheatCodes: RollupCheatCodes;
|
|
168
208
|
committee: readonly `0x${string}`[];
|
|
169
209
|
slashFactory: SlashFactoryContract;
|
|
170
|
-
slashingProposer:
|
|
210
|
+
slashingProposer: EmpireSlashingProposerContract | TallySlashingProposerContract | undefined;
|
|
171
211
|
slashingRoundSize: number;
|
|
172
212
|
aztecSlotDuration: number;
|
|
213
|
+
dateProvider: TestDateProvider;
|
|
173
214
|
logger: Logger;
|
|
174
|
-
sendDummyTx: () => Promise<void>;
|
|
175
215
|
}) {
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
await cheatCodes.advanceToNextEpoch();
|
|
216
|
+
if (!slashingProposer) {
|
|
217
|
+
throw new Error('No slashing proposer configured. Cannot test slashing.');
|
|
218
|
+
}
|
|
180
219
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return events.length > 0 ? events : undefined;
|
|
186
|
-
},
|
|
187
|
-
'slash payload created',
|
|
188
|
-
120,
|
|
189
|
-
1,
|
|
190
|
-
);
|
|
191
|
-
expect(slashPayloadEvents.length).toBe(1);
|
|
220
|
+
logger.info(`Advancing epochs so we start slashing`);
|
|
221
|
+
await cheatCodes.debugRollup();
|
|
222
|
+
await cheatCodes.advanceToNextEpoch({ updateDateProvider: dateProvider });
|
|
223
|
+
await cheatCodes.advanceToNextEpoch({ updateDateProvider: dateProvider });
|
|
192
224
|
|
|
193
|
-
//
|
|
194
|
-
|
|
225
|
+
// Await for the slash payload to be created if empire (no payload is created on tally until execution time)
|
|
226
|
+
if (slashingProposer.type === 'empire') {
|
|
227
|
+
const slashPayloadEvents = await retryUntil(
|
|
228
|
+
async () => {
|
|
229
|
+
const events = await slashFactory.getSlashPayloadCreatedEvents();
|
|
230
|
+
return events.length > 0 ? events : undefined;
|
|
231
|
+
},
|
|
232
|
+
'slash payload created',
|
|
233
|
+
120,
|
|
234
|
+
1,
|
|
235
|
+
);
|
|
236
|
+
expect(slashPayloadEvents.length).toBe(1);
|
|
237
|
+
// The uniqueness check is needed since a validator may be slashed more than once on the same round (eg because they let two epochs be pruned)
|
|
238
|
+
expect(unique(slashPayloadEvents[0].slashes.map(slash => slash.validator.toString()))).toHaveLength(
|
|
239
|
+
committee.length,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
195
242
|
|
|
196
243
|
const attestersPre = await rollup.getAttesters();
|
|
197
244
|
expect(attestersPre.length).toBe(committee.length);
|
|
198
245
|
|
|
199
246
|
for (const attester of attestersPre) {
|
|
200
247
|
const attesterInfo = await rollup.getAttesterView(attester);
|
|
201
|
-
//
|
|
202
|
-
expect(attesterInfo.status).toEqual(1);
|
|
248
|
+
expect(attesterInfo.status).toEqual(1); // Validating
|
|
203
249
|
}
|
|
204
250
|
|
|
205
|
-
|
|
206
|
-
|
|
251
|
+
const timeout = slashingRoundSize * 2 * aztecSlotDuration;
|
|
252
|
+
logger.info(`Waiting for slash to be executed (timeout ${timeout}s)`);
|
|
253
|
+
await awaitProposalExecution(slashingProposer, timeout, logger);
|
|
207
254
|
|
|
208
|
-
// The attesters should still form the committee
|
|
209
|
-
// but they should be reduced to the "living" status
|
|
255
|
+
// The attesters should still form the committee but they should be reduced to the "living" status
|
|
210
256
|
await cheatCodes.debugRollup();
|
|
211
257
|
const committeePostSlashing = await rollup.getCurrentEpochCommittee();
|
|
212
258
|
expect(committeePostSlashing?.length).toBe(attestersPre.length);
|
|
@@ -214,18 +260,15 @@ export async function awaitCommitteeKicked({
|
|
|
214
260
|
const attestersPostSlashing = await rollup.getAttesters();
|
|
215
261
|
expect(attestersPostSlashing.length).toBe(0);
|
|
216
262
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
// expect(attesterInfo.status).toEqual(2);
|
|
222
|
-
// }
|
|
263
|
+
for (const attester of attestersPre) {
|
|
264
|
+
const attesterInfo = await rollup.getAttesterView(attester);
|
|
265
|
+
expect(attesterInfo.status).toEqual(2); // Living
|
|
266
|
+
}
|
|
223
267
|
|
|
268
|
+
logger.info(`Advancing two epochs to check current committee`);
|
|
224
269
|
await cheatCodes.debugRollup();
|
|
225
|
-
await cheatCodes.advanceToNextEpoch();
|
|
226
|
-
await
|
|
227
|
-
await cheatCodes.advanceToNextEpoch();
|
|
228
|
-
await sendDummyTx();
|
|
270
|
+
await cheatCodes.advanceToNextEpoch({ updateDateProvider: dateProvider });
|
|
271
|
+
await cheatCodes.advanceToNextEpoch({ updateDateProvider: dateProvider });
|
|
229
272
|
await cheatCodes.debugRollup();
|
|
230
273
|
|
|
231
274
|
const committeeNextEpoch = await rollup.getCurrentEpochCommittee();
|
|
@@ -296,10 +296,10 @@ export class FullProverTest {
|
|
|
296
296
|
...this.context.aztecNodeConfig,
|
|
297
297
|
txCollectionNodeRpcUrls: [],
|
|
298
298
|
dataDirectory: undefined,
|
|
299
|
-
proverId: this.proverAddress
|
|
299
|
+
proverId: this.proverAddress,
|
|
300
300
|
realProofs: this.realProofs,
|
|
301
301
|
proverAgentCount: 2,
|
|
302
|
-
|
|
302
|
+
publisherPrivateKeys: [new SecretValue(`0x${proverNodePrivateKey!.toString('hex')}` as const)],
|
|
303
303
|
proverNodeMaxPendingJobs: 100,
|
|
304
304
|
proverNodeMaxParallelBlocksPerEpoch: 32,
|
|
305
305
|
proverNodePollingIntervalMs: 100,
|
|
@@ -154,7 +154,7 @@ export async function createValidatorConfig(
|
|
|
154
154
|
const attesterPrivateKey = bufferToHex(getPrivateKeyFromIndex(ATTESTER_PRIVATE_KEYS_START_INDEX + addressIndex)!);
|
|
155
155
|
|
|
156
156
|
config.validatorPrivateKeys = new SecretValue([attesterPrivateKey]);
|
|
157
|
-
config.
|
|
157
|
+
config.publisherPrivateKeys = [new SecretValue(attesterPrivateKey)];
|
|
158
158
|
|
|
159
159
|
const nodeConfig: AztecNodeConfig = {
|
|
160
160
|
...config,
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
type CompleteAddress,
|
|
9
9
|
type ContractFunctionInteraction,
|
|
10
10
|
DefaultWaitForProvenOpts,
|
|
11
|
+
EthAddress,
|
|
11
12
|
type Logger,
|
|
12
13
|
type PXE,
|
|
13
14
|
type Wallet,
|
|
@@ -342,11 +343,14 @@ async function setupFromFresh(
|
|
|
342
343
|
const publisherPrivKeyRaw = hdAccount.getHdKey().privateKey;
|
|
343
344
|
const publisherPrivKey = publisherPrivKeyRaw === null ? null : Buffer.from(publisherPrivKeyRaw);
|
|
344
345
|
|
|
346
|
+
const l1Client = createExtendedL1Client([aztecNodeConfig.l1RpcUrls[0]], hdAccount, foundry);
|
|
347
|
+
|
|
345
348
|
const validatorPrivKey = getPrivateKeyFromIndex(0);
|
|
346
349
|
const proverNodePrivateKey = getPrivateKeyFromIndex(0);
|
|
347
350
|
|
|
348
|
-
aztecNodeConfig.
|
|
351
|
+
aztecNodeConfig.publisherPrivateKeys = [new SecretValue<`0x${string}`>(`0x${publisherPrivKey!.toString('hex')}`)];
|
|
349
352
|
aztecNodeConfig.validatorPrivateKeys = new SecretValue([`0x${validatorPrivKey!.toString('hex')}`]);
|
|
353
|
+
aztecNodeConfig.coinbase = opts.coinbase ?? EthAddress.fromString(`${hdAccount.address}`);
|
|
350
354
|
|
|
351
355
|
const ethCheatCodes = new EthCheatCodesWithState(aztecNodeConfig.l1RpcUrls);
|
|
352
356
|
|
|
@@ -361,7 +365,6 @@ async function setupFromFresh(
|
|
|
361
365
|
opts.initialAccountFeeJuice,
|
|
362
366
|
);
|
|
363
367
|
|
|
364
|
-
const l1Client = createExtendedL1Client([aztecNodeConfig.l1RpcUrls[0]], hdAccount, foundry);
|
|
365
368
|
await deployMulticall3(l1Client, logger);
|
|
366
369
|
|
|
367
370
|
const deployL1ContractsValues = await setupL1Contracts(aztecNodeConfig.l1RpcUrls[0], hdAccount, logger, {
|
|
@@ -439,7 +442,7 @@ async function setupFromFresh(
|
|
|
439
442
|
);
|
|
440
443
|
await blobSink.start();
|
|
441
444
|
|
|
442
|
-
logger.
|
|
445
|
+
logger.info('Creating and synching an aztec node...');
|
|
443
446
|
const aztecNode = await AztecNodeService.createAndSync(
|
|
444
447
|
aztecNodeConfig,
|
|
445
448
|
{ telemetry, dateProvider },
|