11import { times } from '@aztec/foundation/collection' ;
22import { EthAddress } from '@aztec/foundation/eth-address' ;
3+ import { type Logger , createLogger } from '@aztec/foundation/log' ;
34import { AztecLMDBStoreV2 , openTmpStore } from '@aztec/kv-store/lmdb-v2' ;
45import type { ValidatorStatusInSlot } from '@aztec/stdlib/validators' ;
56
@@ -8,11 +9,15 @@ import { SentinelStore } from './store.js';
89describe ( 'sentinel-store' , ( ) => {
910 let kvStore : AztecLMDBStoreV2 ;
1011 let store : SentinelStore ;
12+ let log : Logger ;
13+
1114 const historyLength = 4 ;
15+ const historicProvenPerformanceLength = 3 ;
1216
1317 beforeEach ( async ( ) => {
18+ log = createLogger ( 'sentinel:store:test' ) ;
1419 kvStore = await openTmpStore ( 'sentinel-store-test' ) ;
15- store = new SentinelStore ( kvStore , { historyLength } ) ;
20+ store = new SentinelStore ( kvStore , { historyLength, historicProvenPerformanceLength } ) ;
1621 } ) ;
1722
1823 afterEach ( async ( ) => {
@@ -114,6 +119,85 @@ describe('sentinel-store', () => {
114119 ] ) ;
115120 } ) ;
116121
122+ it ( 'trims proven performance to the specified historicProvenPerformanceLength' , async ( ) => {
123+ const validator = EthAddress . random ( ) ;
124+
125+ // Add 5 epochs worth of proven performance data (more than historicProvenPerformanceLength = 3)
126+ for ( let i = 1 ; i <= 5 ; i ++ ) {
127+ await store . updateProvenPerformance ( BigInt ( i ) , { [ validator . toString ( ) ] : { missed : i , total : 10 } } ) ;
128+ }
129+
130+ const provenPerformance = await store . getProvenPerformance ( validator ) ;
131+
132+ // Should only keep the most recent 3 entries (epochs 3, 4, 5)
133+ expect ( provenPerformance ) . toHaveLength ( historicProvenPerformanceLength ) ;
134+ expect ( provenPerformance ) . toEqual ( [
135+ { epoch : 3n , missed : 3 , total : 10 } ,
136+ { epoch : 4n , missed : 4 , total : 10 } ,
137+ { epoch : 5n , missed : 5 , total : 10 } ,
138+ ] ) ;
139+ } ) ;
140+
141+ it ( 'getHistoricProvenPerformanceLength returns the correct value' , ( ) => {
142+ expect ( store . getHistoricProvenPerformanceLength ( ) ) . toBe ( historicProvenPerformanceLength ) ;
143+ } ) ;
144+
145+ it ( 'proven performance with 2k entries' , async ( ) => {
146+ const validator = EthAddress . random ( ) ;
147+ const totalEntries = 2000 ;
148+
149+ log . info ( `Starting stress test with ${ totalEntries } proven performance entries` ) ;
150+
151+ // Track timing for additions
152+ const addTimes : number [ ] = [ ] ;
153+ const startTime = Date . now ( ) ;
154+
155+ // Add 2k entries
156+ for ( let i = 1 ; i <= totalEntries ; i ++ ) {
157+ const addStart = Date . now ( ) ;
158+ await store . updateProvenPerformance ( BigInt ( i ) , {
159+ [ validator . toString ( ) ] : { missed : i % 10 , total : 10 } ,
160+ } ) ;
161+ const addEnd = Date . now ( ) ;
162+ addTimes . push ( addEnd - addStart ) ;
163+
164+ // Log progress every 500 entries
165+ if ( i % 500 === 0 ) {
166+ const avgAddTime = addTimes . slice ( - 500 ) . reduce ( ( a , b ) => a + b , 0 ) / 500 ;
167+ log . info ( `Added ${ i } /${ totalEntries } entries, avg time per entry: ${ avgAddTime . toFixed ( 2 ) } ms` ) ;
168+ }
169+ }
170+
171+ const totalAddTime = Date . now ( ) - startTime ;
172+ const avgAddTime = addTimes . reduce ( ( a , b ) => a + b , 0 ) / addTimes . length ;
173+
174+ log . info ( `Added ${ totalEntries } entries in ${ totalAddTime } ms with avg ${ avgAddTime . toFixed ( 2 ) } ms per entry` ) ;
175+
176+ // Track timing for retrievals
177+ const retrievalTimes : number [ ] = [ ] ;
178+ const numRetrievals = 10 ;
179+
180+ log . info ( `Starting ${ numRetrievals } retrieval tests` ) ;
181+
182+ for ( let i = 0 ; i < numRetrievals ; i ++ ) {
183+ const retrievalStart = Date . now ( ) ;
184+ const performance = await store . getProvenPerformance ( validator ) ;
185+ const retrievalEnd = Date . now ( ) ;
186+ retrievalTimes . push ( retrievalEnd - retrievalStart ) ;
187+
188+ // Verify we only keep the configured number of entries
189+ expect ( performance ) . toHaveLength ( historicProvenPerformanceLength ) ;
190+
191+ // Verify we kept the most recent entries
192+ const expectedStartEpoch = totalEntries - historicProvenPerformanceLength + 1 ;
193+ expect ( performance [ 0 ] . epoch ) . toBe ( BigInt ( expectedStartEpoch ) ) ;
194+ expect ( performance [ performance . length - 1 ] . epoch ) . toBe ( BigInt ( totalEntries ) ) ;
195+ }
196+
197+ const avgRetrievalTime = retrievalTimes . reduce ( ( a , b ) => a + b , 0 ) / retrievalTimes . length ;
198+ log . info ( `Completed ${ numRetrievals } retrievals with avg time of ${ avgRetrievalTime . toFixed ( 2 ) } ms per retrieval` ) ;
199+ } ) ;
200+
117201 it ( 'does not allow insertion of invalid validator addresses' , async ( ) => {
118202 const validator = '0x123' ;
119203 await expect ( store . updateProvenPerformance ( 1n , { [ validator ] : { missed : 2 , total : 10 } } ) ) . rejects . toThrow ( ) ;
0 commit comments