Курс разработчика EOS. Часть 6. Пишем смарт-контракт. Визитка
В прошлой статье мы написали наш смарт-контракт "Hello word" и разобрались с таблицами и со структурой мультииндекс. Мы уже обладаем некоторыми необходимыми нам знаниями. В этой статье мы создадим смарт-контракт - "Визитку".
Но перед перед тем, как мы к этому приступим, нам предстоит выполнить несколько шагов.
Шаг 1 - Запускаем ноды
Если ноды в отдельном терминале до этого не были запущены, то открываем терминал и вводим команду
nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin --contracts-console
если вдруг возникнет ошибка то вводим
nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin --plugin eosio::producer_plugin --plugin eosio::http_plugin --delete-all-blocks
затем снова запускаем команду
nodeos -e -p eosio --plugin eosio::wallet_api_plugin --plugin eosio::chain_api_plugin --plugin eosio::history_api_plugin --contracts-console
Шаг 2 - Создаем кошелек
В новом терминале (старый не закрываем) создаем кошелек с именем BusinessCard
cleos wallet create -n BusinessCard
Сохраняем ключ от кошелька
Создаем две пары ключей для ActiveKey и OwnerKey. Два раза вводим команду в консоль
cleos create key
и сохраняем private и public ключи.
Импортируем два private ключа в наш кошелек. Также нам требуется импортировать 3-ий ключ - private ключ провайдера, который находится в файле config.ini. По умолчанию он стандартный
5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3
Вводим команды, подставляя значения сгенерированных ранее вами двух private ключей и одного private ключа провайдера
если кошелек за это время залочился, вводим команду, приведенную ниже, а затем и предварительно сохраненный пароль к кошельку
cleos wallet unlock -n BusinessCard
далее
cleos wallet import значение_private_Activekey -n BusinessCard
cleos wallet import значение_private_Ownerkey -n BusinessCard
cleos wallet import 5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3 -n BusinessCard
Шаг 3 - Создаем аккаунт
Давайте создадим аккаунт и назовем его, к примеру buscardacc
cleos create account eosio buscardacc значение_public_Activekey значение_public_Ownerkey
Шаг 4 - Создание визитки
переходим в директорию contracts
cd ~/eos/build/contracts
создаем папку с названием нашего проекта BusinessCard
mkdir BusinessCard
в ней создаем два файла BusinessCard.hpp и BusinessCard.сpp
В BusinessCard.hpp добавьте следующий код
#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
#include <string>
В BusinessCard.сpp
#include "BusinessCard.hpp"
namespace BusinessCard {
using namespace eosio;
using std::string;
class Users : public contract {
using contract::contract;
public:
Users(account_name self) :contract(self) {}
//@abi action
void add(account_name account, string& username) {
/**
* We require that only the owner of an account can use this action
* or somebody with the account authorization
*/
require_auth(account);
/**
* We access the "user" table as creating an object of type "userIndex"
* As parameters we pass code & scope - _self from the parent contract
*/
userIndex users(_self, _self);
/**
* We must verify that the account doesn't exist yet
* If the account is not found the iterator variable should be users.end()
*/
auto iterator = users.find(account);
eosio_assert(iterator == users.end(), "Address for account already exists");
/**
* We add the new user in the table
* The first argument is the payer of the storage which will store the data
*/
users.emplace(account, [&](auto& user) {
user.account_name = account;
user.username = username;
user.age = 30;
user.address = "Minsk";
});
}
private:
///@abi table user i64
struct user {
uint64_t account_name;
string username;
uint64_t age;
string address;
uint64_t primary_key() const { return account_name; }
EOSLIB_SERIALIZE(user, (account_name)(username)(age)(address))
};
typedef multi_index<N(user), user> userIndex;
};
EOSIO_ABI(Users, (add))
}
Содержимое файла BusinessCard.hpp не отличается от hello.hpp и такое же как было в предыдущей статье.
Тут мы просто подключаем хэдер файлы.
Рассмотрим более подробно файл BusinessCard.cpp. Хорошей практикой является создавать в контракте пространство имён. Мы оборачиваем все в namespace BusinessCard {}. Далее мы создаем класс Users, который наследуется от контракта contract - class Users : public contract {}. Аналогично с конструктором. В контракте у нас всего лишь одно действие action add - void add(account_name account, string& username) {}. В него, в качестве входных данных мы передаем название нашего аккаунта - account и имя - username. Рассмотрим данный кусок кода:
struct user {
uint64_t account_name;
string username;
uint64_t age;
string address;
uint64_t primary_key() const { return account_name; }
EOSLIB_SERIALIZE(user, (account_name)(username)(age)(address))
};
typedef multi_index<N(user), user> userIndex;
Мы объявили структуру user. Со следующими полями: имя аккаунта - account_name, имя пользователя - username, возраст - age, адрес - address. Для объявление полей мы использовали только два типа данных строковый - string и целочисленный uint64_t. account_name - является нашим первичным ключом. Ведь мы хотим, чтобы один аккаунт соответствовал одному пользователю. Первичный ключ обязательно должен быть типа uint64_t и должен быть уникальным. Им у нас является поле account_name. Тут у нас также как и в предыдущей статье присутствует обязательная функция primary_key(), возвращающая первичный ключ типа uint64_t . В коде мы объявили шаблонный объект userIndex. Для объявления мы использовали ключевое слово typedef.
typedef multi_index<N(user), user> userIndex;
EOSLIB_SERIALIZE(user, (account_name)(username)(age)(address))
- это макрос, который представляет сериализацию и десериализацию, чтобы действия могли передаваться между контрактами и блокчейном. О нем мы также упоминали .
Строка require_auth(account); необходима, чтобы только владелец учетной записи(аккаунта) мог использовать нашу функцию - добавлять пользователя. Тут мы также делаем проверку на существование аккаунта
auto iterator = users.find(account);
eosio_assert(iterator == users.end(), "Address for account already exists");
Если аккаунт не существует, то как и в предыдущей статье добавляем данные при помощи функции emplace.
users.emplace(account, [&](auto& user) {
user.account_name = account;
user.username = username;
user.age = 30;
user.address = "Minsk";
});
Первый параметр - аккаунт, который будет платить за хранение данных. Второй параметр - лямбда-функция.
Деплой смарт-контракта
Прежде всего нам необходимо сгенерировать два файла .wast и .abi
введем две следующие команды
eosiocpp -o BusinessCard.wast BusinessCard.cpp
eosiocpp -g BusinessCard.abi BusinessCard.cpp
в директории BusinessCard появятся дополнительный файлы
настало время задеплоить наш контракт
cleos set contract buscardacc BusinessCard BusinessCard.wast BusinessCard.abi
Тестируем наш контракт
Давайте добавим данные для нашего пользователя
cleos push action buscardacc add '["buscardacc","Igor Artemenko"]' -p buscardacc@active
Данные успешно добавились.
Резюме
В данном уроке мы написали смарт-контракт визитка, состоящий из одного action. В следующем уроке мы расширим функционал нашей визитки, добавив дополнительные action.