/ learn / local-marketplace.md
local-marketplace.md
  1  ---
  2  outline: [2, 3]
  3  ---
  4  # Running a Local Codex Network with Marketplace Support
  5  
  6  This tutorial will teach you how to run a small Codex network with the
  7  _storage marketplace_ enabled; i.e., the functionality in Codex which
  8  allows participants to offer and buy storage in a market, ensuring that
  9  storage providers honor their part of the deal by means of cryptographic proofs.
 10  
 11  In this tutorial, you will:
 12  
 13  1. [Set Up a Geth PoA network](#_1-set-up-a-geth-poa-network);
 14  2. [Set up The Marketplace](#_2-set-up-the-marketplace);
 15  3. [Run Codex](#_3-run-codex);
 16  4. [Buy and Sell Storage in the Marketplace](#_4-buy-and-sell-storage-on-the-marketplace).
 17  
 18  ## Prerequisites
 19  
 20  To complete this tutorial, you will need:
 21  
 22  * the [geth](https://github.com/ethereum/go-ethereum) Ethereum client;
 23    You need version `1.13.x` of geth as newer versions no longer support
 24    Proof of Authority (PoA). This tutorial was tested using geth version `1.13.15`.
 25  * a Codex binary, which [you can compile from source](https://github.com/codex-storage/nim-codex?tab=readme-ov-file#build-and-run).
 26  
 27  We will also be using [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell))
 28  syntax throughout. If you use a different shell, you may need to adapt
 29  things to your platform.
 30  
 31  To get started, create a new folder where we will keep the tutorial-related
 32  files so that we can keep them separate from the codex repository.
 33  We assume the name of the folder to be `marketplace-tutorial`.
 34  
 35  ## 1. Set Up a Geth PoA Network
 36  
 37  For this tutorial, we will use a simple
 38  [Proof-of-Authority](https://github.com/ethereum/EIPs/issues/225) network
 39  with geth. The first step is creating a _signer account_: an account which
 40  will be used by geth to sign the blocks in the network.
 41  Any block signed by a signer is accepted as valid.
 42  
 43  ### 1.1. Create a Signer Account
 44  
 45  To create a signer account, from the `marketplace-tutorial` directory run:
 46  
 47  ```bash
 48  geth account new --datadir geth-data
 49  ```
 50  
 51  The account generator will ask you to input a password, which you can
 52  leave blank. It will then print some information,
 53  including the account's public address:
 54  
 55  ```bash
 56  INFO [09-29|16:49:24.244] Maximum peer count                       ETH=50 total=50
 57  Your new account is locked with a password. Please give a password. Do not forget this password.
 58  Password:
 59  Repeat password:
 60  
 61  Your new key was generated
 62  
 63  Public address of the key:   0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB
 64  Path of the secret key file: geth-data/keystore/UTC--2024-09-29T14-49-31.655272000Z--33a904ad57d0e2cb8ffe347d3c0e83c2e875e7db
 65  
 66  - You can share your public address with anyone. Others need it to interact with you.
 67  - You must NEVER share the secret key with anyone! The key controls access to your funds!
 68  - You must BACKUP your key file! Without the key, it's impossible to access account funds!
 69  - You must REMEMBER your password! Without the password, it's impossible to decrypt the key!
 70  ```
 71  
 72  In this example, the public address of the signer account is
 73  `0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB`.
 74  Yours will print a different address. Save it for later usage.
 75  
 76  Next set an environment variable for later usage:
 77  
 78  ```bash
 79  export GETH_SIGNER_ADDR="0x0000000000000000000000000000000000000000"
 80  echo ${GETH_SIGNER_ADDR} > geth_signer_address.txt
 81  ```
 82  
 83  > Here make sure you replace `0x0000000000000000000000000000000000000000`
 84  > with your public address of the signer account
 85  > (`0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB` in our example).
 86  
 87  ### 1.2. Configure The Network and Create the Genesis Block
 88  
 89  The next step is telling geth what kind of network you want to run.
 90  We will be running a [pre-merge](https://ethereum.org/en/roadmap/merge/)
 91  network with Proof-of-Authority consensus.
 92  To get that working, create a `network.json` file.
 93  
 94  If you set the `GETH_SIGNER_ADDR` variable above you can run the following
 95  command to create the `network.json` file:
 96  
 97  ```bash
 98  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
 99  ```
100  
101  You can also manually create the file remembering update it with your
102  signer public address:
103  
104  ```json
105  {
106    "config": {
107      "chainId": 12345,
108      "homesteadBlock": 0,
109      "eip150Block": 0,
110      "eip155Block": 0,
111      "eip158Block": 0,
112      "byzantiumBlock": 0,
113      "constantinopleBlock": 0,
114      "petersburgBlock": 0,
115      "istanbulBlock": 0,
116      "berlinBlock": 0,
117      "londonBlock": 0,
118      "arrowGlacierBlock": 0,
119      "grayGlacierBlock": 0,
120      "clique": {
121        "period": 1,
122        "epoch": 30000
123      }
124    },
125    "difficulty": "1",
126    "gasLimit": "8000000",
127    "extradata": "0x000000000000000000000000000000000000000000000000000000000000000033A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
128    "alloc": {
129      "0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB": {
130        "balance": "10000000000000000000000"
131      }
132    }
133  }
134  ```
135  
136  Note that the signer account address is embedded in two different places:
137  * inside of the `"extradata"` string, surrounded by zeroes and stripped of
138    its `0x` prefix;
139  * as an entry key in the `alloc` session.
140    Make sure to replace that ID with the account ID that you wrote down in
141    [Step 1.1](#_1-1-create-a-signer-account).
142  
143  Once `network.json` is created, you can initialize the network with:
144  
145  ```bash
146  geth init --datadir geth-data network.json
147  ```
148  
149  The output of the above command you may include some warnings, like:
150  
151  ```bash
152  WARN [08-21|14:48:12.305] Unknown config environment variable      envvar=GETH_SIGNER_ADDR
153  ```
154  
155  or even errors when running the command for the first time:
156  
157  ```bash
158  ERROR[08-21|14:48:12.399] Head block is not reachable
159  ```
160  
161  The important part is that at the end you should see something similar to:
162  
163  ```bash
164  INFO [08-21|14:48:12.639] Successfully wrote genesis state         database=lightchaindata hash=768bf1..42d06a
165  ```
166  
167  ### 1.3. Start your PoA Node
168  
169  We are now ready to start our $1$-node, private blockchain.
170  To launch the signer node, open a separate terminal in the same working
171  directory and make sure you have the `GETH_SIGNER_ADDR` set.
172  For convenience use the `geth_signer_address.txt`:
173  
174  ```bash
175  export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)
176  ```
177  
178  Having the `GETH_SIGNER_ADDR` variable set, run:
179  
180  ```bash
181  geth\
182    --datadir geth-data\
183    --networkid 12345\
184    --unlock ${GETH_SIGNER_ADDR}\
185    --nat extip:127.0.0.1\
186    --netrestrict 127.0.0.0/24\
187    --mine\
188    --miner.etherbase ${GETH_SIGNER_ADDR}\
189    --http\
190    --allow-insecure-unlock
191  ```
192  
193  Note that, once again, the signer account created in
194  [Step 1.1](#_1-1-create-a-signer-account) appears both in
195  `--unlock` and `--allow-insecure-unlock`.
196  
197  Geth will prompt you to insert the account's password as it starts up.
198  Once you do that, it should be able to start up and begin "mining" blocks.
199  
200  Also here, you may encounter errors like:
201  
202  ```bash
203  ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict   id=c845e51a5e470e44 ip=18.138.108.67
204  ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict   id=f23ac6da7c02f84a ip=3.209.45.79
205  ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict   id=ef2d7ab886910dc8 ip=65.108.70.101
206  ERROR[08-21|15:00:27.625] Bootstrap node filtered by netrestrict   id=6b36f791352f15eb ip=157.90.35.166
207  ```
208  
209  You can safely ignore them.
210  
211  If the command above fails with:
212  
213  ```bash
214  Fatal: Failed to register the Ethereum service: only PoS networks are supported, please transition old ones with Geth v1.13.x
215  ```
216  
217  make sure, you are running the correct Geth version
218  (see Section [Prerequisites](#prerequisites))
219  
220  ## 2. Set Up The Marketplace
221  
222  You will need to open new terminal for this section and geth needs to be
223  running already. Setting up the Codex marketplace entails:
224  
225  1. Deploying the Codex Marketplace contracts to our private blockchain
226  2. Setup Ethereum accounts we will use to buy and sell storage in
227     the Codex marketplace
228  3. Provisioning those accounts with the required token balances
229  
230  ### 2.1. Deploy the Codex Marketplace Contracts
231  
232  Make sure you leave the `marketplace-tutorial` directory, and clone
233  the `codex-storage/nim-codex.git`:
234  
235  ```bash
236  git clone https://github.com/codex-storage/nim-codex.git
237  ```
238  
239  > If you just want to clone the repo to run the tutorial, you can
240  > skip the history and just download the head of the master branch by using
241  > `--depth 1` option: `git clone --depth 1 https://github.com/codex-storage/nim-codex.git`
242  
243  Thus, our directory structure for the purpose of this tutorial looks like this:
244  
245  ```bash
246  |
247  |-- nim-codex
248  └-- marketplace-tutorial
249  ```
250  
251  > You could clone the `codex-storage/nim-codex.git` to some other location.
252  > Just to keeps things nicely separated it is best to make sure that
253  > `nim-codex` is not under `marketplace-tutorial` directory.
254  
255  Now, from the `nim-codex` folder run:
256  
257  ```bash
258  make update && make
259  ```
260  
261  > This may take a moment as it will also build the `nim` compiler. Be patient.
262  
263  Now, in order to start a local Ethereum network run:
264  
265  ```bash
266  cd vendor/codex-contracts-eth
267  npm install
268  ```
269  
270  > While writing the document we used `node` version `v20.17.0` and
271  > `npm` version `11.0.0`.
272  
273  Before continuing you now must **wait until $256$ blocks are mined**
274  **in your PoAnetwork**, or deploy will fail. This should take about
275  $4$ minutes and $30$ seconds. You can check which block height you are
276  currently at by running the following command
277  **from the `marketplace-tutorial` folder**:
278  
279  ```bash
280  geth attach --exec web3.eth.blockNumber ./geth-data/geth.ipc
281  ```
282  
283  once that gets past $256$, you are ready to go.
284  
285  To deploy contracts, from the `codex-contracts-eth` directory run:
286  
287  ```bash
288  export DISTTEST_NETWORK_URL=http://localhost:8545
289  npx hardhat --network codexdisttestnetwork deploy
290  ```
291  
292  If the command completes successfully, you will see the output similar
293  to this one:
294  
295  ```bash
296  Deployed Marketplace with Groth16 Verifier at:
297  0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd
298  ```
299  > of course your address will be different.
300  
301  You are now ready to prepare the accounts.
302  
303  ### 2.2. Generate the Required Accounts
304  
305  We will run $2$ Codex nodes: a **storage provider**, which will sell storage
306  on the network, and a **client**, which will buy and use such storage;
307  we therefore need two valid Ethereum accounts. We could create random
308  accounts by using one of the many  tools available to that end but, since
309  this is a tutorial running on a local private network, we will simply
310  provide you with two pre-made accounts along with their private keys,
311  which you can copy and paste instead:
312  
313  First make sure you're back in the `marketplace-tutorial` folder and
314  not the `codex-contracts-eth` subfolder. Then set these variables:
315  
316  **Storage:**
317  ```bash
318  export ETH_STORAGE_ADDR=0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37
319  export ETH_STORAGE_PK=0x06c7ac11d4ee1d0ccb53811b71802fa92d40a5a174afad9f2cb44f93498322c3
320  echo $ETH_STORAGE_PK > storage.pkey && chmod 0600 storage.pkey
321  ```
322  
323  **Client:**
324  ```bash
325  export ETH_CLIENT_ADDR=0x9F0C62Fe60b22301751d6cDe1175526b9280b965
326  export ETH_CLIENT_PK=0x5538ec03c956cb9d0bee02a25b600b0225f1347da4071d0fd70c521fdc63c2fc
327  echo $ETH_CLIENT_PK > client.pkey && chmod 0600 client.pkey
328  ```
329  
330  ### 2.3. Provision Accounts with Tokens
331  
332  We now need to transfer some ETH to each of the accounts, as well as provide
333  them with some Codex tokens for the storage node to use as collateral and
334  for the client node to buy actual storage.
335  
336  Although the process is not particularly complicated, I suggest you use
337  [the script we prepared](https://github.com/gmega/local-codex-bare/blob/main/scripts/mint-tokens.js)
338  for that. This script, essentially:
339  
340  1. reads the Marketplace contract address and its ABI from the deployment data;
341  2. transfers $1$ ETH from the signer account to a target account if the target
342     account has no ETH balance;
343  3. mints $n$ Codex tokens and adds it into the target account's balance.
344  
345  To use the script, just download it into a local file named `mint-tokens.js`,
346  for instance using `curl` (make sure you are in
347  the `marketplace-tutorial` directory):
348  
349  ```bash
350  # download script
351  curl https://raw.githubusercontent.com/gmega/codex-local-bare/main/scripts/mint-tokens.js -o mint-tokens.js
352  ```
353  
354  Then run:
355  
356  ```bash
357  # set the contract file location (we assume you are in the marketplace-tutorial directory)
358  export CONTRACT_DEPLOY_FULL="../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork"
359  export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)
360  # Installs Web3-js
361  npm install web3
362  # Provides tokens to the storage account.
363  node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37 1000000000000000000
364  # Provides tokens to the client account.
365  node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x9F0C62Fe60b22301751d6cDe1175526b9280b965 1000000000000000000
366  ```
367  
368  If you get a message like 
369  
370  ```bash
371  Usage: mint-tokens.js <token-hardhat-deploy-json> <signer-account> <receiver-account> <token-ammount>
372  ```
373  
374  then you need to ensure you provided all the required arguments.
375  In particular you need to ensure that the `GETH_SIGNER_ADDR` env variable
376  holds the signer address (we used
377  `export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)` above to
378  make sure it is set).
379  
380  ## 3. Run Codex
381  
382  With accounts and geth in place, we can now start the Codex nodes.
383  
384  ### 3.1. Storage Node
385  
386  The storage node will be the one storing data and submitting the proofs of
387  storage to the chain. To do that, it needs access to:
388  
389  1. the address of the Marketplace contract that has been deployed to
390     the local geth node in [Step 2.1](#_2-1-deploy-the-codex-marketplace-contracts);
391  2. the sample ceremony files which are shipped in the Codex contracts repo
392    (`nim-codex/vendor/codex-contracts-eth`).
393  
394  **Address of the Marketplace Contract.** The contract address can be found
395  inside of the file `nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork/Marketplace.json`.
396  We captured that location above in `CONTRACT_DEPLOY_FULL` variable, thus, from
397  the `marketplace-tutorial` folder just run:
398  
399  ```bash
400  grep '"address":' ${CONTRACT_DEPLOY_FULL}/Marketplace.json
401  ```
402  
403  which should print something like:
404  ```bash
405  "address": "0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd",
406  ```
407  
408  > This address should match the address we got earlier when deploying
409  > the Marketplace contract above.
410  
411  Then run the following with the correct market place address:
412  ```bash
413  export MARKETPLACE_ADDRESS="0x0000000000000000000000000000000000000000"
414  echo ${MARKETPLACE_ADDRESS} > marketplace_address.txt
415  ```
416  
417  where you replace `0x0000000000000000000000000000000000000000` with
418  the address of the Marketplace contract.
419  
420  **Prover ceremony files.** The ceremony files are under the
421  `nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork`
422  subdirectory. There are three of them: `proof_main.r1cs`, `proof_main.zkey`,
423  and `prooof_main.wasm`. We will need all of them to start the Codex storage node.
424  
425  **Starting the storage node.** Let:
426  
427  * `PROVER_ASSETS` contain the directory where the prover ceremony files are
428    located. **This must be an absolute path**;
429  * `CODEX_BINARY` contain the location of your Codex binary;
430  * `MARKETPLACE_ADDRESS` contain the address of the Marketplace contract
431    (we have already set it above).
432  
433  Set these paths into environment variables (make sure you are in
434  the `marketplace-tutorial` directory):
435  
436  ```bash
437  export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork")
438  export PROVER_ASSETS=$(realpath "../nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork/")
439  export CODEX_BINARY=$(realpath "../nim-codex/build/codex")
440  export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt)
441  ```
442  > you may notice, that we have already set the `CONTRACT_DEPLOY_FULL` variable
443  > above. Here, we make sure it is an absolute path.
444  
445  To launch the storage node, run:
446  
447  ```bash
448  ${CODEX_BINARY}\
449    --data-dir=./codex-storage\
450    --listen-addrs=/ip4/0.0.0.0/tcp/8080\
451    --api-port=8000\
452    --disc-port=8090\
453    persistence\
454    --eth-provider=http://localhost:8545\
455    --eth-private-key=./storage.pkey\
456    --marketplace-address=${MARKETPLACE_ADDRESS}\
457    --validator\
458    --validator-max-slots=1000\
459    prover\
460    --circom-r1cs=${PROVER_ASSETS}/proof_main.r1cs\
461    --circom-wasm=${PROVER_ASSETS}/proof_main.wasm\
462    --circom-zkey=${PROVER_ASSETS}/proof_main.zkey
463  ```
464  
465  **Starting the client node.**
466  
467  The client node is started similarly except that:
468  
469  * we need to pass the SPR of the storage node so it can form a network with it;
470  * since it does not run any proofs, it does not require any ceremony files.
471  
472  We get the Signed Peer Record (SPR) of the storage node so we can bootstrap
473  the client node with it. To get the SPR, issue the following call:
474  
475  ```bash
476  curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr' --write-out '\n'
477  ```
478  
479  You should get the SPR back starting with `spr:`.
480  
481  Before you proceed, open new terminal, and enter `marketplace-tutorial` directory.
482  
483  Next set these paths into environment variables:
484  
485  ```bash
486  # set the SPR for the storage node
487  export STORAGE_NODE_SPR=$(curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr')
488  # basic vars
489  export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork")
490  export CODEX_BINARY=$(realpath "../nim-codex/build/codex")
491  export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt)
492  ```
493  and then run:
494  
495  ```bash
496  ${CODEX_BINARY}\
497    --data-dir=./codex-client\
498    --listen-addrs=/ip4/0.0.0.0/tcp/8081\
499    --api-port=8001\
500    --disc-port=8091\
501    --bootstrap-node=${STORAGE_NODE_SPR}\
502    persistence\
503    --eth-provider=http://localhost:8545\
504    --eth-private-key=./client.pkey\
505    --marketplace-address=${MARKETPLACE_ADDRESS}
506  ```
507  
508  ## 4. Buy and Sell Storage on the Marketplace
509  
510  Any storage negotiation has two sides: a buyer and a seller.
511  Therefore, before we can actually request storage, we must first offer
512  some of it for sale.
513  
514  ### 4.1 Sell Storage
515  
516  The following request will cause the storage node to put out $5\text{MB}$
517  of storage for sale for $1$ hour, at a minimum price of $1000$ Codex token
518  per byte per second, while expressing that maximum penalty (a collateral)
519  the storage provider is willing to risk for not fulfilling its part of the
520  contract is limited to $50000000$ tokens (wei) for this specific availability.[^1]
521  This total collateral will be distributed across all storage requests matching
522  this availability.
523  
524  ```bash
525  curl 'http://localhost:8000/api/codex/v1/sales/availability' \
526    --header 'Content-Type: application/json' \
527    --data '{
528    "totalSize": "5000000",
529    "duration": "3600",
530    "minPricePerBytePerSecond": "1000",
531    "totalCollateral": "50000000"
532  }'
533  ```
534  
535  This should return a JSON response containing an `id` (e.g. 
536  `"id": "0xb55b3bc7aac2563d5bf08ce8a177a38b5a40254bfa7ee8f9c52debbb176d44b0"`)
537  which identifies this storage offer.
538  
539  > To make JSON responses more readable, you can try
540  > [jq](https://jqlang.github.io/jq/) JSON formatting utility
541  > by just adding `| jq` after the command.
542  > On macOS you can install with `brew install jq`.
543  
544  To check the current storage offers for this node, you can issue:
545  
546  ```bash
547  curl 'http://localhost:8000/api/codex/v1/sales/availability'
548  ```
549  
550  or with `jq`:
551  
552  ```bash
553  curl 'http://localhost:8000/api/codex/v1/sales/availability' | jq
554  ```
555  
556  This should print a list of offers, with the one you just created figuring
557  among them (for our tutorial, there will be only one offer returned
558  at this time).
559  
560  ### 4.2. Buy Storage
561  
562  Before we can buy storage, we must have some actual data to request
563  storage for. Start by uploading a small file to your client node.
564  On Linux (or macOS) you could, for instance, use `dd` to generate a $1M$ file:
565  
566  ```bash
567  dd if=/dev/urandom of=./data.bin bs=1M count=1
568  ```
569  
570  Assuming your file is named `data.bin`, you can upload it with:
571  
572  ```bash
573  curl --request POST http://localhost:8001/api/codex/v1/data --header 'Content-Type: application/octet-stream' --write-out '\n' -T ./data.bin
574  ```
575  
576  Once the upload completes, you should see a _Content Identifier_,
577  or _CID_ (e.g. `zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj`)
578  for the uploaded file printed to the terminal.
579  Use that CID in the purchase request:
580  
581  ```bash
582  # make sure to replace the CID before with the CID you got in the previous step
583  export CID=zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj
584  ```
585  
586  ```bash
587  curl "http://localhost:8001/api/codex/v1/storage/request/${CID}" \
588    --header 'Content-Type: application/octet-stream' \
589    --data "{
590      \"duration\": \"600\",
591      \"pricePerBytePerSecond\": \"2000\",
592      \"proofProbability\": \"3\",
593      \"expiry\": \"500\",
594      \"nodes\": 3,
595      \"tolerance\": 1,
596      \"collateralPerByte\": \"1\"
597    }" \
598    --write-out '\n'
599  ```
600  
601  The parameters under `--data` say that:
602  
603  1. we want to purchase storage for our file for $5$ minutes (`"duration": "600"`);
604  2. we are willing to pay up to $2000$ tokens (wei) per slot per second
605     (`"pricePerBytePerSecond": "2000"`). It is then twice as much as
606     `minPricePerBytePerSecond`, which we set to $1000$ when creating the availability
607     above.
608  3. our file will be split into three pieces (`"nodes": 3`). 
609     Because we set `"tolerance": 1` we only need two (`nodes - tolerance`)
610     pieces to rebuild the file; i.e., we can tolerate that at most one node
611     stops storing our data; either due to failure or other reasons;
612  4. we demand `1` token in collateral from storage providers per byte of storage
613     per second for each piece of data (called _slots_). Because we provide some redundancy to
614     the stored data, the actual size of the stored dataset will be bigger than original
615     content (the bigger the `tolerance` the bigger the resulting dataset).
616  5. finally, the `expiry` puts a time limit for filling all the slots by
617     the storage provider(s). If slots are not filled by the `expire` interval,
618     the request will timeout and fail. 
619  
620  ### 4.3. Track your Storage Requests
621  
622  POSTing a storage request will make it available in the storage market,
623  and a storage node will eventually pick it up.
624  
625  You can poll the status of your request by means of:
626  ```bash
627  export STORAGE_PURCHASE_ID="6b6e2445f33ed624891f0543fe9dbf4bd0a6971febccaae2431375a5b9a2840d"
628  curl "http://localhost:8001/api/codex/v1/storage/purchases/${STORAGE_PURCHASE_ID}"
629  ```
630  
631  This returns a result like:
632  
633  ```json
634  {
635    "requestId": "0x6b6e2445f33ed624891f0543fe9dbf4bd0a6971febccaae2431375a5b9a2840d",
636    "request": {
637      "client": "0x9f0c62fe60b22301751d6cde1175526b9280b965",
638      "ask": {
639        "proofProbability": "3",
640        "pricePerBytePerSecond": "2000",
641        "collateralPerByte": "1",
642        "slots": 3,
643        "slotSize": 524288,
644        "duration": 600,
645        "maxSlotLoss": 1
646      },
647      "content": {
648        "cid": "zDvZRwzm18zqvTRMHhjDmwybKZaomW8bDFtdSyaQ4XM6MmER5fzy"
649      },
650      "expiry": 500,
651      "nonce": "0x5267b832fe9a978fb0dd3912b42fece7c5ac074a9cc3c74bf578b6cee9e43543",
652      "id": "0x6b6e2445f33ed624891f0543fe9dbf4bd0a6971febccaae2431375a5b9a2840d"
653    },
654    "state": "submitted",
655    "error": null
656  }
657  ```
658  
659  Shows that a request has been submitted but has not yet been filled.
660  Your request will be successful once `"state"` shows `"started"`.
661  Anything other than that means the request has not been completely
662  processed yet, and an `"error"` state other than `null` means it failed.
663  
664  Well, it was quite a journey, wasn't it? You can congratulate yourself for
665  successfully finishing the codex marketplace tutorial!
666  
667  [^1]: Codex files get partitioned into pieces called "slots" and distributed
668  to various storage providers. The collateral refers to one such slot,
669  and will be slowly eaten away as the storage provider fails to deliver
670  timely proofs.