Курс разработчика EOS. Часть 5. Cмарт-контракт "Hello world". Создание таблиц для хранения данных

avatar igorart 11 months ago

Назад Содержание Вперед

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

Откроем наш файл hello.cpp, который мы создали в предыдущей  статье и скопируем туда следующий код (заменив тот,который был) и сохраним.


#include <eosiolib/eosio.hpp>
#include <eosiolib/print.hpp>
using namespace eosio;

class hello : public eosio::contract {
    public:
        using contract::contract;

/// @abi table mytable i64
        struct mytable
        {
           account_name account;
           uint64_t primary_key() const {return account;}

EOSLIB_SERIALIZE(mytable,(account))
};

typedef multi_index<N(mytable),mytable> mytableIndex;

         /// @abi action
        void hi(account_name user){

            mytableIndex mytables(_self,_self);
            auto iter = mytables.find(user);

            if(iter == mytables.end()){
                
             print("need insert\t"); 
                mytables.emplace(_self,[&](auto& mytable {
                    mytable.account = user;
                });
            }
            else {
                print("data already exist\t");
            }
            print("hello, world : ", name{user}, " 6");
        }
};

EOSIO_ABI(hello,(hi))

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


        /// @abi table mytable i64
        struct mytable
        {
           account_name account;
           uint64_t primary_key() const {return account;}

            EOSLIB_SERIALIZE(mytable,(account))
        };

@abi table mytable i64 - необходимая аннотация для создания .abi файла (мы указываем, что это будет таблица). Аналогично как @abi action в предыдущей статье. Строка account_name account; - поле, где мы будем хранить имя нашего пользователя, которое, по сути, будет являться нашим первичным ключом. Первичный ключ - это поле или набор полей однозначно иденцифицирующих(определяющих) запись. Рассмотрим, что представляет собой строка uint64_t primary_key() const {return account;}

У каждой таблицы в EOS обязательно должна быть функция primary_key() возвращающая первичный ключ. Возвращаемое значение должно быть целочисленного типа uint64_t. Модификатор const служит

для исключения изменения возвращаемого значения. Иными словами, возвращаемое значение (первичный ключ) не должно изменяться(должно быть константным).

Строка EOSLIB_SERIALIZE(mytable,(account)) - макрос для вызова данных из смартконтракта(сериализация и десериализация), чтобы действия могли передаваться между контрактами и блокчейном. Данный макрос имеет следующую структуру 

EOSLIB_SERIALIZE(struct_name, (property_1)(property_2)(property_3)...(property_n))

Первым параметром  передается имя структуры (у нас это mytable) и затем по порядку в скобках названия полей. В нашем случае одно поле - account и контракт содержит всего лишь одно действие action hi - void hi(account_name usern) {}. В него, в качестве входных данных, мы передаем имя пользователя, которому  будет отправляться сообщение. Мы уже немного упоминали об action в предыдущей статье. Следует еще более подробно остановиться на них. Действие представляет собой операцию внутри смарт-контракта. Каждое действие выполняется в рамках своей собственной среды, называемой контекстом действия. Перед выполнением каждого действия рабочая память очищается. И переменные из другого действия становятся не доступны в контексте текущего. Беда..Что же нам тогда делать? Где хранить переменные? Единственный способ хранить значения наших переменных и затем передать их между действиями - это сохранить их, как мы отмечали выше, в таблицы или, другими словами, в базу данных EOSIO. Для доступа к базе данных EOSIO служит специальный тип данных - мультииндекс. Мультииндекс - это контейнер. Реализация его очень схожа с реализацией multi-index в библиотеке boost .

Строка typedef multi_index<N(mytable),mytable> mytableIndex; и представляет собой мультииндекс. Используя ключевое слово typedef мы объявляем шаблонный объект мультииндекс.Чтобы извлечь информацию из таблицы мы используем "_self,_self" в строке mytableIndex mytables(_self,_self);. mytables(_self,_self) - это, можно сказать, конструктор экземпляра. Первый параметр _self представляет собой аккаунт, который будет владеть таблицей, второй - аккаунт, которым будет использоваться данная таблица. Строкой auto iter = mytables.find(user); мы объявляем итератор и ищем данные пользователя. Если мы доходим до конца и не находим пользователя if(iter == mytables.end()) то мы печатаем print("need insert\t"); и вставляем данные, используя функцию mytables.emplace(_self,[&](auto& mytable). Данная функция используется для добавления нового объекта (строки) в таблицу. Первым параметром _self передается информация об аккаунте, который будет платить за хранение данных. Второй параметр представляет собой лямбда-функцию. Лямда-функции - это безымянные локальные функции, которые можно создавать внутри какого-либо выражения.

Нам нужно перекомпилировать наш код. В консоли из папки hello вводим

eosiocpp -o hello.wast hello.cpp
eosiocpp -g hello.abi hello.cpp

Деплоим наш контракт

cleos set contract hello.code ../hello -p hello.code

Назовем нашего пользователя testuser и вызовем action  hi, передав туда имя пользователя.

cleos push action hello.code hi '["testuser"]' -p hello.code

Просмотрим содержимое нашей таблицы. Введем команду

cleos get table hello.code hello.code mytable

Если после выполнения двух предыдущих команд вы увидите

значит все прошло успешно.

Попробуем добавить того же пользователя еще один раз.

cleos push action hello.code hi '["testuser"]' -p hello.code

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

и это действительно так).

Резюме

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

Назад Содержание Вперед