--- outline: [2, 3] --- # Запуск локальной сети Codex с поддержкой маркетплейса Это руководство научит вас, как запустить небольшую сеть Codex с включенным _маркетплейсом хранения_; т.е. функциональностью в Codex, которая позволяет участникам предлагать и покупать хранилище на рынке, обеспечивая честное выполнение обязательств поставщиками хранилища с помощью криптографических доказательств. В этом руководстве вы: 1. [Настроите сеть Geth PoA](#_1-set-up-a-geth-poa-network); 2. [Настроите маркетплейс](#_2-set-up-the-marketplace); 3. [Запустите Codex](#_3-run-codex); 4. [Купите и продайте хранилище на маркетплейсе](#_4-buy-and-sell-storage-on-the-marketplace). ## Предварительные требования Для прохождения этого руководства вам понадобится: * клиент Ethereum [geth](https://github.com/ethereum/go-ethereum); Вам нужна версия `1.13.x` geth, так как более новые версии больше не поддерживают Proof of Authority (PoA). Это руководство было протестировано с версией geth `1.13.15`. * бинарный файл Codex, который [можно собрать из исходного кода](https://github.com/codex-storage/nim-codex?tab=readme-ov-file#build-and-run). Мы также будем использовать синтаксис [bash](https://en.wikipedia.org/wiki/Bash_(Unix_shell)) на протяжении всего руководства. Если вы используете другую оболочку, вам может потребоваться адаптировать команды под вашу платформу. Для начала создайте новую папку, где мы будем хранить файлы, связанные с руководством, чтобы держать их отдельно от репозитория codex. Предположим, что имя папки будет `marketplace-tutorial`. ## 1. Настройка сети Geth PoA Для этого руководства мы будем использовать простую [Proof-of-Authority](https://github.com/ethereum/EIPs/issues/225) сеть с geth. Первым шагом является создание _учетной записи подписанта_: учетной записи, которая будет использоваться geth для подписи блоков в сети. Любой блок, подписанный подписантом, принимается как действительный. ### 1.1. Создание учетной записи подписанта Чтобы создать учетную запись подписанта, из директории `marketplace-tutorial` выполните: ```bash geth account new --datadir geth-data ``` Генератор учетных записей попросит вас ввести пароль, который вы можете оставить пустым. Затем он выведет некоторую информацию, включая публичный адрес учетной записи: ```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! ``` В этом примере публичный адрес учетной записи подписанта - `0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB`. У вас будет выведен другой адрес. Сохраните его для дальнейшего использования. Затем установите переменную окружения для дальнейшего использования: ```bash export GETH_SIGNER_ADDR="0x0000000000000000000000000000000000000000" echo ${GETH_SIGNER_ADDR} > geth_signer_address.txt ``` > Здесь убедитесь, что вы заменили `0x0000000000000000000000000000000000000000` > на ваш публичный адрес учетной записи подписанта > (`0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB` в нашем примере). ### 1.2. Настройка сети и создание генезис-блока Следующий шаг - указать geth, какую сеть вы хотите запустить. Мы будем запускать [pre-merge](https://ethereum.org/en/roadmap/merge/) сеть с консенсусом Proof-of-Authority. Чтобы это работало, создайте файл `network.json`. Если вы установили переменную `GETH_SIGNER_ADDR` выше, вы можете выполнить следующую команду для создания файла `network.json`: ```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 ``` Вы также можете создать файл вручную, не забыв обновить его своим публичным адресом подписанта: ```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" } } } ``` Обратите внимание, что адрес учетной записи подписанта встроен в два разных места: * внутри строки `"extradata"`, окруженный нулями и без префикса `0x`; * как ключ записи в секции `alloc`. Убедитесь, что вы заменили этот ID на ID учетной записи, который вы записали в [Шаге 1.1](#_1-1-create-a-signer-account). После создания `network.json` вы можете инициализировать сеть с помощью: ```bash geth init --datadir geth-data network.json ``` В выводе этой команды могут быть предупреждения, например: ```bash WARN [08-21|14:48:12.305] Unknown config environment variable envvar=GETH_SIGNER_ADDR ``` или даже ошибки при первом запуске команды: ```bash ERROR[08-21|14:48:12.399] Head block is not reachable ``` Важно, что в конце вы должны увидеть что-то похожее на: ```bash INFO [08-21|14:48:12.639] Successfully wrote genesis state database=lightchaindata hash=768bf1..42d06a ``` ### 1.3. Запуск вашего PoA узла Теперь мы готовы запустить нашу $1$-узловую приватную блокчейн-сеть. Чтобы запустить узел подписанта, откройте отдельный терминал в той же рабочей директории и убедитесь, что у вас установлена переменная `GETH_SIGNER_ADDR`. Для удобства используйте `geth_signer_address.txt`: ```bash export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt) ``` Имея установленную переменную `GETH_SIGNER_ADDR`, выполните: ```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 ``` Обратите внимание, что учетная запись подписанта, созданная в [Шаге 1.1](#_1-1-create-a-signer-account), снова появляется как в `--unlock`, так и в `--allow-insecure-unlock`. Geth попросит вас ввести пароль учетной записи при запуске. После этого он должен запуститься и начать "майнить" блоки. Здесь также могут возникнуть ошибки, например: ```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 ``` Их можно безопасно игнорировать. Если команда выше завершается с ошибкой: ```bash Fatal: Failed to register the Ethereum service: only PoS networks are supported, please transition old ones with Geth v1.13.x ``` убедитесь, что вы используете правильную версию Geth (см. раздел [Предварительные требования](#prerequisites)) ## 2. Настройка маркетплейса Для этого раздела вам нужно открыть новый терминал, и geth должен быть уже запущен. Настройка маркетплейса Codex включает: 1. Развертывание контрактов маркетплейса Codex в нашу приватную блокчейн-сеть 2. Настройку учетных записей Ethereum, которые мы будем использовать для покупки и продажи хранилища в маркетплейсе Codex 3. Обеспечение этих учетных записей необходимыми балансами токенов ### 2.1. Развертывание контрактов маркетплейса Codex Убедитесь, что вы вышли из директории `marketplace-tutorial` и клонируйте `codex-storage/nim-codex.git`: ```bash git clone https://github.com/codex-storage/nim-codex.git ``` > Если вы хотите просто клонировать репозиторий для прохождения руководства, вы можете > пропустить историю и просто скачать голову ветки master, используя > опцию `--depth 1`: `git clone --depth 1 https://github.com/codex-storage/nim-codex.git` Таким образом, структура директорий для целей этого руководства выглядит так: ```bash | |-- nim-codex └-- marketplace-tutorial ``` > Вы можете клонировать `codex-storage/nim-codex.git` в другое место. Теперь из папки `nim-codex` выполните: ```bash make update && make ``` > Это может занять некоторое время, так как это также соберет компилятор `nim`. Будьте терпеливы. Теперь, чтобы запустить локальную сеть Ethereum, выполните: ```bash cd vendor/codex-contracts-eth npm install ``` > При написании документа мы использовали `node` версии `v20.17.0` и > `npm` версии `10.8.2`. Прежде чем продолжить, вы должны **дождаться, пока будет добыто $256$ блоков** **в вашей PoA сети**, иначе развертывание завершится неудачей. Это должно занять около $4$ минут и $30$ секунд. Вы можете проверить, на какой высоте блока вы находитесь, выполнив следующую команду **из папки `marketplace-tutorial`**: ```bash geth attach --exec web3.eth.blockNumber ./geth-data/geth.ipc ``` как только это превысит $256$, вы готовы к работе. Чтобы развернуть контракты, из директории `codex-contracts-eth` выполните: ```bash export DISTTEST_NETWORK_URL=http://localhost:8545 npx hardhat --network codexdisttestnetwork deploy ``` Если команда завершится успешно, вы увидите вывод, похожий на этот: ```bash Deployed Marketplace with Groth16 Verifier at: 0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd ``` > конечно, ваш адрес будет другим. Теперь вы готовы подготовить учетные записи. ### 2.2. Генерация необходимых учетных записей Мы будем запускать $2$ узла Codex: **поставщик хранилища**, который будет продавать хранилище в сети, и **клиент**, который будет покупать и использовать такое хранилище; поэтому нам нужны две действительные учетные записи Ethereum. Мы могли бы создать случайные учетные записи, используя один из многих доступных инструментов, но, поскольку это руководство работает в локальной приватной сети, мы просто предоставим вам две предварительно созданные учетные записи вместе с их приватными ключами, которые вы можете скопировать и вставить: Сначала убедитесь, что вы вернулись в папку `marketplace-tutorial` и не находитесь в подпапке `codex-contracts-eth`. Затем установите эти переменные: **Хранилище:** ```bash export ETH_STORAGE_ADDR=0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37 export ETH_STORAGE_PK=0x06c7ac11d4ee1d0ccb53811b71802fa92d40a5a174afad9f2cb44f93498322c3 echo $ETH_STORAGE_PK > storage.pkey && chmod 0600 storage.pkey ``` **Клиент:** ```bash export ETH_CLIENT_ADDR=0x9F0C62Fe60b22301751d6cDe1175526b9280b965 export ETH_CLIENT_PK=0x5538ec03c956cb9d0bee02a25b600b0225f1347da4071d0fd70c521fdc63c2fc echo $ETH_CLIENT_PK > client.pkey && chmod 0600 client.pkey ``` ### 2.3. Обеспечение учетных записей токенами Теперь нам нужно перевести немного ETH на каждую из учетных записей, а также предоставить им некоторые токены Codex для использования узлом хранилища в качестве залога и для клиентского узла для покупки фактического хранилища. Хотя процесс не особенно сложен, я предлагаю вам использовать [скрипт, который мы подготовили](https://github.com/gmega/local-codex-bare/blob/main/scripts/mint-tokens.js) для этого. Этот скрипт, по сути: 1. читает адрес контракта маркетплейса и его ABI из данных развертывания; 2. переводит $1$ ETH с учетной записи подписанта на целевую учетную запись, если целевая учетная запись не имеет баланса ETH; 3. чеканит $n$ токенов Codex и добавляет их в баланс целевой учетной записи. Чтобы использовать скрипт, просто скачайте его в локальный файл с именем `mint-tokens.js`, например, используя `curl` (убедитесь, что вы находитесь в директории `marketplace-tutorial`): ```bash # скачать скрипт curl https://raw.githubusercontent.com/gmega/codex-local-bare/main/scripts/mint-tokens.js -o mint-tokens.js ``` Затем выполните: ```bash # установить расположение файла контракта (мы предполагаем, что вы находитесь в директории marketplace-tutorial) export CONTRACT_DEPLOY_FULL="../nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork" export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt) # Устанавливает Web3-js npm install web3 # Предоставляет токены учетной записи хранилища. node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37 10000000000 # Предоставляет токены клиентской учетной записи. node ./mint-tokens.js $CONTRACT_DEPLOY_FULL/TestToken.json $GETH_SIGNER_ADDR 0x9F0C62Fe60b22301751d6cDe1175526b9280b965 10000000000 ``` Если вы получите сообщение типа ```bash Usage: mint-tokens.js ``` то вам нужно убедиться, что вы предоставили все необходимые аргументы. В частности, вам нужно убедиться, что переменная окружения `GETH_SIGNER_ADDR` содержит адрес подписанта (мы использовали `export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)` выше, чтобы убедиться, что она установлена). ## 3. Запуск Codex С учетными записями и geth на месте, мы теперь можем запустить узлы Codex. ### 3.1. Узел хранилища Узел хранилища будет тем, который хранит данные и отправляет доказательства хранения в цепочку. Для этого ему нужен доступ к: 1. адресу контракта маркетплейса, который был развернут в локальном узле geth в [Шаге 2.1](#_2-1-deploy-the-codex-marketplace-contracts); 2. образцам файлов церемонии, которые поставляются в репозитории контрактов Codex (`nim-codex/vendor/codex-contracts-eth`). **Адрес контракта маркетплейса.** Адрес контракта можно найти внутри файла `nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork/Marketplace.json`. Мы захватили это расположение выше в переменной `CONTRACT_DEPLOY_FULL`, поэтому из папки `marketplace-tutorial` просто выполните: ```bash grep '"address":' ${CONTRACT_DEPLOY_FULL}/Marketplace.json ``` что должно вывести что-то вроде: ```bash "address": "0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd", ``` > Этот адрес должен соответствовать адресу, который мы получили ранее при развертывании > контракта маркетплейса выше. Затем выполните следующее с правильным адресом маркетплейса: ```bash export MARKETPLACE_ADDRESS="0x0000000000000000000000000000000000000000" echo ${MARKETPLACE_ADDRESS} > marketplace_address.txt ``` где вы заменяете `0x0000000000000000000000000000000000000000` на адрес контракта маркетплейса выше в [Шаге 2.1](#_2-1-deploy-the-codex-marketplace-contracts). **Файлы церемонии провайдера.** Файлы церемонии находятся в подкаталоге `nim-codex/vendor/codex-contracts-eth/verifier/networks/codexdisttestnetwork`. Их три: `proof_main.r1cs`, `proof_main.zkey`, и `prooof_main.wasm`. Нам понадобятся все они для запуска узла хранилища Codex. **Запуск узла хранилища.** Пусть: * `PROVER_ASSETS` содержит директорию, где находятся файлы церемонии провайдера. **Это должен быть абсолютный путь**; * `CODEX_BINARY` содержит расположение вашего бинарного файла Codex; * `MARKETPLACE_ADDRESS` содержит адрес контракта маркетплейса (мы уже установили его выше). Установите эти пути в переменные окружения (убедитесь, что вы находитесь в директории `marketplace-tutorial`): ```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) ``` > вы можете заметить, что мы уже установили переменную `CONTRACT_DEPLOY_FULL` > выше. Здесь мы убеждаемся, что это абсолютный путь. Чтобы запустить узел хранилища, выполните: ```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 ``` **Запуск клиентского узла.** Клиентский узел запускается аналогично, за исключением того, что: * нам нужно передать SPR узла хранилища, чтобы он мог сформировать сеть с ним; * поскольку он не выполняет никаких доказательств, ему не требуются файлы церемонии. Мы получаем Signed Peer Record (SPR) узла хранилища, чтобы мы могли загрузить клиентский узел с ним. Чтобы получить SPR, выполните следующий вызов: ```bash curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr' --write-out '\n' ``` Вы должны получить SPR, начинающийся с `spr:`. Прежде чем продолжить, откройте новый терминал и войдите в директорию `marketplace-tutorial`. Затем установите эти пути в переменные окружения: ```bash # установить SPR для узла хранилища export STORAGE_NODE_SPR=$(curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr') # базовые переменные 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) ``` и затем выполните: ```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\ ``` ## 4. Покупка и продажа хранилища на маркетплейсе Любые переговоры о хранилище имеют две стороны: покупатель и продавец. Поэтому, прежде чем мы сможем фактически запросить хранилище, мы должны сначала предложить его на продажу. ### 4.1 Продажа хранилища Следующий запрос заставит узел хранилища выставить $50\text{MB}$ хранилища на продажу на $1$ час по цене $1$ токен Codex за слот в секунду, при этом выражая готовность принять максимум $1000$ токенов Codex в качестве штрафа за невыполнение своей части контракта.[^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" }' ``` Это должно вернуть JSON-ответ, содержащий `id` (например, `"id": "0xb55b3bc7aac2563d5bf08ce8a177a38b5a40254bfa7ee8f9c52debbb176d44b0"`), который идентифицирует это предложение хранилища. > Чтобы сделать JSON-ответы более читаемыми, вы можете попробовать > утилиту форматирования JSON [jq](https://jqlang.github.io/jq/) > просто добавив `| jq` после команды. > На macOS вы можете установить с помощью `brew install jq`. Чтобы проверить текущие предложения хранилища для этого узла, вы можете выполнить: ```bash curl 'http://localhost:8000/api/codex/v1/sales/availability' ``` или с `jq`: ```bash curl 'http://localhost:8000/api/codex/v1/sales/availability' | jq ``` Это должно вывести список предложений, с тем, который вы только что создали, среди них (для нашего руководства, в это время будет возвращено только одно предложение). ### 4.2. Покупка хранилища Прежде чем мы сможем купить хранилище, у нас должны быть некоторые фактические данные для запроса хранилища. Начните с загрузки небольшого файла на ваш клиентский узел. В Linux (или macOS) вы могли бы, например, использовать `dd` для генерации файла размером $1M$: ```bash dd if=/dev/urandom of=./data.bin bs=1M count=1 ``` Предполагая, что ваш файл называется `data.bin`, вы можете загрузить его с помощью: ```bash curl --request POST http://localhost:8001/api/codex/v1/data --header 'Content-Type: application/octet-stream' --write-out '\n' -T ./data.bin ``` После завершения загрузки вы должны увидеть _Content Identifier_, или _CID_ (например, `zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj`) для загруженного файла, выведенный в терминал. Используйте этот CID в запросе на покупку: ```bash # убедитесь, что заменили CID перед с CID, который вы получили на предыдущем шаге 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' ``` Параметры под `--data` говорят, что: 1. мы хотим купить хранилище для нашего файла на $5$ минут (`"duration": "600"`); 2. мы готовы платить до $1$ токена за слот в секунду (`"reward": "1"`) 3. наш файл будет разделен на три части (`"nodes": 3`). Поскольку мы установили `"tolerance": 1`, нам нужно только две части (`nodes - tolerance`) для восстановления файла; т.е. мы можем допустить, что максимум один узел перестанет хранить наши данные; либо из-за сбоя, либо по другим причинам; 4. мы требуем `1000` токенов в качестве залога от поставщиков хранилища для каждой части. Поскольку есть $3$ такие части, всего будет `3000` залога, зафиксированного поставщиком(ами) хранилища, как только наш запрос будет начат. 5. наконец, `expiry` устанавливает временной лимит для заполнения всех слотов поставщиком(ами) хранилища. Если слоты не заполнены к моменту `expire`, запрос истечет и завершится неудачей. ### 4.3. Отслеживание ваших запросов на хранилище POST-запрос на хранилище сделает его доступным на рынке хранилища, и узел хранилища в конечном итоге подберет его. Вы можете опрашивать статус вашего запроса с помощью: ```bash export STORAGE_PURCHASE_ID="1d0ec5261e3364f8b9d1cf70324d70af21a9b5dccba380b24eb68b4762249185" curl "http://localhost:8001/api/codex/v1/storage/purchases/${STORAGE_PURCHASE_ID}" ``` Например: ```bash > curl 'http://localhost:8001/api/codex/v1/storage/purchases/6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d' ``` Это возвращает результат типа: ```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 } ``` Показывает, что запрос был отправлен, но еще не заполнен. Ваш запрос будет успешным, как только `"state"` покажет `"started"`. Все, что отличается от этого, означает, что запрос еще не полностью обработан, а состояние `"error"` отличное от `null` означает, что он завершился неудачей. Ну что, это было довольно долгое путешествие, не так ли? Вы можете поздравить себя с успешным завершением руководства по маркетплейсу codex! [^1]: Файлы Codex разделяются на части, называемые "слотами", и распределяются по различным поставщикам хранилища. Залог относится к одному такому слоту, и будет медленно уменьшаться по мере того, как поставщик хранилища не сможет предоставить своевременные доказательства, но фактическая логика [более сложна, чем это](https://github.com/codex-storage/codex-contracts-eth/blob/6c9f797f408608958714024b9055fcc330e3842f/contracts/Marketplace.sol#L209).