11. Развертывание в devnet

Перевод | Автор оригинала: Loris

ЭПИЗОД 11

1 МЕСЯЦ НАЗАД

ЧТЕНИЕ 14 МИН.

До сих пор в этой серии мы разрабатывали наше децентрализованное приложение (dApp) только локально на нашей машине. Теперь, когда наше децентрализованное приложение готово, давайте узнаем, как его развернуть, чтобы каждый мог извлечь из него пользу.

В Солане есть несколько кластеров, в которые мы можем развернуться. Основной, который все используют с реальными деньгами, называется «mainnet». Другой, называемый «devnet», можно использовать для тестирования нашей программы на реальном кластере, использующем фальшивые деньги.

При развертывании dApps обычно сначала выполняется развертывание в кластере «devnet», как на промежуточном сервере, а затем, когда вас все устраивает, развертывание в кластере «mainnet» аналогично производственному серверу.

В этом эпизоде мы узнаем, как выполнить развертывание в кластере devnet. Тем не менее, развертывание в основной сети — очень похожий процесс, поэтому вы также сможете это сделать к концу этого эпизода. Кроме того, я обязательно добавлю примечание каждый раз, когда между ними будет небольшая разница.

Хорошо, давайте сделаем это!

Изменение кластера

Первое, что нужно сделать, это изменить наш кластер с локального хоста на devnet. Нам нужно сделать это в двух местах: в терминале и в настройках нашей программы.

Первое легко сделать. Нам просто нужно запустить эту команду в нашем терминале, чтобы сообщить Солане, что мы хотим использовать кластер devnet.

solana config set --url devnet

# Outputs:
# Config File: /Users/loris/.config/solana/cli/config.yml
# RPC URL: https://api.devnet.solana.com
# WebSocket URL: wss://api.devnet.solana.com/ (computed)
# Keypair Path: /Users/loris/.config/solana/id.json
# Commitment: confirmed

Для основной сети вместо этого запустите solana config set --url mainnet-beta.

С этого момента любая команда solana, которую мы запускаем в нашем терминале, будет выполняться в кластере devnet. Это включает в себя «аирдроп в солане», «развертывание в солане» и т. д.

Это означает, что нам больше не нужно запускать локальный реестр на нашем компьютере — используя solana-test-validator или anchor localnet — для взаимодействия с блокчейном.

Следующее место, где нам нужно изменить наш кластер, находится в файле Anchor.toml нашей программы.

Если вы заглянете внутрь этого файла, вы должны увидеть следующее.

[programs.localnet]
solana_twitter = "2BDbYV1ocs2S1PsYnd5c5mqtdLWGf5VbCYvf28rs9LGj"

# ...

[provider]
cluster = "localnet"
wallet = "/Users/loris/.config/solana/id.json"

Важно отметить, что идентификатор программы предоставляется в контексте кластера — в данном случае кластера локальной сети. Это связано с тем, что одна и та же программа может быть развернута по разным адресам в зависимости от кластера. Например, вы можете использовать другой идентификатор программы для кластера основной сети, чтобы только несколько членов с ограниченным доступом имели право на развертывание в основной сети.

Но подождите минутку, этот идентификатор программы общедоступен, верно?

Это правда! Идентификатор программы является общедоступным, но его пара ключей находится в папке target/deploy, и Anchor использует соглашение об именах, чтобы найти его. Если ваша программа называется solana_twitter, она попытается найти пару ключей, расположенную в target/deploy/solana_twitter-keypair.json. Если этот файл не может быть найден при развертывании вашей программы, будет сгенерирована новая пара ключей, дающая нам новый идентификатор программы. Именно поэтому нам пришлось обновить идентификатор программы после самого первого развертывания.

Хотя раньше мы не обращали особого внимания на этот файл target/deploy/solana_twitter-keypair.json, важно признать, что этот файл является доказательством того, что вы являетесь владельцем программы по этому адресу. Если вы развернетесь в основной сети, используя эту пару ключей, и кто-то другой получит ее, этот человек сможет развернуть любые изменения, которые он захочет, в вашу программу.

