Статьи

Миграции трюфелей объяснил

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

Миграция трюфелей позволяет нам «проталкивать» умные контракты в блокчейн Ethereum ( локальный, tesnet или mainnet ) и устанавливать необходимые шаги для связи контрактов с другими контрактами, а также заполнять контракты исходными данными.

Где действительно сияет миграция — это управление адресами контрактов в блокчейне. Эта обычно утомительная работа почти полностью отвлекается от трюфеля.

Предпосылки

Убедитесь, что вы установили Truffle Framework и Ganache CLI .

Начиная

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

 Downloading... Unpacking... Setting up... Unbox successful. Sweet! Commands: Compile: truffle compile Migrate: truffle migrate Test contracts: truffle test 

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

 . ├── contracts │ └── Migrations.sol ├── migrations │ └── 1_initial_migration.js ├── test ├── truffle-config.js └── truffle.js 

Для начала в каталоге contracts создайте новый файл с именем Storage.sol , который должен выглядеть следующим образом:

 pragma solidity ^0.4.21; contract Storage { mapping (string => string) private _store; function addData(string key, string value) public { require(bytes(_store[key]).length == 0); _store[key] = value; } function removeData(string key) public returns (string) { require(bytes(_store[key]).length != 0); string prev = _store[key]; delete _store[key]; return prev; } function changeData(string key, string newValue) public { require(bytes(_store[key]).length != 0); _store[key] = newValue; } } 

Начальные миграции

Как вы могли заметить, при запуске truffle init создаются два файла. Это Migrations.sol и 1_initial_migration.js .

Исходные файлы миграции редко нуждаются в изменении. По сути, они отслеживают адреса в блокчейне.

Файл Migrations.sol может выглядеть так, как вы хотите, но он должен соответствовать фиксированному интерфейсу, который выглядит как интерфейс, созданный командой truffle init . В этих файлах вы можете выполнять некоторые сложные миграции, но, как я уже сказал, это редко требуется.

То же касается и файла 1_initial_migration.js . Он просто Migrations.sol файл Migrations.sol в нужную цепочку блоков.

Данные по миграции

Чтобы развернуть смарт-контракты в блокчейне Ethereum, вы должны сначала написать миграцию. Чтобы начать, в вашем каталоге migrations создайте файл с именем 2_deploy_contracts.js . Структура вашего проекта теперь должна выглядеть так:

 . ├── contracts │ ├── Migrations.sol │ └── Storage.sol ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── test ├── truffle-config.js └── truffle.js 

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

Так откуда все эти данные?

В каталоге вашего проекта запустите truffle compile . Если все идет хорошо, у вас должен быть вывод, подобный этому:

 Compiling ./contracts/Migrations.sol... Compiling ./contracts/Storage.sol... Writing artifacts to ./build/contracts 

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

Теперь снова проверьте структуру каталогов вашего проекта:

 . ├── build │ └── contracts │ ├── Migrations.json │ └── Storage.json ├── contracts │ ├── Migrations.sol │ └── Storage.sol ├── migrations │ ├── 1_initial_migration.js │ └── 2_deploy_contracts.js ├── test ├── truffle-config.js └── truffle.js 

Обратите внимание, что теперь есть папка build содержащая два файла — Migrations.json и Storage.json — которые соответствуют файлам смарт-контрактов в каталоге contracts .

Эти файлы *.json содержат описания соответствующих смарт-контрактов. Описание включает в себя:

  • Название контракта
  • Контракт ABI (двоичный интерфейс приложения — список всех функций в смарт-контрактах, их параметры и возвращаемые значения)
  • Байт-код контракта (скомпилированные данные контракта)
  • Байт-код развернутого контракта (последняя версия байт-кода, которая была развернута в блокчейне)
  • Версия компилятора, с которой последний раз компилировался контракт
  • Список сетей, в которых был развернут контракт, и адрес контракта в каждой из этих сетей.

Этот файл позволяет Truffle создать оболочку JavaScript для связи со смарт-контрактом. Например, когда вы вызываете contract.address в своем коде JavaScript, среда Truffle считывает адрес из файла *.json и обеспечивает легкий переход между версиями контракта и сетями.

Написание Миграции

Вооружившись этими знаниями, давайте напишем нашу первую миграцию. В файле 2_deploy_contracts.js напишите это:

 // Fetch the Storage contract data from the Storage.json file var Storage = artifacts.require("./Storage.sol"); // JavaScript export module.exports = function(deployer) { // Deployer is the Truffle wrapper for deploying // contracts to the network // Deploy the contract to the network deployer.deploy(Storage); } 

Написание миграций так просто. Чтобы запустить скрипт миграции, запустите в терминале следующее:

 truffle migrate 

Вы должны получить сообщение об ошибке:

 Error: No network specified. Cannot determine current network. 

Это означает, что Truffle не может найти сеть, в которой вы хотите развернуть.

Чтобы использовать симулированный блокчейн Ethereum, откройте новую вкладку в терминале и запустите ganache-cli . Вы должны получить вывод, похожий на этот:

 Ganache CLI v6.1.0 (ganache-core: 2.1.0) Available Accounts ================== (0) 0x828da2e7b47f9480838f2077d470d39906ad1d8e (1) 0xa4928865329324560185f1c93b5ebafd7ae6c9e8 (2) 0x957b8b855bed52e11b2d7e9b3e6427771f299f3f (3) 0xf4b6bcabedaf1ccb3d0c89197c4b961460f1f63d (4) 0x4bcae97be4a0d1f9a6dea4c23df8a2bffdb51291 (5) 0xe855c7cccac3a65ad24f006bf084c85c0197a779 (6) 0x168cb232283701a816a3d118897eedfcae2aec9d (7) 0x49563e64868e1d378e20b6ab89813c1bbaa0fd48 (8) 0x467c6f6f526eee9f66776197e3a9798c1cbf78e0 (9) 0xf65b47a3c663e2cc17ded8f197057a091686da43 Private Keys ================== (0) 8729d0f1d876d692f2f454f564042bd11c1e6d0c9b1808954f171f6f7b926fd6 (1) 452dfeee16e5a0e34fa5348f0ef11f39a8b4635e5f454f77fc228ca9598f6997 (2) 9196ad9fd6234f09ee13726cb889dcbc438c15f98e8ff1feb36a93758fa6d10a (3) fa47edd832e896314544b98d7e297ac2ce2097b49f8a9d7e7ae0e38154db8760 (4) 7ba1a96db190c14aaee5401dd5faab1af9074d7e6f24bc2f24b5084514bbf405 (5) 90088ce271f227db6be251c3055872c0d3dbdda9fc23ed119cf9d55db7c91259 (6) c36afd6f8f291b45e94ef0059576a86602e9a982b87e0c6fb25cfab4d68e9030 (7) 2766ac8aee110e9ad1ea68d1f28aaafb464fb1ef2a759bf5b2f628d256043c15 (8) 51ccf45f87806e8e9f30f487d6cdd0b44de3ad103f0d8daf9f1e20d9a4728dd9 (9) 398c0f079448c1e3724c9267f07ca4ab88233fc995a3d463c7c64d1a191688f5 HD Wallet ================== Mnemonic: void august badge future common warfare dismiss earn dog shell vintage dice Base HD Path: m/44'/60'/0'/0/{account_index} Listening on localhost:8545 

Это означает, что вы развернули приватную цепочку блоков, и она работает на localhost:8545 . Теперь давайте настроим Truffle для развертывания в этой сети.

Поместите следующее в файл truffle.js :

 module.exports = { networks: { development: { host: "127.0.0.1", port: 8545, network_id: "*" } } }; 

Это просто означает, что вы развертываете свой контракт в сети, работающей на localhost:8545 .

Теперь беги truffle migrate . Вы должны получить вывод, похожий на этот:

 Using network 'development'. Running migration: 1_initial_migration.js Deploying Migrations... ... 0x06595c0eccde8cb0cf642df07beefea11e3e96bfb470e8dbaf6567cecc37aed8 Migrations: 0x6008e9a2c213d51093d0f18536d1aa3b00a7e058 Saving successful migration to network... ... 0x392fb34c755970d1044dc83c56df6e51d5c4d4011319f659026ba27884126d7b Saving artifacts... Running migration: 2_deploy_contracts.js Deploying Storage... ... 0xb8ec575a9f3eca4a11a3f61170231a1816f7c68940d8487e56567adcf5c0a21e Storage: 0xd8e2af5be9af2a45fc3ee7cdcb68d9bcc37a3c81 Saving successful migration to network... ... 0x15498a1f9d2ce0f867b64cdf4b22ddff56f76aee9cd3d3a92b03b7aa4d881bac Saving artifacts... 

