Перейти к основному содержимому
02БЛОКЧЕЙН · ГЛАВА 02Первая сеть на Waves Enterprise

Первая сеть на Waves Enterprise

Прочитано 0%Квизы 0/5Тренажёры 0/10Уровень 1 · Новичок

Зачем это нужно

В прошлой главе весь блокчейн существовал только на бумаге: хеш, блок, транзакция, консенсус — понятия, которые мы разбирали словами и настоящими, но вручную посчитанными хешами. Пора сделать следующий шаг и увидеть, как всё это устроено внутри настоящей приватной, разрешённой (permissioned) платформы — Waves Enterprise, той самой из сравнения в конце прошлой главы.

Эта глава — про запуск локальной сети из трёх нод у себя на компьютере через Docker, и про то, что вообще происходит, когда в такую сеть разворачивают первый смарт-контракт. Все команды здесь — настоящие терминальные и Docker-команды: в браузере их не исполнить, поэтому вместо запускаемых песочниц (playground) в этой главе — точные посимвольные разборы (SyntaxBreakdown) и тренировка печати (CodeTyping), а все сообщения об ошибках ниже — реальные, снятые вживую с настоящего Docker в этой же сессии. Ничего не считаем очевидным: если в команде есть флаг, у него есть объяснение, зачем он там стоит.

Что понадобится

Прежде чем поднимать сеть и разворачивать первый контракт, нужны два инструмента: Docker — он запускает ноды и локальный registry, — и IDE с поддержкой TypeScript для кода самого контракта. Площадка чемпионата встречается на разных ОС — Windows, macOS и Ubuntu, — поэтому у Docker ниже три отдельные официальные ссылки, а не одна общая.

Совет

Код контракта в этой главе — TypeScript, и писать его можно в любой IDE с его поддержкой. В этой главе и на площадке используется WebStorm — специализированная IDE для JS/TS от JetBrains: понимает декораторы SDK из коробки, знает package.json и npm-скрипты без дополнительной настройки. Подойдёт и VS Code — более лёгкий редактор, который даёт тот же результат после установки расширения TypeScript, но часть возможностей WebStorm в нём нужно донастраивать руками.

Что такое нода в Waves Enterprise

Нода (node) из прошлой главы — здесь не абстракция, а конкретная программа: официальный Docker-образ wavesenterprise/node, упакованный так, что для запуска не нужно ничего компилировать или ставить вручную — достаточно Docker. Каждая запущенная нода:

  • хранит собственную полную копию цепочки блоков (тот самый распределённый реестр из прошлой главы);
  • принимает транзакции и проверяет их — хеши, подписи, всё то, что мы разбирали руками;
  • обменивается новыми блоками с соседними нодами по сети;
  • открывает наружу несколько сетевых портов, через которые с ней можно разговаривать: один — REST API (обычный HTTP, через него приложения отправляют транзакции и читают состояние сети), и ещё несколько — для служебного общения нод друг с другом и с работающими на ней смарт-контрактами.
Нажми на часть выражения

Зачем именно три ноды, а не одна

Одна-единственная нода ничем не отличается от обычной базы данных на одном сервере: если она единственный источник правды, распределённость из прошлой главы попросту не работает — некому сверять хеши и обнаруживать подмену. Минимальное число нод, на котором вообще имеет смысл понятие «согласие большинства» (consensus), — три: тогда большинство — это две ноды из трёх, и сеть продолжает работать и договариваться, даже если одна нода на время отключилась или ведёт себя неправильно. Поэтому все официальные обучающие и демонстрационные окружения Waves Enterprise по умолчанию поднимают именно три ноды — этого достаточно, чтобы вживую увидеть согласование блоков между независимыми участниками, и при этом не тратить лишние ресурсы компьютера.

WAVES ENTERPRISE · СЕТЬ ИЗ ТРЁХ НОДnode-0:6862 :6864 :6865node-1своя копия цепочкиnode-2своя копия цепочкиновый блок с node-0 разносится P2P-портом соседям; большинство — 2 из 3 — работает и при отказе одной ноды
Три ноды сети: node-0 разносит новый блок P2P-портом соседям, большинство — 2 из 3 — работает и при отказе одной ноды.

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

Квест: выключи ровно одну ноду кликом по ней и отправь транзакцию — сеть из двух оставшихся должна её подтвердить.

