Skip to content

Commit b4628af

Browse files
committed
docs: clarify documentation and comments
1 parent ebd08cc commit b4628af

9 files changed

Lines changed: 30 additions & 94 deletions

File tree

apps/backend/.env.example

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,8 @@
2727
# (network-dependent default: off on mainnet, so a shared =true must not enable
2828
# the canary there).
2929
#
30-
# SPs get full-rate testing (DEALS_PER_SP_PER_HOUR, DATASET_CREATIONS_PER_SP_PER_HOUR,
31-
# MIN_NUM_DATASETS_FOR_CHECKS, and the DATASET_LIFECYCLE_CHECK_ENABLED canary) only
32-
# if they're on-chain approved OR listed in
33-
# EXPECTED_APPROVED_SP_IDS/EXPECTED_APPROVED_SP_ADDRESSES (either one is enough —
34-
# they're checked independently, same as BLOCKED_SP_IDS/BLOCKED_SP_ADDRESSES).
35-
# Every other active, unblocked SP (new/unknown/not-yet-vetted) is throttled to a
36-
# fixed trickle tier (1 deal + 1 data-set-creation attempt every 4 hours, 1
37-
# data-set target; see `trickleTierRates` in src/config/constants.ts) to bound
38-
# wallet spend on SPs dealbot hasn't vetted yet. The lifecycle-check canary is
39-
# never scheduled at all for trickle-tier SPs (it creates+terminates a real
40-
# throwaway dataset every run). See #681.
30+
# Full-rate/trickle tiering is applied after provider eligibility. See
31+
# docs/environment-variables.md#full-rate-vs-trickle-tier.
4132
#
4233
# Process globals (database, HTTP ports, ClickHouse batching, pg-boss) have no
4334
# per-network form. Variables for INACTIVE networks are ignored; only active
@@ -153,8 +144,8 @@ CALIBRATION_PULL_PIECE_CLEANUP_INTERVAL_SECONDS=604800
153144
# CALIBRATION_BLOCKED_SP_IDS=1234,5678
154145
# CALIBRATION_BLOCKED_SP_ADDRESSES=0xAbCd...,0x1234...
155146

156-
# SPs eligible for full-rate testing beyond those already isApproved on-chain
157-
# (see #681) — candidates being considered for approval.
147+
# Optional full-rate overrides for unapproved providers. Effective only when
148+
# CALIBRATION_USE_ONLY_APPROVED_PROVIDERS=false.
158149
# CALIBRATION_EXPECTED_APPROVED_SP_IDS=1234,5678
159150
# CALIBRATION_EXPECTED_APPROVED_SP_ADDRESSES=0xAbCd...,0x1234...
160151

@@ -193,8 +184,8 @@ MAINNET_MAINTENANCE_WINDOW_MINUTES=20
193184
# MAINNET_BLOCKED_SP_IDS=1234,5678
194185
# MAINNET_BLOCKED_SP_ADDRESSES=0xAbCd...,0x1234...
195186

196-
# SPs eligible for full-rate testing beyond those already isApproved on-chain
197-
# (see #681) — candidates being considered for approval.
187+
# Optional full-rate overrides for unapproved providers. Effective only when
188+
# MAINNET_USE_ONLY_APPROVED_PROVIDERS=false.
198189
# MAINNET_EXPECTED_APPROVED_SP_IDS=1234,5678
199190
# MAINNET_EXPECTED_APPROVED_SP_ADDRESSES=0xAbCd...,0x1234...
200191

apps/backend/src/common/sp-tier.ts

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,6 @@
1-
import { INetworkConfig } from "src/config/types.js";
1+
import type { INetworkConfig } from "src/config/types.js";
22

3-
/**
4-
* Returns true if the provider qualifies for the full-rate testing tier:
5-
* already `isApproved` on-chain (approved SPs must stay fully monitored
6-
* regardless of manual-list staleness), or manually curated as an
7-
* expected-approval candidate. Every other SP (new, unknown, or not yet
8-
* vetted) defaults to the trickle tier — see #681.
9-
*/
3+
/** Returns whether a provider qualifies for full-rate testing. */
104
export function isFullRateTier(
115
cfg: Pick<INetworkConfig, "expectedApprovedSpAddresses" | "expectedApprovedSpIds">,
126
address: string,

apps/backend/src/config/constants.ts

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,8 @@ export const networkDefaults = {
3737
} satisfies NetworkDefaults;
3838

3939
/**
40-
* Rate/target ceiling applied to SPs that are not on the full-rate tier (see
41-
* `isFullRateTier` in `common/sp-tier.ts`) — i.e. not `isApproved` on-chain
42-
* and not in `EXPECTED_APPROVED_SP_IDS`/`EXPECTED_APPROVED_SP_ADDRESSES`.
43-
*
44-
* Deliberately NOT env-configurable per network: this tier exists to bound
45-
* worst-case wallet spend on new/unknown SPs (#681).
40+
* Fixed rates and dataset target for eligible providers outside the full-rate
41+
* tier. Kept non-configurable to limit wallet-spend exposure.
4642
*/
4743
export const trickleTierRates = {
4844
dealsPerSpPerHour: 1 / 4, // 1 data-storage check every 4 hours

apps/backend/src/config/types.ts

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,14 +141,7 @@ export type BaseNetworkConfig = {
141141
blockedSpIds: Set<string>;
142142
blockedSpAddresses: Set<string>;
143143

144-
/**
145-
* Manually curated SPs that should receive full-rate testing even though
146-
* they aren't (yet) `isApproved` on-chain — e.g. candidates being
147-
* considered for approval. Combined with `isApproved` to gate the
148-
* full-rate tier (see `isFullRateTier` in `common/sp-tier.ts`); every
149-
* other active, unblocked SP defaults to the trickle tier
150-
* (`trickleTierRates` in `config/constants.ts`). See #681.
151-
*/
144+
/** Provider IDs and addresses granted full-rate testing before on-chain approval. */
152145
expectedApprovedSpIds: Set<string>;
153146
expectedApprovedSpAddresses: Set<string>;
154147

apps/backend/src/deal/deal.service.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ export class DealService {
124124
* Pick which data-set slot this deal will target.
125125
*
126126
* Policy:
127-
* - If `minDataSets > 1` (the caller's per-SP tier target — see
128-
* `isFullRateTier`) and a random index > 0 is selected, probe that
127+
* - If `minDataSets > 1` and a random index > 0 is selected, probe that
129128
* slot first. If live, use it. If missing or terminated, fall through
130129
* to baseline (data_set_creation owns repair/provisioning).
131130
* - Probe baseline. If terminated, throw `DealJobTerminatedDataSetError`

apps/backend/src/jobs/jobs.service.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1566,7 +1566,6 @@ describe("JobsService schedule rows", () => {
15661566
} as unknown as JobsServiceDeps[0];
15671567
service = buildService({ configService });
15681568

1569-
// Neither approved nor on the expected-approved list -> trickle tier.
15701569
providerRegistryRepositoryMock.findActiveAddresses.mockResolvedValueOnce([{ address: "0xaaa", isApproved: false }]);
15711570

15721571
await callPrivate(service, "ensureScheduleRows", DEFAULT_NETWORK);

apps/backend/src/jobs/jobs.service.ts

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1078,9 +1078,7 @@ export class JobsService implements OnModuleInit, OnApplicationShutdown {
10781078
return;
10791079
}
10801080

1081-
// Defensive gate: schedules are only created for full-rate-tier providers, but a
1082-
// stale enqueued job (e.g. after the provider dropped out of the full-rate tier)
1083-
// must still no-op safely rather than spend on a throwaway data set.
1081+
// Re-check eligibility because queued jobs can outlive their schedule.
10841082
const provider = await this.storageProviderRepository.findByAddress(spAddress, network);
10851083
const isFullTier = isFullRateTier(networkCfg, spAddress, provider?.isApproved ?? false, provider?.id);
10861084
if (!isFullTier) {
@@ -1254,17 +1252,6 @@ export class JobsService implements OnModuleInit, OnApplicationShutdown {
12541252
}
12551253
}
12561254

1257-
/**
1258-
* Computes job-cadence intervals for a network.
1259-
*
1260-
* `deal`/`data_set_creation` (and the `minDataSets` target) are tiered:
1261-
* a `Full` variant driven by the network's configured rate, and a fixed,
1262-
* conservative `Trickle` variant (`trickleTierRates`) applied to SPs that
1263-
* aren't on the full-rate tier — see `isFullRateTier`. Every other job
1264-
* type stays a single network-wide cadence for now; only deals and
1265-
* data-set creation drive the wallet-spend the trickle tier exists to
1266-
* bound (#681).
1267-
*/
12681255
private getIntervalSecondsForRates(network: Network): {
12691256
dealIntervalSecondsFull: number;
12701257
dealIntervalSecondsTrickle: number;
@@ -1394,9 +1381,6 @@ export class JobsService implements OnModuleInit, OnApplicationShutdown {
13941381
const trickleTierAddresses: string[] = [];
13951382

13961383
for (const { address, providerId, isApproved } of unblockedProviders) {
1397-
// Full-rate tier: on-chain approved, or manually curated as an
1398-
// expected-approval candidate. Everyone else gets the trickle tier,
1399-
// which bounds worst-case spend on new/unknown SPs — see #681.
14001384
let dealIntervalSeconds = dealIntervalSecondsFull;
14011385
let dataSetCreationIntervalSeconds = dataSetCreationIntervalSecondsFull;
14021386
let minDataSets = minDataSetsFull;
@@ -1434,7 +1418,6 @@ export class JobsService implements OnModuleInit, OnApplicationShutdown {
14341418
dataSetCreationStartAt,
14351419
);
14361420
}
1437-
// The trickle-tier SPs never get the lifecycle canary scheduled at all.
14381421
if (lifecycleCheckScheduleEnabled && isFullTier) {
14391422
await this.jobScheduleRepository.upsertSchedule(
14401423
"data_set_lifecycle_check",
@@ -1492,9 +1475,6 @@ export class JobsService implements OnModuleInit, OnApplicationShutdown {
14921475
});
14931476
}
14941477
} else if (trickleTierAddresses.length > 0) {
1495-
// A provider that dropped out of the full-rate tier (e.g. lost
1496-
// isApproved, or was removed from EXPECTED_APPROVED) isn't eligible
1497-
// for dataSet lifecycle canary check.
14981478
const removed = await this.jobScheduleRepository.deleteSchedulesForAddresses(
14991479
"data_set_lifecycle_check",
15001480
trickleTierAddresses,

apps/backend/src/jobs/repositories/job-schedule.repository.ts

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,6 @@ export class JobScheduleRepository {
144144
return typeof rowCount === "number" ? rowCount : 0;
145145
}
146146

147-
/**
148-
* Deletes schedule rows for a specific job type and set of addresses.
149-
*
150-
* Used to stop a per-SP job (e.g. `data_set_lifecycle_check`) for
151-
* addresses that dropped out of eligibility (e.g. left the full-rate
152-
* tier — see #681) without touching their other job-type schedules.
153-
*
154-
* @param jobType - The job type to remove schedules for.
155-
* @param addresses - Provider addresses whose schedule for this job type should be deleted.
156-
* @param network - Only remove schedules belonging to this network.
157-
* @returns Array of storage provider addresses whose schedules were deleted.
158-
*/
159147
async deleteSchedulesForAddresses(jobType: JobType, addresses: string[], network: Network): Promise<string[]> {
160148
if (addresses.length === 0) return [];
161149
const [rows] = (await this.dataSource.query(

docs/environment-variables.md

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -518,7 +518,7 @@ CALIBRATION_SUBGRAPH_ENDPOINT=https://api.goldsky.com/api/public/<project>/subgr
518518

519519
**Role**: Minimum number of datasets provisioned per storage provider before running checks on this network. When > 1, the `data_set_creation` job is responsible for provisioning any additional datasets.
520520

521-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs target a fixed 1 dataset regardless of this value. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
521+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs target one dataset regardless of this value.
522522

523523
---
524524

@@ -546,14 +546,16 @@ Dealbot uses pg-boss for rate-based scheduling — see [Jobs (pg-boss)](#jobs-pg
546546

547547
### Full-rate vs. trickle tier
548548

549-
Every active, unblocked SP is assigned to one of two testing tiers, re-evaluated on every scheduler tick:
549+
Provider eligibility is applied before tiering. When `<NET>_USE_ONLY_APPROVED_PROVIDERS=true`, only active, on-chain-approved providers are scheduled; expected-approved lists do not override this setting. When it is `false`, all active providers are eligible unless blocked.
550+
551+
Eligible providers are assigned to one of two tiers on each scheduler tick:
550552

551553
- **Full-rate tier**: the SP is already `isApproved` on-chain (FWSS), **or** its ID/address is listed in [`<NET>_EXPECTED_APPROVED_SP_IDS`](#net_expected_approved_sp_ids) / [`<NET>_EXPECTED_APPROVED_SP_ADDRESSES`](#net_expected_approved_sp_addresses). It gets the configured `<NET>_DEALS_PER_SP_PER_HOUR`, `<NET>_DATASET_CREATIONS_PER_SP_PER_HOUR`, and `<NET>_MIN_NUM_DATASETS_FOR_CHECKS` rates, and (if enabled) the `data_set_lifecycle_check` canary.
552-
- **Trickle tier**: every other SP — new, unknown, or not yet vetted. Deals and dataset-creation attempts are capped at a fixed rate of one every 4 hours, with a target of 1 dataset, regardless of the configured full-rate values. The `data_set_lifecycle_check` canary is never scheduled for trickle-tier SPs at all (not just at a slower rate), since it creates and terminates a real throwaway dataset on every run.
554+
- **Trickle tier**: every other eligible SP. Deals and dataset-creation attempts run at a fixed rate of one every 4 hours, with a target of one dataset. The `data_set_lifecycle_check` canary is not scheduled because every run creates and terminates a real throwaway dataset.
553555

554-
The trickle tier's rate is a fixed constant (`trickleTierRates` in `src/config/constants.ts`), not env-configurable per network — it exists specifically to bound worst-case wallet spend on SPs dealbot hasn't vetted yet, so it can't be loosened by config alone. Retrieval, pull-check, and piece-cleanup cadences are not tiered; they apply uniformly regardless of tier.
556+
Trickle rates are fixed in `trickleTierRates` and are not configurable. Retrieval, pull-check, and piece-cleanup cadences are shared by both tiers.
555557

556-
**Why**: before this, every active SP — including brand-new or ephemeral ones that appeared on-chain and were not yet blocklisted — received full-rate testing immediately, which could drain the dealbot wallet before anyone noticed and blocklisted the SP. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
558+
**Why**: when testing includes unapproved providers, newly registered or ephemeral SPs previously received full-rate testing until manually blocklisted, increasing wallet-spend exposure. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
557559

558560
### `<NET>_DEALS_PER_SP_PER_HOUR`
559561

@@ -566,7 +568,7 @@ The trickle tier's rate is a fixed constant (`trickleTierRates` in `src/config/c
566568

567569
**Notes**: Fractional values are supported (e.g. `0.25` ⇒ one deal every 4 hours per SP).
568570

569-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs are capped at a fixed one deal every 4 hours regardless of this value. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
571+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs use the fixed trickle rate.
570572

571573
---
572574

@@ -644,7 +646,7 @@ The trickle tier's rate is a fixed constant (`trickleTierRates` in `src/config/c
644646

645647
**Role**: Target dataset-creation rate per storage provider on this network.
646648

647-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs are capped at a fixed one attempt every 4 hours regardless of this value. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
649+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). Trickle-tier SPs use the fixed trickle rate.
648650

649651
---
650652

@@ -745,14 +747,12 @@ check types (data-storage, retrieval, and data-retention). Matching is case-inse
745747
- **Required**: No
746748
- **Default**: `""` (empty — no manually-listed candidates)
747749

748-
**Role**: Manually curated list, by provider numeric ID, of SPs that should receive [full-rate tier](#full-rate-vs-trickle-tier) testing even though they aren't (yet) `isApproved` on-chain — e.g. candidates being considered for approval that need enough test data to evaluate. SPs already `isApproved` on-chain get the full-rate tier automatically and do not need to be listed here.
750+
**Role**: Provider IDs granted [full-rate testing](#full-rate-vs-trickle-tier) before on-chain approval. Approved providers do not need to be listed. This setting has no effect when `<NET>_USE_ONLY_APPROVED_PROVIDERS=true` because unapproved providers are excluded before tiering.
749751

750-
**Notes**: An SP only needs to appear in **either** `<NET>_EXPECTED_APPROVED_SP_IDS` **or** `<NET>_EXPECTED_APPROVED_SP_ADDRESSES` — not both — to qualify. They're checked independently (matching either one is sufficient), the same as `<NET>_BLOCKED_SP_IDS`/`<NET>_BLOCKED_SP_ADDRESSES`. Use whichever identifier you have on hand; listing both for the same SP is harmless but redundant.
752+
**Notes**: Matching either the ID list or address list is sufficient.
751753

752754
**Example**: `<NET>_EXPECTED_APPROVED_SP_IDS=1234,5678`
753755

754-
**See also**: [issue #681](https://github.com/FilOzone/dealbot/issues/681)
755-
756756
---
757757

758758
### `<NET>_EXPECTED_APPROVED_SP_ADDRESSES`
@@ -761,14 +761,10 @@ check types (data-storage, retrieval, and data-retention). Matching is case-inse
761761
- **Required**: No
762762
- **Default**: `""` (empty — no manually-listed candidates)
763763

764-
**Role**: Address-based counterpart to [`<NET>_EXPECTED_APPROVED_SP_IDS`](#net_expected_approved_sp_ids) — same effect, matched by provider address instead of numeric ID. Matching is case-insensitive.
765-
766-
**Notes**: Either this or `<NET>_EXPECTED_APPROVED_SP_IDS` is sufficient for a given SP; you don't need to set both.
764+
**Role**: Address-based counterpart to [`<NET>_EXPECTED_APPROVED_SP_IDS`](#net_expected_approved_sp_ids). Matching is case-insensitive.
767765

768766
**Example**: `<NET>_EXPECTED_APPROVED_SP_ADDRESSES=0xAbCd...,0x1234...`
769767

770-
**See also**: [issue #681](https://github.com/FilOzone/dealbot/issues/681)
771-
772768
---
773769

774770
### `<NET>_MAX_DATASET_STORAGE_SIZE_BYTES`
@@ -947,7 +943,7 @@ These variables are **global** (not per-network) and control the shared pg-boss
947943

948944
**Notes**: Fractional values are supported. For example, `0.25` means one deal every 4 hours per storage provider.
949945

950-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
946+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier).
951947

952948
---
953949

@@ -980,7 +976,7 @@ These variables are **global** (not per-network) and control the shared pg-boss
980976
- Increase if you want more datasets per provider to raise the density of data-retention proof samples.
981977
- Decrease to reduce on-chain footprint per provider during testing.
982978

983-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
979+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier).
984980

985981
**See also**: [`docs/data-set-creation.md`](./data-set-creation.md)
986982

@@ -1011,7 +1007,7 @@ These variables are **global** (not per-network) and control the shared pg-boss
10111007

10121008
**Notes**: Fractional values are supported. For example, `0.5` means one dataset creation every 2 hours per storage provider.
10131009

1014-
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier). See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
1010+
**Tiering**: Only applies to SPs on the [full-rate tier](#full-rate-vs-trickle-tier).
10151011

10161012
---
10171013

@@ -1025,7 +1021,7 @@ These variables are **global** (not per-network) and control the shared pg-boss
10251021

10261022
**Notes**: Self-contained — it does not touch the managed check data sets and does not depend on `data_set_creation`. When disabled, stale schedules are removed so they stop enqueuing no-op jobs.
10271023

1028-
**Tiering**: Regardless of this setting, the canary is **never scheduled for trickle-tier SPs** — see [full-rate vs. trickle tier](#full-rate-vs-trickle-tier). It creates and terminates a real throwaway dataset every run, so trickle-tier (unvetted) SPs are fully excluded rather than just throttled. A provider that drops out of the full-rate tier has its stale schedule row removed on the next scheduler tick. See [issue #681](https://github.com/FilOzone/dealbot/issues/681).
1024+
**Tiering**: Only [full-rate providers](#full-rate-vs-trickle-tier) receive this canary because each run creates and terminates a real throwaway dataset.
10291025

10301026
**See also**: [`docs/checks/data-set-lifecycle-check.md`](./checks/data-set-lifecycle-check.md)
10311027

@@ -1043,7 +1039,7 @@ These variables are **global** (not per-network) and control the shared pg-boss
10431039

10441040
**Notes**: Independent of `DATASET_CREATIONS_PER_SP_PER_HOUR`. Fractional values are supported.
10451041

1046-
**Tiering**: This rate is irrelevant for trickle-tier SPs — the canary is never scheduled for them at all. See [`DATASET_LIFECYCLE_CHECK_ENABLED`](#dataset_lifecycle_check_enabled) and [full-rate vs. trickle tier](#full-rate-vs-trickle-tier).
1042+
**Tiering**: Only applies to [full-rate providers](#full-rate-vs-trickle-tier).
10471043

10481044
---
10491045

0 commit comments

Comments
 (0)