Трюфель перенес ваш контракт в сеть и сохранил артефакты. В каталоге сборки в файле Storage.json проверьте правильность этого, проверив объект networks . Вы должны увидеть что-то похожее на это:

 "networks": { "1525343635906": { "events": {}, "links": {}, "address": "0xd8e2af5be9af2a45fc3ee7cdcb68d9bcc37a3c81", "transactionHash": "0xb8ec575a9f3eca4a11a3f61170231a1816f7c68940d8487e56567adcf5c0a21e" } } 

1525343635906 — это идентификатор сети. (Основная сеть Ethereum и все основные тестовые сети имеют фиксированные идентификаторы, такие как 1,2,3 и т. Д.)

address — это адрес, по которому был развернут контракт.

transactionHash — это хэш транзакции, который использовался для развертывания контракта

Мы увидим, как это полезно позже в этом уроке.

Несколько контрактов

Миграции Truffle действительно хороши, когда есть несколько контрактов для компиляции, развертывания и отслеживания (что есть почти во всех проектах блокчейнов).

Миграции не только позволяют нам развертывать несколько контрактов с помощью одной команды, но и позволяют нам запускать произвольные функции в контрактах, получать возвращаемые значения этих функций и передавать их в последующие контракты.

Теперь в каталоге contracts создайте файл с именем InfoManager.sol . В файле напишите контракт так:

 pragma solidity ^0.4.21; import "./Storage.sol"; contract InfoManager { Storage private _dataStore; uint private _lastAdded; function InfoManager(Storage dataStore) public { _dataStore = dataStore; } function addData(string key, string value) public { require((now - 1 days) > _lastAdded); _dataStore.addData(key, value); } } 

Как видим, этот договор зависит от договора Storage . Мало того, он принимает контракт Storage в качестве параметра в своем конструкторе. Давайте рассмотрим миграции, которые сделают это возможным. Миграции содержатся в том же файле с именем 2_deploy_contracts.js :

 var Storage = artifacts.require("./Storage.sol"); var InfoManager = artifacts.require("./InfoManager.sol"); module.exports = function(deployer) { // Deploy the Storage contract deployer.deploy(Storage) // Wait until the storage contract is deployed .then(() => Storage.deployed()) // Deploy the InfoManager contract, while passing the address of the // Storage contract .then(() => deployer.deploy(InfoManager, Storage.address)); } 

Синтаксис для развертывания:

 ... deployer.deploy(`ContractName`, [`constructor params`]) // Returns a promise ... 

Поскольку функция deploy(...) возвращает обещание, вы можете обработать его любым удобным вам способом, за исключением заметного исключения async по какой-то причине не работающего в миграциях.

Вы также можете выполнить пользовательские шаги после развертывания контракта. Например, миграция может выглядеть так:

 deployer.deploy(Storage) .then(() => Storage.deployed()) .then((instance) => { instance.addData("Hello", "world") }).then(() => deployer.deploy(InfoManager, Storage.address)); 

Это приведет к заполнению контракта на Storage строковым world ключевых data перед развертыванием контракта InfoManager .

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

сети

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

Это делается путем «расширения» вставленных параметров функции module.exports :

 module.exports = function(deployer, network) { if (network == "live") { // do one thing } else if (network == "development") { // do other thing } } 

Счета

Функция module.exports умолчанию также предоставляет учетные записи, к которым у вас есть доступ через ваш узел Ethereum или провайдера кошелька. Вот пример:

 module.exports = function(deployer, network, accounts) { var defaultAccount; if (network == "live") { defaultAccount = accounts[0] } else { defaultAccount = accounts[1] } } 

Библиотеки

Вы также можете связать существующие библиотеки (уже развернутые), используя функцию deployer.link(...) :

 ... deployer.deploy(MyLibrary); deployer.link(MyLibrary, MyContract); deployer.deploy(MyContract); ... 

Вывод

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