Время прочтения: 10 мин.

Введение

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

Что такое смарт-контракт и как он работает?

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

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

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

Плюсы смарт-контракта:

  • Безопасность
    Конечно всё зависит от сложности условий контракта, но если соблюдены все формальности и весь необходимый функционал, то такой смарт-контракт будет в разы безопаснее, чем даже договор на бумаге. Взлом смарт-контрактов практически невозможен.
  • Автономность
    Что делать, если юрист или нотариус ночью не работает? Придется ждать до утра. Но если бы договор проходил по смарт-контракту, то ждать бы не пришлось. У умных контрактов нет обеда и нет необходимости во сне, он выполняет свою задачу, когда это необходимо.
  • Низкие комиссии или их отсутствие
    Юристу придется заплатить гораздо большую сумму, чем если работать через смарт-контракт. Например, в приложениях Dapp взымается комиссия (Gas), и она зависит от объема обрабатываемого кода. То есть, чем сложнее код, тем больше будет Gas. Но, как бы то ни было, комиссия будет всё равно ниже, чем за услуги юриста или нотариуса.
  • Быстрое выполнение транзакций
    К автономности стоит добавить и скорость обработки транзакций сторон. Понятно, что скорость вычислительных машин по сравнению с человеческими в разы выше, что и делает процессы выполнения контрактов быстрее.

Что такое блокчейн?

Именно в блокчейне будут храниться разработанные смарт-контракты.

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

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

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

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

На каком IDE сделать свой смарт-контракт?

Выбор сделан на один из самых популярных языков программирования для создания собственных смарт-контрактов – Solidity. Это объектно-ориентированный язык программирования, созданный в рамках проекта Ethereum.

Чтобы начать программировать, имеется достаточное количество IDE, в которых можно запускать и компилировать контракты Solidity:

Мой выбор пал на IDE Remix. Удобен он тем, что его можно использовать не только как desktop-приложение, но и как приложение через браузер.

Запуск и отладка смарт-контрактов — самое важное для обеспечения оптимизации и безопасности в разработке, а такой базовый функционал в Remix имеется.

Программирование на Solidity. Создание персонажа для игры

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

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

Первый контракт можно создать, начиная с самых азов:

pragma solidity ^0.4.19;

contract PersonCreator {

}

Что-то похожее на объявление классов, contract – это основной блок для создания приложений и логики на Ethereum.

pragma solidity ^0.4.19; — это функция объявления версии компилятора Solidity.

Целочисленные переменные

Теперь добавлю целочисленную переменную, хотя здесь не всё так просто. Существуют несколько типов целочисленных типов:

  • uint или uint256
  • uint8
  • uint16
  • uint32
  • И т.д

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

pragma solidity ^0.4.19;

contract PersonCreator {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

}

Были объявлены две целочисленные переменные, которые равны числу и результату математической операции над числом и переменной. Что за операция «**»? Ответ прост: это возведение числа в степень, то есть 10 в степени 16 по примеру. О других типах переменных можно почитать в официальном руководстве.

В итоге были созданы две целочисленные переменные, где uint dnaDigits – ДНК героя, которая определяется числом из 16 цифр. Переменная uint dnaModulus будет использоваться для проверки размерности ДНК.

Структура

struct – структура, требуется для более сложных типов данных.

Создам заготовку персонажа с характеристиками: имя, уровень, сила, ловкость, интеллект.

pragma solidity ^0.4.19;

contract PersonCreator {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Person {
        string name;	// имя героя
        uint level;	// уровень
        uint strength;	// сила
	  uint agility;	// ловкость
	  uint intellect;	// интеллект
    }

}

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

Массивы

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

uint[] example1; // динамический массив
        uint[5] example2; // статический массив с максимальным размером равным 5

Создам массив героев:

pragma solidity ^0.4.19;

contract PersonCreator {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Person {
        string name;
        uint level;
        uint strength;
	  uint agility;
	  uint intellect;
    }

    Person[] public heroes; //Динамический массив героев

}

Если задать public , то Solidity автоматически создаст для переменной функцию геттер, что позволит другим контрактам читать данные этой переменной (но не записывать в неё).

Public — это модификатор для функций, он может быть вызван откуда угодно.

Подробнее о видах модификаторов видимости можно прочитать здесь.

Функции

Инициализирую функцию, которая будет создавать и добавлять в массив новых героев:

pragma solidity ^0.4.19;

contract PersonCreator {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Person {
        string name;
	  uint dna;
        uint level;
        uint strength;
	  uint agility;
	  uint intellect;
    }


    Person[] public heroes;

    function _createHeroes(string _name, uint _dna) private {
        // Создание и добавление героя в массив heroes, единицы — это значения
	  // указанные характеристик персонажа. 1 уровень, 1 сила и т.д
  heroes.push(heroes(_name, _dna, 1, 1, 1, 1));
    }


}

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

string _name, uint _dna //перед названием используется нижнее подчеркивание

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

function _createHeroes // если функция private, то нижнее подчеркивание в имени

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

Keccak256 и преобразование типов данных

Теперь, чтобы создать уникального героя, ему необходимо присвоить случайный ДНК типа uint. В Ethereum есть встроенная хэш-функция keccak256, по-русски произносится как «кечак». Такая функция преобразовывает строку в случайное 256-битное шестнадцатиричное число, в моем случае строкой будет имя персонажа. И если изменится хотя бы буква в имени, то хэш будет полностью изменен.

Создам функцию для генерации ДНК:

function _generateDna(string _name) private view returns (uint) {
        uint random = uint(keccak256(_str));
        return random % dnaModulus;
    }

returns (uint) – ключевое слово с параметром типа вывода.

return random % dnaModulus; // операция вывода ограниченная 16 числами

Во второй строке было произведено преобразование типов данных функцией uint().

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

function createRandomHero(string _name) public {
        uint randDna = _generateDna(_name);
        _createHeroes(_name, randDna);
    }

События

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

contract PersonCreator {

   event HeroAdded(uint heroId, string name, uint dna); // Ивент

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Person {
        string name;
	  uint dna;
        uint level;
        uint strength;
	  uint agility;
	  uint intellect;
    }

    Person[] public heroes;

    function _createHeroes(string _name, uint _dna) private {
        uint id = heroes.push(Person(_name, _dna, 1,1,1,1)) - 1;
        HeroAdded(id, _name, _dna); // триггер ивента
    }

    function _generateDna(string _name) private view returns (uint) {
        uint random = uint(keccak256(_str));
        return random % dnaModulus;
    }


    function createRandomHero(string _name) public {
        uint randDna = _generateDna(_name);
        _createHeroes(_name, randDna);
    }


}

Теперь внешнее приложение сможет услышать наше событие и выполнить действие. Но в дальнейшем, для развития проекта, нужно иметь индивидуальный идентификатор (id) созданного героя. Для этого была создана такая переменная, которая будет брать номер последнего созданного героя, находящегося в массиве heroes.

uint id = heroes.push(Person(_name, _dna, 1,1,1,1)) - 1;

Как выглядит функция на JavaScript, которая реагирует на событие:

YourContract.HeroesAdded(function(error, result) {
  // Здесь использовать результат
})

Заключение

Первый контракт Solidity готов! Теперь предстоит написать внешний интерфейс приложения на Javascript, который будет взаимодействовать с нашим смарт-контрактом. В Ethereum есть библиотека Javascript под названием Web3.js.

В следующей публикации подробно продемонстрирую, как развернуть контракт и настроить Web3.js.