node-0node-1node-2
Отвечено вопросов: 0 из 3
Что открывает нода наружу, чтобы приложения могли отправлять транзакции?
Почему одной ноды недостаточно, чтобы продемонстрировать идею блокчейна?
Почему минимум для демонстрации согласия — именно три ноды, а не две?
Отвечено верно: 0 из 3

docker-compose: поднимаем сеть по шагам

Docker Compose — инструмент, который описывает несколько связанных контейнеров в одном файле docker-compose.yml и запускает их одной командой, вместо того чтобы вручную набирать docker run для каждого сервиса по отдельности. Для сети из трёх нод такой файл описывает три похожих сервиса — node-0, node-1, node-2 — каждый на основе одного и того же образа, но со своими портами и своим набором файлов конфигурации.

Шаг 1. Файл сети: три сервиса в одном docker-compose.yml

Вот как выглядит описание одной ноды внутри такого файла — фрагмент в духе официального образа Waves Enterprise.

Нажми на часть выражения
Совет

Отступы в YAML — не просто оформление, а часть синтаксиса: они показывают, что вложено во что. Сбитый хотя бы на пробел отступ ломает разбор всего файла.

Ниже — реальная ошибка, которую покажет docker compose, если один из отступов сбит хотя бы на пробел (снята вживую в этой сессии):

yaml: while parsing a block mapping at line 2, column 5: line 5, column 6: did not find expected key

Теперь собери этот фрагмент файла сам — строки перемешаны, а отступы в них уже проставлены.

Нажимай строки внизу по порядку — отступы уже проставлены, следи за вложенностью

Шаг 2. Конфиги и ключи нод — до первого запуска

Прежде чем запускать сеть, у каждой ноды должен появиться файл настроек (node.conf) и собственная пара криптографических ключей — тех самых, которыми нода будет подписывать и проверять транзакции (вспомни цифровую подпись из прошлой главы). Для этого используют отдельный, одноразовый служебный контейнер-генератор: он один раз запускается, создаёт папку с конфигами и ключами для всех трёх нод и завершает работу — сам в сети не участвует.

Вместе с конфигами генератор создаёт файл с учётными данными каждой ноды: её адрес в сети, пароль от связки ключей, ключ доступа к REST API.

Важно

Файл с учётными данными нод — как связка паролей: он должен появиться на диске один раз локально, никогда не попадать в git-репозиторий и не публиковаться никуда. Если он всё же случайно засветился — правильное действие одно: перегенерировать ключи заново, а не «просто удалить файл» — старые ключи к этому моменту уже нельзя считать секретом.

Шаг 3. Проверить активацию нужных возможностей сети

В файле настроек каждой ноды есть раздел, перечисляющий функциональные возможности (features) протокола, которые сеть поддерживает с самого первого блока — например, возможность выполнять смарт-контракты. Список активированных возможностей должен быть одинаковым во всех нодах сети одновременно, иначе ноды перестанут соглашаться друг с другом о содержимом новых блоков. Готовые шаблоны конфигурации обычно уже включают всё нужное для актуальной версии ноды — но если написан свой конфиг «с нуля» или взят шаблон от старой версии образа, стоит свериться с журналом изменений (changelog) именно той версии ноды, которую запускаешь: набор доступных возможностей и их номера меняются от версии к версии.

Шаг 4. Запуск и остановка сети

Когда файл сети и конфиги готовы, саму сеть поднимают и останавливают двумя короткими командами.

Нажми на часть выражения

Цель: скорость ≥ 100 зн/мин, точность ≥ 90%

docker compose up -d

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

Нажми на часть выражения

Цель: скорость ≥ 100 зн/мин, точность ≥ 90%

docker compose down -v

Все четыре шага вместе — это конкретный порядок команд. Выстрой его сам, от генерации конфигов до полной остановки.

Выстрой команды в порядке работы с сетью: клик по карточке внизу добавляет её сюда
Отвечено вопросов: 0 из 4
Что делает отступ в YAML-файле docker-compose.yml?
Что означает запись "6862:6862" в разделе ports?
Зачем нужен флаг -v у команды docker compose down?
Почему учётные данные, которые создаёт генератор конфигов, нельзя класть в git?
Отвечено верно: 0 из 4

