diff --git a/docs/Marketplace.md b/docs/Marketplace.md deleted file mode 100644 index 4a2f82f0..00000000 --- a/docs/Marketplace.md +++ /dev/null @@ -1,666 +0,0 @@ -# Running a Local Codex Network with Marketplace Support - -This tutorial will teach you how to run a small Codex network with the -_storage marketplace_ enabled; i.e., the functionality in Codex which -allows participants to offer and buy storage in a market, ensuring that -storage providers honor their part of the deal by means of cryptographic proofs. - -In this tutorial, you will: - -1. [Set Up a Geth PoA network](#1-set-up-a-geth-poa-network); -2. [Set up The Marketplace](#2-set-up-the-marketplace); -3. [Run Codex](#3-run-codex); -4. [Buy and Sell Storage in the Marketplace](#4-buy-and-sell-storage-on-the-marketplace). - -## Prerequisites - -To complete this tutorial, you will need: - -* the [geth](https://github.com/ethereum/go-ethereum) Ethereum client; - You need version `1.13.x` of geth as newer versions no longer support - Proof of Authority (PoA). This tutorial was tested using geth version `1.13.15`. -* a Codex binary, which [you can compile from source](https://github.com/codex-storage/nim-codex?tab=readme-ov-file#build-and-run). - -We will also be using [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) -syntax throughout. If you use a different shell, you may need to adapt -things to your platform. - -To get started, create a new folder where we will keep the tutorial-related -files so that we can keep them separate from the codex repository. -We assume the name of the folder to be `marketplace-tutorial`. - -## 1. Set Up a Geth PoA Network - -For this tutorial, we will use a simple -[Proof-of-Authority](https://github.com/ethereum/EIPs/issues/225) network -with geth. The first step is creating a _signer account_: an account which -will be used by geth to sign the blocks in the network. -Any block signed by a signer is accepted as valid. - -### 1.1. Create a Signer Account - -To create a signer account, from the `marketplace-tutorial` directory run: - -```bash -geth account new --datadir geth-data -``` - -The account generator will ask you to input a password, which you can -leave blank. It will then print some information, -including the account's public address: - -```bash -INFO [09-29|16:49:24.244] Maximum peer count ETH=50 total=50 -Your new account is locked with a password. Please give a password. Do not forget this password. -Password: -Repeat password: - -Your new key was generated - -Public address of the key: 0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB -Path of the secret key file: geth-data/keystore/UTC--2024-09-29T14-49-31.655272000Z--33a904ad57d0e2cb8ffe347d3c0e83c2e875e7db - -- You can share your public address with anyone. Others need it to interact with you. -- You must NEVER share the secret key with anyone! The key controls access to your funds! -- You must BACKUP your key file! Without the key, it's impossible to access account funds! -- You must REMEMBER your password! Without the password, it's impossible to decrypt the key! -``` - -In this example, the public address of the signer account is -`0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB`. -Yours will print a different address. Save it for later usage. - -Next set an environment variable for later usage: - -```bash -export GETH_SIGNER_ADDR="0x0000000000000000000000000000000000000000" -echo ${GETH_SIGNER_ADDR} > geth_signer_address.txt -``` - -> Here make sure you replace `0x0000000000000000000000000000000000000000` -> with your public address of the signer account -> (`0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB` in our example). - -### 1.2. Configure The Network and Create the Genesis Block - -The next step is telling geth what kind of network you want to run. -We will be running a [pre-merge](https://ethereum.org/en/roadmap/merge/) -network with Proof-of-Authority consensus. -To get that working, create a `network.json` file. - -If you set the `GETH_SIGNER_ADDR` variable above you can run the following -command to create the `network.json` file: - -```bash -echo "{\"config\": { \"chainId\": 12345, \"homesteadBlock\": 0, \"eip150Block\": 0, \"eip155Block\": 0, \"eip158Block\": 0, \"byzantiumBlock\": 0, \"constantinopleBlock\": 0, \"petersburgBlock\": 0, \"istanbulBlock\": 0, \"berlinBlock\": 0, \"londonBlock\": 0, \"arrowGlacierBlock\": 0, \"grayGlacierBlock\": 0, \"clique\": { \"period\": 1, \"epoch\": 30000 } }, \"difficulty\": \"1\", \"gasLimit\": \"8000000\", \"extradata\": \"0x0000000000000000000000000000000000000000000000000000000000000000${GETH_SIGNER_ADDR:2}0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\", \"alloc\": { \"${GETH_SIGNER_ADDR}\": { \"balance\": \"10000000000000000000000\"}}}" > network.json -``` - -You can also manually create the file remembering update it with your -signer public address: - -```json -{ - "config": { - "chainId": 12345, - "homesteadBlock": 0, - "eip150Block": 0, - "eip155Block": 0, - "eip158Block": 0, - "byzantiumBlock": 0, - "constantinopleBlock": 0, - "petersburgBlock": 0, - "istanbulBlock": 0, - "berlinBlock": 0, - "londonBlock": 0, - "arrowGlacierBlock": 0, - "grayGlacierBlock": 0, - "clique": { - "period": 1, - "epoch": 30000 - } - }, - "difficulty": "1", - "gasLimit": "8000000", - "extradata": "0x000000000000000000000000000000000000000000000000000000000000000033A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", - "alloc": { - "0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB": { - "balance": "10000000000000000000000" - } - } -} -``` - -Note that the signer account address is embedded in two different places: -* inside of the `"extradata"` string, surrounded by zeroes and stripped of - its `0x` prefix; -* as an entry key in the `alloc` session. - Make sure to replace that ID with the account ID that you wrote down in - [Step 1.1](#11-create-a-signer-account). - -Once `network.json` is created, you can initialize the network with: - -```bash -geth init --datadir geth-data network.json -``` - -The output of the above command you may include some warnings, like: - -```bash -WARN [08-21|14:48:12.305] Unknown config environment variable envvar=GETH_SIGNER_ADDR -``` - -or even errors when running the command for the first time: - -```bash -ERROR[08-21|14:48:12.399] Head block is not reachable -``` - -The important part is that at the end you should see something similar to: - -```bash -INFO [08-21|14:48:12.639] Successfully wrote genesis state database=lightchaindata hash=768bf1..42d06a -``` - -### 1.3. Start your PoA Node - -We are now ready to start our $1$-node, private blockchain. -To launch the signer node, open a separate terminal in the same working -directory and make sure you have the `GETH_SIGNER_ADDR` set. -For convenience use the `geth_signer_address.txt`: - -```bash -export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt) -``` - -Having the `GETH_SIGNER_ADDR` variable set, run: - -```bash -geth\ - --datadir geth-data\ - --networkid 12345\ - --unlock ${GETH_SIGNER_ADDR}\ - --nat extip:127.0.0.1\ - --netrestrict 127.0.0.0/24\ - --mine\ - --miner.etherbase ${GETH_SIGNER_ADDR}\ - --http\ - --allow-insecure-unlock -``` - -Note that, once again, the signer account created in -[Step 1.1](#11-create-a-signer-account) appears both in -`--unlock` and `--allow-insecure-unlock`. - -Geth will prompt you to insert the account's password as it starts up. -Once you do that, it should be able to start up and begin "mining" blocks. - -Also here, you may encounter errors like: - -```bash -ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict id=c845e51a5e470e44 ip=18.138.108.67 -ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict id=f23ac6da7c02f84a ip=3.209.45.79 -ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict id=ef2d7ab886910dc8 ip=65.108.70.101 -ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict id=6b36f791352f15eb ip=157.90.35.166 -``` - -You can safely ignore them. - -If the command above fails with: - -```bash -Fatal: Failed to register the Ethereum service: only PoS networks are supported, please transition old ones with Geth v1.13.x -``` - -make sure, you are running the correct Geth version -(see Section [Prerequisites](#prerequisites)) - -## 2. Set Up The Marketplace - -You will need to open new terminal for this section and geth needs to be -running already. Setting up the Codex marketplace entails: - -1. Deploying the Codex Marketplace contracts to our private blockchain -2. Setup Ethereum accounts we will use to buy and sell storage in - the Codex marketplace -3. Provisioning those accounts with the required token balances - -### 2.1. Deploy the Codex Marketplace Contracts - -Make sure you leave the `marketplace-tutorial` directory, and clone -the `codex-storage/nim-codex.git`: - -```bash -git clone https://github.com/codex-storage/nim-codex.git -``` - -> If you just want to clone the repo to run the tutorial, you can -> skip the history and just download the head of the master branch by using -> `--depth 1` option: `git clone --depth 1 https://github.com/codex-storage/nim-codex.git` - -Thus, our directory structure for the purpose of this tutorial looks like this: - -```bash -| -|-- nim-codex -└-- marketplace-tutorial -``` - -> You could clone the `codex-storage/nim-codex.git` to some other location. -> Just to keeps things nicely separated it is best to make sure that -> `nim-codex` is not under `marketplace-tutorial` directory. - -Now, from the `nim-codex` folder run: - -```bash -make update && make -``` - -> This may take a moment as it will also build the `nim` compiler. Be patient. - -Now, in order to start a local Ethereum network run: - -```bash -cd vendor/codex-contracts-eth -npm install -``` - -> While writing the document we used `node` version `v20.17.0` and -> `npm` version `10.8.2`. - -Before continuing you now must **wait until $256$ blocks are mined** -**in your PoAnetwork**, or deploy will fail. This should take about -$4$ minutes and $30$ seconds. You can check which block height you are -currently at by running the following command -**from the `marketplace-tutorial` folder**: - -```bash -geth attach --exec web3.eth.blockNumber ./geth-data/geth.ipc -``` - -once that gets past $256$, you are ready to go. - -To deploy contracts, from the `codex-contracts-eth` directory run: - -```bash -export DISTTEST_NETWORK_URL=http://localhost:8545 -npx hardhat --network codexdisttestnetwork deploy -``` - -If the command completes successfully, you will see the output similar -to this one: - -```bash -Deployed Marketplace with Groth16 Verifier at: -0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd -``` -> of course your address will be different. - -You are now ready to prepare the accounts. - -### 2.2. Generate the Required Accounts - -We will run $2$ Codex nodes: a **storage provider**, which will sell storage -on the network, and a **client**, which will buy and use such storage; -we therefore need two valid Ethereum accounts. We could create random -accounts by using one of the many tools available to that end but, since -this is a tutorial running on a local private network, we will simply -provide you with two pre-made accounts along with their private keys, -which you can copy and paste instead: - -First make sure you're back in the `marketplace-tutorial` folder and -not the `codex-contracts-eth` subfolder. Then set these variables: - -**Storage:** -```bash -export ETH_STORAGE_ADDR=0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37 -export ETH_STORAGE_PK=0x06c7ac11d4ee1d0ccb53811b71802fa92d40a5a174afad9f2cb44f93498322c3 -echo $ETH_STORAGE_PK > storage.pkey && chmod 0600 storage.pkey -``` - -**Client:** -```bash -export ETH_CLIENT_ADDR=0x9F0C62Fe60b22301751d6cDe1175526b9280b965 -export ETH_CLIENT_PK=0x5538ec03c956cb9d0bee02a25b600b0225f1347da4071d0fd70c521fdc63c2fc -echo $ETH_CLIENT_PK > client.pkey && chmod 0600 client.pkey -``` - -### 2.3. Provision Accounts with Tokens - -We now need to transfer some ETH to each of the accounts, as well as provide -them with some Codex tokens for the storage node to use as collateral and -for the client node to buy actual storage. - -Although the process is not particularly complicated, I suggest you use -[the script we prepared](https://github.com/gmega/local-codex-bare/blob/main/scripts/mint-tokens.js) -for that. This script, essentially: - -1. reads the Marketplace contract address and its ABI from the deployment data; -2. transfers $1$ ETH from the signer account to a target account if the target - account has no ETH balance; -3. mints $n$ Codex tokens and adds it into the target account's balance. - -To use the script, just download it into a local file named `mint-tokens.js`, -for instance using `curl` (make sure you are in -the `marketplace-tutorial` directory): - -```bash -# download script -curl https://raw.githubusercontent.com/gmega/codex-local-bare/main/scripts/mint-tokens.js -o mint-tokens.js -``` - -Then run: - -```bash -# set the contract file location (we assume you are in the marketplace-tutorial directory) -export CONTRACT_DEPLOY_FULL="../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork" -export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt) -# Installs Web3-js -npm install web3 -# Provides tokens to the storage account. -node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37 10000000000 -# Provides tokens to the client account. -node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x9F0C62Fe60b22301751d6cDe1175526b9280b965 10000000000 -``` - -If you get a message like - -```bash -Usage: mint-tokens.js -``` - -then you need to ensure you provided all the required arguments. -In particular you need to ensure that the `GETH_SIGNER_ADDR` env variable -holds the signer address (we used -`export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)` above to -make sure it is set). - -## 3. Run Codex - -With accounts and geth in place, we can now start the Codex nodes. - -### 3.1. Storage Node - -The storage node will be the one storing data and submitting the proofs of -storage to the chain. To do that, it needs access to: - -1. the address of the Marketplace contract that has been deployed to - the local geth node in [Step 2.1](#21-deploy-the-codex-marketplace-contracts); -2. the sample ceremony files which are shipped in the Codex contracts repo - (`nim-codex/vendor/codex-contracts-eth`). - -**Address of the Marketplace Contract.** The contract address can be found -inside of the file `nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork/Marketplace.json`. -We captured that location above in `CONTRACT_DEPLOY_FULL` variable, thus, from -the `marketplace-tutorial` folder just run: - -```bash -grep '"address":' ${CONTRACT_DEPLOY_FULL}/Marketplace.json -``` - -which should print something like: -```bash -"address": "0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd", -``` - -> This address should match the address we got earlier when deploying -> the Marketplace contract above. - -Then run the following with the correct market place address: -```bash -export MARKETPLACE_ADDRESS="0x0000000000000000000000000000000000000000" -echo ${MARKETPLACE_ADDRESS} > marketplace_address.txt -``` - -where you replace `0x0000000000000000000000000000000000000000` with -the Marketplace contract above in -[Step 2.1](#21-deploy-the-codex-marketplace-contracts). - -**Prover ceremony files.** The ceremony files are under the -`nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork` -subdirectory. There are three of them: `proof_main.r1cs`, `proof_main.zkey`, -and `prooof_main.wasm`. We will need all of them to start the Codex storage node. - -**Starting the storage node.** Let: - -* `PROVER_ASSETS` contain the directory where the prover ceremony files are - located. **This must be an absolute path**; -* `CODEX_BINARY` contain the location of your Codex binary; -* `MARKETPLACE_ADDRESS` contain the address of the Marketplace contract - (we have already set it above). - -Set these paths into environment variables (make sure you are in -the `marketplace-tutorial` directory): - -```bash -export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork") -export PROVER_ASSETS=$(realpath "../nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork/") -export CODEX_BINARY=$(realpath "../nim-codex/build/codex") -export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt) -``` -> you may notice, that we have already set the `CONTRACT_DEPLOY_FULL` variable -> above. Here, we make sure it is an absolute path. - -To launch the storage node, run: - -```bash -${CODEX_BINARY}\ - --data-dir=./codex-storage\ - --listen-addrs=/ip4/0.0.0.0/tcp/8080\ - --api-port=8000\ - --disc-port=8090\ - persistence\ - --eth-provider=http://localhost:8545\ - --eth-private-key=./storage.pkey\ - --marketplace-address=${MARKETPLACE_ADDRESS}\ - --validator\ - --validator-max-slots=1000\ - prover\ - --circom-r1cs=${PROVER_ASSETS}/proof_main.r1cs\ - --circom-wasm=${PROVER_ASSETS}/proof_main.wasm\ - --circom-zkey=${PROVER_ASSETS}/proof_main.zkey -``` - -**Starting the client node.** - -The client node is started similarly except that: - -* we need to pass the SPR of the storage node so it can form a network with it; -* since it does not run any proofs, it does not require any ceremony files. - -We get the Signed Peer Record (SPR) of the storage node so we can bootstrap -the client node with it. To get the SPR, issue the following call: - -```bash -curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr' --write-out '\n' -``` - -You should get the SPR back starting with `spr:`. - -Before you proceed, open new terminal, and enter `marketplace-tutorial` directory. - -Next set these paths into environment variables: - -```bash -# set the SPR for the storage node -export STORAGE_NODE_SPR=$(curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr') -# basic vars -export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork") -export CODEX_BINARY=$(realpath "../nim-codex/build/codex") -export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt) -``` -and then run: - -```bash -${CODEX_BINARY}\ - --data-dir=./codex-client\ - --listen-addrs=/ip4/0.0.0.0/tcp/8081\ - --api-port=8001\ - --disc-port=8091\ - --bootstrap-node=${STORAGE_NODE_SPR}\ - persistence\ - --eth-provider=http://localhost:8545\ - --eth-private-key=./client.pkey\ - --marketplace-address=${MARKETPLACE_ADDRESS} -``` - -## 4. Buy and Sell Storage on the Marketplace - -Any storage negotiation has two sides: a buyer and a seller. -Therefore, before we can actually request storage, we must first offer -some of it for sale. - -### 4.1 Sell Storage - -The following request will cause the storage node to put out $50\text{MB}$ -of storage for sale for $1$ hour, at a price of $1$ Codex token -per slot per second, while expressing that it's willing to take at most -a $1000$ Codex token penalty for not fulfilling its part of the contract.[^1] - -```bash -curl 'http://localhost:8000/api/codex/v1/sales/availability' \ - --header 'Content-Type: application/json' \ - --data '{ - "totalSize": "50000000", - "duration": "3600", - "minPrice": "1", - "maxCollateral": "1000" -}' -``` - -This should return a JSON response containing an `id` (e.g. `"id": "0xb55b3bc7aac2563d5bf08ce8a177a38b5a40254bfa7ee8f9c52debbb176d44b0"`) -which identifies this storage offer. - -> To make JSON responses more readable, you can try -> [jq](https://jqlang.github.io/jq/) JSON formatting utility -> by just adding `| jq` after the command. -> On macOS you can install with `brew install jq`. - -To check the current storage offers for this node, you can issue: - -```bash -curl 'http://localhost:8000/api/codex/v1/sales/availability' -``` - -or with `jq`: - -```bash -curl 'http://localhost:8000/api/codex/v1/sales/availability' | jq -``` - -This should print a list of offers, with the one you just created figuring -among them (for our tutorial, there will be only one offer returned -at this time). - -## 4.2. Buy Storage - -Before we can buy storage, we must have some actual data to request -storage for. Start by uploading a small file to your client node. -On Linux (or macOS) you could, for instance, use `dd` to generate a $1M$ file: - -```bash -dd if=/dev/urandom of=./data.bin bs=1M count=1 -``` - -Assuming your file is named `data.bin`, you can upload it with: - -```bash -curl --request POST http://localhost:8001/api/codex/v1/data --header 'Content-Type: application/octet-stream' --write-out '\n' -T ./data.bin -``` - -Once the upload completes, you should see a _Content Identifier_, -or _CID_ (e.g. `zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj`) -for the uploaded file printed to the terminal. -Use that CID in the purchase request: - -```bash -# make sure to replace the CID before with the CID you got in the previous step -export CID=zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj -``` - -```bash -curl "http://localhost:8001/api/codex/v1/storage/request/${CID}" \ - --header 'Content-Type: application/octet-stream' \ - --data "{ - \"duration\": \"600\", - \"reward\": \"1\", - \"proofProbability\": \"3\", - \"expiry\": \"500\", - \"nodes\": 3, - \"tolerance\": 1, - \"collateral\": \"1000\" - }" \ - --write-out '\n' -``` - -The parameters under `--data` say that: - -1. we want to purchase storage for our file for $5$ minutes (`"duration": "600"`); -2. we are willing to pay up to $1$ token per slot per second (`"reward": "1"`) -3. our file will be split into three pieces (`"nodes": 3`). - Because we set `"tolerance": 1` we only need two (`nodes - tolerance`) - pieces to rebuild the file; i.e., we can tolerate that at most one node - stops storing our data; either due to failure or other reasons; -4. we demand `1000` tokens in collateral from storage providers for each piece. - Since there are $3$ such pieces, there will be `3000` in total collateral - committed by the storage provider(s) once our request is started. -5. finally, the `expiry` puts a time limit for filling all the slots by - the storage provider(s). If slot are not filled by the `expire` interval, - the request will timeout and fail. - -## 4.3. Track your Storage Requests - -POSTing a storage request will make it available in the storage market, -and a storage node will eventually pick it up. - -You can poll the status of your request by means of: -```bash -export STORAGE_PURCHASE_ID="1d0ec5261e3364f8b9d1cf70324d70af21a9b5dccba380b24eb68b4762249185" -curl "http://localhost:8001/api/codex/v1/storage/purchases/${STORAGE_PURCHASE_ID}" -``` - -For instance: - -```bash -> curl 'http://localhost:8001/api/codex/v1/storage/purchases/6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d' -``` - -This returns a result like: - -```json -{ - "requestId": "0x86501e4677a728c6a8031971d09b921c3baa268af06b9f17f1b745e7dba5d330", - "request": { - "client": "0x9f0c62fe60b22301751d6cde1175526b9280b965", - "ask": { - "slots": 3, - "slotSize": "262144", - "duration": "1000", - "proofProbability": "3", - "reward": "1", - "collateral": "1", - "maxSlotLoss": 1 - }, - "content": { - "cid": "zDvZRwzkyw1E7ABaUSmgtNEDjC7opzhUoHo99Vpvc98cDWeCs47u" - }, - "expiry": "1711992852", - "nonce": "0x9f5e651ecd3bf73c914f8ed0b1088869c64095c0d7bd50a38fc92ebf66ff5915", - "id": "0x6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d" - }, - "state": "submitted", - "error": null -} -``` - -Shows that a request has been submitted but has not yet been filled. -Your request will be successful once `"state"` shows `"started"`. -Anything other than that means the request has not been completely -processed yet, and an `"error"` state other than `null` means it failed. - -Well, it was quite a journey, wasn't it? You can congratulate yourself for -successfully finishing the codex marketplace tutorial! - -[^1]: Codex files get partitioned into pieces called "slots" and distributed -to various storage providers. The collateral refers to one such slot, -and will be slowly eaten away as the storage provider fails to deliver -timely proofs, but the actual logic is [more involved than that](https://github.com/codex-storage/codex-contracts-eth/blob/6c9f797f408608958714024b9055fcc330e3842f/contracts/Marketplace.sol#L209).