gcx/blockchain/gas-relayer/src/modules/nonce/nonce-manager.service.ts

57 lines
2.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 链上 nonceRedis 原子操作,防 nonce 冲突) */
async next(relayerAddress: string): Promise<number> {
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<void> {
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<boolean> {
const key = `user:nonce:${userAddress}`;
return (await this.redis.sismember(key, nonce.toString())) === 1;
}
/** 标记用户 nonce 已使用 */
async markNonceUsed(userAddress: string, nonce: number): Promise<void> {
const key = `user:nonce:${userAddress}`;
await this.redis.sadd(key, nonce.toString());
}
/** 获取用户每分钟请求计数(用于熔断) */
async getUserRateCount(userAddress: string): Promise<number> {
const key = `rate:${userAddress}`;
const count = await this.redis.get(key);
return parseInt(count || '0', 10);
}
/** 递增用户请求计数 */
async incrementUserRate(userAddress: string): Promise<number> {
const key = `rate:${userAddress}`;
const count = await this.redis.incr(key);
if (count === 1) {
await this.redis.expire(key, 60); // 60秒过期
}
return count;
}
}