В нашем случае мы упростим задачу и будем использовать одну и ту же пару ключей для всех кластеров, но я бы рекомендовал использовать другую пару ключей, по крайней мере, для кластера основной сети.

[programs.localnet]
solana_twitter = "2BDbYV1ocs2S1PsYnd5c5mqtdLWGf5VbCYvf28rs9LGj"

[programs.devnet]
solana_twitter = "2BDbYV1ocs2S1PsYnd5c5mqtdLWGf5VbCYvf28rs9LGj"

[programs.mainnet]
solana_twitter = "2BDbYV1ocs2S1PsYnd5c5mqtdLWGf5VbCYvf28rs9LGj"

# ...

[provider]
cluster = "devnet"
wallet = "/Users/loris/.config/solana/id.json"

Как вы можете видеть в приведенном выше коде, мы дважды продублировали раздел [programs.localnet]. Один раз для [programs.devnet] и один раз для [programs.mainnet]. Затем мы обновили cluster до devnet в разделе [provider].

🚀 Для основной сети просто установите cluster на mainnet.

И это все! Теперь мы в кластере devnet.

Аирдроп на devnet

Прежде чем мы сможем развернуть нашу программу в devnet, нам нужно получить немного денег на этот кластер.

Если вы помните, в прошлом мы использовали команду solana airdrop, чтобы получить немного денег в нашем локальном кластере. Что ж, мы можем сделать то же самое в кластере devnet, за исключением того, что команда имеет ограничение около 5 SOL.

Итак, давайте дадим себе несколько SOL в devnet. Мы сделаем это для обоих наших кошельков: того, который мы используем на нашей локальной машине для развертывания программы, и нашего «настоящего» кошелька, который мы используем в браузере для совершения транзакций.

В первом случае нам не нужно указывать адрес, потому что он находится в ~/.config/solana/id.json, который является местом по умолчанию для поиска пары ключей вашего компьютера. Поэтому мы можем запустить следующее.

solana airdrop 5

Обратите внимание, что если нам нужно более 5 SOL, мы можем запустить эту команду еще раз. Просто мы не можем получить слишком много SOL за раз.

Если вы получаете следующую ошибку «Ошибка: невозможно подтвердить транзакцию. Это может произойти в таких ситуациях, как истечение срока действия транзакции и недостаточное количество средств плательщика. Часто это означает, что кран devnet исчерпан, и вам следует повторить попытку чуть позже. Вы также можете попробовать запросить меньше SOL и посмотреть, сработает ли это. Не стесняйтесь проверять раздор Соланы, чтобы получать обновления, когда он пополняется.

Для другого кошелька — то есть нашего «настоящего» кошелька — мы можем запустить ту же команду, но нам нужно указать его адрес в качестве второго аргумента. Для меня это выглядит так.

solana airdrop 5 B1AfN7AgpMyctfFbjmvRAvE1yziZFDb9XCwydBjJwtRN

Теперь мы готовы к развертыванию!

Для основной сети мы не можем использовать команду airdrop, так как мы работаем с реальными деньгами. Поэтому вам нужно будет перевести немного денег на кошелек вашего локального компьютера из вашего реального кошелька. В качестве альтернативы вы можете импортировать свой реальный кошелек локально на свой компьютер и использовать его при развертывании в основной сети, указав путь к его паре ключей в опции wallet файла конфигурации Anchor.toml.

Развертывание программы

Давайте, наконец, развернем нашу программу в devnet! Как обычно, мы будем использовать команду anchor deploy, теперь, когда Anchor знает, в какой кластер выполнять развертывание. Хотя в этом нет необходимости, мне всегда нравится запускать сборку привязки перед развертыванием, чтобы убедиться, что я развертываю последнюю версию своего кода.

anchor build
anchor deploy

Поздравляем! Ваш код теперь доступен в кластере devnet для всех!

Теперь давайте обновим наш интерфейс, чтобы мы могли взаимодействовать с нашей программой в кластере devnet, а не в локальном.

Для этого нам нужно изменить URL-адрес кластера внутри составного файла useWorkspace.

Замените URL-адрес локального хоста следующим, и, бум, наш интерфейс теперь также использует кластер devnet!

