From ca49c5feec2ed0a6f912937c206433352be16d15 Mon Sep 17 00:00:00 2001 From: Eric <5089238+emizzle@users.noreply.github.com> Date: Mon, 7 Apr 2025 17:37:34 +1000 Subject: [PATCH] Various updates - Move until to multiple availabilities feature - Rearrange sections to align with their parent module - Add mermaid diagrams for overview, entire Sales architecture, cleanup flows - reduce SalesOrder properties to only those that are really needed - change passive cleanup to corrective cleanup. no need to run continuously at intervals - add design rules - add explanations for things that are hard to remember like why refCount needs to be persisted --- design/sales flow charts/renewals_cleanup.jpg | Bin 119923 -> 0 bytes design/sales2.md | 677 +++++++++++++----- 2 files changed, 509 insertions(+), 168 deletions(-) delete mode 100644 design/sales flow charts/renewals_cleanup.jpg diff --git a/design/sales flow charts/renewals_cleanup.jpg b/design/sales flow charts/renewals_cleanup.jpg deleted file mode 100644 index a266b2a943452ec83ea17708d36f1ea8002fe9be..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 119923 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 i-zBk)x{!^luljBLx!v^2cq SalesStorage + SalesStorage --"queries"--> Sales + + SalesStorage --"Availability +
SaleOrder
state"--> MetadataStore + SalesStorage --"dedicated quota"--> SalesRepo + + %% storageDelete --> salesRepoDelete + %% salesActiveCleanup --> storageDelete + %% salesPassiveCleanup --> storageDelete + + %% storageStore --> salesRepoStore + %% salesDownload --> storageCreateSalesObj + + %% salesRepoStore --> metaRefCountCrud + %% salesRepoDelete --> metaRefCountCrud + + %% storageCreateSalesObj --> metaCreateSalesObj + + %% storageArchiveSalesObj --> metaArchiveSalesObj + + + -```ascii ------------------------------------------------------------------- -| | -| Sales | -| | -| ^ | | -| | | updates ------------------ | -| | --------------> | | | -| | | SalesStorage | | -| ------------------- | | | -| queries ------------------ | -| ^ ^ | -| | | | -| | | Availability + SaleOrder | -| dedicated quota | | state | -| v v | -| ---------------- ----------------- | -| | SalesRepo | | MetadataStore | | -| ---------------- ----------------- | ------------------------------------------------------------------- ``` + +Full diagram + +```mermaid +--- +config: + theme: redux + look: neo + layout: elk +--- +flowchart TB + subgraph sales["Sales"] + salesLoad["Load"] + salesProcessSlot["Process slot"] + salesActiveCleanup["Active cleanup"] + salesPassiveCleanup["Corrective cleanup"] + salesDownload["Download"] + salesLoad --> salesPassiveCleanup + end + subgraph storage["Sales storage"] + storageDelete["Delete dataset"] + storageStore["Store dataset"] + storageCreateSalesObj["Create SalesObject"] + storageArchiveSalesObj["Archive SalesObject"] + storageCreateSalesObj --> storageStore + storageDelete --> storageArchiveSalesObj + end + +subgraph metaData["MetadataStore"] + metaCreateSalesObj["Create SalesObject"] + metaArchiveSalesObj["Archive SalesObject"] + metaRefCountCrud["Ref count CRUD"] + end + subgraph fsm["Sales state machine"] + preparing["Preparing"] + reserving["Reserving"] + download["Download"] + initialProving["Gen proof"] + filling["Filling"] + filled["Filled"] + proving["Proving"] + payout["Payout"] + finished["Finished"] + errored["Errored"] + cancelled["Cancelled"] + ignored["Ignored"] + failed["Failed"] + preparing --> reserving + preparing --> ignored + preparing --> errored + reserving --> download + reserving --> ignored + reserving --> errored + download --> initialProving + download --> errored + initialProving --> filling + initialProving --> errored + filling --> filled + filling --> ignored + filling --> errored + filled --> proving + filled --> errored + proving --> payout + proving --> errored + payout --> finished + payout --> errored + finished --> errored + failed --> errored + end + subgraph contracts["Marketplace contracts"] + contractsFreeSlot["Free slot"] + end + subgraph market["Market abstraction"] + marketFreeSlot["Free slot"] + end + subgraph salesRepo["SalesRepo"] + salesRepoStore["Store dataset"] + salesRepoDelete["Delete dataset"] + end + subgraph salesAgent["SalesAgent"] + agentCleanUp["Active cleanup"] + agentDownload["Download"] + end + + + storageDelete --> salesRepoDelete + salesActiveCleanup --> storageDelete + salesPassiveCleanup --> storageDelete + + storageStore --> salesRepoStore + download --> agentDownload + agentCleanUp --> salesActiveCleanup + agentDownload --> salesDownload + salesDownload --> storageCreateSalesObj + + salesRepoStore --> metaRefCountCrud + salesRepoDelete --> metaRefCountCrud + + storageCreateSalesObj --> metaCreateSalesObj + + storageArchiveSalesObj --> metaArchiveSalesObj + + salesProcessSlot --> salesAgent + %% salesAgent <--> fsm + + cancelled --> agentCleanUp + failed --> agentCleanUp + finished --> agentCleanUp + errored --> agentCleanUp + + marketFreeSlot --> contractsFreeSlot + + payout --> marketFreeSlot + failed --> marketFreeSlot + cancelled --> marketFreeSlot + +``` + +## `SalesStorage` module + The `SalesStorage` module manages the SP's availability and snapshots of past -and present sales or `SalesOrders`, both of which are persisted in the `MetadataStore`. SPs can add -and update their availability, which is managed through the `SalesStorage` -module. As a `SalesOrder` traverses the sales state machine, it is created and -updated1 through the `SalesStorage` module. Queries for availability -and `SalesOrders` will also occur in the `SalesStorage` module. Datasets that -are downloaded and deleted as part of the sales process will be handled in the -`SalesRepo` module. +and present sales or `SalesOrders`, both of which are persisted in the +`MetadataStore`. SPs can add and update their availability, which is managed +through the `SalesStorage` module. As a `SalesOrder` traverses the sales state +machine, it is created and updated[^updates_trackstate] through the +`SalesStorage` module. Queries for availability and `SalesOrders` will also +occur in the `SalesStorage` module. Datasets that are downloaded and deleted as +part of the sales process will be managed in the `SalesRepo` module. -1 Updates are only needed to support [tracking the latest state in - the `SalesOrder`](#tracking-latest-state-machine-state). +[^updates_trackstate]: Updates are only needed to support [tracking the latest + state in the `SalesOrder`](#tracking-latest-state-machine-state). -## Query support +### Availability + +The SP's availability determines which sales it is willing to attempt to enter +into. In other words, it represents *future sales* that an SP is willing to take +on[^designrules]. It consists of parameters that will be matched to incoming storage +requests via the slot queue. + +[^designrules]: See [design rules](#design-rules) for a further explanation. + +| Property | Description | +|----------------------------|---------------------------------------------------------------------------------| +| `duration` | Maximum duration of a storage request the SP is willing to host new slots for. | +| `minPricePerBytePerSecond` | Minimum price per byte per second that the SP is willing to host new slots for. | + +The availability of a SP consists of the maximum duration and the minimum price +per byte per second to sell storage for. + +#### `Availability` lifecycle + +A user can add, update, or delete an `Availability` at any time. The +`Availability` will be stored in the MetadataStore. Only one `Availability` can +be created and once created, it will exist permanently in the MetadataStore +until it is deleted. The properties of a created `Availability` can be updated +at any time. + +Because availability(ies) represents *future* sales (and not active sales), and +because fields of the matching `Availability` are persisted in a `SalesOrder`, +availabilities are not tied to active sales and can be manipulated at any time. + +### `SalesOrder` object + +The `SalesOrder` object represents a slot that a SP attempted to, or eventually +did host. `SalesOrders` are created only when there is an attempt to download +the slot data, meaning there was a successful availability match and a +successful slot reservation. The purpose of `SalesOrders` is to keep track of +sales for dataset cleanup operations, and to provide historical information for +the SP. + +Cleanup routines will be able to query `SalesOrders` and compare them to those +that are filled on chain, to ensure that datasets that are no longer being +hosted do not remain on disk. + +In addition, SPs will likely want to list slots that have been hosted in the +past. After a `StorageRequest` is completed, it is removed from the contract's +`mySlots` storage, with the `StorageRequest` information queryable only by +random access with the `RequestId`. Therefore, at a minimum, the `RequestId` and +slot index of the slot that was hosted would need to be persisted by the SP for +the SP to keep track of slots that were hosted. + +| Property | Description | +|-------------|------------------------------------------------------------------------------------------------------------------------------------------------------| +| `requestId` | `RequestId` of the `StorageRequest`. Can be used to retrieve storage request details. | +| `slotIndex` | Slot index of the slot being hosted. | +| `treeCid` | CID of the manifest dataset, used for `SalesRepo` interaction. TODO: `manifestCid` may be sufficient. Depends on the final design of the `RepoStore` | + +#### `SalesOrder` lifecycle + +At the point a SP reaches the `SaleDownload` state, a `SalesOrder` is created +and it will live permanently in the MetadataStore. `SalesOrder` objects cannot +be deleted as they represent historical sales of the SP. + +When the `SalesOrder` object is first created, its key will be created in the +`/active` namespace. After data for the `SalesOrder` has been deleted (if there +is any) in a clean up procedure, the key will be moved from the `/active` +namespace to the `/archive` namespace. These key namespace manipulations +facilitate future lookups in active/corrective clean up operations. + +If there's support for [tracking the latest state in the +`SalesOrder`](#tracking-latest-state-machine-state), `SalesOrder.state` +will be modified as the sale progresses through each state of the Sales state +machine. + +### Query support The `SalesStorage` module will need to support querying the availability and sales data so the caller can understand if a sale can be serviced and to support clean @@ -51,16 +253,16 @@ up routines. The following queries will need to be supported: unnecessary resource consumption](#concurrent-workers-prevent-unnecessary-resource-consumption), by additionally querying the slot size of `SalesOrders` that are in or past - the Downloading state. + the Downloading state (`/active` `SalesOrders`). 2. Clean up routines will need to know the "active sales", or any `SalesOrders` in the `/active` key namespace (those that have not been archived) through the state machine or clean up routines. -3. Servicing a new slot will require sufficient "total collateral", which is the - remaining balance in the funding account. In the future, this can be - optimised to [prevent unnecessary resource +3. Servicing a new slot will require sufficient [total + collateral](#total-collateral), which is the remaining balance in the funding + account. In the future, this can be optimised to [prevent unnecessary + resource consumption](#concurrent-workers-prevent-unnecessary-resource-consumption), - by additionally querying the collateral of `SalesOrders` that are in or past - the Downloading state. + by additionally querying the collateral of `/active` `SalesOrders`. ## `SalesRepo` module @@ -96,48 +298,7 @@ direction TB classDef focusClass fill:#c4fdff,stroke:#333,stroke-width:4px,color:black ``` -## Availability - -The SP's availability determines which sales it is willing to attempt to enter -into. In other words, it represents *future sales* that an SP is willing to take -on. It consists of parameters that will be matched to incoming storage -requests via the slot queue. - - -| Property | Description | -|----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------| -| `id` | ID of the Availability. Note: this is only needed if there is support for [multiple availabilities](#multiple-availabilities). | -| `duration` | Maximum duration of a storage request the SP is willing to host new slots for. | -| `minPricePerBytePerSecond` | Minimum price per byte per second that the SP is willing to host new slots for. | -| `enabled` | If set to false, the availability will not accept new slots. Updates to this value will not impact any existing slots that are already being hosted. | -| `until` | Specifies the latest timestamp after which the availability will no longer host any slots. If set to 0, there will be no restrictions. | - -The availability of a SP consists of the maximum duration and the minimum price -per byte per second to sell storage for. - -### Funding account vs profit account - -SPs should control two accounts: a funding account, and a profits account. The -funds in the funding account represent the total collateral that a SP is willing -to risk in all of its sales combined. This account will need to have some funds -in it before slots can be hosted, assuming the storage request requires -collateral. If a SP has been partially or wholly slashed in one of their sales, -they may wish to top up this account to ensure there is sufficient collateral -for future sales. - -The profits account is the account for which proceeds from sales are paid into. -To minimise risk, this account should be stored in cold storage. - -It is recommended that the profit account is a separate account from the funding -account so that profits are not placed at risk by being used as collateral. If a -SP specifies the same account for funding and profits, and the SP is (partially -or wholly) slashed, future collateral deposits may use their profits from -previous sales. - -Note: having a separate profit account relies on the ability of the Vault -contract to support multiple accounts. - -### Total collateral +## Total collateral The concept of "total collateral" means the total collateral the SP is willing to risk at any one point in time. In other words, it is willing to risk "total @@ -149,87 +310,134 @@ slots. From the marketplace perspective, slots cannot be filled if there is an insufficient balance in the funding account. -### `Availability` lifecycle +### Funding account vs profit account -A user can add, update, or delete an `Availability` at any time. The -`Availability` will be stored in the MetadataStore. Only one `Availability` can -be created and once created, it will exist permanently in the MetadataStore -until it is deleted. The properties of a created `Availability` can be updated -at any time. +SPs should control two accounts to safely host slots: a funding account, and a +profits account. -Because availability(ies) represents *future* sales (and not active sales), and -because fields of the matching `Availability` are persisted in a `SalesOrder`, -availabilities are not tied to active sales and can be manipulated at any time. +The funds in the funding account represent the total collateral +that a SP is willing to risk in all of its sales combined. This account will +need to have some funds in it before slots can be hosted, assuming the storage +request requires collateral. If a SP has been partially or wholly slashed in one +of their sales, they may wish to top up this account to ensure there is +sufficient collateral for future sales. -## `SalesOrder` object +The profits account is the account for which proceeds from sales are paid into. +To minimise risk, this account should be stored in cold storage. -The `SalesOrder` object represents a snapshot of the sale parameters at the time -a slot is processed in the slot queue. It can be thought of as the market -conditions at the time of sale. It includes fields of the storage request, slot, -and the matching availability fields. +While a SP could technically specify the same address for both accounts, it is +recommended that the profit account is a separate account from the funding +account so that profits are not placed at risk by being used as collateral. If a +SP specifies the same account for funding and profits, and the SP is (partially +or wholly) slashed, future collateral deposits may use their profits from +previous sales. -| Property | Description | -|----------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `requestId` | RequestId of the StorageRequest. Can be used to retrieve storage request details. | -| `slotIndex` | lot index of the slot being processed. | -| `duration` | `duration` from the matched Availability. | -| `minPricePerBytePerSecond` | `minPricePerByte` from the matched Availabilty. | -| `state` | Latest state in the sales state machine that was reached. Note: this is only needed when there support for [tracking the latest state in the `SalesOrder`](#tracking-latest-state-machine-state). | - -### `SalesOrder` lifecycle - -At the point a SP reaches the `SaleDownload` state, a `SalesOrder` is created -and it will live permanently in the MetadataStore. `SalesOrder` objects cannot -be deleted as they represent historical sales of the SP. - -When the `SalesOrder` object is first created, its key will be created in the -`/active` namespace. After data for the `SalesOrder` has been deleted (if there -is any) in a clean up procedure, the key will be moved from the `/active` -namespace to the `/archive` namespace. These key namespace manipulations -facilitate future lookups in active/passive clean up operations. - -If there's support for [tracking the latest state in the -`SalesOrder`](#tracking-latest-state-machine-state), `SalesOrder.state` -will be modified as the sale progresses through each state of the Sales state -machine. +Note: having a separate profit account relies on the ability of the Vault +contract to support multiple accounts. ## Cleanup routines -The responsibility of the cleanup routine is to ensure that any data associated -with a Sale is deleted from the `SalesRepo`. Once the data has been deleted, the -`SalesOrder` will reflect that it has been cleaned up by being archived. +The responsibility of the cleanup routine is to ensure that any data that is no +longer part of an active sales is deleted from the `SalesRepo`. Once the data +has been deleted, the `SalesOrder` will reflect that it has been cleaned up by +being archived. There are two types of cleanup routines that a SP node will take part in: active -and passive. Active cleanup routines are run as part of a state in the Sales -state machine. Passive cleanup routines are continuously run at a specified time -interval. Both perform a similar task, however the active cleanups operate on a -single `SalesOrder`, while passive cleanups operate over a set of `SalesOrders` -and have additional conditions for cleanup. +and corrective. Active cleanup routines are run as part of a final state in the +Sales state machine. Corrective cleanup routines are continuously run at a +specified time interval with the goal of cleaning up any datasets that may not +have been cleaned up by active cleanup due to a node restart. Both perform a +similar task, however the active cleanups operate on a single `SalesOrder`, +while corrective cleanups operate over a set of `SalesOrders` and have additional +conditions for cleanup. + +```mermaid +--- +config: + theme: redux +--- +flowchart TB + subgraph Sales + Load[Load] + ProcessSlot[Process Slot] + ActiveCleanup[Active cleanup] + PassiveCleanup[Corrective cleanup] + Load --> PassiveCleanup + end + FSM[Sales state machine] + ProcessSlot --> SalesAgent + SalesAgent <--> FSM + SalesAgent --> ActiveCleanup +``` ### Active cleanup The active cleanup routine is typically run as part of the a final state in the -Sales state machine, ie `SaleFinished`. In this routine, active sales will be +Sales state machine, eg `SaleFinished`. In this routine, active sales will be retrieved from the Marketplace contract via `mySlots`. If the slot id associated with the sale is not in the set of active sales, any data associated with the -slot will be deleted. Then, the `SalesOrder` will be archived, by moving its key -to the `/archive` namespace. +slot will be deleted. Finally, the `SalesOrder` will be archived, by moving its +key to the `/archive` namespace. -### Passive cleanup +```mermaid +--- +config: + theme: redux +--- +flowchart TB + CleanUp(["Active CleanUp"]) -- Current SaleOrder --> -At regular time intervals, active sales will be retrieved from the Marketplace + Delete["Delete dataset"] --> + Archive["Archive SalesOrder"] --> + Done + + CleanUp:::start + classDef start fill:#000000, color:#FFFFFF +``` + +Note that in the case of [renewals](#renewals-prevent-dataset-deletion) or in +any case that the same dataset as the one being deleted is simultaneously being +downloaded or processed, the dataset ref count is enough to prevent deletion of +the dataset. + +### Corrective cleanup + +Node shutdowns can sometimes come in the middle of a non-atomic operation such +as persisting a `SalesOrder` and downloading a dataset. In this case, corrective +cleanup is needed to ensure that datasets that not being actively hosted are +removed from the node. + +On node startup, active sales will be retrieved from the Marketplace contract via `mySlots`. Then, all `SalesOrders` in the `/active` namespace will -be queried. Any `SalesOrders` with a slot id not in the set of active sales and -with a `StorageRequest` state that is "completed" (failed, cancelled, finished), -will have the data associated with the slot deleted, if there is any. Then, the +be queried. Any `SalesOrders` with a slot id not in the set of active sales +(`mySlots`) will have the data associated with the slot deleted, if there is +any. Any `SalesOrders` associated with `StorageRequests` that are in the `New` +or `Fulfilled` state should be ignored in this process, otherwise datasets of +sales that are in the process of being processed may be impacted. Finally, the `SalesOrder` will be archived by moving its key to the `/archive` namespace. -`SalesOrders` with a `StorageRequest` state that is not yet completed should not -have their data deleted, as the SP may be in the process of starting to host a -slot, with the sale in an early state of the Sales state machine. -### Node startup +```mermaid +--- +config: + theme: redux +--- +flowchart TB + CleanUp(["Corrective CleanUp"]) --/active SalesOrders --> + %% TimeInterval[Every time interval] -- /active SalesOrders--> + QueryResults(("SalesOrders not
actively filled
on chain")) -On node startup, the passive cleanup routine should be run. + CleanUp -- Active sales on chain --> + QueryResults --"SalesOrder"--> + IsActiveRequest{Is request active?} --"No"--> + Delete["Delete dataset"] --> + Archive["Archive SalesOrder"] --> + QueryResults + + IsActiveRequest --"Yes"--> QueryResults + + CleanUp:::start + classDef start fill:#000000, color:#FFFFFF +``` ## Sale flow @@ -247,6 +455,26 @@ availability ID stored in the `SalesOrder` object. Note that the total collateral across all availabilities that a SP is willing to risk remains as the balance of funds in the funding account. +Support for multiple availabilities will need to add new properties to the +`Availability` object: + +| Property | Description | +|-----------|----------------------------------------------------------------------------------------------------------------------------------| +| `id` | ID of the Availability. | +| `enabled` | If set to false, the SP will not use this Availability to host new slots. | +| `until` | Only accept slots whose request ends before `until`. If set to 0, there will be no restrictions. Useful for planned maintenance. | + +The `id` property will be used to form the key for storage in the +`MetadataStore`. This value will be used to uniquely identify the `Availability` +for CRUD and REST API operations. + +The `enabled` property will allow an Availability to be disabled so that other, +enabled Availabilities can still be used to match new sales. + +The `until` property matches Availabilities with requests that end before +`until`. This is useful if there is upcoming planning maintenance, such as a +disk swap. + ### Concurrent workers support Concurrent workers allow a SP to reserve, download, generate an initial proof @@ -282,6 +510,12 @@ process, ie downloading, proof generating, or filling. To facilitate this, `SalesOrder.state` would need to track the latest state of the sale in the sales state machine. +The following property would need to be added to the `SalesOrder` object: + +| Property | Description | +|----------|-----------------------------------------------------------| +| `state` | Latest state in the sales state machine that was reached. | + Tracking the latest state opens up the possibility for further optimisations, see below. @@ -291,36 +525,75 @@ Depends on: Tracking latest state machine state
Depends on: Concurrent workers To prevent unnecessary reserving, downloading, and proof generation when there -are concurrent workers, when checking to ensure there's enough collateral -available, instead of only checking the funding account's current balance, also -check collateral that will be used to fill slots in `SalesOrders` that are -`/active`. Without this check, SPs may reserve, download, and generate a proof -for a sale that would ultimately result in not having enough collateral. For -example, if funding account balance is 100, and the SP is currently downloading -two sales with 100 collateral each, then that would mean that the download that -finishes last will ultimately be wasted as the SP would not have enough -collateral to fill both slots. +are concurrent workers, collateral and storage quota checks can be optimised. +Instead of only checking the funding account's current balance for collateral, +and only checking the remaining storage quota, also check collateral and slot +size for sales that are downloading and proof generating. This can be done by +querying `/active` `SalesOrders` that are not filled on chain (in `mySlots`). +Without this check, SPs may reserve, download, and generate a proof for a sale +that would ultimately result in not having enough collateral. For example, if +funding account balance is 100, and the SP is currently downloading two sales +with 100 collateral each, then that would mean that the download that finishes +last will ultimately be wasted as the SP would not have enough collateral to +fill both slots. + +To ensure the [design rules](#objects-must-not-perform-accounting) are adhered +to, we should avoid using only `/active` `SalesOrders` to determine total +collateral and slot size, as opposed to using only those not filled on chain (in +`mySlots`). This is because there are many circumstances that may lead to +incorrectly accounted `SalesOrders` and that would affect the SPs ability to +fill slots. In the language of the design rules, `SalesOrders` state for filled +slots is not the "source of truth" and therefore should not be relied upon. + +One caveat, however, is that slot matching must wait for cleanup routines to +complete during startup. This is because on startup, there may be `/active` +`SalesOrders` not in `/mySlots` that will get deleted/archived during startup +cleanup and should not count towards total collateral or slot size. + +The following properties would need to be added to the `SalesOrder` object in +order to prevent unnecessary resource consumption: + +| Property | Description | +|--------------|-------------------------------------------------------------------------------------------| +| `slotSize` | `slotSize` from the `StorageAsk`. | +| `collateral` | Collateral consumed for the request, calculated using `collateralPerByte` and `slotSize`. | ### Renewals: prevent dataset deletion During renewals, there could potentially be a new sale for the same dataset that is already in an active sale. The `SlotId` (and `RequestId`) will differ, -however the CID will be the same. Renewals should occur well before the initial -sale finishes. However, if the new sale is close in time to the completion of -the first sale, then as the dataset for the first sale is being cleaned up, -it may delete the dataset that is needed by the new sale. The new sale may have -been in the process of being downloaded, or having proofs generated. +however the manifest CID and potentially the slot index will be the same, +resulting in the same dataset being hosted. Renewals should occur well before +the initial sale finishes. However, if the new sale is close in time to the +completion of the first sale, then as the dataset for the first sale is being +cleaned up, it may delete the dataset that is needed by the new sale. The new +sale may have been in the process of being downloaded, or having proofs +generated. -This can be prevented by having an in-memory ref count of datasets. When a +This can be prevented by having a persisted ref count of datasets. When a dataset is stored, the ref count of the dataset (`hash(treeCid, slotIndex)`) is -incremented. When the dataset is deleted, the ref count is decremented. Only -when the ref count is 0 is the dataset actually deleted in the `RepoStore`. The -ref count does not require persistence because on startup, hosted slots will not -be deleted. +incremented. TODO: `manifestCid` may be used instead depending on find +`RepoStore` design. When the dataset is deleted, the ref count is decremented. +Only when the ref count is 0 is the dataset actually deleted in the underlying +`RepoStore`. -Ref count handling can be managed in `SalesRepo` module. This module is -responsible for interacting with the underlying `RepoStore`, and managing the -internal ref count. It will expose functions for storing and deleting datasets. +On startup, state machine states are restored for active slots, effectively +skipping previous states that incremented the ref count. Therefore, the ref +count must be persisted so that the ref count reflects the full and partial +datasets on disk. To illustrate, let's use the case where the node hosted a slot +and it went down in the process of renewing the same slot but had not filled it +yet. In this case, the ref count for a dataset would be 2. Upon node restart, +two things will happen: the corrective cleanup routine will try to delete the +renewal dataset that was being processed and the filled slot would get restored +to its previous point in the state machine, where it will attempt to delete the +dataset when it's finished. If the ref count had not been persisted, it would be +0, and the corrective cleanup would delete the dataset that is currently filled, +which could cause the SP to be slashed. + +Ref count handling can be managed in `SalesRepo` module, and it can be persisted +in the `MetadataStore`. This module is responsible for interacting with the +underlying `RepoStore`, and managing the internal ref count. It will expose +functions for storing and deleting datasets. Note that any calls to ref count should be locked, as they may be read and updated concurrently. @@ -357,14 +630,82 @@ direction TB #### Alternative idea -Preventing deletion of datasets that are being downloaded or proof generating -can also be achieved by first checking if the slot id exists in `/mySlots` (at this stage -the initial sale should no longer be in `/mySlots`). If it does not, then check -if there are more than one `/active` (reached downloading) `SalesOrders` with -the same `hash(treeCid, slotIndex)` that exist. If there are not, delete the -dataset. Finally, archive the `SalesOrder`. +Preventing deletion of datasets that are downloading or generating proofs can +also be achieved by checking if there are more than one `/active` (reached +downloading) `SalesOrders` with the same `hash(treeCid, slotIndex)` that exist. +If there are not, delete the dataset. Finally, archive the `SalesOrder`. -![Cleanup handling renewals]() +```mermaid +--- +config: + theme: redux +--- +flowchart TB + CleanUp(["Clean Up"]) --"SalesOrder"--> + ExistsMultiple{"Exists more than
one /active SalesOrder
with slot id?"} -- "No" --> + Delete["Delete dataset (if
one exists)"] --> + Archive["Archive SalesOrder"] + + ExistsMultiple -- "Yes" --> + DoNotDelete["Do not delete dataset"] + + CleanUp:::start + classDef start fill:#000000, color:#FFFFFF +``` ## Purchasing +## Design rules + +Based on past implementations of the sales and purchasing modules, a couple of +rules have been created that the design in this document should not deviate +from. + +### Objects MUST NOT perform accounting + +The first, and most important, rule is that there should never be any accounting +operations where there is a "source of truth", particularly `Availabilities`. +Accounting incorporates actions done in other modules of the Codex node (eg +storage) or in the contracts (eg collateral), and then reflecting those changed +values back into the `Availability`. Accounting is not a good idea for several +reasons. + +Firstly, there are a large number of logic branches that are +created where accounting updates need to occur, creating a significant +amount of complexity in the codebase. This makes the code difficult to reason +about and therefore difficult to ensure that all possible scenarios are covered. +In other words, this creates many edge cases, associated bugs, and a larger +testing burden. This is further exacerbated with concurrent workers. + +Secondly, accounting updates are not atomic with their underlying operation. +This opens up the potential for unrecoverable exceptions or a `SIGTERM` after +the underlying operation but before the accounting update, leaving the object, +eg `Availability`, out of sync. + +Finally, values that would require accounting should instead be sourced from +their underlying modules, as the "source of truth". For example, "available +collateral" can be sourced from the balance of the funding wallet, and +"available storage" can be sourced from the remaining quota of the `SalesRepo`. + +Examples of the "no accounting" rule: + +1. No slot size accounting +2. No collateral accounting +3. No reservations accounting (reservations were removed anyway due to a design + change in the RepoStore) + +An example of how this rule does not apply is with the `SalesRepo` module. The +`SalesRepo` module stores a `refCount`, but only because that information does +not exist in the underlying `RepoStore` as the "source of truth". + +### `Availabilities` MUST NOT represent past or active sales + +`Availabilities` MUST represent future sales only. A SP's availability defines +the conditions of sales they are willing to enter into. After entering +into a sale, a SP can update its availability, and therefore change the +conditions to be met for future sales. If the `Availability` was linked to the +past or future sales, updating the availability would lose information +pertaining to those sales. + +In the design, this rule has been followed by copying information from the +matched `Availability` into a `SalesOrder`.