@cheqd/studio 3.18.0-develop.2 → 3.18.0-develop.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -6
- package/dist/controllers/api/account.d.ts +4 -2
- package/dist/controllers/api/account.d.ts.map +1 -1
- package/dist/controllers/api/account.js +76 -26
- package/dist/controllers/api/account.js.map +1 -1
- package/dist/database/entities/faucet-request.entity.d.ts +1 -2
- package/dist/database/entities/faucet-request.entity.d.ts.map +1 -1
- package/dist/database/entities/faucet-request.entity.js +11 -12
- package/dist/database/entities/faucet-request.entity.js.map +1 -1
- package/dist/database/migrations/1791500000002-studio-migrations.d.ts +7 -0
- package/dist/database/migrations/1791500000002-studio-migrations.d.ts.map +1 -0
- package/dist/database/migrations/1791500000002-studio-migrations.js +10 -0
- package/dist/database/migrations/1791500000002-studio-migrations.js.map +1 -0
- package/dist/database/types/types.d.ts.map +1 -1
- package/dist/database/types/types.js +3 -0
- package/dist/database/types/types.js.map +1 -1
- package/dist/helpers/faucet-quota.d.ts +30 -2
- package/dist/helpers/faucet-quota.d.ts.map +1 -1
- package/dist/helpers/faucet-quota.js +73 -9
- package/dist/helpers/faucet-quota.js.map +1 -1
- package/dist/helpers/faucet.d.ts +5 -0
- package/dist/helpers/faucet.d.ts.map +1 -1
- package/dist/helpers/faucet.js +18 -0
- package/dist/helpers/faucet.js.map +1 -1
- package/dist/services/api/faucet-request.d.ts +24 -2
- package/dist/services/api/faucet-request.d.ts.map +1 -1
- package/dist/services/api/faucet-request.js +102 -13
- package/dist/services/api/faucet-request.js.map +1 -1
- package/dist/static/swagger-api.json +2 -2
- package/dist/types/constants.d.ts +2 -0
- package/dist/types/constants.d.ts.map +1 -1
- package/dist/types/constants.js +10 -2
- package/dist/types/constants.js.map +1 -1
- package/example.env +4 -0
- package/package.json +1 -1
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export class StudioMigrations1791500000002 {
|
|
2
|
+
name = 'StudioMigrations1791500000002';
|
|
3
|
+
async up(queryRunner) {
|
|
4
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ALTER COLUMN "createdAt" SET DEFAULT now()`);
|
|
5
|
+
}
|
|
6
|
+
async down(queryRunner) {
|
|
7
|
+
await queryRunner.query(`ALTER TABLE "faucetRequest" ALTER COLUMN "createdAt" DROP DEFAULT`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=1791500000002-studio-migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"1791500000002-studio-migrations.js","sourceRoot":"","sources":["../../../src/database/migrations/1791500000002-studio-migrations.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,6BAA6B;IACzC,IAAI,GAAG,+BAA+B,CAAC;IAEhC,KAAK,CAAC,EAAE,CAAC,WAAwB;QACvC,MAAM,WAAW,CAAC,KAAK,CAAC,wEAAwE,CAAC,CAAC;IACnG,CAAC;IAEM,KAAK,CAAC,IAAI,CAAC,WAAwB;QACzC,MAAM,WAAW,CAAC,KAAK,CAAC,mEAAmE,CAAC,CAAC;IAC9F,CAAC;CACD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAiErC,MAAM,WAAW,gBAAgB;IAChC,KAAK,IAAI,UAAU,CAAC;CACpB;AAED,qBAAa,MAAO,YAAW,gBAAgB;IAC9C,KAAK,IAAI,UAAU;CAWnB;AAED,qBAAa,QAAS,YAAW,gBAAgB;IAChD,KAAK,IAAI,UAAU;CAyGnB"}
|
|
@@ -56,6 +56,7 @@ import { UpdateWriteCursors1762775500000 } from '../migrations/1762775500000-Upd
|
|
|
56
56
|
import { StudioMigrations1766408271347 } from '../migrations/1766408271347-studio-migrations.js';
|
|
57
57
|
import { StudioMigrations1791500000000 } from '../migrations/1791500000000-studio-migrations.js';
|
|
58
58
|
import { StudioMigrations1791500000001 } from '../migrations/1791500000001-studio-migrations.js';
|
|
59
|
+
import { StudioMigrations1791500000002 } from '../migrations/1791500000002-studio-migrations.js';
|
|
59
60
|
dotenv.config();
|
|
60
61
|
const { EXTERNAL_DB_CONNECTION_URL, EXTERNAL_DB_CERT } = process.env;
|
|
61
62
|
export class Memory {
|
|
@@ -150,6 +151,8 @@ export class Postgres {
|
|
|
150
151
|
StudioMigrations1791500000000,
|
|
151
152
|
// Track whether each faucet request completed, so abandoned reservations can stop counting
|
|
152
153
|
StudioMigrations1791500000001,
|
|
154
|
+
// Let the database set faucetRequest.createdAt
|
|
155
|
+
StudioMigrations1791500000002,
|
|
153
156
|
],
|
|
154
157
|
entities: [
|
|
155
158
|
...Entities,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/database/types/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,12 +19,22 @@ export declare function maxRequestableNcheq(roomUnderCapNcheq: bigint, quotaRema
|
|
|
19
19
|
/** Remaining quota, never negative. */
|
|
20
20
|
export declare function remainingQuota(usedNcheq: bigint, limitNcheq: bigint): bigint;
|
|
21
21
|
export declare function buildFaucetQuotaSummary(usedNcheq: bigint, limitNcheq: bigint, window: FaucetQuotaWindow): FaucetQuotaSummary;
|
|
22
|
+
/** Rows older than this are past retention: the start of the calendar month `retentionMonths` months ago (UTC). */
|
|
23
|
+
export declare function retentionCutoff(now: Date, retentionMonths: number): Date;
|
|
22
24
|
/** Seconds the customer still has to wait before their next faucet request; 0 if one is allowed now. */
|
|
23
25
|
export declare function secondsUntilNextRequest(lastRequestAt: Date | null, minIntervalSeconds: number, now?: Date): number;
|
|
26
|
+
/** Room left under an address cap once its balance and requests still in flight are accounted for; never negative. */
|
|
27
|
+
export declare function addressRoomNcheq(capNcheq: bigint, currentBalanceNcheq: bigint, inFlightNcheq: bigint): bigint;
|
|
28
|
+
export interface FaucetAddressCap {
|
|
29
|
+
capNcheq: bigint;
|
|
30
|
+
currentBalanceNcheq: bigint;
|
|
31
|
+
settleSeconds: number;
|
|
32
|
+
}
|
|
24
33
|
export interface FaucetReserveOptions {
|
|
25
34
|
limitNcheq: bigint;
|
|
26
35
|
pendingTimeoutSeconds?: number;
|
|
27
36
|
minIntervalSeconds?: number;
|
|
37
|
+
addressCap?: FaucetAddressCap;
|
|
28
38
|
}
|
|
29
39
|
export type FaucetReservation = {
|
|
30
40
|
reserved: true;
|
|
@@ -41,6 +51,11 @@ export type FaucetReservation = {
|
|
|
41
51
|
reason: 'too_frequent';
|
|
42
52
|
retryAfterSeconds: number;
|
|
43
53
|
window: FaucetQuotaWindow;
|
|
54
|
+
} | {
|
|
55
|
+
reserved: false;
|
|
56
|
+
reason: 'address_cap_exceeded';
|
|
57
|
+
roomNcheq: bigint;
|
|
58
|
+
window: FaucetQuotaWindow;
|
|
44
59
|
};
|
|
45
60
|
/** Storage for quota reservations; implemented by `FaucetRequestService` and faked in tests. */
|
|
46
61
|
export interface FaucetQuotaLedger {
|
|
@@ -59,6 +74,9 @@ export type FaucetCreditResult = {
|
|
|
59
74
|
outcome: 'too_frequent';
|
|
60
75
|
retryAfterSeconds: number;
|
|
61
76
|
window: FaucetQuotaWindow;
|
|
77
|
+
} | {
|
|
78
|
+
outcome: 'address_cap_exceeded';
|
|
79
|
+
roomNcheq: bigint;
|
|
62
80
|
} | {
|
|
63
81
|
outcome: 'credited';
|
|
64
82
|
usedNcheq: bigint;
|
|
@@ -68,14 +86,23 @@ export type FaucetCreditResult = {
|
|
|
68
86
|
status: number;
|
|
69
87
|
error: string;
|
|
70
88
|
};
|
|
89
|
+
export type FaucetLogLevel = 'info' | 'warn' | 'error';
|
|
90
|
+
export type FaucetLogger = (level: FaucetLogLevel, event: string, fields: Record<string, unknown>) => void;
|
|
91
|
+
/**
|
|
92
|
+
* Writes one structured log line per faucet decision, e.g. `{"event":"faucet.quota_exceeded","customerId":"..."}`,
|
|
93
|
+
* so "why was this customer blocked?" and "where did this month's quota go?" can be answered from the logs.
|
|
94
|
+
* Amounts are ncheq strings. Pass another logger to creditWithinQuota to capture events in tests.
|
|
95
|
+
*/
|
|
96
|
+
export declare const logFaucetEvent: FaucetLogger;
|
|
71
97
|
/**
|
|
72
98
|
* Reserves quota, calls the faucet, and settles the reservation.
|
|
73
|
-
* - Quota does not
|
|
99
|
+
* - Quota, minimum interval or address cap does not allow it: the faucet is not called.
|
|
74
100
|
* - Faucet answers with an error status: it definitely did not credit the account, so the quota is released.
|
|
75
101
|
* - Faucet call throws (e.g. a timeout): the outcome is unknown and tokens may have been sent, so the
|
|
76
102
|
* reservation is marked unknown and keeps counting for good (over-counting is safer than refunding quota
|
|
77
103
|
* for tokens that were credited).
|
|
78
104
|
* - Faucet confirms: the reservation is marked completed.
|
|
105
|
+
* Every outcome is logged through `log`.
|
|
79
106
|
*/
|
|
80
107
|
export declare function creditWithinQuota(ledger: FaucetQuotaLedger, delegate: () => Promise<{
|
|
81
108
|
status: number;
|
|
@@ -86,5 +113,6 @@ export declare function creditWithinQuota(ledger: FaucetQuotaLedger, delegate: (
|
|
|
86
113
|
amountNcheq: bigint;
|
|
87
114
|
limitNcheq: bigint;
|
|
88
115
|
minIntervalSeconds?: number;
|
|
89
|
-
|
|
116
|
+
addressCap?: FaucetAddressCap;
|
|
117
|
+
}, log?: FaucetLogger): Promise<FaucetCreditResult>;
|
|
90
118
|
//# sourceMappingURL=faucet-quota.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet-quota.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,KAAK,EAAuB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGnF;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;CACf;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,IAAiB,GAAG,iBAAiB,CAO9E;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAE3F;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAElG;AAED,sHAAsH;AACtH,wBAAgB,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,MAAM,CAGlG;AAED,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACvB,kBAAkB,CAUpB;AAED,wGAAwG;AACxG,wBAAgB,uBAAuB,CACtC,aAAa,EAAE,IAAI,GAAG,IAAI,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,GAAG,GAAE,IAAiB,GACpB,MAAM,CAIR;AAED,MAAM,WAAW,oBAAoB;IAEpC,UAAU,EAAE,MAAM,CAAC;IAEnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"faucet-quota.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yCAAyC,CAAC;AAC9E,OAAO,KAAK,EAAuB,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAGnF;;;GAGG;AAEH,MAAM,WAAW,iBAAiB;IACjC,KAAK,EAAE,IAAI,CAAC;IACZ,QAAQ,EAAE,IAAI,CAAC;CACf;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,GAAG,GAAE,IAAiB,GAAG,iBAAiB,CAO9E;AAED,2EAA2E;AAC3E,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,GAAE,IAAiB,GAAG,MAAM,CAE3F;AAED,uFAAuF;AACvF,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,OAAO,CAElG;AAED,sHAAsH;AACtH,wBAAgB,mBAAmB,CAAC,iBAAiB,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,GAAG,MAAM,CAGlG;AAED,uCAAuC;AACvC,wBAAgB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAE5E;AAED,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACvB,kBAAkB,CAUpB;AAED,mHAAmH;AACnH,wBAAgB,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI,CAExE;AAED,wGAAwG;AACxG,wBAAgB,uBAAuB,CACtC,aAAa,EAAE,IAAI,GAAG,IAAI,EAC1B,kBAAkB,EAAE,MAAM,EAC1B,GAAG,GAAE,IAAiB,GACpB,MAAM,CAIR;AAED,sHAAsH;AACtH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,mBAAmB,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,MAAM,CAG7G;AAED,MAAM,WAAW,gBAAgB;IAEhC,QAAQ,EAAE,MAAM,CAAC;IAEjB,mBAAmB,EAAE,MAAM,CAAC;IAE5B,aAAa,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,oBAAoB;IAEpC,UAAU,EAAE,MAAM,CAAC;IAEnB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAE/B,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B;AAED,MAAM,MAAM,iBAAiB,GAC1B;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,eAAe,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GACzF;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC3F;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,cAAc,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GACjG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,sBAAsB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AAErG,gGAAgG;AAChG,MAAM,WAAW,iBAAiB;IACjC,OAAO,CACN,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,oBAAoB,GAC3B,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAC9B,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,uCAAuC;IACvC,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,uGAAuG;IACvG,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACpD;AAiBD,MAAM,MAAM,kBAAkB,GAC3B;IAAE,OAAO,EAAE,gBAAgB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GAC3E;IAAE,OAAO,EAAE,cAAc,CAAC;IAAC,iBAAiB,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GACjF;IAAE,OAAO,EAAE,sBAAsB,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAEtD;IAAE,OAAO,EAAE,UAAU,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE,GACrE;IAAE,OAAO,EAAE,eAAe,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AACvD,MAAM,MAAM,YAAY,GAAG,CAAC,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAC;AAE3G;;;;GAIG;AACH,eAAO,MAAM,cAAc,EAAE,YAE5B,CAAC;AAIF;;;;;;;;;GASG;AACH,wBAAsB,iBAAiB,CACtC,MAAM,EAAE,iBAAiB,EACzB,QAAQ,EAAE,MAAM,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1D,MAAM,EAAE;IACP,QAAQ,EAAE,cAAc,CAAC;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,UAAU,CAAC,EAAE,gBAAgB,CAAC;CAC9B,EACD,GAAG,GAAE,YAA6B,GAChC,OAAO,CAAC,kBAAkB,CAAC,CA6E7B"}
|
|
@@ -36,6 +36,10 @@ export function buildFaucetQuotaSummary(usedNcheq, limitNcheq, window) {
|
|
|
36
36
|
resetsAt: window.resetsAt.toISOString(),
|
|
37
37
|
};
|
|
38
38
|
}
|
|
39
|
+
/** Rows older than this are past retention: the start of the calendar month `retentionMonths` months ago (UTC). */
|
|
40
|
+
export function retentionCutoff(now, retentionMonths) {
|
|
41
|
+
return new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth() - retentionMonths, 1));
|
|
42
|
+
}
|
|
39
43
|
/** Seconds the customer still has to wait before their next faucet request; 0 if one is allowed now. */
|
|
40
44
|
export function secondsUntilNextRequest(lastRequestAt, minIntervalSeconds, now = new Date()) {
|
|
41
45
|
if (!lastRequestAt || minIntervalSeconds <= 0)
|
|
@@ -43,52 +47,112 @@ export function secondsUntilNextRequest(lastRequestAt, minIntervalSeconds, now =
|
|
|
43
47
|
const remainingMs = minIntervalSeconds * 1000 - (now.getTime() - lastRequestAt.getTime());
|
|
44
48
|
return remainingMs > 0 ? Math.ceil(remainingMs / 1000) : 0;
|
|
45
49
|
}
|
|
46
|
-
|
|
50
|
+
/** Room left under an address cap once its balance and requests still in flight are accounted for; never negative. */
|
|
51
|
+
export function addressRoomNcheq(capNcheq, currentBalanceNcheq, inFlightNcheq) {
|
|
52
|
+
const room = capNcheq - currentBalanceNcheq - inFlightNcheq;
|
|
53
|
+
return room > 0n ? room : 0n;
|
|
54
|
+
}
|
|
55
|
+
async function markUnknownBestEffort(ledger, faucetRequestId, log) {
|
|
47
56
|
try {
|
|
48
57
|
await ledger.markUnknown(faucetRequestId);
|
|
49
58
|
}
|
|
50
59
|
catch (error) {
|
|
51
|
-
|
|
60
|
+
log('error', 'faucet.mark_unknown_failed', {
|
|
61
|
+
reservationId: faucetRequestId,
|
|
62
|
+
error: error?.message ?? String(error),
|
|
63
|
+
});
|
|
52
64
|
}
|
|
53
65
|
}
|
|
66
|
+
/**
|
|
67
|
+
* Writes one structured log line per faucet decision, e.g. `{"event":"faucet.quota_exceeded","customerId":"..."}`,
|
|
68
|
+
* so "why was this customer blocked?" and "where did this month's quota go?" can be answered from the logs.
|
|
69
|
+
* Amounts are ncheq strings. Pass another logger to creditWithinQuota to capture events in tests.
|
|
70
|
+
*/
|
|
71
|
+
export const logFaucetEvent = (level, event, fields) => {
|
|
72
|
+
console[level](JSON.stringify({ event, ...fields }));
|
|
73
|
+
};
|
|
74
|
+
const shorten = (text) => (text.length > 200 ? `${text.slice(0, 200)}...` : text);
|
|
54
75
|
/**
|
|
55
76
|
* Reserves quota, calls the faucet, and settles the reservation.
|
|
56
|
-
* - Quota does not
|
|
77
|
+
* - Quota, minimum interval or address cap does not allow it: the faucet is not called.
|
|
57
78
|
* - Faucet answers with an error status: it definitely did not credit the account, so the quota is released.
|
|
58
79
|
* - Faucet call throws (e.g. a timeout): the outcome is unknown and tokens may have been sent, so the
|
|
59
80
|
* reservation is marked unknown and keeps counting for good (over-counting is safer than refunding quota
|
|
60
81
|
* for tokens that were credited).
|
|
61
82
|
* - Faucet confirms: the reservation is marked completed.
|
|
83
|
+
* Every outcome is logged through `log`.
|
|
62
84
|
*/
|
|
63
|
-
export async function creditWithinQuota(ledger, delegate, params) {
|
|
85
|
+
export async function creditWithinQuota(ledger, delegate, params, log = logFaucetEvent) {
|
|
86
|
+
const base = {
|
|
87
|
+
customerId: params.customer.customerId,
|
|
88
|
+
address: params.address,
|
|
89
|
+
amountNcheq: params.amountNcheq.toString(),
|
|
90
|
+
};
|
|
64
91
|
const reservation = await ledger.reserve(params.customer, params.address, params.amountNcheq, {
|
|
65
92
|
limitNcheq: params.limitNcheq,
|
|
66
93
|
minIntervalSeconds: params.minIntervalSeconds,
|
|
94
|
+
addressCap: params.addressCap,
|
|
67
95
|
});
|
|
68
96
|
if (!reservation.reserved) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
97
|
+
switch (reservation.reason) {
|
|
98
|
+
case 'too_frequent':
|
|
99
|
+
log('warn', 'faucet.too_frequent', { ...base, retryAfterSeconds: reservation.retryAfterSeconds });
|
|
100
|
+
return {
|
|
101
|
+
outcome: 'too_frequent',
|
|
102
|
+
retryAfterSeconds: reservation.retryAfterSeconds,
|
|
103
|
+
window: reservation.window,
|
|
104
|
+
};
|
|
105
|
+
case 'address_cap_exceeded':
|
|
106
|
+
log('warn', 'faucet.address_cap_exceeded', { ...base, roomNcheq: reservation.roomNcheq.toString() });
|
|
107
|
+
return { outcome: 'address_cap_exceeded', roomNcheq: reservation.roomNcheq };
|
|
108
|
+
default:
|
|
109
|
+
log('warn', 'faucet.quota_exceeded', {
|
|
110
|
+
...base,
|
|
111
|
+
usedNcheq: reservation.usedNcheq.toString(),
|
|
112
|
+
limitNcheq: params.limitNcheq.toString(),
|
|
113
|
+
});
|
|
114
|
+
return { outcome: 'quota_exceeded', usedNcheq: reservation.usedNcheq, window: reservation.window };
|
|
115
|
+
}
|
|
72
116
|
}
|
|
117
|
+
const withReservation = { ...base, reservationId: reservation.faucetRequestId };
|
|
118
|
+
log('info', 'faucet.reserved', {
|
|
119
|
+
...withReservation,
|
|
120
|
+
usedNcheq: reservation.usedNcheq.toString(),
|
|
121
|
+
limitNcheq: params.limitNcheq.toString(),
|
|
122
|
+
});
|
|
73
123
|
let faucet;
|
|
74
124
|
try {
|
|
75
125
|
faucet = await delegate();
|
|
76
126
|
}
|
|
77
127
|
catch (error) {
|
|
78
|
-
|
|
128
|
+
log('warn', 'faucet.unknown', {
|
|
129
|
+
...withReservation,
|
|
130
|
+
error: shorten(error?.message ?? String(error)),
|
|
131
|
+
note: 'faucet call threw; tokens may have been sent, so the reservation keeps counting',
|
|
132
|
+
});
|
|
133
|
+
await markUnknownBestEffort(ledger, reservation.faucetRequestId, log);
|
|
79
134
|
throw error;
|
|
80
135
|
}
|
|
81
136
|
if (faucet.status !== 200) {
|
|
82
137
|
await ledger.release(reservation.faucetRequestId);
|
|
138
|
+
log('warn', 'faucet.released', {
|
|
139
|
+
...withReservation,
|
|
140
|
+
faucetStatus: faucet.status,
|
|
141
|
+
error: shorten(faucet.error),
|
|
142
|
+
});
|
|
83
143
|
return { outcome: 'faucet_failed', status: faucet.status, error: faucet.error };
|
|
84
144
|
}
|
|
85
145
|
try {
|
|
86
146
|
await ledger.complete(reservation.faucetRequestId);
|
|
147
|
+
log('info', 'faucet.completed', withReservation);
|
|
87
148
|
}
|
|
88
149
|
catch (error) {
|
|
89
150
|
// The tokens were sent, so do not fail the request. The reservation stays pending and keeps counting
|
|
90
151
|
// until it times out.
|
|
91
|
-
|
|
152
|
+
log('error', 'faucet.complete_failed', {
|
|
153
|
+
...withReservation,
|
|
154
|
+
error: shorten(error?.message ?? String(error)),
|
|
155
|
+
});
|
|
92
156
|
}
|
|
93
157
|
return {
|
|
94
158
|
outcome: 'credited',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet-quota.js","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYzC,oDAAoD;AACpD,MAAM,UAAU,oBAAoB,CAAC,MAAY,IAAI,IAAI,EAAE;IAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO;QACN,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACzC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;KAChD,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,MAAyB,EAAE,MAAY,IAAI,IAAI,EAAE;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,cAAsB,EAAE,UAAkB;IACxF,OAAO,SAAS,GAAG,cAAc,IAAI,UAAU,CAAC;AACjD,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,mBAAmB,CAAC,iBAAyB,EAAE,mBAA2B;IACzF,MAAM,MAAM,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACjG,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,UAAkB;IACnE,OAAO,SAAS,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,UAAkB,EAClB,MAAyB;IAEzB,MAAM,cAAc,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,KAAa,EAAuB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC/G,OAAO;QACN,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE;KACvC,CAAC;AACH,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,uBAAuB,CACtC,aAA0B,EAC1B,kBAA0B,EAC1B,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,aAAa,IAAI,kBAAkB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,kBAAkB,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1F,OAAO,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;
|
|
1
|
+
{"version":3,"file":"faucet-quota.js","sourceRoot":"","sources":["../../src/helpers/faucet-quota.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAYzC,oDAAoD;AACpD,MAAM,UAAU,oBAAoB,CAAC,MAAY,IAAI,IAAI,EAAE;IAC1D,MAAM,IAAI,GAAG,GAAG,CAAC,cAAc,EAAE,CAAC;IAClC,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,EAAE,CAAC;IAChC,OAAO;QACN,KAAK,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;QACzC,QAAQ,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;KAChD,CAAC;AACH,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,iBAAiB,CAAC,MAAyB,EAAE,MAAY,IAAI,IAAI,EAAE;IAClF,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,EAAE,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;AACnF,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,cAAsB,EAAE,UAAkB;IACxF,OAAO,SAAS,GAAG,cAAc,IAAI,UAAU,CAAC;AACjD,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,mBAAmB,CAAC,iBAAyB,EAAE,mBAA2B;IACzF,MAAM,MAAM,GAAG,iBAAiB,GAAG,mBAAmB,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,mBAAmB,CAAC;IACjG,OAAO,MAAM,GAAG,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;AAClC,CAAC;AAED,uCAAuC;AACvC,MAAM,UAAU,cAAc,CAAC,SAAiB,EAAE,UAAkB;IACnE,OAAO,SAAS,IAAI,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,SAAS,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,UAAkB,EAClB,MAAyB;IAEzB,MAAM,cAAc,GAAG,cAAc,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAG,CAAC,KAAa,EAAuB,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,WAAW,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAC/G,OAAO;QACN,MAAM,EAAE,OAAO;QACf,KAAK,EAAE,MAAM,CAAC,UAAU,CAAC;QACzB,IAAI,EAAE,MAAM,CAAC,SAAS,CAAC;QACvB,SAAS,EAAE,MAAM,CAAC,cAAc,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAC,WAAW,EAAE;KACvC,CAAC;AACH,CAAC;AAED,mHAAmH;AACnH,MAAM,UAAU,eAAe,CAAC,GAAS,EAAE,eAAuB;IACjE,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,GAAG,CAAC,WAAW,EAAE,GAAG,eAAe,EAAE,CAAC,CAAC,CAAC,CAAC;AACzF,CAAC;AAED,wGAAwG;AACxG,MAAM,UAAU,uBAAuB,CACtC,aAA0B,EAC1B,kBAA0B,EAC1B,MAAY,IAAI,IAAI,EAAE;IAEtB,IAAI,CAAC,aAAa,IAAI,kBAAkB,IAAI,CAAC;QAAE,OAAO,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,kBAAkB,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,OAAO,EAAE,GAAG,aAAa,CAAC,OAAO,EAAE,CAAC,CAAC;IAC1F,OAAO,WAAW,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,sHAAsH;AACtH,MAAM,UAAU,gBAAgB,CAAC,QAAgB,EAAE,mBAA2B,EAAE,aAAqB;IACpG,MAAM,IAAI,GAAG,QAAQ,GAAG,mBAAmB,GAAG,aAAa,CAAC;IAC5D,OAAO,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9B,CAAC;AA2CD,KAAK,UAAU,qBAAqB,CACnC,MAAyB,EACzB,eAAuB,EACvB,GAAiB;IAEjB,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,OAAO,EAAE,4BAA4B,EAAE;YAC1C,aAAa,EAAE,eAAe;YAC9B,KAAK,EAAG,KAAe,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC;SACjD,CAAC,CAAC;IACJ,CAAC;AACF,CAAC;AAaD;;;;GAIG;AACH,MAAM,CAAC,MAAM,cAAc,GAAiB,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE;IACpE,OAAO,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,GAAG,MAAM,EAAE,CAAC,CAAC,CAAC;AACtD,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAE1F;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,MAAyB,EACzB,QAA0D,EAC1D,MAOC,EACD,MAAoB,cAAc;IAElC,MAAM,IAAI,GAAG;QACZ,UAAU,EAAE,MAAM,CAAC,QAAQ,CAAC,UAAU;QACtC,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,WAAW,EAAE,MAAM,CAAC,WAAW,CAAC,QAAQ,EAAE;KAC1C,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,OAAO,EAAE,MAAM,CAAC,WAAW,EAAE;QAC7F,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,kBAAkB,EAAE,MAAM,CAAC,kBAAkB;QAC7C,UAAU,EAAE,MAAM,CAAC,UAAU;KAC7B,CAAC,CAAC;IACH,IAAI,CAAC,WAAW,CAAC,QAAQ,EAAE,CAAC;QAC3B,QAAQ,WAAW,CAAC,MAAM,EAAE,CAAC;YAC5B,KAAK,cAAc;gBAClB,GAAG,CAAC,MAAM,EAAE,qBAAqB,EAAE,EAAE,GAAG,IAAI,EAAE,iBAAiB,EAAE,WAAW,CAAC,iBAAiB,EAAE,CAAC,CAAC;gBAClG,OAAO;oBACN,OAAO,EAAE,cAAc;oBACvB,iBAAiB,EAAE,WAAW,CAAC,iBAAiB;oBAChD,MAAM,EAAE,WAAW,CAAC,MAAM;iBAC1B,CAAC;YACH,KAAK,sBAAsB;gBAC1B,GAAG,CAAC,MAAM,EAAE,6BAA6B,EAAE,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;gBACrG,OAAO,EAAE,OAAO,EAAE,sBAAsB,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,CAAC;YAC9E;gBACC,GAAG,CAAC,MAAM,EAAE,uBAAuB,EAAE;oBACpC,GAAG,IAAI;oBACP,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE;oBAC3C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE;iBACxC,CAAC,CAAC;gBACH,OAAO,EAAE,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,CAAC;QACrG,CAAC;IACF,CAAC;IAED,MAAM,eAAe,GAAG,EAAE,GAAG,IAAI,EAAE,aAAa,EAAE,WAAW,CAAC,eAAe,EAAE,CAAC;IAChF,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE;QAC9B,GAAG,eAAe;QAClB,SAAS,EAAE,WAAW,CAAC,SAAS,CAAC,QAAQ,EAAE;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE;KACxC,CAAC,CAAC;IAEH,IAAI,MAAyC,CAAC;IAC9C,IAAI,CAAC;QACJ,MAAM,GAAG,MAAM,QAAQ,EAAE,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE;YAC7B,GAAG,eAAe;YAClB,KAAK,EAAE,OAAO,CAAE,KAAe,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;YAC1D,IAAI,EAAE,iFAAiF;SACvF,CAAC,CAAC;QACH,MAAM,qBAAqB,CAAC,MAAM,EAAE,WAAW,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC;QACtE,MAAM,KAAK,CAAC;IACb,CAAC;IACD,IAAI,MAAM,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAC3B,MAAM,MAAM,CAAC,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QAClD,GAAG,CAAC,MAAM,EAAE,iBAAiB,EAAE;YAC9B,GAAG,eAAe;YAClB,YAAY,EAAE,MAAM,CAAC,MAAM;YAC3B,KAAK,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC;SAC5B,CAAC,CAAC;QACH,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC;IACjF,CAAC;IACD,IAAI,CAAC;QACJ,MAAM,MAAM,CAAC,QAAQ,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;QACnD,GAAG,CAAC,MAAM,EAAE,kBAAkB,EAAE,eAAe,CAAC,CAAC;IAClD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,qGAAqG;QACrG,sBAAsB;QACtB,GAAG,CAAC,OAAO,EAAE,wBAAwB,EAAE;YACtC,GAAG,eAAe;YAClB,KAAK,EAAE,OAAO,CAAE,KAAe,EAAE,OAAO,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC;SAC1D,CAAC,CAAC;IACJ,CAAC;IACD,OAAO;QACN,OAAO,EAAE,UAAU;QACnB,SAAS,EAAE,WAAW,CAAC,SAAS,GAAG,MAAM,CAAC,WAAW;QACrD,MAAM,EAAE,WAAW,CAAC,MAAM;KAC1B,CAAC;AACH,CAAC"}
|
package/dist/helpers/faucet.d.ts
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ICommonErrorResponse } from '../types/authentication.js';
|
|
2
|
+
/**
|
|
3
|
+
* True for a lowercase cheqd bech32 account address (20 bytes, valid checksum). Other bech32 strings with the cheqd
|
|
4
|
+
* prefix, such as 32-byte contract addresses, are not ordinary accounts and are rejected.
|
|
5
|
+
*/
|
|
6
|
+
export declare function isValidFaucetAddress(address: string): boolean;
|
|
2
7
|
/** True if the error is the faucet call timing out or being aborted, as opposed to the faucet answering. */
|
|
3
8
|
export declare function isFaucetTimeout(error: unknown): boolean;
|
|
4
9
|
export declare class FaucetHelper {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"faucet.d.ts","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,4BAA4B,CAAC;AAcvE;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAW7D;AAED,4GAA4G;AAC5G,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGvD;AAED,qBAAa,YAAY;WAEX,cAAc,CAC1B,OAAO,EAAE,MAAM,EACf,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,MAAM,SAAgB,EACtB,SAAS,SAAwC,GAC/C,OAAO,CAAC,oBAAoB,CAAC;CA+BhC"}
|
package/dist/helpers/faucet.js
CHANGED
|
@@ -1,4 +1,22 @@
|
|
|
1
|
+
import { fromBech32 } from '@cosmjs/encoding';
|
|
1
2
|
import { MINIMAL_DENOM, FAUCET_URI, FAUCET_AMOUNT, FAUCET_API_KEY, FAUCET_ACCESS_CLIENT_ID, FAUCET_ACCESS_CLIENT_SECRET, FAUCET_REQUEST_TIMEOUT_SECONDS, } from '../types/constants.js';
|
|
3
|
+
const FAUCET_ADDRESS_PREFIX = 'cheqd';
|
|
4
|
+
const ACCOUNT_ADDRESS_BYTES = 20;
|
|
5
|
+
/**
|
|
6
|
+
* True for a lowercase cheqd bech32 account address (20 bytes, valid checksum). Other bech32 strings with the cheqd
|
|
7
|
+
* prefix, such as 32-byte contract addresses, are not ordinary accounts and are rejected.
|
|
8
|
+
*/
|
|
9
|
+
export function isValidFaucetAddress(address) {
|
|
10
|
+
try {
|
|
11
|
+
const { prefix, data } = fromBech32(address);
|
|
12
|
+
return (prefix === FAUCET_ADDRESS_PREFIX &&
|
|
13
|
+
data.length === ACCOUNT_ADDRESS_BYTES &&
|
|
14
|
+
address === address.toLowerCase());
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
2
20
|
/** True if the error is the faucet call timing out or being aborted, as opposed to the faucet answering. */
|
|
3
21
|
export function isFaucetTimeout(error) {
|
|
4
22
|
const name = error?.name;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet.js","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"faucet.js","sourceRoot":"","sources":["../../src/helpers/faucet.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAE9C,OAAO,EACN,aAAa,EACb,UAAU,EACV,aAAa,EACb,cAAc,EACd,uBAAuB,EACvB,2BAA2B,EAC3B,8BAA8B,GAC9B,MAAM,uBAAuB,CAAC;AAE/B,MAAM,qBAAqB,GAAG,OAAO,CAAC;AACtC,MAAM,qBAAqB,GAAG,EAAE,CAAC;AAEjC;;;GAGG;AACH,MAAM,UAAU,oBAAoB,CAAC,OAAe;IACnD,IAAI,CAAC;QACJ,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7C,OAAO,CACN,MAAM,KAAK,qBAAqB;YAChC,IAAI,CAAC,MAAM,KAAK,qBAAqB;YACrC,OAAO,KAAK,OAAO,CAAC,WAAW,EAAE,CACjC,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,KAAK,CAAC;IACd,CAAC;AACF,CAAC;AAED,4GAA4G;AAC5G,MAAM,UAAU,eAAe,CAAC,KAAc;IAC7C,MAAM,IAAI,GAAI,KAAkC,EAAE,IAAI,CAAC;IACvD,OAAO,IAAI,KAAK,cAAc,IAAI,IAAI,KAAK,YAAY,CAAC;AACzD,CAAC;AAED,MAAM,OAAO,YAAY;IACxB,MAAM;IACN,MAAM,CAAC,KAAK,CAAC,cAAc,CAC1B,OAAe,EACf,SAAiB,EACjB,QAAgB,EAChB,KAAa,EACb,MAAM,GAAG,aAAa,EACtB,SAAS,GAAG,8BAA8B,GAAG,IAAI;QAEjD,MAAM,SAAS,GAAG,UAAU,CAAC;QAC7B,MAAM,UAAU,GAAG;YAClB,KAAK,EAAE,aAAa;YACpB,OAAO,EAAE,OAAO;YAChB,KAAK,EAAE,KAAK;YACZ,UAAU,EAAE,SAAS;YACrB,SAAS,EAAE,QAAQ;YACnB,OAAO,EAAE,4BAA4B;YACrC,MAAM;YACN,eAAe,EAAE,KAAK;SACtB,CAAC;QACF,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,SAAS,EAAE;YACvC,OAAO,EAAE;gBACR,cAAc,EAAE,kBAAkB;gBAClC,WAAW,EAAE,cAAc;gBAC3B,qBAAqB,EAAE,uBAAuB;gBAC9C,yBAAyB,EAAE,2BAA2B;aACtD;YACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC;YAChC,MAAM,EAAE,MAAM;YACd,qGAAqG;YACrG,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,SAAS,CAAC;SACtC,CAAC,CAAC;QACH,OAAO;YACN,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,KAAK,EAAE,MAAM,QAAQ,CAAC,IAAI,EAAE;YAC5B,IAAI,EAAE,EAAE;SACR,CAAC;IACH,CAAC;CAED"}
|
|
@@ -1,11 +1,19 @@
|
|
|
1
1
|
import type { Repository } from 'typeorm';
|
|
2
2
|
import type { CustomerEntity } from '../../database/entities/customer.entity.js';
|
|
3
3
|
import { FaucetRequestEntity } from '../../database/entities/faucet-request.entity.js';
|
|
4
|
-
import { type FaucetQuotaLedger, type FaucetReservation, type FaucetReserveOptions } from '../../helpers/faucet-quota.js';
|
|
4
|
+
import { type FaucetQuotaLedger, type FaucetQuotaWindow, type FaucetReservation, type FaucetReserveOptions } from '../../helpers/faucet-quota.js';
|
|
5
5
|
export declare class FaucetRequestService implements FaucetQuotaLedger {
|
|
6
6
|
faucetRequestRepository: Repository<FaucetRequestEntity>;
|
|
7
|
+
private lastRetentionCleanupAt;
|
|
7
8
|
static instance: FaucetRequestService;
|
|
8
9
|
constructor(repository?: Repository<FaucetRequestEntity>);
|
|
10
|
+
/**
|
|
11
|
+
* The database's clock. `createdAt` is set by the database, so every comparison (month window, minimum interval,
|
|
12
|
+
* pending timeout, settle window, retention) must use its clock too, not the application's: instances with
|
|
13
|
+
* slightly different clocks would otherwise disagree about how old a row is. `clock_timestamp()` rather than
|
|
14
|
+
* `now()`, because inside a transaction `now()` stays at the start of the transaction, before any wait for the lock.
|
|
15
|
+
*/
|
|
16
|
+
private static databaseNow;
|
|
9
17
|
private static pendingCutoff;
|
|
10
18
|
/**
|
|
11
19
|
* Total ncheq requested by the customer since `since`, using the given manager (and so its transaction).
|
|
@@ -13,6 +21,8 @@ export declare class FaucetRequestService implements FaucetQuotaLedger {
|
|
|
13
21
|
* `pendingCutoff`, after which it is treated as abandoned.
|
|
14
22
|
*/
|
|
15
23
|
private static sumRequestedNcheq;
|
|
24
|
+
/** Total ncheq requested for one address since `since`, excluding abandoned reservations. */
|
|
25
|
+
private static sumRecentForAddress;
|
|
16
26
|
/** When the customer last made a faucet request, or null if never. */
|
|
17
27
|
private static lastRequestAt;
|
|
18
28
|
/**
|
|
@@ -20,6 +30,11 @@ export declare class FaucetRequestService implements FaucetQuotaLedger {
|
|
|
20
30
|
* before any expensive work; `reserve` repeats the check under the lock so concurrent requests cannot slip through.
|
|
21
31
|
*/
|
|
22
32
|
getSecondsUntilNextAllowed(customer: CustomerEntity, minIntervalSeconds: number, now?: Date): Promise<number>;
|
|
33
|
+
/** What the customer has requested in the current quota window, and the window itself (both on the database clock). */
|
|
34
|
+
getQuotaStatus(customer: CustomerEntity, now?: Date, pendingTimeoutSeconds?: number): Promise<{
|
|
35
|
+
usedNcheq: bigint;
|
|
36
|
+
window: FaucetQuotaWindow;
|
|
37
|
+
}>;
|
|
23
38
|
/** Total ncheq requested by the customer in the current quota window. */
|
|
24
39
|
getUsedNcheq(customer: CustomerEntity, now?: Date, pendingTimeoutSeconds?: number): Promise<bigint>;
|
|
25
40
|
/**
|
|
@@ -28,7 +43,14 @@ export declare class FaucetRequestService implements FaucetQuotaLedger {
|
|
|
28
43
|
* Afterwards call `complete` when the faucet confirms, `release` if it definitely failed, or `markUnknown`
|
|
29
44
|
* if the call threw.
|
|
30
45
|
*/
|
|
31
|
-
reserve(customer: CustomerEntity, address: string, amountNcheq: bigint, { limitNcheq, minIntervalSeconds, pendingTimeoutSeconds, }: FaucetReserveOptions,
|
|
46
|
+
reserve(customer: CustomerEntity, address: string, amountNcheq: bigint, { limitNcheq, minIntervalSeconds, pendingTimeoutSeconds, addressCap, }: FaucetReserveOptions, nowOverride?: Date): Promise<FaucetReservation>;
|
|
47
|
+
/** Deletes faucet request rows older than the retention period. Returns how many were deleted. */
|
|
48
|
+
runRetentionCleanup(now?: Date, retentionMonths?: number): Promise<number>;
|
|
49
|
+
/**
|
|
50
|
+
* Runs the retention cleanup in the background, at most once an hour per process. Called from normal requests
|
|
51
|
+
* because Studio has no scheduler; it never delays or fails the request that triggered it.
|
|
52
|
+
*/
|
|
53
|
+
private maybeRunRetentionCleanup;
|
|
32
54
|
/** Releases a reservation, e.g. when the upstream faucet reported that it did not credit the account. */
|
|
33
55
|
release(faucetRequestId: string): Promise<void>;
|
|
34
56
|
/** The faucet confirmed the credit. Also applies to a reservation that was already marked abandoned. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"faucet-request.d.ts","sourceRoot":"","sources":["../../../src/services/api/faucet-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAGvF,OAAO,
|
|
1
|
+
{"version":3,"file":"faucet-request.d.ts","sourceRoot":"","sources":["../../../src/services/api/faucet-request.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,UAAU,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4CAA4C,CAAC;AACjF,OAAO,EAAE,mBAAmB,EAAE,MAAM,kDAAkD,CAAC;AAGvF,OAAO,EAON,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,oBAAoB,EACzB,MAAM,+BAA+B,CAAC;AAKvC,qBAAa,oBAAqB,YAAW,iBAAiB;IACtD,uBAAuB,EAAE,UAAU,CAAC,mBAAmB,CAAC,CAAC;IAChE,OAAO,CAAC,sBAAsB,CAAK;IAEnC,OAAc,QAAQ,uBAA8B;gBAExC,UAAU,CAAC,EAAE,UAAU,CAAC,mBAAmB,CAAC;IAKxD;;;;;OAKG;mBACkB,WAAW;IAMhC,OAAO,CAAC,MAAM,CAAC,aAAa;IAI5B;;;;OAIG;mBACkB,iBAAiB;IAmBtC,6FAA6F;mBACxE,mBAAmB;IAiBxC,sEAAsE;mBACjD,aAAa;IAUlC;;;OAGG;IACU,0BAA0B,CACtC,QAAQ,EAAE,cAAc,EACxB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,CAAC,EAAE,IAAI,GACR,OAAO,CAAC,MAAM,CAAC;IAWlB,uHAAuH;IAC1G,cAAc,CAC1B,QAAQ,EAAE,cAAc,EACxB,GAAG,CAAC,EAAE,IAAI,EACV,qBAAqB,GAAE,MAAuC,GAC5D,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,iBAAiB,CAAA;KAAE,CAAC;IAa5D,yEAAyE;IAC5D,YAAY,CACxB,QAAQ,EAAE,cAAc,EACxB,GAAG,CAAC,EAAE,IAAI,EACV,qBAAqB,GAAE,MAAuC,GAC5D,OAAO,CAAC,MAAM,CAAC;IAIlB;;;;;OAKG;IACU,OAAO,CACnB,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,EACnB,EACC,UAAU,EACV,kBAAsB,EACtB,qBAAsD,EACtD,UAAU,GACV,EAAE,oBAAoB,EACvB,WAAW,CAAC,EAAE,IAAI,GAChB,OAAO,CAAC,iBAAiB,CAAC;IAyE7B,kGAAkG;IACrF,mBAAmB,CAC/B,GAAG,CAAC,EAAE,IAAI,EACV,eAAe,GAAE,MAAwC,GACvD,OAAO,CAAC,MAAM,CAAC;IAYlB;;;OAGG;IACH,OAAO,CAAC,wBAAwB;IAiBhC,yGAAyG;IAC5F,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI5D,wGAAwG;IAC3F,QAAQ,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO7D,uGAAuG;IAC1F,WAAW,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAGhE"}
|
|
@@ -1,14 +1,29 @@
|
|
|
1
1
|
import { FaucetRequestEntity } from '../../database/entities/faucet-request.entity.js';
|
|
2
2
|
import { Connection } from '../../database/connection/connection.js';
|
|
3
|
-
import { FAUCET_PENDING_TIMEOUT_SECONDS } from '../../types/constants.js';
|
|
4
|
-
import { fitsInQuota, getFaucetQuotaWindow, secondsUntilNextRequest, } from '../../helpers/faucet-quota.js';
|
|
3
|
+
import { FAUCET_PENDING_TIMEOUT_SECONDS, FAUCET_REQUEST_RETENTION_MONTHS } from '../../types/constants.js';
|
|
4
|
+
import { addressRoomNcheq, fitsInQuota, getFaucetQuotaWindow, logFaucetEvent, retentionCutoff, secondsUntilNextRequest, } from '../../helpers/faucet-quota.js';
|
|
5
|
+
// Retention cleanup runs inside normal requests (Studio has no scheduler), at most this often per process
|
|
6
|
+
const RETENTION_CLEANUP_INTERVAL_MS = 60 * 60 * 1000;
|
|
5
7
|
export class FaucetRequestService {
|
|
6
8
|
faucetRequestRepository;
|
|
9
|
+
lastRetentionCleanupAt = 0;
|
|
7
10
|
static instance = new FaucetRequestService();
|
|
8
11
|
constructor(repository) {
|
|
9
12
|
this.faucetRequestRepository =
|
|
10
13
|
repository ?? Connection.instance.dbConnection.getRepository(FaucetRequestEntity);
|
|
11
14
|
}
|
|
15
|
+
/**
|
|
16
|
+
* The database's clock. `createdAt` is set by the database, so every comparison (month window, minimum interval,
|
|
17
|
+
* pending timeout, settle window, retention) must use its clock too, not the application's: instances with
|
|
18
|
+
* slightly different clocks would otherwise disagree about how old a row is. `clock_timestamp()` rather than
|
|
19
|
+
* `now()`, because inside a transaction `now()` stays at the start of the transaction, before any wait for the lock.
|
|
20
|
+
*/
|
|
21
|
+
static async databaseNow(manager) {
|
|
22
|
+
if (manager.connection.options.type !== 'postgres')
|
|
23
|
+
return new Date();
|
|
24
|
+
const [row] = await manager.query('SELECT clock_timestamp() AS now');
|
|
25
|
+
return new Date(row.now);
|
|
26
|
+
}
|
|
12
27
|
static pendingCutoff(now, pendingTimeoutSeconds) {
|
|
13
28
|
return new Date(now.getTime() - pendingTimeoutSeconds * 1000);
|
|
14
29
|
}
|
|
@@ -27,6 +42,18 @@ export class FaucetRequestService {
|
|
|
27
42
|
.getRawOne();
|
|
28
43
|
return BigInt(row?.total ?? '0');
|
|
29
44
|
}
|
|
45
|
+
/** Total ncheq requested for one address since `since`, excluding abandoned reservations. */
|
|
46
|
+
static async sumRecentForAddress(manager, customerId, address, since) {
|
|
47
|
+
const row = await manager
|
|
48
|
+
.createQueryBuilder(FaucetRequestEntity, 'request')
|
|
49
|
+
.select('COALESCE(SUM(request.amountNcheq), 0)', 'total')
|
|
50
|
+
.where('request.customerId = :customerId', { customerId })
|
|
51
|
+
.andWhere('request.address = :address', { address })
|
|
52
|
+
.andWhere('request.createdAt >= :since', { since })
|
|
53
|
+
.andWhere(`request.status <> 'abandoned'`)
|
|
54
|
+
.getRawOne();
|
|
55
|
+
return BigInt(row?.total ?? '0');
|
|
56
|
+
}
|
|
30
57
|
/** When the customer last made a faucet request, or null if never. */
|
|
31
58
|
static async lastRequestAt(manager, customerId) {
|
|
32
59
|
const row = await manager
|
|
@@ -41,16 +68,24 @@ export class FaucetRequestService {
|
|
|
41
68
|
* Seconds the customer must still wait before their next request (0 if allowed). This is a cheap read taken
|
|
42
69
|
* before any expensive work; `reserve` repeats the check under the lock so concurrent requests cannot slip through.
|
|
43
70
|
*/
|
|
44
|
-
async getSecondsUntilNextAllowed(customer, minIntervalSeconds, now
|
|
71
|
+
async getSecondsUntilNextAllowed(customer, minIntervalSeconds, now) {
|
|
45
72
|
if (minIntervalSeconds <= 0)
|
|
46
73
|
return 0;
|
|
47
|
-
const
|
|
48
|
-
|
|
74
|
+
const manager = this.faucetRequestRepository.manager;
|
|
75
|
+
const last = await FaucetRequestService.lastRequestAt(manager, customer.customerId);
|
|
76
|
+
return secondsUntilNextRequest(last, minIntervalSeconds, now ?? (await FaucetRequestService.databaseNow(manager)));
|
|
77
|
+
}
|
|
78
|
+
/** What the customer has requested in the current quota window, and the window itself (both on the database clock). */
|
|
79
|
+
async getQuotaStatus(customer, now, pendingTimeoutSeconds = FAUCET_PENDING_TIMEOUT_SECONDS) {
|
|
80
|
+
const manager = this.faucetRequestRepository.manager;
|
|
81
|
+
const at = now ?? (await FaucetRequestService.databaseNow(manager));
|
|
82
|
+
const window = getFaucetQuotaWindow(at);
|
|
83
|
+
const usedNcheq = await FaucetRequestService.sumRequestedNcheq(manager, customer.customerId, window.start, FaucetRequestService.pendingCutoff(at, pendingTimeoutSeconds));
|
|
84
|
+
return { usedNcheq, window };
|
|
49
85
|
}
|
|
50
86
|
/** Total ncheq requested by the customer in the current quota window. */
|
|
51
|
-
async getUsedNcheq(customer, now
|
|
52
|
-
|
|
53
|
-
return FaucetRequestService.sumRequestedNcheq(this.faucetRequestRepository.manager, customer.customerId, start, FaucetRequestService.pendingCutoff(now, pendingTimeoutSeconds));
|
|
87
|
+
async getUsedNcheq(customer, now, pendingTimeoutSeconds = FAUCET_PENDING_TIMEOUT_SECONDS) {
|
|
88
|
+
return (await this.getQuotaStatus(customer, now, pendingTimeoutSeconds)).usedNcheq;
|
|
54
89
|
}
|
|
55
90
|
/**
|
|
56
91
|
* Atomically checks the customer's monthly quota and, if the amount fits, records it as pending.
|
|
@@ -58,14 +93,16 @@ export class FaucetRequestService {
|
|
|
58
93
|
* Afterwards call `complete` when the faucet confirms, `release` if it definitely failed, or `markUnknown`
|
|
59
94
|
* if the call threw.
|
|
60
95
|
*/
|
|
61
|
-
async reserve(customer, address, amountNcheq, { limitNcheq, minIntervalSeconds = 0, pendingTimeoutSeconds = FAUCET_PENDING_TIMEOUT_SECONDS, },
|
|
62
|
-
const
|
|
63
|
-
return this.faucetRequestRepository.manager.transaction(async (manager) => {
|
|
96
|
+
async reserve(customer, address, amountNcheq, { limitNcheq, minIntervalSeconds = 0, pendingTimeoutSeconds = FAUCET_PENDING_TIMEOUT_SECONDS, addressCap, }, nowOverride) {
|
|
97
|
+
const reservation = await this.faucetRequestRepository.manager.transaction(async (manager) => {
|
|
64
98
|
if (manager.connection.options.type === 'postgres') {
|
|
65
99
|
await manager.query('SELECT pg_advisory_xact_lock(hashtext($1))', [
|
|
66
100
|
`faucet-quota:${customer.customerId}`,
|
|
67
101
|
]);
|
|
68
102
|
}
|
|
103
|
+
// Taken after the lock, so it is current even if this request had to wait for another one
|
|
104
|
+
const now = nowOverride ?? (await FaucetRequestService.databaseNow(manager));
|
|
105
|
+
const window = getFaucetQuotaWindow(now);
|
|
69
106
|
if (minIntervalSeconds > 0) {
|
|
70
107
|
const retryAfterSeconds = secondsUntilNextRequest(await FaucetRequestService.lastRequestAt(manager, customer.customerId), minIntervalSeconds, now);
|
|
71
108
|
if (retryAfterSeconds > 0) {
|
|
@@ -74,7 +111,7 @@ export class FaucetRequestService {
|
|
|
74
111
|
}
|
|
75
112
|
// Reservations that were never confirmed stop counting; mark them so the history shows why
|
|
76
113
|
const pendingCutoff = FaucetRequestService.pendingCutoff(now, pendingTimeoutSeconds);
|
|
77
|
-
await manager
|
|
114
|
+
const swept = await manager
|
|
78
115
|
.createQueryBuilder()
|
|
79
116
|
.update(FaucetRequestEntity)
|
|
80
117
|
.set({ status: 'abandoned' })
|
|
@@ -82,13 +119,65 @@ export class FaucetRequestService {
|
|
|
82
119
|
.andWhere(`status = 'pending'`)
|
|
83
120
|
.andWhere('createdAt < :pendingCutoff', { pendingCutoff })
|
|
84
121
|
.execute();
|
|
122
|
+
if (swept.affected) {
|
|
123
|
+
logFaucetEvent('warn', 'faucet.reservations_abandoned', {
|
|
124
|
+
customerId: customer.customerId,
|
|
125
|
+
count: swept.affected,
|
|
126
|
+
pendingTimeoutSeconds,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
85
129
|
const usedNcheq = await FaucetRequestService.sumRequestedNcheq(manager, customer.customerId, window.start, pendingCutoff);
|
|
86
130
|
if (!fitsInQuota(usedNcheq, amountNcheq, limitNcheq)) {
|
|
87
131
|
return { reserved: false, reason: 'quota_exceeded', usedNcheq, window };
|
|
88
132
|
}
|
|
133
|
+
// The balance the caller read may not include transfers still on their way, so subtract recent requests
|
|
134
|
+
// for this address. Done under the lock, this stops simultaneous requests each getting the full room.
|
|
135
|
+
if (addressCap) {
|
|
136
|
+
const inFlightNcheq = await FaucetRequestService.sumRecentForAddress(manager, customer.customerId, address, new Date(now.getTime() - addressCap.settleSeconds * 1000));
|
|
137
|
+
const roomNcheq = addressRoomNcheq(addressCap.capNcheq, addressCap.currentBalanceNcheq, inFlightNcheq);
|
|
138
|
+
if (amountNcheq > roomNcheq) {
|
|
139
|
+
return { reserved: false, reason: 'address_cap_exceeded', roomNcheq, window };
|
|
140
|
+
}
|
|
141
|
+
}
|
|
89
142
|
const entity = await manager.save(new FaucetRequestEntity(customer, address, amountNcheq));
|
|
90
143
|
return { reserved: true, faucetRequestId: entity.faucetRequestId, usedNcheq, window };
|
|
91
144
|
});
|
|
145
|
+
this.maybeRunRetentionCleanup();
|
|
146
|
+
return reservation;
|
|
147
|
+
}
|
|
148
|
+
/** Deletes faucet request rows older than the retention period. Returns how many were deleted. */
|
|
149
|
+
async runRetentionCleanup(now, retentionMonths = FAUCET_REQUEST_RETENTION_MONTHS) {
|
|
150
|
+
if (retentionMonths <= 0)
|
|
151
|
+
return 0;
|
|
152
|
+
now ??= await FaucetRequestService.databaseNow(this.faucetRequestRepository.manager);
|
|
153
|
+
const result = await this.faucetRequestRepository
|
|
154
|
+
.createQueryBuilder()
|
|
155
|
+
.delete()
|
|
156
|
+
.from(FaucetRequestEntity)
|
|
157
|
+
.where('createdAt < :cutoff', { cutoff: retentionCutoff(now, retentionMonths) })
|
|
158
|
+
.execute();
|
|
159
|
+
return result.affected ?? 0;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Runs the retention cleanup in the background, at most once an hour per process. Called from normal requests
|
|
163
|
+
* because Studio has no scheduler; it never delays or fails the request that triggered it.
|
|
164
|
+
*/
|
|
165
|
+
maybeRunRetentionCleanup() {
|
|
166
|
+
if (FAUCET_REQUEST_RETENTION_MONTHS <= 0)
|
|
167
|
+
return;
|
|
168
|
+
// This only throttles how often this process runs the cleanup, so the process clock is the right one here
|
|
169
|
+
const processNow = Date.now();
|
|
170
|
+
if (processNow - this.lastRetentionCleanupAt < RETENTION_CLEANUP_INTERVAL_MS)
|
|
171
|
+
return;
|
|
172
|
+
this.lastRetentionCleanupAt = processNow;
|
|
173
|
+
this.runRetentionCleanup()
|
|
174
|
+
.then((deleted) => {
|
|
175
|
+
if (deleted > 0)
|
|
176
|
+
logFaucetEvent('info', 'faucet.retention_deleted', { deleted });
|
|
177
|
+
})
|
|
178
|
+
.catch((error) => logFaucetEvent('error', 'faucet.retention_failed', {
|
|
179
|
+
error: error?.message ?? String(error),
|
|
180
|
+
}));
|
|
92
181
|
}
|
|
93
182
|
/** Releases a reservation, e.g. when the upstream faucet reported that it did not credit the account. */
|
|
94
183
|
async release(faucetRequestId) {
|
|
@@ -96,7 +185,7 @@ export class FaucetRequestService {
|
|
|
96
185
|
}
|
|
97
186
|
/** The faucet confirmed the credit. Also applies to a reservation that was already marked abandoned. */
|
|
98
187
|
async complete(faucetRequestId) {
|
|
99
|
-
await this.faucetRequestRepository.update({ faucetRequestId }, { status: 'completed', completedAt:
|
|
188
|
+
await this.faucetRequestRepository.update({ faucetRequestId }, { status: 'completed', completedAt: () => 'now()' });
|
|
100
189
|
}
|
|
101
190
|
/** The faucet call threw, so tokens may have been sent: keep counting this reservation permanently. */
|
|
102
191
|
async markUnknown(faucetRequestId) {
|