Локальный Docker registry: зачем он нужен

У Waves Enterprise необычная архитектура выполнения смарт-контрактов: сама нода не выполняет код контракта внутри себя — вместо этого она запускает контракт как отдельный Docker-контейнер рядом с собой и общается с ним по сети (тем самым gRPC-портом из первого раздела). Значит, чтобы развернуть контракт, нода должна суметь скачать его образ — точно так же, как она скачивает wavesenterprise/node при первом запуске.

Для локальной сети, которая ни с кем в интернете не связана, публичный Docker Hub для собственных учебных образов не подходит — нужен свой, локальный реестр образов (registry). Он поднимается как ещё один обычный контейнер, официальный образ registry c Docker Hub:

Нажми на часть выражения
Важно

Важно не перепутать термины. В прошлой главе слово «реестр» (ledger) означало учётную книгу всех транзакций блокчейна. Docker registry — это совсем другое: обычное хранилище Docker-образов, к блокчейн-реестру прямого отношения не имеющее. Совпадение перевода — случайность, но в разговоре лучше уточнять: «блокчейн-реестр» или «Docker-реестр образов».

Первый смарт-контракт: как это устроено концептуально

Смарт-контракт (smart contract) в терминах прошлой главы — это ещё один участник сети со своим кодом, который умеет читать и менять состояние, хранящееся в блокчейне, по строго заданным правилам. В экосистеме Waves Enterprise контракты для JavaScript/TypeScript пишут с помощью специального набора инструментов (SDK) с декораторами — короткими пометками над классом и его методами, которые говорят платформе, как именно устроен контракт.

@Contract()
class ScoreCounter {
@State() state

@Var({ key: 'SCORE' })
score

@Action({ onInit: true })
init() {
this.score.set(0)
}

@Action()
increment() {
// прочитать текущее значение и увеличить на единицу
}
}

Разберём назначение декораторов — не как исполняемый код (это TypeScript, а не Kotlin, и в этой главе он не запускается), а как концептуальную схему:

  • @Contract() — помечает класс как смарт-контракт целиком: именно из него платформа соберёт Docker-образ, который позже запустит рядом с нодой.
  • @State() — доступ к общему состоянию контракта, хранящемуся в блокчейне, а не в оперативной памяти обычной программы.
  • @Var({ key: 'SCORE' }) — отдельная именованная переменная внутри состояния контракта: значение под ключом SCORE останется в блокчейне даже после перезапуска контракта.
  • @Action({ onInit: true }) — действие, которое выполнится ровно один раз, в момент создания контракта в сети; здесь удобно задавать значения по умолчанию.
  • @Action() без параметров — обычное действие контракта, которое можно вызывать сколько угодно раз после того, как контракт уже создан и работает.
