2023-06-06 13:28:57 +10:00
|
|
|
# Sales module
|
2022-11-22 11:58:23 +01:00
|
|
|
|
|
|
|
The sales module is responsible for selling a node's available storage in the
|
|
|
|
[marketplace](./marketplace.md). In order to do so it needs to know how much
|
|
|
|
storage is available. It also needs to be able to reserve parts of the storage,
|
2023-03-28 19:28:16 +02:00
|
|
|
to make sure that it is not used for other purposes.
|
2022-11-22 11:58:23 +01:00
|
|
|
|
|
|
|
---------------------------------------------------
|
|
|
|
| |
|
|
|
|
| Sales |
|
|
|
|
| |
|
|
|
|
| ^ | |
|
|
|
|
| | | updates ------------------ |
|
|
|
|
| | --------------> | | |
|
|
|
|
| | | Reservations | |
|
|
|
|
| ------------------- | | |
|
|
|
|
| queries ------------------ |
|
|
|
|
| ^ ^ |
|
|
|
|
----------------------------|---------|-----------
|
|
|
|
| |
|
2023-03-28 19:28:16 +02:00
|
|
|
reserved space | | state
|
2022-11-22 11:58:23 +01:00
|
|
|
v v
|
|
|
|
---------------- -----------------
|
|
|
|
| Repo | | Datastore |
|
|
|
|
---------------- -----------------
|
|
|
|
|
2023-03-28 19:28:16 +02:00
|
|
|
The reservations module keeps track of storage that is available to be sold.
|
|
|
|
Users are able to add availability to indicate how much storage they are willing
|
|
|
|
to sell and under which conditions.
|
|
|
|
|
|
|
|
Availability
|
|
|
|
amount
|
|
|
|
maximum duration
|
|
|
|
minimum price
|
|
|
|
|
|
|
|
Availabilities consist of an amount of storage, the maximum duration and minimum
|
|
|
|
price to sell it for. They represent storage that is for sale, but not yet sold.
|
|
|
|
This is information local to the node that can be altered without affecting
|
|
|
|
global state.
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Adding availability
|
2023-03-28 19:28:16 +02:00
|
|
|
|
2023-05-09 15:29:00 +02:00
|
|
|
When a user adds availability, then the reservations module will check whether
|
2023-03-28 19:28:16 +02:00
|
|
|
there is enough space available in the Repo. If there is enough space, then it
|
|
|
|
will increase the amount of reserved space in the Repo. It persists the state of
|
|
|
|
all availabilities to the Datastore, to ensure that they can be restored when a
|
|
|
|
node is restarted.
|
|
|
|
|
|
|
|
User Reservations Repo Datastore
|
|
|
|
| | | |
|
|
|
|
| add availability | | |
|
|
|
|
| ---------------->| check free space | |
|
|
|
|
| |----------------->| |
|
|
|
|
| | reserve amount | |
|
|
|
|
| |----------------->| |
|
|
|
|
| | |
|
|
|
|
| | persist availability |
|
|
|
|
| |------------------------------>|
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Selling storage
|
2022-11-22 11:58:23 +01:00
|
|
|
|
2023-03-28 19:28:16 +02:00
|
|
|
When a request for storage is submitted on chain, the sales module decides
|
|
|
|
whether or not it wants to act on it. First, it tries to find an availability
|
2023-05-09 15:29:00 +02:00
|
|
|
that matches the requested amount, duration, and price. If an availability
|
|
|
|
matches, but is larger than the requested storage, then the Sales module may
|
|
|
|
decide to split the availability into a part that we can use for the request,
|
|
|
|
and a remainder that can be sold separately. The matching availability will be
|
|
|
|
set aside so that it can't be sold twice.
|
2023-03-28 19:28:16 +02:00
|
|
|
|
|
|
|
It then selects a slot from the request to fill, and starts downloading its
|
|
|
|
content chunk by chunk. For each chunk that is successfully downloaded, a bit of
|
|
|
|
reserved space in the Repo is released. The content is stored in the Repo with a
|
|
|
|
time-to-live value that ensures that the content remains in the Repo until the
|
|
|
|
request expires.
|
|
|
|
|
|
|
|
Once the entire content is downloaded, the sales module will calculate a storage
|
|
|
|
proof, and submit the proof on chain. If these steps are all successful, then
|
|
|
|
this node has filled the slot. Once the other slots are filled by other nodes
|
|
|
|
the request will start. The time-to-live value of the content should then be
|
|
|
|
updated to match the duration of the storage request.
|
|
|
|
|
|
|
|
Marketplace Sales Reservations Repo
|
|
|
|
| | | |
|
|
|
|
| incoming request | | |
|
|
|
|
|------------------->| find reservation | |
|
|
|
|
| |-------------------->| |
|
|
|
|
| | remove reservation | |
|
|
|
|
| |-------------------->| |
|
|
|
|
| | | |
|
|
|
|
| | store content |
|
|
|
|
| |----------------------------------->|
|
|
|
|
| | set time-to-live |
|
|
|
|
| |----------------------------------->|
|
|
|
|
| | release reserved space |
|
|
|
|
| |----------------------------------->|
|
|
|
|
| submit proof | |
|
|
|
|
|<-------------------| |
|
|
|
|
| | |
|
|
|
|
. . .
|
|
|
|
. . .
|
|
|
|
| request started | |
|
|
|
|
|------------------->| update time-to-live |
|
|
|
|
| |----------------------------------->|
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Ending a request
|
2023-03-28 19:28:16 +02:00
|
|
|
|
|
|
|
When a storage request comes to an end, then the content can be removed from the
|
|
|
|
repo and the storage space can be made available for sale again. The same should
|
|
|
|
happen when something went wrong in the process of selling storage.
|
|
|
|
|
|
|
|
The time-to-live value should be removed from the content in the Repo, reserved
|
|
|
|
space in the Repo should be increased again, and the availability that was used
|
|
|
|
for the request can be re-added to the reservations module.
|
|
|
|
|
|
|
|
Sales Reservations Repo
|
|
|
|
| | |
|
|
|
|
| | |
|
|
|
|
| |
|
|
|
|
| remove time to live |
|
|
|
|
|----------------------------------->|
|
|
|
|
| increase reserved space |
|
|
|
|
|----------------------------------->|
|
|
|
|
| |
|
|
|
|
| re-add availability | |
|
|
|
|
|-------------------->| |
|
|
|
|
| | |
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Persisting state
|
2023-03-28 19:28:16 +02:00
|
|
|
|
|
|
|
The sales module keeps state in a number of places. Most state is kept on chain,
|
|
|
|
this includes the slots that a host is filling and the state of each slot. This
|
|
|
|
ensures that a node's local view of slot states does not deviate from the
|
|
|
|
network view, even when the network changes while the node is down. The rest of
|
|
|
|
the state is kept on local disk by the Repo and the Datastore. How much space is
|
|
|
|
reserved to be sold is persisted on disk by the Repo. The availabilities are
|
|
|
|
persisted on disk by the Datastore.
|
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
## Slot queue
|
2023-06-06 13:28:57 +10:00
|
|
|
|
2023-06-05 17:20:27 +10:00
|
|
|
Once a new request for storage is created on chain, all hosts will receive a
|
|
|
|
contract event announcing the storage request and decide if they want to act on
|
|
|
|
the request by matching their availabilities with the incoming request. Because
|
|
|
|
there will be many requests being announced over time, each host will create a
|
2023-06-28 10:43:22 +10:00
|
|
|
queue of matching request slots, adding each new storage slot to the queue.
|
2023-06-06 13:28:57 +10:00
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
### Adding slots to the queue
|
2023-06-06 13:28:57 +10:00
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
Slots will be added to the queue when request for storage events are received
|
2023-06-06 13:28:57 +10:00
|
|
|
from the contracts. Additionally, when slots are freed, a contract event will
|
2023-06-28 10:43:22 +10:00
|
|
|
also be received, and the slot will be added to the queue. Duplicates are
|
|
|
|
ignored.
|
2023-06-06 13:28:57 +10:00
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
When all slots of a request are added to the queue, the order should be randomly
|
|
|
|
shuffled, as there will be many hosts in the network that could potentially pick
|
|
|
|
up the request and will process the first slot in the queue at the same time.
|
|
|
|
This should avoid some clashes in slot indices chosen by competing hosts.
|
|
|
|
|
2023-07-25 10:46:04 +10:00
|
|
|
Before slots can be added to the queue, availabilities must be checked to ensure
|
|
|
|
a matching availability exists. This filtering prevents all slots in the network
|
|
|
|
from entering the queue.
|
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
### Removing slots from the queue
|
2023-06-06 13:28:57 +10:00
|
|
|
|
2023-10-24 10:26:29 +08:00
|
|
|
Hosts will also receive contract events for when any contract is started,
|
2023-06-28 10:43:22 +10:00
|
|
|
failed, or cancelled. In all of these cases, slots in the queue pertaining to
|
|
|
|
these requests should be removed as they are no longer fillable by the host.
|
|
|
|
Note: expired request slots will be checked when a request is processed and its
|
|
|
|
state is validated.
|
2023-06-06 13:28:57 +10:00
|
|
|
|
|
|
|
### Sort order
|
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
Slots in the queue should be sorted in the following order:
|
|
|
|
1. Profit (descending)<sup>1</sup>
|
2023-06-05 17:20:27 +10:00
|
|
|
2. Collateral required (ascending)
|
|
|
|
3. Time before expiry (descending)
|
2023-06-06 13:28:57 +10:00
|
|
|
4. Dataset size (ascending)
|
|
|
|
|
2023-06-28 10:43:22 +10:00
|
|
|
<sup>1</sup> While profit cannot yet be calculated correctly as this calculation will
|
|
|
|
involve bandwidth incentives, profit can be estimated as `duration * reward`
|
|
|
|
for now.
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
Note: datset size may eventually be included in the profit algorithm and may not
|
2023-06-28 10:43:22 +10:00
|
|
|
need to be included on its own in the future. Additionally, data dispersal may
|
|
|
|
also impact the datset size to be downloaded by the host, and consequently the
|
|
|
|
profitability of servicing a storage request, which will need to be considered
|
|
|
|
in the future once profitability can be calculated.
|
2023-06-05 17:20:27 +10:00
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
### Queue processing
|
|
|
|
|
|
|
|
Queue processing will be started only once, when the sales module starts and
|
2023-06-28 10:43:22 +10:00
|
|
|
will process slots continuously, in order, until the queue is empty. If the
|
2023-06-05 17:20:27 +10:00
|
|
|
queue is empty, processing of the queue will resume once items have been added
|
2023-07-25 10:46:04 +10:00
|
|
|
to the queue. If the queue is not empty, but there are no availabilities, queue
|
|
|
|
processing will resume once availabilites have been added.
|
2023-06-28 10:43:22 +10:00
|
|
|
|
|
|
|
As soon as items are available in the queue, and there are workers available for
|
2023-07-25 10:46:04 +10:00
|
|
|
processing, an item is popped from the queue and processed.
|
2023-06-28 10:43:22 +10:00
|
|
|
|
|
|
|
When a slot is processed, it is first checked to ensure there is a matching
|
2023-10-24 10:26:29 +08:00
|
|
|
availability, as these availabilities will have changed over time. Then, the
|
2023-06-28 10:43:22 +10:00
|
|
|
sales process will begin. The start of the sales process should ensure that the
|
|
|
|
slot being processed is indeed available (slot state is "free") before
|
|
|
|
continuing. If it is not available, the sales process will exit and the host
|
|
|
|
will continue to process the top slot in the queue. The start of the sales
|
|
|
|
process should also check to ensure the host is allowed to fill the slot, due to
|
|
|
|
the [sliding window
|
2023-06-07 17:32:41 +10:00
|
|
|
mechanism](https://github.com/codex-storage/codex-research/blob/master/design/marketplace.md#dispersal).
|
|
|
|
If the host is not allowed to fill the slot, the sales process will exit and the
|
2023-06-28 10:43:22 +10:00
|
|
|
host will process the top slot in the queue.
|
|
|
|
|
|
|
|
#### Queue workers
|
|
|
|
Each time an item in the queue is processed, it is assigned to a workers. The
|
|
|
|
number of allowed workers can be specified during queue creation. Specifying a
|
|
|
|
limited number of workers allows the number of concurrent items being processed
|
|
|
|
to be capped to prevent too many slots from being processed at once.
|
|
|
|
|
|
|
|
During queue processing, only when there is a free worker will an item be popped
|
|
|
|
from the queue and processed. Each time an item is popped and processed, a
|
|
|
|
worker is removed from the available workers. If there are no available workers,
|
|
|
|
queue processing will resume once there are workers available.
|
2023-06-05 17:20:27 +10:00
|
|
|
|
2023-07-25 10:46:04 +10:00
|
|
|
#### Adding availabilities
|
|
|
|
When a host adds an availability, a signal is triggered in the slot queue with
|
|
|
|
information about the availability. This triggers a lookup of past request for
|
|
|
|
storage events, capped at a certain number of past events or blocks. The slots
|
|
|
|
of the requests in each of these events are added to the queue, where slots
|
|
|
|
without matching availabilities are filtered out (see [Adding slots
|
|
|
|
to the queue](#adding-slots-to-the-queue) above). Additionally, when slots of
|
|
|
|
these requests are processed in the queue, they will be checked to ensure that
|
|
|
|
the slots are not filled (see [Queue processing](#queue-processing) above).
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
### Implementation tips
|
2023-06-05 17:20:27 +10:00
|
|
|
|
|
|
|
Request queue implementations should keep in mind that requests will likely need
|
|
|
|
to be accessed randomly (by key, eg request id) and by index (for sorting), so
|
2023-06-06 13:28:57 +10:00
|
|
|
implemented structures should handle these types of operations in as little time
|
2023-06-05 17:20:27 +10:00
|
|
|
as possible.
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Repo
|
2023-03-28 19:28:16 +02:00
|
|
|
|
|
|
|
The Repo exposes the following functions that allow the reservations module to
|
|
|
|
query the amount of available storage, to update the amount of reserved
|
|
|
|
space, and to store data for a guaranteed amount of time.
|
2022-11-22 11:58:23 +01:00
|
|
|
|
|
|
|
Repository API:
|
|
|
|
function available(): amount
|
|
|
|
function reserve(amount)
|
|
|
|
function release(amount)
|
2023-03-28 19:28:16 +02:00
|
|
|
function setTtl(cid, ttl)
|
|
|
|
|
2023-06-06 13:28:57 +10:00
|
|
|
## Datastore
|
2022-11-22 11:58:23 +01:00
|
|
|
|
|
|
|
The Datastore is a generic key-value store that is used to persist the state of
|
|
|
|
the Reservations module, so that it survives node restarts.
|
|
|
|
|
|
|
|
Datastore API:
|
|
|
|
function put(key, value)
|
|
|
|
function get(key): value
|