import { Injectable, Logger } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import Redis from 'ioredis'; @Injectable() export class NonceManagerService { private readonly logger = new Logger(NonceManagerService.name); private redis: Redis; constructor(private config: ConfigService) { this.redis = new Redis(this.config.get('REDIS_URL') || 'redis://localhost:6379/2'); } /** 获取并递增 Relayer 链上 nonce(Redis 原子操作,防 nonce 冲突) */ async next(relayerAddress: string): Promise { const key = `relayer:nonce:${relayerAddress}`; const nonce = await this.redis.incr(key); return nonce - 1; // incr returns value after increment } /** 初始化 nonce(从链上同步) */ async initialize(relayerAddress: string, chainNonce: number): Promise { const key = `relayer:nonce:${relayerAddress}`; await this.redis.set(key, chainNonce); this.logger.log(`Nonce initialized for ${relayerAddress}: ${chainNonce}`); } /** 检查用户 meta-tx nonce 是否已使用(防重放) */ async isNonceUsed(userAddress: string, nonce: number): Promise { const key = `user:nonce:${userAddress}`; return (await this.redis.sismember(key, nonce.toString())) === 1; } /** 标记用户 nonce 已使用 */ async markNonceUsed(userAddress: string, nonce: number): Promise { const key = `user:nonce:${userAddress}`; await this.redis.sadd(key, nonce.toString()); } /** 获取用户每分钟请求计数(用于熔断) */ async getUserRateCount(userAddress: string): Promise { const key = `rate:${userAddress}`; const count = await this.redis.get(key); return parseInt(count || '0', 10); } /** 递增用户请求计数 */ async incrementUserRate(userAddress: string): Promise { const key = `rate:${userAddress}`; const count = await this.redis.incr(key); if (count === 1) { await this.redis.expire(key, 60); // 60秒过期 } return count; } }