@aztec/p2p 4.0.0-nightly.20260111 → 4.0.0-nightly.20260113

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.
Files changed (86) hide show
  1. package/dest/client/interface.d.ts +18 -5
  2. package/dest/client/interface.d.ts.map +1 -1
  3. package/dest/client/p2p_client.d.ts +9 -12
  4. package/dest/client/p2p_client.d.ts.map +1 -1
  5. package/dest/client/p2p_client.js +59 -103
  6. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts +61 -42
  7. package/dest/mem_pools/attestation_pool/attestation_pool.d.ts.map +1 -1
  8. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts +1 -1
  9. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.d.ts.map +1 -1
  10. package/dest/mem_pools/attestation_pool/attestation_pool_test_suite.js +225 -262
  11. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts +21 -18
  12. package/dest/mem_pools/attestation_pool/kv_attestation_pool.d.ts.map +1 -1
  13. package/dest/mem_pools/attestation_pool/kv_attestation_pool.js +113 -108
  14. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts +17 -16
  15. package/dest/mem_pools/attestation_pool/memory_attestation_pool.d.ts.map +1 -1
  16. package/dest/mem_pools/attestation_pool/memory_attestation_pool.js +89 -128
  17. package/dest/mem_pools/attestation_pool/mocks.d.ts +7 -6
  18. package/dest/mem_pools/attestation_pool/mocks.d.ts.map +1 -1
  19. package/dest/mem_pools/attestation_pool/mocks.js +9 -8
  20. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts +4 -4
  21. package/dest/msg_validators/attestation_validator/attestation_validator.d.ts.map +1 -1
  22. package/dest/msg_validators/attestation_validator/attestation_validator.js +12 -10
  23. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts +5 -5
  24. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.d.ts.map +1 -1
  25. package/dest/msg_validators/attestation_validator/fisherman_attestation_validator.js +5 -5
  26. package/dest/msg_validators/index.d.ts +2 -2
  27. package/dest/msg_validators/index.d.ts.map +1 -1
  28. package/dest/msg_validators/index.js +1 -1
  29. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts +9 -0
  30. package/dest/msg_validators/proposal_validator/block_proposal_validator.d.ts.map +1 -0
  31. package/dest/msg_validators/proposal_validator/block_proposal_validator.js +6 -0
  32. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts +9 -0
  33. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.d.ts.map +1 -0
  34. package/dest/msg_validators/proposal_validator/checkpoint_proposal_validator.js +6 -0
  35. package/dest/msg_validators/proposal_validator/index.d.ts +4 -0
  36. package/dest/msg_validators/proposal_validator/index.d.ts.map +1 -0
  37. package/dest/msg_validators/proposal_validator/index.js +3 -0
  38. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts +13 -0
  39. package/dest/msg_validators/proposal_validator/proposal_validator.d.ts.map +1 -0
  40. package/dest/msg_validators/{block_proposal_validator/block_proposal_validator.js → proposal_validator/proposal_validator.js} +19 -21
  41. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts +23 -0
  42. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.d.ts.map +1 -0
  43. package/dest/msg_validators/proposal_validator/proposal_validator_test_suite.js +183 -0
  44. package/dest/services/dummy_service.d.ts +6 -2
  45. package/dest/services/dummy_service.d.ts.map +1 -1
  46. package/dest/services/dummy_service.js +3 -0
  47. package/dest/services/encoding.d.ts +1 -1
  48. package/dest/services/encoding.d.ts.map +1 -1
  49. package/dest/services/encoding.js +4 -2
  50. package/dest/services/libp2p/libp2p_service.d.ts +26 -10
  51. package/dest/services/libp2p/libp2p_service.d.ts.map +1 -1
  52. package/dest/services/libp2p/libp2p_service.js +218 -88
  53. package/dest/services/service.d.ts +16 -3
  54. package/dest/services/service.d.ts.map +1 -1
  55. package/dest/testbench/p2p_client_testbench_worker.js +25 -11
  56. package/dest/testbench/worker_client_manager.d.ts +1 -1
  57. package/dest/testbench/worker_client_manager.d.ts.map +1 -1
  58. package/dest/testbench/worker_client_manager.js +6 -1
  59. package/package.json +14 -14
  60. package/src/client/interface.ts +19 -4
  61. package/src/client/p2p_client.ts +69 -110
  62. package/src/mem_pools/attestation_pool/attestation_pool.ts +68 -41
  63. package/src/mem_pools/attestation_pool/attestation_pool_test_suite.ts +231 -287
  64. package/src/mem_pools/attestation_pool/kv_attestation_pool.ts +162 -140
  65. package/src/mem_pools/attestation_pool/memory_attestation_pool.ts +141 -164
  66. package/src/mem_pools/attestation_pool/mocks.ts +13 -9
  67. package/src/msg_validators/attestation_validator/attestation_validator.ts +16 -13
  68. package/src/msg_validators/attestation_validator/fisherman_attestation_validator.ts +7 -7
  69. package/src/msg_validators/index.ts +1 -1
  70. package/src/msg_validators/proposal_validator/block_proposal_validator.ts +10 -0
  71. package/src/msg_validators/proposal_validator/checkpoint_proposal_validator.ts +13 -0
  72. package/src/msg_validators/proposal_validator/index.ts +3 -0
  73. package/src/msg_validators/{block_proposal_validator/block_proposal_validator.ts → proposal_validator/proposal_validator.ts} +23 -28
  74. package/src/msg_validators/proposal_validator/proposal_validator_test_suite.ts +206 -0
  75. package/src/services/dummy_service.ts +6 -0
  76. package/src/services/encoding.ts +3 -1
  77. package/src/services/libp2p/libp2p_service.ts +258 -94
  78. package/src/services/service.ts +19 -4
  79. package/src/testbench/p2p_client_testbench_worker.ts +34 -11
  80. package/src/testbench/worker_client_manager.ts +6 -1
  81. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts +0 -12
  82. package/dest/msg_validators/block_proposal_validator/block_proposal_validator.d.ts.map +0 -1
  83. package/dest/msg_validators/block_proposal_validator/index.d.ts +0 -2
  84. package/dest/msg_validators/block_proposal_validator/index.d.ts.map +0 -1
  85. package/dest/msg_validators/block_proposal_validator/index.js +0 -1
  86. package/src/msg_validators/block_proposal_validator/index.ts +0 -1
