Разработка, тестирование и развертывание смарт-контрактов – это фундаментальные понятия блокчейн-разработки. В этом руководстве вы узнаете, как начать работать со смарт-контрактами в блокчейне Ethereum.
Прежде чем перейти к делу, важно ознакомиться с основными концепциями криптографии и блокчейна. Если вы еще не знакомы с ними, рекомендуем посмотреть это короткое видео.
Что такое смарт-контракт?
Смарт-контракты – это неизменяемые программы, хранящиеся в блокчейне. Они автоматизируют выполнение транзакций по заранее определенным условиям, что позволяет им широко использоваться для децентрализованного исполнения контрактов без посредников.
Смарт-контракты способны возвращать лишь чётко определенный набор результатов, регулируемый их неизменяемым кодом, благодаря которому участники контракта могут быть уверены в согласованности его исполнения. Никакого участия третьих лиц и потерь времени – контракты исполняются сразу после выполнения условий, заложенных в их исходном коде.
Смарт-контракты могут быть развёрнуты только внутри блокчейна. Ethereum поддерживает смарт-контракты, написанные на языке программирования Solidity.
Предварительные требования
В данном руководстве по работе со смарт-контрактами в блокчейне Ethereum используются JavaScript и Solidity, поэтому вам понадобятся базовые знания этих языков программирования, а также Node.js.
Solidity похож на JavaScript, поэтому описываемые концепции будут довольно просты для понимания.
Создание проекта
Мы будем использовать следующие инструменты разработки и тестирования смарт-контрактов:
- Ganache
- Truffle (устанавливается командой
npm i truffle -g
) - JavaScript для тестирования
- Solidity для программирования смарт-контрактов
Инициализация Truffle
Truffle предоставляет все необходимые инструменты для разработки и тестирования смарт-контрактов. Вы можете инициализировать
проект Truffle с помощью команды truffle init
. Она создаст новый проект с тремя папками (/contracts
, /migrations
и /tests
) и одним конфигурационным файлом – truffle-config.js
.
$ truffle init
✔ Preparing to download
✔ Downloading
✔ Cleaning up temporary files
✔ Setting up box
Unbox successful. Sweet!
Commands:
Compile: truffle compile
Migrate: truffle migrate
Test contracts: truffle test
$ ~
Создание экземпляра блокчейна Ganache Ethereum
После загрузки и установки Ganache запустите экземпляр блокчейна.
Проверьте порт в конфигурации RPC-сервера (обычно 7545
) и внесите при необходимости изменения в truffle-config.js
.
module.exports = {
networks: {
development: {
host: "127.0.0.1", // Localhost (по-умолчанию: none)
port: 7545, // Стандартный порт Ethereum (по-умолчанию: none)
network_id: "*" // Маска для любой сети (по-умолчанию: none)
}
}
}
Теперь введите команду truffle migrate
. Если она не выдаст никаких ошибок, значит Truffle готов к работе с созданным
экземпляром блокчейна.
Создание смарт-контракта
Смарт-контракты хранятся в папке /contracts
. Обратите внимание, что в ней уже есть контракт migrations.sol
.
Создайте в этой папке новый файл с именем TruffleTutorial.sol
.
pragma solidity >=0.4.22 <0.9.0;
contract TruffleTutorial {
address public owner = msg.sender;
string public message;
// конструктор вызывается при развертывании контракта
constructor() public {
// установка начального значения сообщения
message = "Hello World!";
}
modifier ownerOnly() {
require(
msg.sender == owner,
"This function is restricted to the contract's owner"
);
_;
}
// функция для установки значения сообщения, которую может вызывать только владелец смарт-контракта
function setMessage(string memory _message)
public
ownerOnly
returns(string memory)
{
// устанавливаемое значение не должно быть пустым
require(bytes(_message).length > 0);
// установка нового значения сообщения
message = _message;
return message;
}
}
Вы можете посмотреть комментарии в исходном коде, чтобы понять, что делает каждая функция. Также объясним всю суть здесь.
Этот смарт-контракт хранит текстовое сообщение и позволяет менять его только владельцу контракта. Контракт также позволяет читать это сообщение всем участникам блокчейна. При развёртывании контракта в блокчейн запишется сообщение “Hello World!”.
* Вы можете быстро протестировать смарт-контракт в Remix IDE.
Развертывание смарт-контракта в локальной тестовой сети Ganache Ethereum
Теперь давайте развернем этот смарт-контракт в локальной тестовой сети экземпляра блокчейна Ganache.
Если вы раньше работали с базами данных, например, такими как MySQL или PostgreSQL, то, скорее всего, уже сталкивались с миграциями.
В папке migrations
вы можете увидеть пример миграции, созданный при инициализации Truffle. Для нашего смарт-контракта
потребуется создать отдельный файл миграции, назовём его 2_TruffleTutorial_migration.js
.
// Находим смарт-контракт `TruffleTutorial.sol` в папке `/contracts`
const TruffleTutorial = artifacts.require("TruffleTutorial");
module.exports = function(deployer) {
// Развёртываем смарт-контракт при помощи Truffle
deployer.deploy(TruffleTutorial);
};
Вам может быть интересно, как работают функции artifacts
и deployer
. За них отвечает Truffle. При запуске команды
truffle migrate
программа просматривает папку /migrations
и развёртывает смарт-контракты согласно файлам миграций.
После того как вы создадите файл миграции с приведённым выше кодом, введите команду truffle migrate
--reset
, она развернёт
контракты в тестовой сети. Также будут сгенерированы файлы с ABI и байт-кодом, которые способна интерпретировать
виртуальная машина Ethereum.
Взаимодействие с развёрнутым смарт-контрактом
Теперь, когда смарт-контракт находится в блокчейне, с ним можно взаимодействовать при помощи консоли Truffle.
* Более простой способ взаимодействия – графический интерфейс Remix IDE.
Введите команду truffle console
, чтобы открыть консоль для взаимодействия со смарт-контрактом.
$ truffle console
truffle(development)> const truffleTutorial = await TruffleTutorial.deployed()
truffle(development)> const address = await truffleTutorial.address
truffle(development)> address
'0x46C00D73bF785000B3c3F93569E84415AB2381f2'
Попробуйте ввести все эти строки, чтобы увидеть адрес смарт-контракта. Если вам удастся это сделать, значит смарт-контракт успешно развёрнут, и с ним смогут взаимодействовать другие ваши проекты.
Теперь попробуйте получить сообщение, хранящееся в смарт-контракте.
truffle(development)> const message = await truffleTutorial.message()
truffle(development)> message
'Hello World!'
А теперь попробуйте сохранить сообщение в смарт-контракт.
truffle(development)> await truffleTutorial.setMessage('Hi there!')
truffle(development)> await truffleTutorial.message()
'Hi there!'
Несмотря на то, что мы проверяли работу смарт-контракта вручную, всё то же самое можно было бы сделать автоматически при помощи тестов.
Тестирование смарт-контракта
Тесты хранятся в папке /tests
. Создайте в ней новый файл с именем TruffleTutorial.js
.
const { assert } = require("chai")
const TruffleTutorial = artifacts.require("./TruffleTutorial.sol")
require("chai")
.use(require("chai-as-promised"))
.should()
contract('TruffleTutorial', ([contractOwner, secondAddress, thirdAddress]) => {
let truffleTutorial
// сохраняем развёрнутый смарт-контракт вместе с его методами в переменную
// `truffleTutorial` до того, как будут запущены тесты
before(async () => {
truffleTutorial = await TruffleTutorial.deployed()
})
// проверяем успешность развёртывания
describe('deployment', () => {
// проверяем адрес смарт-контракта
it('deploys successfully', async () => {
const address = await truffleTutorial.address
assert.notEqual(address, '')
assert.notEqual(address, undefined)
assert.notEqual(address, null)
assert.notEqual(address, 0x0)
})
// проверяем начальное значение сообщения
it('has a message', async () => {
const message = await truffleTutorial.message()
assert.equal(message, 'Hello World!')
})
})
describe('message', () => {
// проверяем возможность установки нового значения
// сообщения владельцем смарт контракта
it('contract owner sets a message', async () => {
// установка нового значения сообщения
await truffleTutorial.setMessage('Hi there!', { from: contractOwner })
// `from` помогает вызвать метод смарт-контракта от имени заданного участника
// (в данном случае владельца смарт-контракта)
// проверка нового значения сообщения
const message = await truffleTutorial.message()
assert.equal(message, 'Hi there!')
})
// проверяем, что только владелец смарт-контракта может устанавливать
// значение сообщения и никто другой
it('address that is not the owner fails to set a message', async () => {
await truffleTutorial.setMessage('Hi there!', { from: secondAddress })
.should.be.rejected
// сообщаем Chai, что тест считается пройденным успешно,
// если не удалось вызвать setMessage
await truffleTutorial.setMessage('Hi there!', { from: thirdAddress })
.should.be.rejected
})
})
})
Здесь для тестирования используется фреймворк Chai. С его помощью производится сопоставление переменных с их эталонными значениями. Если обратить внимание на код теста, то можно обнаружить некоторые сходства с тем, что до этого делалось вручную при помощи консоли Truffle, за исключением того, что здесь используется фреймворк Chai.
* assert.equal
принимает два аргумента, и если они совпадают, тест проходит успешно.
Теперь введите команду truffle test
и убедитесь в том, что все тесты проходят успешно.
$ truffle test
Using network 'development'.
Contract: TruffleTutorial
deployment
✓ deploys successfully
✓ has a message (236ms)
message
✓ contract owner sets a message (165ms)
✓ address that is not the owner fails to set a message (250ms)
4 passing (735ms)
Бонус: Развёртывание смарт-контракта в основной сети Ethereum
* Обратите внимание, что развёртывание смарт-контрактов в основной сети Ethereum требует платы за газ (говоря простым
языком комиссию). Кроме того, каждый раз, когда вы что-то записываете в блокчейн (например, вызываете функцию setMessage
),
с вас так же будет взята плата за газ (комиссия). Чтение данных при этом по-прежнему останется бесплатным.
Сначала установите расширение браузера Metamask. Затем перейдите в Remix IDE.
Внести изменение в автоматически сгенерированный проект – создайте в папке /contracts
файл TruffleTutorial.sol
и вставьте
в него весь исходный код смарт-контракта.
На боковой панели редактора расположена вкладка SOLIDITY COMPILER, переключитесь на неё и скомпилируйте Solidity-код смарт-контракта. Компилятор создаст файлы с ABI и байт-кодом.
После компиляции переключитесь на вкладку DEPLOY & RUN TRANSACTIONS, расположенную так же на боковой панели редактора. Здесь можно попробовать развернуть смарт-контракт в тестовой сети Remix VM. На этой же вкладке вы можете вручную протестировать развёрнутый смарт-контракт, используя инструменты Remix IDE.
Теперь давайте развернём смарт-контракт в основной сети Ethereum. Перед этим убедитесь, что у вас есть Ethereum на балансе кошелька Metamask, чтобы оплатить комиссию (за газ).
Переключите окружение на вкладке DEPLOY & RUN TRANSACTIONS с Remix VM на Injected Provider, чтобы подключиться к кошельку Metamask, который в свою очередь должен быть подключен к основной сети Ethereum. После нажатия кнопки Deploy вам будет предложено подписать транзакцию в Metamask. Как только вы это сделаете, смарт-контракт будет развёрнут и готов к работе.
* После развёртывания смарт-контракта не забудьте запомнить, а лучше записать присвоенный ему адрес, чтобы взаимодействовать с ним через клиентские приложения.
Материал подготовлен при поддержке Atlantida Community: