Skip to content

Commit f6bfa49

Browse files
authored
Merge pull request #357 from olawaleakanbi035-maker/feat/oracle-worker-queue
Feat/oracle worker queue
2 parents 3f5386d + ede78ba commit f6bfa49

8 files changed

Lines changed: 70 additions & 15 deletions

File tree

oracle/README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,30 @@ npm test
104104
- ✅ Already-finalized raffle handling
105105
- ✅ Error handling and retry behavior
106106

107+
## Queue & Redis
108+
109+
The oracle uses **Bull** (backed by Redis) to reliably process randomness requests.
110+
111+
| Setting | Value |
112+
|---------|-------|
113+
| Queue name | `randomness-queue` |
114+
| Retries | 5 attempts, exponential backoff (2 s base) |
115+
| Failed jobs | Retained in Redis for inspection (`removeOnFail: false`) |
116+
| Alert | `[ALERT]` log emitted when all attempts are exhausted |
117+
118+
**Required environment variables:**
119+
120+
```
121+
REDIS_HOST=localhost # Redis server hostname
122+
REDIS_PORT=6379 # Redis server port
123+
```
124+
125+
Redis must be running before starting the oracle. A minimal local setup:
126+
127+
```bash
128+
docker run -d -p 6379:6379 redis:7-alpine
129+
```
130+
107131
## Configuration
108132

109133
The service requires the following environment variables for queue operations:

oracle/jest.config.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ module.exports = {
88
'^.+\\.(t|j)s$': 'ts-jest',
99
},
1010
transformIgnorePatterns: [
11-
// Transform ESM modules from @noble and @stellar packages
12-
'node_modules/(?!(@noble|@stellar|stellar-sdk)/)',
11+
'node_modules/(?!(.pnpm/)?(@noble|@stellar|stellar-sdk))',
1312
],
1413
moduleNameMapper: {
1514
'^src/(.*)$': '<rootDir>/src/$1',
15+
'^@noble/curves/(.*)(?<!\\.js)$': '@noble/curves/$1.js',
1616
},
1717
globals: {
1818
'ts-jest': {

oracle/src/queue/queue.module.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,9 @@ import { RANDOMNESS_QUEUE } from './randomness.queue';
3131
name: RANDOMNESS_QUEUE,
3232
defaultJobOptions: {
3333
attempts: 5,
34-
backoff: {
35-
type: 'exponential',
36-
delay: 2000,
37-
},
34+
backoff: { type: 'exponential', delay: 2000 },
3835
removeOnComplete: true,
36+
removeOnFail: false,
3937
},
4038
}),
4139
],
@@ -51,6 +49,6 @@ import { RANDOMNESS_QUEUE } from './randomness.queue';
5149
HealthService,
5250
LagMonitorService,
5351
],
54-
exports: [RandomnessWorker, CommitRevealWorker, BullModule],
52+
exports: [RandomnessWorker, CommitRevealWorker, BullModule.registerQueue({ name: RANDOMNESS_QUEUE })],
5553
})
5654
export class QueueModule { }
Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,3 @@
11
export const RANDOMNESS_QUEUE = 'randomness-queue';
22

3-
export interface RandomnessJobPayload {
4-
raffleId: number;
5-
requestId: string;
6-
prizeAmount?: number;
7-
}
3+
export { RandomnessRequest as RandomnessJobPayload } from './queue.types';

oracle/src/queue/randomness.worker.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,11 @@ export class RandomnessWorker {
199199
@OnQueueFailed()
200200
onFailed(job: Job, err: Error) {
201201
this.logger.error(`Failed job ${job.id} of type ${job.name}: ${err.message}`);
202+
if (job.attemptsMade >= (job.opts.attempts ?? 1)) {
203+
this.logger.error(
204+
`[ALERT] Job ${job.id} exhausted all ${job.opts.attempts} attempts for raffle ${job.data?.raffleId}, request ${job.data?.requestId}. Manual intervention required.`,
205+
);
206+
}
202207
}
203208

204209
private determineMethod(prizeAmount: number): RandomnessMethod {

oracle/src/submitter/tx-submitter.service.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,10 @@ export class TxSubmitterService {
2727
private readonly POLL_TIMEOUT_MS = 30000;
2828
private readonly POLL_INTERVAL_MS = 1000;
2929

30-
constructor(private readonly configService: ConfigService) {
30+
constructor(
31+
private readonly configService: ConfigService,
32+
private readonly feeEstimator: FeeEstimatorService,
33+
) {
3134
const primary =
3235
this.configService.get<string>('SOROBAN_RPC_URL') ||
3336
'https://soroban-testnet.stellar.org';
@@ -86,9 +89,10 @@ export class TxSubmitterService {
8689

8790
let attempt = 0;
8891
let lastError: any = null;
92+
let feeBump = 1;
8993

9094
// Get initial fee estimate from network stats
91-
const feeEstimate = await this.feeEstimator.estimateFee(rafflePrizeXLM);
95+
const feeEstimate = await this.feeEstimator.estimateFee(0);
9296
let currentFee = feeEstimate.cappedFee;
9397

9498
this.logger.log(
@@ -181,7 +185,7 @@ export class TxSubmitterService {
181185
feeStroops: number,
182186
) {
183187
const account = await this.rpcServer.getAccount(sourceAddress);
184-
const fee = (Number((StellarSdk as any).BASE_FEE || 100) * feeBump).toString();
188+
const fee = (Number((StellarSdk as any).BASE_FEE || 100) * feeStroops).toString();
185189

186190
const seedBytes = this.parseToBytes(randomness.seed, 32);
187191
const proofBytes = this.parseToBytes(randomness.proof, 64);

oracle/test/randomness.worker.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { TxSubmitterService } from '../src/submitter/tx-submitter.service';
77
import { RandomnessRequest, RandomnessMethod } from '../src/queue/queue.types';
88
import { HealthService } from '../src/health/health.service';
99
import { LagMonitorService } from '../src/health/lag-monitor.service';
10+
import { OracleRegistryService } from '../src/multi-oracle/oracle-registry.service';
11+
import { MultiOracleCoordinatorService } from '../src/multi-oracle/multi-oracle-coordinator.service';
1012

1113
describe('RandomnessWorker', () => {
1214
let worker: RandomnessWorker;
@@ -60,6 +62,24 @@ describe('RandomnessWorker', () => {
6062
updateCurrentLedger: jest.fn(),
6163
},
6264
},
65+
{
66+
provide: OracleRegistryService,
67+
useValue: {
68+
isMultiOracleMode: jest.fn().mockReturnValue(false),
69+
getLocalOracleId: jest.fn().mockReturnValue('oracle-1'),
70+
getLocalOracle: jest.fn(),
71+
getThreshold: jest.fn().mockReturnValue(1),
72+
},
73+
},
74+
{
75+
provide: MultiOracleCoordinatorService,
76+
useValue: {
77+
isTracked: jest.fn().mockReturnValue(false),
78+
startTracking: jest.fn(),
79+
hasSubmitted: jest.fn().mockReturnValue(false),
80+
recordSubmission: jest.fn().mockReturnValue({ ready: false, aggregated: null }),
81+
},
82+
},
6383
],
6484
}).compile();
6585

oracle/test/vrf.service.spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { VrfService } from '../src/randomness/vrf.service';
33
import { KeyService } from '../src/keys/key.service';
44
import { ConfigService } from '@nestjs/config';
55
import { Keypair } from 'stellar-sdk';
6+
import { OracleRegistryService } from '../src/multi-oracle/oracle-registry.service';
67

78
describe('VrfService', () => {
89
let service: VrfService;
@@ -20,6 +21,13 @@ describe('VrfService', () => {
2021
},
2122
},
2223
KeyService,
24+
{
25+
provide: OracleRegistryService,
26+
useValue: {
27+
getOracle: jest.fn(),
28+
getLocalKeypair: jest.fn().mockReturnValue(Keypair.random()),
29+
},
30+
},
2331
],
2432
}).compile();
2533

0 commit comments

Comments
 (0)