SMART-КОНТРАКТ · ДЕКОРАТОРЫ SDK@Contract()class ScoreCounter {@State() state@Var({ key: 'SCORE' })score@Action({ onInit: true })init() { ... }@Action() increment() { ... }@Contract()класс целиком = контракт@State() / @Varданные хранятся в блокчейне@Action({ onInit })выполнится один раз при создании@Action()обычное действие — вызывай снова и сновадекораторы говорят платформе, как устроен контракт — это не код, а разметка над классом и методами
Декораторы SDK над классом и его методами: платформа читает их, чтобы понять, как устроен контракт.

Разворачивание контракта в сеть и последующий вызов его действий — это два разных вида транзакций, которые ноды проверяют по тем же общим правилам из прошлой главы (подпись, хеш, согласие большинства нод):

  • CreateContractTx — «создать контракт»: транзакция, которая регистрирует контракт в сети. Образ к этому моменту уже собран и загружен в registry разработчиком (обычными docker build и docker push) — в самой транзакции лежат только ссылка на образ и его хеш. Получив такую транзакцию, каждая нода скачивает образ по ссылке, сверяет хеш с записанным в блокчейне и один раз выполняет действие с onInit: true. С этого момента у контракта есть свой идентификатор (contract id) в сети.
  • CallContractTx — «вызвать контракт»: транзакция, которая называет уже существующий контракт по его идентификатору, конкретное действие (например, increment) и параметры для него. Такую транзакцию, как и обычный перевод из прошлой главы, подписывает отправитель — без действительной подписи нода её не примет.
SMART-КОНТРАКТ · CREATE И CALLпроисходит один разCreateContractTxобраз уже загружен в registrycontract id: abc123можно вызывать сколько угодно разCallContractTxincrement()SCORE: 0 → 1 → 2…Create разворачивает контракт один раз, Call вызывает его действия сколько угодно раз
CreateContractTx регистрирует контракт один раз и выдаёт id; CallContractTx вызывает его действия — сколько угодно раз после.
Отвечено вопросов: 0 из 4
Как Waves Enterprise выполняет код смарт-контракта?
Чем отличаются CreateContractTx и CallContractTx?
Что делает декоратор @Action({ onInit: true }) в примере контракта?
Зачем локальной сети из трёх нод собственный Docker registry?
Отвечено верно: 0 из 4

В WebStorm: где в проекте искать декораторы

Все декораторы из прошлого раздела живут в одном конкретном файле — проще разобраться, когда видно, где он лежит среди остальных. Официальный CLI create-we-contract (часть того же js-contract-sdk) командой npm create we-contract MyContract разворачивает готовый каркас проекта — вот как он выглядит в панели Project слева и с открытым contract.ts справа.

WEBSTORM · PROJECT▾ my-contract▸ node_modules▾ srccontract.tscontract.config.jspackage.jsontsconfig.jsoncontract.ts@Contract()export default class MyContract {@Var() counter!: ContractValue<number>@Action() increment(...) {}декораторы SDK — прямо в редакторе, без переключения окон
Дерево TS-проекта контракта в WebStorm: src/contract.ts с декораторами SDK рядом с contract.config.js и package.json
Панель Project слева открывается и прячется сочетанием Alt+1 (Windows/Linux) или ⌘1 (macOS), без обращения к мыши. Встроенный терминал внизу — обычный терминал системы: команды docker compose из этой главы выполняются прямо в нём, без переключения окон.

Порядок первого запуска один и тот же вне зависимости от IDE — от открытия папки до нажатия Run. Собери его сам.

Клик по карточке внизу добавляет её сюда — выстрой шаги мастера по порядку

Выполнено: 0 из 6

Нажми сочетание: Показать/скрыть панель Project

Сочетание Alt+1 перехватит браузер — здесь его не проверить. Потренируй его в той программе, где оно нужно.

Совет

Run/Debug запускает не сам файл, а сохранённую конфигурацию запуска (run configuration) — какой именно npm-скрипт выполнить. В WebStorm открой package.json: рядом с каждым скриптом в разделе scripts (build, deploy, deploy:testnet) появляется значок ▶ — клик по нему создаёт конфигурацию автоматически и сразу запускает скрипт; повторный клик по кнопке Run просто перезапускает уже созданную конфигурацию, не создавая новую.

Типичные ошибки

Все четыре сообщения ниже — реальный вывод настоящего Docker Engine, снятый вживую в этой сессии, а не восстановленный по памяти.

1. Команда запущена не в той папке

no configuration file provided: not found

Перевод: «конфигурационный файл не предоставлен: не найден». docker compose по умолчанию ищет файл docker-compose.yml в текущей папке — и не находит его, если запустить команду не оттуда, где файл лежит, или до того, как файл вообще создан.

Как починить: перейти (cd) в папку, где лежит docker-compose.yml, либо явно указать путь к файлу флагом -f путь/до/docker-compose.yml.

2. Сбитый отступ в docker-compose.yml

yaml: while parsing a block mapping at line 2, column 5: line 5, column 6: did not find expected key

Перевод: «yaml: при разборе блочного отображения в строке 2, столбце 5: строка 5, столбец 6: не найден ожидаемый ключ». YAML определяет вложенность по отступам — если один из элементов списка портов сдвинут не на ту же ширину, что и соседние, парсер теряет структуру файла и не понимает, какому ключу что принадлежит.

Как починить: выровнять отступы во всём файле одинаково — например, ровно по два пробела на уровень вложенности, без табуляций вперемешку с пробелами.

3. Порт уже занят

Bind for 0.0.0.0:6862 failed: port is already allocated

Перевод: «привязка к 0.0.0.0:6862 не удалась: порт уже занят». Один и тот же порт на компьютере не могут слушать два процесса сразу, а порт 6862 уже кем-то занят: контейнером из другой папки (другого проекта Compose), забытым контейнером с прошлого раза или вообще посторонней программой.

Отдельно запомни, чего эта ошибка не значит: повторный docker compose up -d в той же самой папке её не вызывает. Compose узнаёт свои уже запущенные контейнеры по имени проекта и просто печатает Container node-0 Running, ничего не поднимая заново.

Как починить: сначала найти виновника — docker ps --filter publish=6862 покажет контейнер, который держит порт. Дальше либо остановить его (docker stop имя-контейнера, а для чужой сети — docker compose down в её папке), либо поменять в своём docker-compose.yml первое число в паре host:container на свободный порт, например 16862:6862.

4. Опечатка в имени образа

Error response from daemon: pull access denied for wavesenterprise/nonexistent-demo-image, repository does not exist or may require 'docker login'

Перевод: «ответ демона с ошибкой: доступ к скачиванию запрещён для …, репозиторий не существует либо требует docker login». Docker не смог найти образ с указанным именем — либо в имени или теге версии опечатка, либо образ действительно приватный и требует авторизации, которой у нас нет.

Как починить: свериться с точным названием и тегом образа в официальной документации проекта, исправить опечатку в docker-compose.yml; если образ приватный — выполнить docker login с доступом, который действительно есть.

Отвечено вопросов: 0 из 4
Что означает сообщение "no configuration file provided: not found"?
Из-за чего возникает ошибка "did not find expected key" в YAML-файле?
Как починить ошибку "port is already allocated"?
Что вероятнее всего вызывает "pull access denied ... repository does not exist"?
Отвечено верно: 0 из 4

Слова главы

container
1 / 15

Проверь себя руками

Команду запуска сети — до автоматизма:

Цель: скорость ≥ 120 зн/мин, точность ≥ 90%

docker compose up -d

И полную очистку с удалением томов:

Цель: скорость ≥ 120 зн/мин, точность ≥ 90%

docker compose down -v

А теперь — все команды и строки этой главы вперемешку, с живой клавиатурой:

Цель: скорость ≥ 140 зн/мин, точность ≥ 90%

docker compose up -d
клавиша ``клавиша 11клавиша 22клавиша 33клавиша 44клавиша 55клавиша 66клавиша 77клавиша 88клавиша 99клавиша 00клавиша --клавиша ==клавиша ⌫клавиша TabTabклавиша QQклавиша WWклавиша EEклавиша RRклавиша TTклавиша YYклавиша UUклавиша IIклавиша OOклавиша PPклавиша [[клавиша ]]клавиша \\клавиша Caps LockCaps Lockклавиша AAклавиша SSклавиша DDклавиша FFклавиша GGклавиша HHклавиша JJклавиша KKклавиша LLклавиша ;;клавиша ''клавиша EnterEnterклавиша ShiftShiftклавиша ZZклавиша XXклавиша CCклавиша VVклавиша BBклавиша NNклавиша MMклавиша ,,клавиша ..клавиша //клавиша ShiftShiftклавиша CtrlCtrlклавиша WinWinклавиша AltAltклавиша ПробелПробелклавиша AltAltклавиша WinWinклавиша MenuMenuклавиша CtrlCtrl
следующая: D — средний левой руки

Символы и команды главы

Команда / флагЧто делаетПример
docker compose upстроит и запускает все сервисы из docker-compose.ymldocker compose up -d
docker compose downостанавливает и удаляет контейнеры, сети и (с -v) томаdocker compose down -v
-ddetached: запустить контейнер(ы) в фоне, не занимая терминалdocker run -d ...
-v (у down)удалить тома с данными вместе с контейнерамиdocker compose down -v
-p host:containerпробросить порт с компьютера внутрь контейнера-p 5000:5000
--nameзадать контейнеру человекочитаемое имя--name registry
--restart alwaysперезапускать контейнер автоматически при падении или перезагрузкеdocker run --restart always ...
image: (в YAML)ключ docker-compose.yml — какой образ использовать для сервисаimage: wavesenterprise/node:v1.16.0
ports: (в YAML)ключ docker-compose.yml — список пробрасываемых портов сервисаports:
Углубиться

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

Registry — не только для приватных сетей. Даже у публичного Docker Hub — та же природа: хранилище образов с версиями (тегами), из которого клиенты их скачивают. Разница только в том, кто имеет право публиковать и скачивать — весь интернет или ограниченный круг участников локальной сети, совсем как с приватным и публичным блокчейном из прошлой главы.

Дальше по теме:

Куда дальше: проверенные ресурсы

Челлендж ⭐

Ступень 1. Вечером перед защитой проекта на чемпионате ты поднимаешь сеть в папке ~/champ/network — а утром возился с прошлогодней копией той же сети в папке ~/champ/network-old и выключить её забыл. Терминал показал:

Bind for 0.0.0.0:6862 failed: port is already allocated

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

Ступень 2 ⭐. В docker-compose.yml сети node-0 занимает порт 6862, node-1 и node-2 — свои собственные (у каждой ноды в этой сети порты разные, чтобы все три могли работать на одном компьютере одновременно). Тебе нужно добавить в файл ещё один сервис — веб-панель мониторинга, слушающую внутри своего контейнера порт 3000 — и пробросить его наружу на свободный порт 9090 компьютера. Напиши, как будет выглядеть нужная строка в разделе ports: этого нового сервиса, и объясни, что случится при запуске, если по ошибке вместо 9090 написать снова 6862.

Решение (сначала попробуй сам)

Ступень 1: утренняя нода из network-old всё ещё работает и держит порт 6862 на компьютере. Compose считает каждую папку отдельным проектом со своими именами контейнеров, поэтому про соседа он ничего не знает — и честно пытается занять уже занятый порт, а один и тот же порт на хосте не может слушать два процесса одновременно.

Найти виновника: docker ps --filter publish=6862 — в колонке NAMES будет контейнер из network-old. Два рабочих способа: (1) погасить старую сеть — cd ~/champ/network-old && docker compose down (без -v, если данные прошлогодней сети ещё нужны) или точечно docker stop имя-контейнера, после чего спокойно поднять новую; (2) не трогать старую сеть, а в своём docker-compose.yml поменять первое число пары host:container на свободный порт, например 16862:6862 — тогда обе сети живут на одном компьютере, просто REST API новой открыт на 16862.

Чего в этой ситуации точно не было: повторный docker compose up -d в одной и той же папке такой ошибки не даёт — Compose узнаёт свои контейнеры и печатает Container node-0 Running.

Ступень 2 ⭐:

ports:
- "9090:3000"

Если по ошибке написать "6862:6862" вместо "9090:3000", при запуске Docker Compose попытается занять портом мониторинга тот же порт 6862, который уже держит node-0 — и получится ровно та же ошибка port is already allocated, что и в ступени 1, только теперь конфликтуют два сервиса внутри одного файла, а не два разных проекта Compose.

Сводная проверка по всей главе: 7 вопросов, 5:00 на всё, без подсказок по ходу. Вопросы показываются по одному, ответ изменить нельзя. Если время выйдет — экзамен завершится с тем, что успел ответить. Пересдавать можно сколько угодно раз.

Что должен уметь

  • Объяснять, что физически представляет собой нода Waves Enterprise, и какие сетевые порты она открывает и зачем.
  • Обосновывать, почему минимальная демонстрационная сеть — это три ноды, а не одна и не две.
  • Читать файл docker-compose.yml для сети из нескольких нод: назначение ключей image, ports, вложенность через отступы YAML.
  • Разбирать по частям команды docker compose up -d и docker compose down -v, включая назначение каждого флага.
  • Объяснять, зачем локальной сети собственный Docker registry и чем он отличается от блокчейн-реестра (ledger) из прошлой главы.
  • Описывать концептуально, как устроен смарт-контракт на Waves Enterprise: назначение декораторов @Contract, @State, @Action, и разницу между CreateContractTx и CallContractTx.
  • Находить src/contract.ts в структуре TS-проекта контракта и пользоваться в WebStorm панелью Project, встроенным терминалом и run configuration для npm-скриптов.
  • Узнавать по тексту четыре типичные ошибки Docker — отсутствие конфигурационного файла, сбитый отступ YAML, занятый порт, опечатку в имени образа — и знать, как каждую починить.

Комментарии

Комментарии появятся после настройки. Нужен аккаунт GitHub — вход прямо в виджете выше.