-
Notifications
You must be signed in to change notification settings - Fork 333
/
Copy pathreex.test.ts
130 lines (105 loc) · 4.35 KB
/
reex.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import { type AztecNodeService } from '@aztec/aztec-node';
import { type SentTx, sleep } from '@aztec/aztec.js';
/* eslint-disable-next-line no-restricted-imports */
import { BlockProposal, SignatureDomainSeperator, getHashedSignaturePayload } from '@aztec/circuit-types';
import { beforeAll, describe, it, jest } from '@jest/globals';
import fs from 'fs';
import { createNodes } from '../fixtures/setup_p2p_test.js';
import { P2PNetworkTest } from './p2p_network.js';
import { submitComplexTxsTo } from './shared.js';
const NUM_NODES = 4;
const NUM_TXS_PER_NODE = 1;
const BOOT_NODE_UDP_PORT = 41000;
const DATA_DIR = './data/re-ex';
describe('e2e_p2p_reex', () => {
let t: P2PNetworkTest;
let nodes: AztecNodeService[];
beforeAll(async () => {
nodes = [];
t = await P2PNetworkTest.create({
testName: 'e2e_p2p_reex',
numberOfNodes: NUM_NODES,
basePort: BOOT_NODE_UDP_PORT,
});
t.logger.verbose('Setup account');
await t.setupAccount();
t.logger.verbose('Deploy spam contract');
await t.deploySpamContract();
t.logger.verbose('Apply base snapshots');
await t.applyBaseSnapshots();
t.logger.verbose('Setup nodes');
await t.setup();
});
afterAll(async () => {
// shutdown all nodes.
await t.stopNodes(nodes);
await t.teardown();
for (let i = 0; i < NUM_NODES; i++) {
fs.rmSync(`${DATA_DIR}-${i}`, { recursive: true, force: true });
}
});
it('validators should re-execute transactions before attesting', async () => {
// create the bootstrap node for the network
if (!t.bootstrapNodeEnr) {
throw new Error('Bootstrap node ENR is not available');
}
t.ctx.aztecNodeConfig.validatorReexecute = true;
nodes = await createNodes(
t.ctx.aztecNodeConfig,
t.peerIdPrivateKeys,
t.bootstrapNodeEnr,
NUM_NODES,
BOOT_NODE_UDP_PORT,
);
// Hook into the node and intercept re-execution logic, ensuring that it was infact called
const reExecutionSpies = [];
for (const node of nodes) {
// Make sure the nodes submit faulty proposals, in this case a faulty proposal is one where we remove one of the transactions
// Such that the calculated archive will be different!
jest.spyOn((node as any).p2pClient, 'broadcastProposal').mockImplementation(async (...args: unknown[]) => {
// We remove one of the transactions, therefore the block root will be different!
const proposal = args[0] as BlockProposal;
const { txHashes } = proposal.payload;
// We need to mutate the proposal, so we cast to any
(proposal.payload as any).txHashes = txHashes.slice(0, txHashes.length - 1);
// We sign over the proposal using the node's signing key
// Abusing javascript to access the nodes signing key
const signer = (node as any).sequencer.sequencer.validatorClient.validationService.keyStore;
const newProposal = new BlockProposal(
proposal.payload,
await signer.signMessage(getHashedSignaturePayload(proposal.payload, SignatureDomainSeperator.blockProposal)),
);
return (node as any).p2pClient.p2pService.propagate(newProposal);
});
// Store re-execution spys node -> sequencer Client -> seqeuncer -> validator
const spy = jest.spyOn((node as any).sequencer.sequencer.validatorClient, 'reExecuteTransactions');
reExecutionSpies.push(spy);
}
// wait a bit for peers to discover each other
await sleep(4000);
nodes.forEach(node => {
node.getSequencer()?.updateSequencerConfig({
minTxsPerBlock: NUM_TXS_PER_NODE,
maxTxsPerBlock: NUM_TXS_PER_NODE,
});
});
const txs = await submitComplexTxsTo(t.logger, t.spamContract!, NUM_TXS_PER_NODE);
// We ensure that the transactions are NOT mined
try {
await Promise.all(
txs.map(async (tx: SentTx, i: number) => {
t.logger.info(`Waiting for tx ${i}: ${await tx.getTxHash()} to be mined`);
return tx.wait();
}),
);
} catch (e) {
t.logger.info('Failed to mine all txs, as planned');
}
// Expect that all of the re-execution attempts failed with an invalid root
for (const spy of reExecutionSpies) {
for (const result of spy.mock.results) {
await expect(result.value).rejects.toThrow('Validator Error: Re-execution state mismatch');
}
}
});
});