@@ -23,8 +23,9 @@ const minaClient = new Client({ network: 'testnet' });
2323
2424admin . initializeApp ( ) ;
2525
26- // Rate limit duration between heartbeats from the same submitter (15 seconds)
27- const HEARTBEAT_RATE_LIMIT_MS = 15000 ;
26+ // Rate limit configuration: sliding window
27+ const WINDOW_SIZE_MS = 60000 ; // 1 minute window
28+ const MAX_REQUESTS_PER_WINDOW = 6 ;
2829
2930function validateSignature (
3031 data : string ,
@@ -96,21 +97,36 @@ export const handleValidationAndStore = onCall(
9697 const newHeartbeatRef = db . collection ( 'heartbeats' ) . doc ( ) ;
9798
9899 await db . runTransaction ( async ( transaction ) => {
99- const doc = await transaction . get ( rateLimitRef ) ;
100+ const rateLimitDoc = await transaction . get ( rateLimitRef ) ;
100101 const now = Date . now ( ) ;
101- const cutoff = now - HEARTBEAT_RATE_LIMIT_MS ;
102+ const windowStart = now - WINDOW_SIZE_MS ;
102103
103- if ( doc . exists ) {
104- const lastCall = doc . data ( ) ?. [ 'lastCall' ] ;
105- if ( lastCall > cutoff ) {
104+ if ( rateLimitDoc . exists ) {
105+ const data = rateLimitDoc . data ( ) ;
106+ const previousTimestamps : number [ ] = data ?. timestamps || [ ] ;
107+ const currentWindowTimestamps = previousTimestamps . filter ( ts => ts > windowStart ) ;
108+
109+ currentWindowTimestamps . push ( now ) ;
110+
111+ if ( currentWindowTimestamps . length > MAX_REQUESTS_PER_WINDOW ) {
106112 throw new functions . https . HttpsError (
107113 'resource-exhausted' ,
108- 'Rate limit exceeded for this public key ' ,
114+ 'Rate limit exceeded' ,
109115 ) ;
110116 }
117+
118+ transaction . set ( rateLimitRef , {
119+ timestamps : currentWindowTimestamps ,
120+ lastCall : FieldValue . serverTimestamp ( ) ,
121+ } ) ;
122+ } else {
123+ // First request for this public key
124+ transaction . set ( rateLimitRef , {
125+ timestamps : [ now ] ,
126+ lastCall : FieldValue . serverTimestamp ( ) ,
127+ } ) ;
111128 }
112129
113- transaction . set ( rateLimitRef , { lastCall : FieldValue . serverTimestamp ( ) } , { merge : true } ) ;
114130 transaction . create ( newHeartbeatRef , {
115131 ...data ,
116132 createTime : FieldValue . serverTimestamp ( ) ,
0 commit comments