EOSIO Source Code Analysis - EOSIO Contract Development Library

EOSIO contract development library

Through simple source code analysis, you can clearly see that the EOSIO contract development library is under the directory libraries. The functions of each library are as follows:
Note: Due to space constraints, only the most important and commonly used ones are introduced.

CDT: 总目录
 |----libraries: 合约开发库总目录
    |----boost: 经过裁剪的boost库
    |----eosiolib: 链提供的合约API接口
        |----capi: 访问链的宿主函数API接口
        |----contracts: 宿主函数API的部分封装
            |----action.hpp: 对action分装,调用内联action,使用此类
            |----contract.hpp: 合约基类
            |----multi_index.hpp: 合约中最重要的表结构的实现
            |----permission.hpp: 权限封装,合约中权限的判断,详细参考多签名合约 
            |----singleton.hpp: 单例实现,借助multi_index实现,里面只有一条记录
            |----transaction.hpp: 在EOSIO中可以通过合约给链发送指定于行的交易,该类实现了合约中交易基本的打包与发送
        |----core: 合约开发基本类型
            |----asset.hpp: 代币类型,代币符号类型
            |----check.hpp: 对于合约断言的分装
            |----crypto.hpp: 加密解密
            |----datastream.hpp: 结构打包程序,对结构体pack与unpack
            |----name.hpp: name基本类型封装
            |----print.hpp: 日志打印封装
            |----time.hpp: 实现对microseconds,time_point,block_timestamp封装
    |----libc: c库源码
    |----libc++: c++库源码

The structure of the table in the EOSIO contract

Discussions are multi_indexbased on structures, and singletontables are also multi_indeximplemented by relying on

definition

// 以token合约账号表为例,每个账户都独立建立一张代币余额表
struct [[eosio::table]] account {
    asset    balance; //账户余额
    uint64_t primary_key()const { return balance.symbol.code().raw(); }  // 以代币符号ID作为主键
};
typedef eosio::multi_index< "accounts"_n, account > accounts;

The code is expanded as follows:
In this sentence of code, the code is expanded as follows in turn:

// “”_n运算符重载,在name.hpp中实现
template <typename T, T... Str>
inline constexpr eosio::name operator""_n() {
   constexpr auto x = eosio::name{std::string_view{eosio::detail::to_const_char_arr<Str...>::value, sizeof...(Str)}};
   return x;
}
/**
  由于multi_index是模版类,在这里先对该模版进行了特化,特化参数指明了存储的对象类型与表名,表名必须是name类型
  注意在这里,特化参数必须能隐式转化成int类型才可以,所以这里实现了对""_n重载,看过name类的实现,uint64与name是可以进行互转的
*/ 
template<name::raw TableName, typename T, typename... Indices>
class multi_index
{
    ...
}

use and access

// 获取实例
/**
 * get_self()   :合约账号 
 * owner.value  :代币账户
*/
accounts to_acnts( get_self(), owner.value );

In the above code, directly call the multi_index constructor to complete the instantiation of the multi_index object to_acnts

multi_index( name code, uint64_t scope )
:_code(code),_scope(scope),_next_primary_key(unset_next_primary_key)
{}

So far, the entire account table has been confirmed

  • Contract account number (code): Determine which contract the table belongs to. The table data cannot be modified across contracts, but it can be accessed and read
  • Table name (table): determine the table name
  • Small category (scope): Determine the small category of records. In this example, scope is the account name, so token is equivalent to creating a separate balance table for each account
  • Primary key (primary_key): Determine the primary key of the record, and cooperate with scope to determine a unique record in the table
// 查询,添加,修改,删除
auto to = to_acnts.find( value.symbol.code().raw() );
if( to == to_acnts.end() ) {
    to_acnts.emplace( ram_payer, [&]( auto& a ){
        a.balance = value;
    });
} else {
    to_acnts.modify( to, same_payer, [&]( auto& a ) {
        a.balance += value;
    });
}

if (to != to_acnts.end()){
    to_acnts.erase( to );
}

In the above code, it is a bit long to expand sequentially through the multi_inex class. The main core code is as follows

// 向表中添加数据,具体步骤如下
/**
1 将对象obj,pack进入buffer,统计计算出数据包大小
2 计算获取主键,凑足确定一条记录的三个要素,payer.value是付费账号,eosio是资源消耗型付费
3 调用宿主函数db_store_i64将数据插入表
*/
template<typename Lambda>
const_iterator emplace( name payer, Lambda&& constructor ) {
    ...
    datastream<char*> ds( (char*)buffer, size );
    ds << obj;
    auto pk = obj.primary_key();
    i.__primary_itr = internal_use_do_not_use::db_store_i64( _scope, static_cast<uint64_t>(TableName), payer.value, pk, buffer, size );
    ...
}

Similarly, referring to the emplace function, the analysis of the modify, erase, and find functions shows that the three functions finally call the corresponding host function

  • modify: call the db_update_i64 function
  • erase: call the db_remove_i64 function
  • find: call the db_find_i64 function
// 遍历, 类似stl中list等的遍历,在multi_index中,实现了表的迭代器,这里可以参考stl的源码进行解读
for ( auto itr = to_acnts.begin(); itr != to_acnts.end(); itr++ ) {
    // do somethings
}

Summarize

Through the analysis of the EOSIO contract development library and contract examples, we interpreted some new knowledge points and gained a deeper understanding of the development of EOSIO contracts

  • The code format of the entire contract conforms to the C++ grammar specification and has a clear structure
  • You can use C++ basic bitter functions, such as strcmp, strlen, etc., and you can also use some common development libraries of boost, such as vector, list, etc.
  • The contract finally realizes the access to the chain and the database by importing the host function. We will discuss what a host function is later
  • In this article, we only analyze the most commonly used operations in contract development, and other richer operations require readers to do their own analysis

Guess you like

Origin blog.csdn.net/whg1016/article/details/126561999