Запуск локальной сети Codex с поддержкой маркетплейса
Это руководство научит вас, как запустить небольшую сеть Codex с включенным маркетплейсом хранения; т.е. функциональностью в Codex, которая позволяет участникам предлагать и покупать хранилище на рынке, обеспечивая честное выполнение обязательств поставщиками хранилища с помощью криптографических доказательств.
В этом руководстве вы:
- Настроите сеть Geth PoA;
- Настроите маркетплейс;
- Запустите Codex;
- Купите и продайте хранилище на маркетплейсе.
Предварительные требования
Для прохождения этого руководства вам понадобится:
- клиент Ethereum geth; Вам нужна версия
1.13.x
geth, так как более новые версии больше не поддерживают Proof of Authority (PoA). Это руководство было протестировано с версией geth1.13.15
. - бинарный файл Codex, который можно собрать из исходного кода.
Мы также будем использовать синтаксис bash на протяжении всего руководства. Если вы используете другую оболочку, вам может потребоваться адаптировать команды под вашу платформу.
Для начала создайте новую папку, где мы будем хранить файлы, связанные с руководством, чтобы держать их отдельно от репозитория codex. Предположим, что имя папки будет marketplace-tutorial
.
1. Настройка сети Geth PoA
Для этого руководства мы будем использовать простую Proof-of-Authority сеть с geth. Первым шагом является создание учетной записи подписанта: учетной записи, которая будет использоваться geth для подписи блоков в сети. Любой блок, подписанный подписантом, принимается как действительный.
1.1. Создание учетной записи подписанта
Чтобы создать учетную запись подписанта, из директории marketplace-tutorial
выполните:
geth account new --datadir geth-data
Генератор учетных записей попросит вас ввести пароль, который вы можете оставить пустым. Затем он выведет некоторую информацию, включая публичный адрес учетной записи:
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
. У вас будет выведен другой адрес. Сохраните его для дальнейшего использования.
Затем установите переменную окружения для дальнейшего использования:
export GETH_SIGNER_ADDR="0x0000000000000000000000000000000000000000"
echo ${GETH_SIGNER_ADDR} > geth_signer_address.txt
Здесь убедитесь, что вы заменили
0x0000000000000000000000000000000000000000
на ваш публичный адрес учетной записи подписанта (0x33A904Ad57D0E2CB8ffe347D3C0E83C2e875E7dB
в нашем примере).
1.2. Настройка сети и создание генезис-блока
Следующий шаг - указать geth, какую сеть вы хотите запустить. Мы будем запускать pre-merge сеть с консенсусом Proof-of-Authority. Чтобы это работало, создайте файл network.json
.
Если вы установили переменную GETH_SIGNER_ADDR
выше, вы можете выполнить следующую команду для создания файла network.json
:
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
Вы также можете создать файл вручную, не забыв обновить его своим публичным адресом подписанта:
{
"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.
После создания network.json
вы можете инициализировать сеть с помощью:
geth init --datadir geth-data network.json
В выводе этой команды могут быть предупреждения, например:
WARN [08-21|14:48:12.305] Unknown config environment variable envvar=GETH_SIGNER_ADDR
или даже ошибки при первом запуске команды:
ERROR[08-21|14:48:12.399] Head block is not reachable
Важно, что в конце вы должны увидеть что-то похожее на:
INFO [08-21|14:48:12.639] Successfully wrote genesis state database=lightchaindata hash=768bf1..42d06a
1.3. Запуск вашего PoA узла
Теперь мы готовы запустить нашу GETH_SIGNER_ADDR
. Для удобства используйте geth_signer_address.txt
:
export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)
Имея установленную переменную GETH_SIGNER_ADDR
, выполните:
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, снова появляется как в --unlock
, так и в --allow-insecure-unlock
.
Geth попросит вас ввести пароль учетной записи при запуске. После этого он должен запуститься и начать "майнить" блоки.
Здесь также могут возникнуть ошибки, например:
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
Их можно безопасно игнорировать.
Если команда выше завершается с ошибкой:
Fatal: Failed to register the Ethereum service: only PoS networks are supported, please transition old ones with Geth v1.13.x
убедитесь, что вы используете правильную версию Geth (см. раздел Предварительные требования)
2. Настройка маркетплейса
Для этого раздела вам нужно открыть новый терминал, и geth должен быть уже запущен. Настройка маркетплейса Codex включает:
- Развертывание контрактов маркетплейса Codex в нашу приватную блокчейн-сеть
- Настройку учетных записей Ethereum, которые мы будем использовать для покупки и продажи хранилища в маркетплейсе Codex
- Обеспечение этих учетных записей необходимыми балансами токенов
2.1. Развертывание контрактов маркетплейса Codex
Убедитесь, что вы вышли из директории marketplace-tutorial
и клонируйте codex-storage/nim-codex.git
:
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
Таким образом, структура директорий для целей этого руководства выглядит так:
|
|-- nim-codex
└-- marketplace-tutorial
Вы можете клонировать
codex-storage/nim-codex.git
в другое место.
Теперь из папки nim-codex
выполните:
make update && make
Это может занять некоторое время, так как это также соберет компилятор
nim
. Будьте терпеливы.
Теперь, чтобы запустить локальную сеть Ethereum, выполните:
cd vendor/codex-contracts-eth
npm install
При написании документа мы использовали
node
версииv20.17.0
иnpm
версии10.8.2
.
Прежде чем продолжить, вы должны дождаться, пока будет добыто marketplace-tutorial
:
geth attach --exec web3.eth.blockNumber ./geth-data/geth.ipc
как только это превысит
Чтобы развернуть контракты, из директории codex-contracts-eth
выполните:
export DISTTEST_NETWORK_URL=http://localhost:8545
npx hardhat --network codexdisttestnetwork deploy
Если команда завершится успешно, вы увидите вывод, похожий на этот:
Deployed Marketplace with Groth16 Verifier at:
0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd
конечно, ваш адрес будет другим.
Теперь вы готовы подготовить учетные записи.
2.2. Генерация необходимых учетных записей
Мы будем запускать
Сначала убедитесь, что вы вернулись в папку marketplace-tutorial
и не находитесь в подпапке codex-contracts-eth
. Затем установите эти переменные:
Хранилище:
export ETH_STORAGE_ADDR=0x45BC5ca0fbdD9F920Edd12B90908448C30F32a37
export ETH_STORAGE_PK=0x06c7ac11d4ee1d0ccb53811b71802fa92d40a5a174afad9f2cb44f93498322c3
echo $ETH_STORAGE_PK > storage.pkey && chmod 0600 storage.pkey
Клиент:
export ETH_CLIENT_ADDR=0x9F0C62Fe60b22301751d6cDe1175526b9280b965
export ETH_CLIENT_PK=0x5538ec03c956cb9d0bee02a25b600b0225f1347da4071d0fd70c521fdc63c2fc
echo $ETH_CLIENT_PK > client.pkey && chmod 0600 client.pkey
2.3. Обеспечение учетных записей токенами
Теперь нам нужно перевести немного ETH на каждую из учетных записей, а также предоставить им некоторые токены Codex для использования узлом хранилища в качестве залога и для клиентского узла для покупки фактического хранилища.
Хотя процесс не особенно сложен, я предлагаю вам использовать скрипт, который мы подготовили для этого. Этот скрипт, по сути:
- читает адрес контракта маркетплейса и его ABI из данных развертывания;
- переводит
ETH с учетной записи подписанта на целевую учетную запись, если целевая учетная запись не имеет баланса ETH; - чеканит
токенов Codex и добавляет их в баланс целевой учетной записи.
Чтобы использовать скрипт, просто скачайте его в локальный файл с именем mint-tokens.js
, например, используя curl
(убедитесь, что вы находитесь в директории marketplace-tutorial
):
# скачать скрипт
curl https://raw.githubusercontent.com/gmega/codex-local-bare/main/scripts/mint-tokens.js -o mint-tokens.js
Затем выполните:
# установить расположение файла контракта (мы предполагаем, что вы находитесь в директории 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
Если вы получите сообщение типа
Usage: mint-tokens.js <token-hardhat-deploy-json> <signer-account> <receiver-account> <token-ammount>
то вам нужно убедиться, что вы предоставили все необходимые аргументы. В частности, вам нужно убедиться, что переменная окружения GETH_SIGNER_ADDR
содержит адрес подписанта (мы использовали export GETH_SIGNER_ADDR=$(cat geth_signer_address.txt)
выше, чтобы убедиться, что она установлена).
3. Запуск Codex
С учетными записями и geth на месте, мы теперь можем запустить узлы Codex.
3.1. Узел хранилища
Узел хранилища будет тем, который хранит данные и отправляет доказательства хранения в цепочку. Для этого ему нужен доступ к:
- адресу контракта маркетплейса, который был развернут в локальном узле geth в Шаге 2.1;
- образцам файлов церемонии, которые поставляются в репозитории контрактов Codex (
nim-codex/vendor/codex-contracts-eth
).
Адрес контракта маркетплейса. Адрес контракта можно найти внутри файла nim-codex/vendor/codex-contracts-eth/deployments/codexdisttestnetwork/Marketplace.json
. Мы захватили это расположение выше в переменной CONTRACT_DEPLOY_FULL
, поэтому из папки marketplace-tutorial
просто выполните:
grep '"address":' ${CONTRACT_DEPLOY_FULL}/Marketplace.json
что должно вывести что-то вроде:
"address": "0xCf0df6C52B02201F78E8490B6D6fFf5A82fC7BCd",
Этот адрес должен соответствовать адресу, который мы получили ранее при развертывании контракта маркетплейса выше.
Затем выполните следующее с правильным адресом маркетплейса:
export MARKETPLACE_ADDRESS="0x0000000000000000000000000000000000000000"
echo ${MARKETPLACE_ADDRESS} > marketplace_address.txt
где вы заменяете 0x0000000000000000000000000000000000000000
на адрес контракта маркетплейса выше в Шаге 2.1.
Файлы церемонии провайдера. Файлы церемонии находятся в подкаталоге 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
):
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
выше. Здесь мы убеждаемся, что это абсолютный путь.
Чтобы запустить узел хранилища, выполните:
${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, выполните следующий вызов:
curl -H 'Accept: text/plain' 'http://localhost:8000/api/codex/v1/spr' --write-out '\n'
Вы должны получить SPR, начинающийся с spr:
.
Прежде чем продолжить, откройте новый терминал и войдите в директорию marketplace-tutorial
.
Затем установите эти пути в переменные окружения:
# установить 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)
и затем выполните:
${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 Продажа хранилища
Следующий запрос заставит узел хранилища выставить
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 просто добавив
| jq
после команды. На macOS вы можете установить с помощьюbrew install jq
.
Чтобы проверить текущие предложения хранилища для этого узла, вы можете выполнить:
curl 'http://localhost:8000/api/codex/v1/sales/availability'
или с jq
:
curl 'http://localhost:8000/api/codex/v1/sales/availability' | jq
Это должно вывести список предложений, с тем, который вы только что создали, среди них (для нашего руководства, в это время будет возвращено только одно предложение).
4.2. Покупка хранилища
Прежде чем мы сможем купить хранилище, у нас должны быть некоторые фактические данные для запроса хранилища. Начните с загрузки небольшого файла на ваш клиентский узел. В Linux (или macOS) вы могли бы, например, использовать dd
для генерации файла размером
dd if=/dev/urandom of=./data.bin bs=1M count=1
Предполагая, что ваш файл называется data.bin
, вы можете загрузить его с помощью:
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 в запросе на покупку:
# убедитесь, что заменили CID перед с CID, который вы получили на предыдущем шаге
export CID=zDvZRwzm2mK7tvDzKScRLapqGdgNTLyyEBvx1TQY37J2CdWdS6Sj
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
говорят, что:
- мы хотим купить хранилище для нашего файла на
минут ( "duration": "600"
); - мы готовы платить до
токена за слот в секунду ( "reward": "1"
) - наш файл будет разделен на три части (
"nodes": 3
). Поскольку мы установили"tolerance": 1
, нам нужно только две части (nodes - tolerance
) для восстановления файла; т.е. мы можем допустить, что максимум один узел перестанет хранить наши данные; либо из-за сбоя, либо по другим причинам; - мы требуем
1000
токенов в качестве залога от поставщиков хранилища для каждой части. Поскольку естьтакие части, всего будет 3000
залога, зафиксированного поставщиком(ами) хранилища, как только наш запрос будет начат. - наконец,
expiry
устанавливает временной лимит для заполнения всех слотов поставщиком(ами) хранилища. Если слоты не заполнены к моментуexpire
, запрос истечет и завершится неудачей.
4.3. Отслеживание ваших запросов на хранилище
POST-запрос на хранилище сделает его доступным на рынке хранилища, и узел хранилища в конечном итоге подберет его.
Вы можете опрашивать статус вашего запроса с помощью:
export STORAGE_PURCHASE_ID="1d0ec5261e3364f8b9d1cf70324d70af21a9b5dccba380b24eb68b4762249185"
curl "http://localhost:8001/api/codex/v1/storage/purchases/${STORAGE_PURCHASE_ID}"
Например:
> curl 'http://localhost:8001/api/codex/v1/storage/purchases/6c698cd0ad71c41982f83097d6fa75beb582924e08a658357a1cd4d7a2a6766d'
Это возвращает результат типа:
{
"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!
Файлы Codex разделяются на части, называемые "слотами", и распределяются по различным поставщикам хранилища. Залог относится к одному такому слоту, и будет медленно уменьшаться по мере того, как поставщик хранилища не сможет предоставить своевременные доказательства, но фактическая логика более сложна, чем это. ↩︎