const connection = new Connection('https://api.devnet.solana.com', commitment)

На этом этапе вы должны иметь возможность отправлять и читать твиты из кластера devnet с помощью приложений VueJS.

Теперь все, что осталось сделать, это развернуть наш интерфейс где-нибудь на сервере, чтобы другие пользователи тоже могли взаимодействовать с ним. Но сначала поговорим о затратах.

Стоимость развертывания

Ранее мы запустили команду для сброса 5 SOL в кошелек нашего локального компьютера, чтобы у него было достаточно денег для развертывания нашей программы. Посмотрим, во сколько нам это обошлось. Мы можем запустить следующую команду, чтобы получить доступ к балансу нашего локального кошелька.

solana balance

# Outputs:
# 2.361026

Теперь у нас есть 2,361026 SOL, что означает, что развертывание обошлось нам в 2,638974 SOL. На момент написания статьи это около 450 долларов.

К счастью для нас, мы развернули в devnet, где мы можем раздать себе немного денег, но если мы хотим развернуть это в кластере основной сети, нам нужно будет заплатить это из собственного кармана.

Так почему же это стоит так дорого и должны ли мы платить такие деньги каждый раз, когда развертываем?

Причина, по которой это стоит так дорого, заключается в том, что, как и при создании учетных записей, нам нужно выделить некоторое хранилище в блокчейне для хранения кода нашей программы. После компиляции наш код требует довольно много байтов, что означает, что сумма без арендной платы обычно довольно высока. Кроме того, Solana по умолчанию выделяет вдвое больше места, чем необходимо для хранения кода. Почему это происходит? Потому что наша программа, вероятно, получит обновления в будущем, и она пытается учесть тот факт, что при следующем развертывании нам может понадобиться больше места.

При необходимости мы можем изменить это, явно указав Солане, сколько места мы хотим выделить для нашей программы.

Таким образом, развертывание в кластере в первый раз является дорогостоящей транзакцией из-за первоначальных денег, не облагаемых арендной платой, но впоследствии повторное развертывание не должно стоить практически ничего — т. е. цена транзакции — потому что мы уже заплатили за наше хранилище.

Хорошо, теперь, когда мы знаем больше об экономике развертывания, давайте вернемся к развертыванию внешнего интерфейса нашего приложения.

Копирование IDL-файла

Если вы заглянете внутрь составного файла useWorkspace, то увидите, что мы импортируем файл IDL, сгенерированный Anchor, используя относительный путь, который находится за пределами каталога app, содержащего наше приложение VueJS.

import idl from '../../../target/idl/solana_twitter.json'

Это работает на нашей машине, потому что мы создали приложение локально, и поэтому целевая папка была создана правильно. Однако при развертывании нашего интерфейса на сервере у него не будет доступа к этому target репозиторию. Поэтому нам нужно скопировать и вставить сгенерированный IDL куда-нибудь в нашу папку app.

Чтобы сделать нашу жизнь немного проще, давайте добавим собственный скрипт copy-idl в наш файл Anchor.toml.

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/**/*.ts"
copy-idl = "mkdir -p app/src/idl && cp target/idl/solana_twitter.json app/src/idl/solana_twitter.json"

Первая часть скрипта гарантирует, что папка app/src/idl существует, а вторая часть копирует в нее IDL-файл.

Теперь каждый раз, когда мы хотим скопировать этот IDL в наш интерфейс, все, что нам нужно сделать, это запустить следующую команду. Давайте сделаем это сейчас, чтобы мы могли получить доступ к файлу IDL в нашем интерфейсе.

anchor run copy-idl

Наконец, нам нужно обновить путь импорта нашего IDL-файла внутри компонуемого useWorkspace, чтобы он указывал на новый IDL-путь.

import idl from '@/idl/solana_twitter.json'

Обратите внимание, что начиная с Anchor v0.19.0 появилась новая функция, которая позволяет нам указать пользовательский каталог, в который следует копировать файл IDL каждый раз, когда мы запускаем anchor build.

[workspace]
types = "app/src/idl/"