@@ -1,243 +1,172 @@
1
1
  import { SlotNumber } from '@aztec/foundation/branded-types';
2
2
  import { Secp256k1Signer } from '@aztec/foundation/crypto/secp256k1-signer';
3
3
  import { Fr } from '@aztec/foundation/curves/bn254';
4
- import { BlockProposal as BlockProposalClass, ConsensusPayload, SignatureDomainSeparator, getHashedSignaturePayloadEthSignedMessage } from '@aztec/stdlib/p2p';
5
- import { makeL2BlockHeader } from '@aztec/stdlib/testing';
6
- import { TxHash } from '@aztec/stdlib/tx';
7
- import { jest } from '@jest/globals';
8
- import { mock } from 'jest-mock-extended';
9
- import { mockAttestation } from './mocks.js';
4
+ import { makeBlockProposal, makeCheckpointProposal, makeL2BlockHeader } from '@aztec/stdlib/testing';
5
+ import { MAX_PROPOSALS_PER_SLOT } from './kv_attestation_pool.js';
6
+ import { mockCheckpointAttestation } from './mocks.js';
10
7
  const NUMBER_OF_SIGNERS_PER_TEST = 4;
11
8
  export function describeAttestationPool(getAttestationPool) {
12
9
  let ap;
13
10
  let signers;
14
- // Check that metrics are recorded correctly
15
- let metricsMock;
16
11
  beforeEach(()=>{
17
12
  ap = getAttestationPool();
18
13
  signers = Array.from({
19
14
  length: NUMBER_OF_SIGNERS_PER_TEST
20
15
  }, ()=>Secp256k1Signer.random());
21
- metricsMock = mock();
22
- // Can i overwrite this like this??
23
- ap.metrics = metricsMock;
24
16
  });
25
- const createAttestationsForSlot = (slotNumber)=>{
26
- const archive = Fr.random();
27
- return signers.map((signer)=>mockAttestation(signer, slotNumber, archive));
17
+ const createCheckpointAttestationsForSlot = (slotNumber, archive)=>{
18
+ const archiveToUse = archive ?? Fr.random();
19
+ return signers.map((signer)=>mockCheckpointAttestation(signer, slotNumber, archiveToUse));
28
20
  };
29
- const mockBlockProposal = (signer, slotNumber, archive = Fr.random())=>{
21
+ const mockBlockProposalForPool = (signer, slotNumber, archive = Fr.random())=>{
30
22
  const header = makeL2BlockHeader(1, 2, slotNumber);
31
- const payload = new ConsensusPayload(header.toCheckpointHeader(), archive);
32
- const hash = getHashedSignaturePayloadEthSignedMessage(payload, SignatureDomainSeparator.blockProposal);
33
- const signature = signer.sign(hash);
34
- const txHashes = [
35
- TxHash.random(),
36
- TxHash.random()
37
- ]; // Mock tx hashes
38
- return new BlockProposalClass(payload, signature, txHashes);
23
+ return makeBlockProposal({
24
+ signer,
25
+ blockHeader: header,
26
+ archiveRoot: archive
27
+ });
39
28
  };
40
- // We compare buffers as the objects can have cached values attached to them which are not serialised
41
- // using array containing as the kv store does not respect insertion order
42
- const compareAttestations = (a1, a2)=>{
29
+ // Compare checkpoint attestations buffers
30
+ // Using array containing as the kv store does not respect insertion order
31
+ const compareCheckpointAttestations = (a1, a2)=>{
43
32
  const a1Buffer = a1.map((attestation)=>attestation.toBuffer());
44
33
  const a2Buffer = a2.map((attestation)=>attestation.toBuffer());
45
34
  expect(a1Buffer.length).toBe(a2Buffer.length);
46
35
  expect(a1Buffer).toEqual(expect.arrayContaining(a2Buffer));
47
36
  };
48
- it('should add attestations to pool', async ()=>{
49
- const slotNumber = 420;
50
- const archive = Fr.random();
51
- const attestations = signers.slice(0, -1).map((signer)=>mockAttestation(signer, slotNumber, archive));
52
- await ap.addAttestations(attestations);
53
- const retrievedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
54
- expect(retrievedAttestations.length).toBe(attestations.length);
55
- compareAttestations(retrievedAttestations, attestations);
56
- // Check hasAttestation for added attestations
57
- for (const attestation of attestations){
58
- expect(await ap.hasAttestation(attestation)).toBe(true);
59
- }
60
- const retrievedAttestationsForSlot = await ap.getAttestationsForSlot(SlotNumber(slotNumber));
61
- expect(retrievedAttestationsForSlot.length).toBe(attestations.length);
62
- compareAttestations(retrievedAttestationsForSlot, attestations);
63
- // Add another one
64
- const newAttestation = mockAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
65
- await ap.addAttestations([
66
- newAttestation
67
- ]);
68
- const retrievedAttestationsAfterAdd = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
69
- expect(retrievedAttestationsAfterAdd.length).toBe(attestations.length + 1);
70
- compareAttestations(retrievedAttestationsAfterAdd, [
71
- ...attestations,
72
- newAttestation
73
- ]);
74
- expect(await ap.hasAttestation(newAttestation)).toBe(true);
75
- const retrievedAttestationsForSlotAfterAdd = await ap.getAttestationsForSlot(SlotNumber(slotNumber));
76
- expect(retrievedAttestationsForSlotAfterAdd.length).toBe(attestations.length + 1);
77
- compareAttestations(retrievedAttestationsForSlotAfterAdd, [
78
- ...attestations,
79
- newAttestation
80
- ]);
81
- // Delete by slot
82
- await ap.deleteAttestationsForSlot(SlotNumber(slotNumber));
83
- const retreivedAttestationsAfterDelete = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
84
- expect(retreivedAttestationsAfterDelete.length).toBe(0);
85
- // Check hasAttestation after deletion
86
- for (const attestation of attestations){
87
- expect(await ap.hasAttestation(attestation)).toBe(false);
88
- }
89
- expect(await ap.hasAttestation(newAttestation)).toBe(false);
90
- });
91
- it('should handle duplicate proposals in a slot', async ()=>{
92
- const slotNumber = 420;
93
- const archive = Fr.random();
94
- // Use the same signer for all attestations
95
- const attestations = [];
96
- const signer = signers[0];
97
- for(let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++){
98
- attestations.push(mockAttestation(signer, slotNumber, archive));
99
- }
100
- // Add them to store and check we end up with only one
101
- await ap.addAttestations(attestations);
102
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
103
- expect(retreivedAttestations.length).toBe(1);
104
- expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
105
- expect(retreivedAttestations[0].getSender()?.toString()).toEqual(signer.address.toString());
106
- // Try adding them on another operation and check they are still not duplicated
107
- await ap.addAttestations([
108
- attestations[0]
109
- ]);
110
- expect(await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString())).toHaveLength(1);
111
- });
112
- it('should store attestations by differing slot', async ()=>{
113
- const slotNumbers = [
114
- 1,
115
- 2,
116
- 3,
117
- 4
118
- ];
119
- const attestations = signers.map((signer, i)=>mockAttestation(signer, slotNumbers[i]));
120
- await ap.addAttestations(attestations);
121
- for (const attestation of attestations){
122
- const slot = attestation.payload.header.slotNumber;
123
- const archive = attestation.archive.toString();
124
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(slot, archive);
125
- expect(retreivedAttestations.length).toBe(1);
126
- expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
127
- expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
128
- }
129
- });
130
- it('should store attestations by differing slot and archive', async ()=>{
131
- const slotNumbers = [
132
- 1,
133
- 1,
134
- 2,
135
- 3
136
- ];
137
- const archives = [
138
- Fr.random(),
139
- Fr.random(),
140
- Fr.random(),
141
- Fr.random()
142
- ];
143
- const attestations = signers.map((signer, i)=>mockAttestation(signer, slotNumbers[i], archives[i]));
144
- await ap.addAttestations(attestations);
145
- for (const attestation of attestations){
146
- const slot = attestation.payload.header.slotNumber;
147
- const proposalId = attestation.archive.toString();
148
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(slot, proposalId);
37
+ describe('CheckpointAttestation', ()=>{
38
+ it('should add attestations to pool', async ()=>{
39
+ const slotNumber = 420;
40
+ const archive = Fr.random();
41
+ const attestations = signers.slice(0, -1).map((signer)=>mockCheckpointAttestation(signer, slotNumber, archive));
42
+ await ap.addCheckpointAttestations(attestations);
43
+ const retrievedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
44
+ expect(retrievedAttestations.length).toBe(attestations.length);
45
+ compareCheckpointAttestations(retrievedAttestations, attestations);
46
+ // Check hasCheckpointAttestation for added attestations
47
+ for (const attestation of attestations){
48
+ expect(await ap.hasCheckpointAttestation(attestation)).toBe(true);
49
+ }
50
+ const retrievedAttestationsForSlot = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
51
+ expect(retrievedAttestationsForSlot.length).toBe(attestations.length);
52
+ compareCheckpointAttestations(retrievedAttestationsForSlot, attestations);
53
+ // Add another one
54
+ const newAttestation = mockCheckpointAttestation(signers[NUMBER_OF_SIGNERS_PER_TEST - 1], slotNumber, archive);
55
+ await ap.addCheckpointAttestations([
56
+ newAttestation
57
+ ]);
58
+ const retrievedAttestationsAfterAdd = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
59
+ expect(retrievedAttestationsAfterAdd.length).toBe(attestations.length + 1);
60
+ compareCheckpointAttestations(retrievedAttestationsAfterAdd, [
61
+ ...attestations,
62
+ newAttestation
63
+ ]);
64
+ expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(true);
65
+ const retrievedAttestationsForSlotAfterAdd = await ap.getCheckpointAttestationsForSlot(SlotNumber(slotNumber));
66
+ expect(retrievedAttestationsForSlotAfterAdd.length).toBe(attestations.length + 1);
67
+ compareCheckpointAttestations(retrievedAttestationsForSlotAfterAdd, [
68
+ ...attestations,
69
+ newAttestation
70
+ ]);
71
+ // Delete by slot
72
+ await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(slotNumber + 1));
73
+ const retreivedAttestationsAfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
74
+ expect(retreivedAttestationsAfterDelete.length).toBe(0);
75
+ // Check hasCheckpointAttestation after deletion
76
+ for (const attestation of attestations){
77
+ expect(await ap.hasCheckpointAttestation(attestation)).toBe(false);
78
+ }
79
+ expect(await ap.hasCheckpointAttestation(newAttestation)).toBe(false);
80
+ });
81
+ it('should handle duplicate proposals in a slot', async ()=>{
82
+ const slotNumber = 420;
83
+ const archive = Fr.random();
84
+ // Use the same signer for all attestations
85
+ const attestations = [];
86
+ const signer = signers[0];
87
+ for(let i = 0; i < NUMBER_OF_SIGNERS_PER_TEST; i++){
88
+ attestations.push(mockCheckpointAttestation(signer, slotNumber, archive));
89
+ }
90
+ // Add them to store and check we end up with only one
91
+ await ap.addCheckpointAttestations(attestations);
92
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString());
149
93
  expect(retreivedAttestations.length).toBe(1);
150
- expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
151
- expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
152
- }
153
- });
154
- it('should delete attestations', async ()=>{
155
- const slotNumber = 420;
156
- const archive = Fr.random();
157
- const attestations = signers.map((signer)=>mockAttestation(signer, slotNumber, archive));
158
- const proposalId = attestations[0].archive.toString();
159
- await ap.addAttestations(attestations);
160
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
161
- expect(retreivedAttestations.length).toBe(NUMBER_OF_SIGNERS_PER_TEST);
162
- compareAttestations(retreivedAttestations, attestations);
163
- // Check hasAttestation before deletion
164
- for (const attestation of attestations){
165
- expect(await ap.hasAttestation(attestation)).toBe(true);
166
- }
167
- await ap.deleteAttestations(attestations);
168
- const gottenAfterDelete = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
169
- expect(gottenAfterDelete.length).toBe(0);
170
- // Check hasAttestation after deletion
171
- for (const attestation of attestations){
172
- expect(await ap.hasAttestation(attestation)).toBe(false);
173
- }
174
- });
175
- it('should blanket delete attestations per slot', async ()=>{
176
- const slotNumber = 420;
177
- const archive = Fr.random();
178
- const attestations = signers.map((signer)=>mockAttestation(signer, slotNumber, archive));
179
- const proposalId = attestations[0].archive.toString();
180
- await ap.addAttestations(attestations);
181
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
182
- expect(retreivedAttestations.length).toBe(NUMBER_OF_SIGNERS_PER_TEST);
183
- compareAttestations(retreivedAttestations, attestations);
184
- await ap.deleteAttestationsForSlot(SlotNumber(slotNumber));
185
- const retreivedAttestationsAfterDelete = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
186
- expect(retreivedAttestationsAfterDelete.length).toBe(0);
187
- });
188
- it('should blanket delete attestations per slot and proposal', async ()=>{
189
- const slotNumber = 420;
190
- const archive = Fr.random();
191
- const attestations = signers.map((signer)=>mockAttestation(signer, slotNumber, archive));
192
- const proposalId = attestations[0].archive.toString();
193
- // Add another set of attestations with a different proposalId, yet the same slot
194
- const archive2 = Fr.random();
195
- const attestations2 = signers.map((signer)=>mockAttestation(signer, slotNumber, archive2));
196
- const proposalId2 = attestations2[0].archive.toString();
197
- await ap.addAttestations(attestations);
198
- await ap.addAttestations(attestations2);
199
- const retreivedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
200
- expect(retreivedAttestations.length).toBe(NUMBER_OF_SIGNERS_PER_TEST);
201
- compareAttestations(retreivedAttestations, attestations);
202
- await ap.deleteAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
203
- const retreivedAttestationsAfterDelete = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
204
- expect(retreivedAttestationsAfterDelete.length).toBe(0);
205
- const retreivedAttestationsAfterDeleteForOtherProposal = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId2);
206
- expect(retreivedAttestationsAfterDeleteForOtherProposal.length).toBe(NUMBER_OF_SIGNERS_PER_TEST);
207
- compareAttestations(retreivedAttestationsAfterDeleteForOtherProposal, attestations2);
208
- });
209
- it('should delete attestations older than a given slot', async ()=>{
210
- const slotNumbers = [
211
- 1,
212
- 2,
213
- 3,
214
- 69,
215
- 72,
216
- 74,
217
- 88,
218
- 420
219
- ];
220
- const attestations = (await Promise.all(slotNumbers.map((slotNumber)=>createAttestationsForSlot(slotNumber)))).flat();
221
- const proposalId = attestations[0].archive.toString();
222
- await ap.addAttestations(attestations);
223
- const attestationsForSlot1 = await ap.getAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
224
- expect(attestationsForSlot1.length).toBe(signers.length);
225
- const deleteAttestationsSpy = jest.spyOn(ap, 'deleteAttestationsForSlot');
226
- await ap.deleteAttestationsOlderThan(SlotNumber(73));
227
- const attestationsForSlot1AfterDelete = await ap.getAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
228
- expect(attestationsForSlot1AfterDelete.length).toBe(0);
229
- expect(deleteAttestationsSpy).toHaveBeenCalledTimes(5);
230
- expect(deleteAttestationsSpy).toHaveBeenCalledWith(SlotNumber(1));
231
- expect(deleteAttestationsSpy).toHaveBeenCalledWith(SlotNumber(2));
232
- expect(deleteAttestationsSpy).toHaveBeenCalledWith(SlotNumber(3));
233
- expect(deleteAttestationsSpy).toHaveBeenCalledWith(SlotNumber(69));
234
- expect(deleteAttestationsSpy).toHaveBeenCalledWith(SlotNumber(72));
94
+ expect(retreivedAttestations[0].toBuffer()).toEqual(attestations[0].toBuffer());
95
+ expect(retreivedAttestations[0].getSender()?.toString()).toEqual(signer.address.toString());
96
+ // Try adding them on another operation and check they are still not duplicated
97
+ await ap.addCheckpointAttestations([
98
+ attestations[0]
99
+ ]);
100
+ expect(await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(slotNumber), archive.toString())).toHaveLength(1);
101
+ });
102
+ it('should store attestations by differing slot', async ()=>{
103
+ const slotNumbers = [
104
+ 1,
105
+ 2,
106
+ 3,
107
+ 4
108
+ ];
109
+ const attestations = signers.map((signer, i)=>mockCheckpointAttestation(signer, slotNumbers[i]));
110
+ await ap.addCheckpointAttestations(attestations);
111
+ for (const attestation of attestations){
112
+ const slot = attestation.payload.header.slotNumber;
113
+ const archive = attestation.archive.toString();
114
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, archive);
115
+ expect(retreivedAttestations.length).toBe(1);
116
+ expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
117
+ expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
118
+ }
119
+ });
120
+ it('should store attestations by differing slot and archive', async ()=>{
121
+ const slotNumbers = [
122
+ 1,
123
+ 1,
124
+ 2,
125
+ 3
126
+ ];
127
+ const archives = [
128
+ Fr.random(),
129
+ Fr.random(),
130
+ Fr.random(),
131
+ Fr.random()
132
+ ];
133
+ const attestations = signers.map((signer, i)=>mockCheckpointAttestation(signer, slotNumbers[i], archives[i]));
134
+ await ap.addCheckpointAttestations(attestations);
135
+ for (const attestation of attestations){
136
+ const slot = attestation.payload.header.slotNumber;
137
+ const proposalId = attestation.archive.toString();
138
+ const retreivedAttestations = await ap.getCheckpointAttestationsForSlotAndProposal(slot, proposalId);
139
+ expect(retreivedAttestations.length).toBe(1);
140
+ expect(retreivedAttestations[0].toBuffer()).toEqual(attestation.toBuffer());
141
+ expect(retreivedAttestations[0].payload.header.slotNumber).toEqual(slot);
142
+ }
143
+ });
144
+ it('should delete attestations older than a given slot', async ()=>{
145
+ const slotNumbers = [
146
+ 1,
147
+ 2,
148
+ 3,
149
+ 69,
150
+ 72,
151
+ 74,
152
+ 88,
153
+ 420
154
+ ];
155
+ const attestations = (await Promise.all(slotNumbers.map((slotNumber)=>createCheckpointAttestationsForSlot(slotNumber)))).flat();
156
+ const proposalId = attestations[0].archive.toString();
157
+ await ap.addCheckpointAttestations(attestations);
158
+ const attestationsForSlot1 = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
159
+ expect(attestationsForSlot1.length).toBe(signers.length);
160
+ await ap.deleteCheckpointAttestationsOlderThan(SlotNumber(73));
161
+ const attestationsForSlot1AfterDelete = await ap.getCheckpointAttestationsForSlotAndProposal(SlotNumber(1), proposalId);
162
+ expect(attestationsForSlot1AfterDelete.length).toBe(0);
163
+ });
235
164
  });
236
165
  describe('BlockProposal in attestation pool', ()=>{
237
166
  it('should add and retrieve block proposal', async ()=>{
238
167
  const slotNumber = 420;
239
168
  const archive = Fr.random();
240
- const proposal = mockBlockProposal(signers[0], slotNumber, archive);
169
+ const proposal = await mockBlockProposalForPool(signers[0], slotNumber, archive);
241
170
  const proposalId = proposal.archive.toString();
242
171
  await ap.addBlockProposal(proposal);
243
172
  const retrievedProposal = await ap.getBlockProposal(proposalId);
@@ -257,11 +186,11 @@ export function describeAttestationPool(getAttestationPool) {
257
186
  it('should update block proposal if added twice with same id', async ()=>{
258
187
  const slotNumber = 420;
259
188
  const archive = Fr.random();
260
- const proposal1 = mockBlockProposal(signers[0], slotNumber, archive);
189
+ const proposal1 = await mockBlockProposalForPool(signers[0], slotNumber, archive);
261
190
  const proposalId = proposal1.archive.toString();
262
191
  await ap.addBlockProposal(proposal1);
263
192
  // Create a new proposal with same archive but different signer
264
- const proposal2 = mockBlockProposal(signers[1], slotNumber, archive);
193
+ const proposal2 = await mockBlockProposalForPool(signers[1], slotNumber, archive);
265
194
  await ap.addBlockProposal(proposal2);
266
195
  const retrievedProposal = await ap.getBlockProposal(proposalId);
267
196
  expect(retrievedProposal).toBeDefined();
@@ -271,8 +200,8 @@ export function describeAttestationPool(getAttestationPool) {
271
200
  });
272
201
  it('should handle block proposals with different slots and same archive', async ()=>{
273
202
  const archive = Fr.random();
274
- const proposal1 = mockBlockProposal(signers[0], 100, archive);
275
- const proposal2 = mockBlockProposal(signers[1], 200, archive);
203
+ const proposal1 = await mockBlockProposalForPool(signers[0], 100, archive);
204
+ const proposal2 = await mockBlockProposalForPool(signers[1], 200, archive);
276
205
  const proposalId = archive.toString();
277
206
  await ap.addBlockProposal(proposal1);
278
207
  await ap.addBlockProposal(proposal2);
@@ -282,65 +211,99 @@ export function describeAttestationPool(getAttestationPool) {
282
211
  expect(retrievedProposal.toBuffer()).toEqual(proposal2.toBuffer());
283
212
  expect(retrievedProposal.slotNumber).toBe(SlotNumber(200));
284
213
  });
285
- it('should delete block proposal when deleting attestations for slot and proposal', async ()=>{
214
+ });
215
+ describe('CheckpointProposal in attestation pool', ()=>{
216
+ const mockCheckpointProposalForPool = (signer, slotNumber, archive = Fr.random())=>{
217
+ const header = makeL2BlockHeader(1, 2, slotNumber);
218
+ return makeCheckpointProposal({
219
+ signer,
220
+ checkpointHeader: header.toCheckpointHeader(),
221
+ archiveRoot: archive,
222
+ lastBlock: {
223
+ blockHeader: header
224
+ }
225
+ });
226
+ };
227
+ it('should add and retrieve checkpoint proposal as core (without lastBlock)', async ()=>{
286
228
  const slotNumber = 420;
287
229
  const archive = Fr.random();
288
- const proposal = mockBlockProposal(signers[0], slotNumber, archive);
230
+ const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
289
231
  const proposalId = proposal.archive.toString();
290
- // Add proposal and some attestations
291
- await ap.addBlockProposal(proposal);
292
- const attestations = signers.map((signer)=>mockAttestation(signer, slotNumber, archive));
293
- await ap.addAttestations(attestations);
294
- // Verify proposal exists
295
- let retrievedProposal = await ap.getBlockProposal(proposalId);
232
+ await ap.addCheckpointProposal(proposal);
233
+ const retrievedProposal = await ap.getCheckpointProposal(proposalId);
296
234
  expect(retrievedProposal).toBeDefined();
297
- expect(await ap.hasBlockProposal(proposalId)).toBe(true);
298
- // Delete attestations for slot and proposal
299
- await ap.deleteAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
300
- // Proposal should be deleted
301
- retrievedProposal = await ap.getBlockProposal(proposalId);
302
- expect(retrievedProposal).toBeUndefined();
303
- expect(await ap.hasBlockProposal(proposalId)).toBe(false);
235
+ // Should return core version (without lastBlock)
236
+ expect(retrievedProposal.toBuffer()).toEqual(proposal.toCore().toBuffer());
237
+ // Check hasCheckpointProposal with both id and object
238
+ expect(await ap.hasCheckpointProposal(proposalId)).toBe(true);
239
+ expect(await ap.hasCheckpointProposal(proposal)).toBe(true);
304
240
  });
305
- it('should delete block proposal when deleting attestations for slot', async ()=>{
241
+ it('should extract and store block proposal when adding checkpoint proposal with lastBlock', async ()=>{
306
242
  const slotNumber = 420;
307
243
  const archive = Fr.random();
308
- const proposal = mockBlockProposal(signers[0], slotNumber, archive);
244
+ const proposal = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
309
245
  const proposalId = proposal.archive.toString();
310
- // Add proposal
311
- await ap.addBlockProposal(proposal);
312
- // Verify proposal exists
313
- let retrievedProposal = await ap.getBlockProposal(proposalId);
314
- expect(retrievedProposal).toBeDefined();
315
- expect(await ap.hasBlockProposal(proposal)).toBe(true);
316
- // Delete attestations for slot
317
- await ap.deleteAttestationsForSlot(SlotNumber(slotNumber));
318
- // Proposal should be deleted
319
- retrievedProposal = await ap.getBlockProposal(proposalId);
320
- expect(retrievedProposal).toBeUndefined();
321
- expect(await ap.hasBlockProposal(proposal)).toBe(false);
246
+ // Verify the proposal has a lastBlock
247
+ const expectedBlockProposal = proposal.getBlockProposal();
248
+ expect(expectedBlockProposal).toBeDefined();
249
+ await ap.addCheckpointProposal(proposal);
250
+ // The block proposal should be stored separately and retrievable
251
+ const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
252
+ expect(retrievedBlockProposal).toBeDefined();
253
+ expect(retrievedBlockProposal.archive.toString()).toBe(archive.toString());
254
+ expect(retrievedBlockProposal.blockHeader.toBuffer()).toEqual(expectedBlockProposal.blockHeader.toBuffer());
322
255
  });
323
- it('should be able to fetch both block proposal and attestations', async ()=>{
256
+ it('should not store block proposal when checkpoint proposal has no lastBlock', async ()=>{
324
257
  const slotNumber = 420;
325
258
  const archive = Fr.random();
326
- const proposal = mockBlockProposal(signers[0], slotNumber, archive);
259
+ const header = makeL2BlockHeader(1, 2, slotNumber);
260
+ // Create a checkpoint proposal WITHOUT lastBlock
261
+ const proposal = await makeCheckpointProposal({
262
+ signer: signers[0],
263
+ checkpointHeader: header.toCheckpointHeader(),
264
+ archiveRoot: archive
265
+ });
327
266
  const proposalId = proposal.archive.toString();
328
- // Add proposal first
329
- await ap.addBlockProposal(proposal);
330
- // Add attestations for the same proposal
331
- const attestations = signers.slice(1).map((signer)=>mockAttestation(signer, slotNumber, archive));
332
- await ap.addAttestations(attestations);
333
- // Retrieve both proposal and attestations
334
- const retrievedProposal = await ap.getBlockProposal(proposalId);
335
- const retrievedAttestations = await ap.getAttestationsForSlotAndProposal(SlotNumber(slotNumber), proposalId);
267
+ await ap.addCheckpointProposal(proposal);
268
+ // The checkpoint proposal should be stored
269
+ const retrievedCheckpointProposal = await ap.getCheckpointProposal(proposalId);
270
+ expect(retrievedCheckpointProposal).toBeDefined();
271
+ // But no block proposal should be stored (archive key won't have a block proposal)
272
+ const retrievedBlockProposal = await ap.getBlockProposal(proposalId);
273
+ expect(retrievedBlockProposal).toBeUndefined();
274
+ });
275
+ it('should return undefined for non-existent checkpoint proposal', async ()=>{
276
+ const nonExistentId = Fr.random().toString();
277
+ const retrievedProposal = await ap.getCheckpointProposal(nonExistentId);
278
+ expect(retrievedProposal).toBeUndefined();
279
+ // Check hasCheckpointProposal returns false for non-existent proposal
280
+ expect(await ap.hasCheckpointProposal(nonExistentId)).toBe(false);
281
+ });
282
+ it('should update checkpoint proposal if added twice with same id', async ()=>{
283
+ const slotNumber = 420;
284
+ const archive = Fr.random();
285
+ const proposal1 = await mockCheckpointProposalForPool(signers[0], slotNumber, archive);
286
+ const proposalId = proposal1.archive.toString();
287
+ await ap.addCheckpointProposal(proposal1);
288
+ // Create a new proposal with same archive but different signer
289
+ const proposal2 = await mockCheckpointProposalForPool(signers[1], slotNumber, archive);
290
+ await ap.addCheckpointProposal(proposal2);
291
+ const retrievedProposal = await ap.getCheckpointProposal(proposalId);
336
292
  expect(retrievedProposal).toBeDefined();
337
- expect(retrievedProposal).toEqual(proposal);
338
- expect(await ap.hasBlockProposal(proposalId)).toBe(true);
339
- compareAttestations(retrievedAttestations, attestations);
340
- // Check hasAttestation for all attestations
341
- for (const attestation of attestations){
342
- expect(await ap.hasAttestation(attestation)).toBe(true);
293
+ // Should have the second proposal (as core)
294
+ expect(retrievedProposal.toBuffer()).toEqual(proposal2.toCore().toBuffer());
295
+ expect(retrievedProposal.getSender()?.toString()).toBe(signers[1].address.toString());
296
+ });
297
+ it('should throw ProposalSlotCapExceededError when exceeding capacity', async ()=>{
298
+ const slotNumber = 420;
299
+ // Add MAX_PROPOSALS_PER_SLOT proposals
300
+ for(let i = 0; i < MAX_PROPOSALS_PER_SLOT; i++){
301
+ const proposal = await mockCheckpointProposalForPool(signers[i % NUMBER_OF_SIGNERS_PER_TEST], slotNumber);
302
+ await ap.addCheckpointProposal(proposal);
343
303
  }
304
+ // The next proposal should throw
305
+ const extraProposal = await mockCheckpointProposalForPool(signers[0], slotNumber);
306
+ await expect(ap.addCheckpointProposal(extraProposal)).rejects.toThrow('Maximum checkpoint proposals per slot');
344
307
  });
345
308
  });
346
309
  }