/ ko / 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 `10.8.2`.
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 10000000000
364  # Provides tokens to the client account.
365  node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x9F0C62Fe60b22301751d6cDe1175526b9280b965 10000000000
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 Marketplace contract above in
419  [Step 2.1](#_2-1-deploy-the-codex-marketplace-contracts).
420  
421  **Prover ceremony files.** The ceremony files are under the
422  `nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork`
423  subdirectory. There are three of them: `proof_main.r1cs`, `proof_main.zkey`,
424  and `prooof_main.wasm`. We will need all of them to start the Codex storage node.
425  
426  **Starting the storage node.** Let:
427  
428  * `PROVER_ASSETS` contain the directory where the prover ceremony files are
429    located. **This must be an absolute path**;
430  * `CODEX_BINARY` contain the location of your Codex binary;
431  * `MARKETPLACE_ADDRESS` contain the address of the Marketplace contract
432    (we have already set it above).
433  
434  Set these paths into environment variables (make sure you are in
435  the `marketplace-tutorial` directory):
436  
437  ```bash
438  export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork")
439  export PROVER_ASSETS=$(realpath "../nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork/")
440  export CODEX_BINARY=$(realpath "../nim-codex/build/codex")
441  export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt)
442  ```
443  > you may notice, that we have already set the `CONTRACT_DEPLOY_FULL` variable
444  > above. Here, we make sure it is an absolute path.
445  
446  To launch the storage node, run:
447  
448  ```bash
449  ${CODEX_BINARY}\
450    --data-dir=./codex-storage\
451    --listen-addrs=/ip4/0.0.0.0/tcp/8080\
452    --api-port=8000\
453    --disc-port=8090\
454    persistence\
455    --eth-provider=http://localhost:8545\
456    --eth-private-key=./storage.pkey\
457    --marketplace-address=${MARKETPLACE_ADDRESS}\
458    --validator\
459    --validator-max-slots=1000\
460    prover\
461    --circom-r1cs=${PROVER_ASSETS}/proof_main.r1cs\
462    --circom-wasm=${PROVER_ASSETS}/proof_main.wasm\
463    --circom-zkey=${PROVER_ASSETS}/proof_main.zkey
464  ```
465  
466  **Starting the client node.**
467  
468  The client node is started similarly except that:
469  
470  * we need to pass the SPR of the storage node so it can form a network with it;
471  * since it does not run any proofs, it does not require any ceremony files.
472  
473  We get the Signed Peer Record (SPR) of the storage node so we can bootstrap
474  the client node with it. To get the SPR, issue the following call:
475  
476  ```bash
477  curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr' --write-out '\n'
478  ```
479  
480  You should get the SPR back starting with `spr:`.
481  
482  Before you proceed, open new terminal, and enter `marketplace-tutorial` directory.
483  
484  Next set these paths into environment variables:
485  
486  ```bash
487  # set the SPR for the storage node
488  export STORAGE_NODE_SPR=$(curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr')
489  # basic vars
490  export CONTRACT_DEPLOY_FULL=$(realpath "../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork")
491  export CODEX_BINARY=$(realpath "../nim-codex/build/codex")
492  export MARKETPLACE_ADDRESS=$(cat marketplace_address.txt)
493  ```
494  and then run:
495  
496  ```bash
497  ${CODEX_BINARY}\
498    --data-dir=./codex-client\
499    --listen-addrs=/ip4/0.0.0.0/tcp/8081\
500    --api-port=8001\
501    --disc-port=8091\
502    --bootstrap-node=${STORAGE_NODE_SPR}\
503    persistence\
504    --eth-provider=http://localhost:8545\
505    --eth-private-key=./client.pkey\
506    --marketplace-address=${MARKETPLACE_ADDRESS}
507  ```
508  
509  ## 4. Buy and Sell Storage on the Marketplace
510  
511  Any storage negotiation has two sides: a buyer and a seller.
512  Therefore, before we can actually request storage, we must first offer
513  some of it for sale.
514  
515  ### 4.1 Sell Storage
516  
517  The following request will cause the storage node to put out $50\text{MB}$
518  of storage for sale for $1$ hour, at a price of $1$ Codex token
519  per slot per second, while expressing that it's willing to take at most
520  a $1000$ Codex token penalty for not fulfilling its part of the contract.[^1]
521  
522  ```bash
523  curl 'http://localhost:8000/api/codex/v1/sales/availability' \
524    --header 'Content-Type: application/json' \
525    --data '{
526    "totalSize": "50000000",
527    "duration": "3600",
528    "minPrice": "1",
529    "maxCollateral": "1000"
530  }'
531  ```
532  
533  This should return a JSON response containing an `id` (e.g. `"id": "0xb55b3bc7aac2563d5bf08ce8a177a38b5a40254bfa7ee8f9c52debbb176d44b0"`)
534  which identifies this storage offer.
535  
536  > To make JSON responses more readable, you can try
537  > [jq](https://jqlang.github.io/jq/) JSON formatting utility
538  > by just adding `| jq` after the command.
539  > On macOS you can install with `brew install jq`.
540  
541  To check the current storage offers for this node, you can issue:
542  
543  ```bash
544  curl 'http://localhost:8000/api/codex/v1/sales/availability'
545  ```
546  
547  or with `jq`:
548  
549  ```bash
550  curl 'http://localhost:8000/api/codex/v1/sales/availability' | jq
551  ```
552  
553  This should print a list of offers, with the one you just created figuring
554  among them (for our tutorial, there will be only one offer returned
555  at this time).
556  
557  ### 4.2. Buy Storage
558  
559  Before we can buy storage, we must have some actual data to request
560  storage for. Start by uploading a small file to your client node.
561  On Linux (or macOS) you could, for instance, use `dd` to generate a $1M$ file:
562  
563  ```bash
564  dd if=/dev/urandom of=./data.bin bs=1M count=1
565  ```
566  
567  Assuming your file is named `data.bin`, you can upload it with:
568  
569  ```bash
570  curl --request POST http://localhost:8001/api/codex/v1/data --header 'Content-Type: application/octet-stream' --write-out '\n' -T ./data.bin
571  ```
572  
573  Once the upload completes, you should see a _Content Identifier_,
574  or _CID_ (e.g. `zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj`)
575  for the uploaded file printed to the terminal.
576  Use that CID in the purchase request:
577  
578  ```bash
579  # make sure to replace the CID before with the CID you got in the previous step
580  export CID=zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj
581  ```
582  
583  ```bash
584  curl "http://localhost:8001/api/codex/v1/storage/request/${CID}" \
585    --header 'Content-Type: application/octet-stream' \
586    --data "{
587      \"duration\": \"600\",
588      \"reward\": \"1\",
589      \"proofProbability\": \"3\",
590      \"expiry\": \"500\",
591      \"nodes\": 3,
592      \"tolerance\": 1,
593      \"collateral\": \"1000\"
594    }" \
595    --write-out '\n'
596  ```
597  
598  The parameters under `--data` say that:
599  
600  1. we want to purchase storage for our file for $5$ minutes (`"duration": "600"`);
601  2. we are willing to pay up to $1$ token per slot per second (`"reward": "1"`)
602  3. our file will be split into three pieces (`"nodes": 3`). 
603     Because we set `"tolerance": 1` we only need two (`nodes - tolerance`)
604     pieces to rebuild the file; i.e., we can tolerate that at most one node
605     stops storing our data; either due to failure or other reasons;
606  4. we demand `1000` tokens in collateral from storage providers for each piece.
607     Since there are $3$ such pieces, there will be `3000` in total collateral
608     committed by the storage provider(s) once our request is started.
609  5. finally, the `expiry` puts a time limit for filling all the slots by
610     the storage provider(s). If slot are not filled by the `expire` interval,
611     the request will timeout and fail. 
612  
613  ### 4.3. Track your Storage Requests
614  
615  POSTing a storage request will make it available in the storage market,
616  and a storage node will eventually pick it up.
617  
618  You can poll the status of your request by means of:
619  ```bash
620  export STORAGE_PURCHASE_ID="1d0ec5261e3364f8b9d1cf70324d70af21a9b5dccba380b24eb68b4762249185"
621  curl "http://localhost:8001/api/codex/v1/storage/purchases/${STORAGE_PURCHASE_ID}"
622  ```
623  
624  For instance:
625  
626  ```bash
627  > curl 'http://localhost:8001/api/codex/v1/storage/purchases/6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d'
628  ```
629  
630  This returns a result like:
631  
632  ```json
633  {
634    "requestId": "0x86501e4677a728c6a8031971d09b921c3baa268af06b9f17f1b745e7dba5d330",
635    "request": {
636      "client": "0x9f0c62fe60b22301751d6cde1175526b9280b965",
637      "ask": {
638        "slots": 3,
639        "slotSize": "262144",
640        "duration": "1000",
641        "proofProbability": "3",
642        "reward": "1",
643        "collateral": "1",
644        "maxSlotLoss": 1
645      },
646      "content": {
647        "cid": "zDvZRwzkyw1E7ABaUSmgtNEDjC7opzhUoHo99Vpvc98cDWeCs47u"
648      },
649      "expiry": "1711992852",
650      "nonce": "0x9f5e651ecd3bf73c914f8ed0b1088869c64095c0d7bd50a38fc92ebf66ff5915",
651      "id": "0x6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d"
652    },
653    "state": "submitted",
654    "error": null
655  }
656  ```
657  
658  Shows that a request has been submitted but has not yet been filled.
659  Your request will be successful once `"state"` shows `"started"`.
660  Anything other than that means the request has not been completely
661  processed yet, and an `"error"` state other than `null` means it failed.
662  
663  Well, it was quite a journey, wasn't it? You can congratulate yourself for
664  successfully finishing the codex marketplace tutorial!
665  
666  [^1]: Codex files get partitioned into pieces called "slots" and distributed
667  to various storage providers. The collateral refers to one such slot,
668  and will be slowly eaten away as the storage provider fails to deliver
669  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).