Звучит идеально, но на момент написания статьи он не копировал идентификатор программы внутри IDL, который нам нужен для нашего рабочего пространства. Поэтому я решил использовать традиционный сценарий копирования, но при этом следить за этой функцией, так как я уверен, что она будет продолжать улучшаться.

Несколько сред в интерфейсе

В настоящее время нам приходится вручную обновлять URL-адрес кластера внутри составного файла useWorkspace каждый раз, когда мы хотим переключать кластеры.

Было бы намного лучше, если бы это можно было установить динамически. Одним из способов добиться этого было бы небольшое раскрывающееся меню в приложении, где пользователи могут выбрать свой кластер. Однако я предпочитаю иметь один явный кластер, определенный для каждой среды, в которой выполняется развертывание. Например, solana-twitter.com будет использовать кластер основной сети, тогда как devnet.solana-twitter.com будет использовать кластер devnet.

К счастью, приложения VueJS поддерживают несколько сред с помощью функции «режим».

Вот как это работает.

  1. Мы создадим несколько файлов среды внутри нашего каталога app: .env для локальных переменных, .env.devnet для переменных devnet и .env.mainnet для переменных основной сети.
  2. При компиляции нашего внешнего интерфейса мы можем указать VueJS, какой режим мы компилируем, и он будет использовать файл среды, соответствующий этому режиму. Это откат к использованию .env, поэтому мы используем его локально.
  3. Любые переменные, начинающиеся с VUE_APP_ внутри этих файлов окружения, будут автоматически внедрены в process.env нашего внешнего интерфейса.
  4. Мы можем получить доступ к этим переменным внутри useWorkspace, компонуемого для динамического предоставления URL-адреса кластера.
  5. Наконец, мы добавим еще несколько скриптов в наш файл package.json, чтобы помочь нам скомпилировать внешний интерфейс для всех различных режимов.

Хорошо, давайте это реализуем.

(1) Начните с добавления следующих файлов в каталог app и скопируйте/вставьте их содержимое. Мы определим только одну переменную, которая предоставляет URL-адрес каждого кластера.

(4) Затем обновите компоновку useWorkspace, чтобы использовать эту переменную.

// ...

const clusterUrl = process.env.VUE_APP_CLUSTER_URL
const preflightCommitment = 'processed'
const commitment = 'processed'
const programID = new PublicKey(idl.metadata.address)
const workspaceSymbol = Symbol()

export const useWorkspace = () => inject(workspaceSymbol)

export const initWorkspace = () => {
    const wallet = useAnchorWallet()
    const connection = new Connection(clusterUrl, commitment)
    // ...
}

(5) Наконец, добавьте следующие сценарии в файл app/package.json.

"scripts": {
  "serve": "vue-cli-service serve",
  "serve:devnet": "vue-cli-service serve --mode devnet",
  "serve:mainnet": "vue-cli-service serve --mode mainnet",
  "build": "vue-cli-service build",
  "build:devnet": "vue-cli-service build --mode devnet",
  "build:mainnet": "vue-cli-service build --mode mainnet",
  "lint": "vue-cli-service lint"
},

Сделано! Теперь мы можем создать наш интерфейс для devnet, используя npm run build:devnet, и он автоматически узнает, что нужно использовать URL-адрес кластера devnet.

Обратите внимание, что если у вас в данный момент запущен npm run serve на терминале, вам нужно будет выйти из него (Ctrl+C) и вместо этого запустить npn run serve:devnet, чтобы он использовал правильный URL-адрес кластера.

Развертывание интерфейса

Хорошо, теперь пришло время представить наше внешнее приложение миру. Существует миллион вариантов развертывания внешнего приложения, так что не стесняйтесь использовать тот метод, который вы предпочитаете.

В моем случае я привык развертывать приложения только для внешнего интерфейса с помощью Netlify, потому что это бесплатно и, честно говоря, довольно удивительно.

Все, что вам нужно сделать, это разместить свой код где-нибудь в репозитории, и он запросит у вас команду для запуска и каталог для обслуживания. Красиво и просто, как и должно быть.

Обратите внимание, что перед развертыванием я добавил небольшую фавиконку и обновил метаданные файла index.html в папке app/public. Не стесняйтесь загружать и извлекать следующий ZIP-файл в ваш «общедоступный» каталог, если вы хотите сделать то же самое.

Загрузить ZIP-файл

Затем в своей учетной записи Netlify я добавил новый сайт и предоставил следующие параметры.

Screenshot of the Netlify app when creating a new site with the settings mentioned above.

И это все. Наш внешний интерфейс был развернут на случайном поддомене netlify.app. Вы можете подключить свое собственное доменное имя бесплатно или, как и я, просто используете это как учебный проект, вы можете обновить субдомен Netlify до чего-то более приятного. В моем случае я использовал solana-twitter.netlify.app.

Screenshot of the Netlify app where I’m updating the Netlify subdomain of the site we created.

Еще одна приятная особенность Netlify заключается в том, что она автоматически запускает новое развертывание каждый раз, когда вы отправляете фиксацию в выбранную вами ветку, которая по умолчанию имеет значение «main».

Готово! Теперь вы можете поделиться своим URL-адресом со всеми своими друзьями и начать твитить в Солане!

Solana Twitter URL

Бонус: опубликуйте свой IDL

Это необязательный шаг, но полезно знать, что вы можете опубликовать свой IDL-файл в блокчейне. Это позволяет другим инструментам в экосистеме Solana распознавать вашу программу и понимать, что она может предложить.

Вот пример с исследователем Solana, который я создаю. Несмотря на то, что проводник ничего не знает о нашей программе, он может получить файл IDL и соответствующим образом декодировать учетную запись Tweet, чтобы показать некоторую ценную информацию.

Screenshot of an explorer app that displays all the information of a Tweet after having given its public key.

Чтобы опубликовать файл IDL, все, что вам нужно сделать, это запустить в терминале следующее.

anchor idl init <programId> -f <target/idl/program.json>

И если ваша программа изменится в будущем, вы можете обновить опубликованный IDL, запустив:

anchor idl upgrade <programId> -f <target/idl/program.json>

Цикл разработки

Прежде чем я покину вас, давайте кратко рассмотрим цикл разработки, который мы использовали при создании и развертывании dApp в Солане. Я дам это вам как код, потому что, будем честными, это лучшее, что можно прочитать.

# Make sure you’re on the localnet.
solana config set --url localhost
# And check your Anchor.toml file.

# Code…

# Run the tests.
anchor test

# Build, deploy and start a local ledger.
anchor localnet
# Or
solana-test-validator
anchor build
anchor deploy

# Copy the new IDL to the frontend.
anchor run copy-idl

# Serve your frontend application locally.
npm run serve

# Switch to the devnet cluster to deploy there.
solana config set --url devnet
# And update your Anchor.toml file.

# Airdrop yourself some money if necessary.
solana airdrop 5

# Build and deploy to devnet.
anchor build
anchor deploy

# Push your code to the main branch to auto-deploy on Netlify.
git push

Вывод

Мы сделали это! Вы можете поздравить себя с завершением этой серии, потому что это, безусловно, было трудным путешествием. 💪

Я надеюсь, что вы многому научились на этом пути и, надеюсь, достаточно, чтобы вы могли начать разрабатывать больше dApps. Если вы это сделаете, я хотел бы услышать о том, что вы строите! Ничто не сделало бы меня более счастливым, чем видеть, как эта серия статей помогает другим создавать удивительные вещи.

Если есть что-то еще, что вы хотели бы узнать о разработке Solana, не стесняйтесь обращаться к нам. Я планирую добавить больше бонусных эпизодов в эту серию в будущем и сделать их «только спонсорами GitHub», чтобы они могли немного помочь мне финансово.

Кроме того, я планирую бесплатно добавлять в свой блог больше общих статей о Солане, так что не стесняйтесь подписываться на меня в Твиттере, чтобы получать обновления.

Как обычно, вы можете найти репозиторий этого эпизода на GitHub и сравнить его код с предыдущим эпизодом.

Просмотреть эпизод 11 на GitHub

Сравните с Эпизодом 10

До новых встреч в Солане! 😘