01
目的
本文档介绍了工程化开发智能合约项目的工程树目录,介绍了各个文件夹及文件的含义和用途。本文档将沿用之前文章中实现的地址簿合约内容,以初始化项目为例展开介绍,适合刚接触合约开发的开发人员用来了解智能合约项目,帮助其快速了解以及上手智能合约。
02
智能合约介绍
区块链作为一种分布式可信计算平台,去中心化是其最本质的特征。每笔交易的记录不可篡改地存储在区块链上。智能合约中定义可以在区块链上执行的动作 action 和交易 transaction 的代码。可以在区块链上执行,并将合约执行状态作为该区块链实例不可变历史的一部分。
因此,开发人员可以依赖该区块链作为可信计算环境,其中智能合约的输入、执行和结果都是独立的,不受外部影响。
03
术语解释
WebAssembly(WASM)
用于执行可移植二进制代码格式的虚拟机,托管在 nodeos 中。
应用程序二进制接口(ABI)
定义如何将数据编组进出 WebAssembly 虚拟机的接口。
CMake
CMake 是一个跨平台的安装(编译)工具,可以用简单的语句来描述所有平台的安装(编译过程)。它能够输出各种各样的 makefile 或者 project 文件,能测试编译器所支持的 C++特性,组态档取名为 CMakeLists.txt。熟悉某个集成开发环境(IDE)的开发者可以通过 CMake 用标准的方式建构软件。
04
目录树详解
(一)综述
在中移链合约常用开发介绍(二)多索引表的使用一文中,介绍了如何在智能合约中编写代码使用多索引表,完成一个可以增删改查的地址簿合约。该合约通过一个 addressbook.cpp 文件完成了所有代码,并不符合工程化开发的要求。
在本文中,我们将以工程化开发的方式重写这一合约,方便介绍各目录的含义和用途。
首先依旧是使用 eosio-init 命令创建 addressbook 项目。
eosio-init --path=. --project=addressbook
复制代码
初次被创建的项目结构如下:
├── CMakeLists.txt c++编译描述文件
├── README.txt 引导文件
├── include
│ └── addressbook.hpp 项目头文件,包含表结构定义和接口定义
├── ricardian
│ └── addressbook.contracts.md 李嘉图合约,合约的数字文档
└── src
├── CMakeLists.txt c++编译描述文件
└── addressbook.cpp 项目源文件,包含接口实现
复制代码
(二)头文件目录 include
智能合约以 C++程序呈现,文件夹也大多遵循 C++项目的命名习惯。其中 include 文件夹用于存放头文件,即后缀为.hpp 的文件,可称为头文件目录。
头文件中编写了合约动作(函数)的声明、表格(结构体)等。
在 addressbook 项目中,合约的构造函数、两个动作 upsert 和 erase 的声明、多索引表 people,都应写在 addressbook.hpp 文件中并放置于头文件目录 include.
addressbook.hpp 内容如下:
#include <eosio/eosio.hpp>
using namespace eosio;
class [[eosio::contract("addressbook")]] addressbook : public eosio::contract {
public:
addressbook(name receiver, name code, datastream<const char*> ds):
contract(receiver, code, ds) {}
ACTION upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state);
ACTION erase(name user);
private:
TABLE person {
name key;
std::string first_name;
std::string last_name;
uint64_t age;
std::string street;
std::string city;
std::string state;
uint64_t primary_key() const { return key.value; }
uint64_t get_secondary_1() const { return age; }
};
using address_index = eosio::multi_index<"people"_n, person, indexed_by<"byage"_n, const_mem_fun<person, uint64_t, &person::get_secondary_1>>>;
};
复制代码
(三)源文件目录 src
src 是 source 的缩写,在目录中是源文件的意思。src 文件夹用于存放源文件,即后缀为.cpp 的文件,可称为源文件目录。
1、源文件
源文件中编写了合约动作(函数)的实现。
在 addressbook 项目中,合约的两个动作 upsert 和 erase 的实现,都写在 addressbook.cpp 文件中并放置于源文件目录 src.
addressbook.cpp 内容如下:
#include <addressbook.hpp>
ACTION addressbook::upsert(name user, std::string first_name, std::string last_name, uint64_t age, std::string street, std::string city, std::string state) {
require_auth( user );
address_index addresses(get_first_receiver(),get_first_receiver().value);
auto iterator = addresses.find(user.value);
if( iterator == addresses.end() )
{
addresses.emplace(user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
});
}
else {
addresses.modify(iterator, user, [&]( auto& row ) {
row.key = user;
row.first_name = first_name;
row.last_name = last_name;
row.age = age;
row.street = street;
row.city = city;
row.state = state;
});
}
}
ACTION addressbook::erase(name user) {
require_auth(user);
address_index addresses(get_self(), get_first_receiver().value);
auto iterator = addresses.find(user.value);
check(iterator != addresses.end(), "Record does not exist");
addresses.erase(iterator);
}
复制代码
2、CMakeLists.txt
CMake 通过 CMakeLists.txt 配置项目的构建系统,配合使用 cmake 命令行工具生成构建系统并执行编译、测试,相比于手动编写构建系统要高效许多。
由 eosio-init 自动生成的 CMakeLists.txt 文件内容如下:
project(addressbook)
set(EOSIO_WASM_OLD_BEHAVIOR "Off")
find_package(eosio.cdt)
add_contract( addressbook addressbook addressbook.cpp )
target_include_directories( addressbook PUBLIC ${CMAKE_SOURCE_DIR}/../include )
target_ricardian_directory( addressbook ${CMAKE_SOURCE_DIR}/../ricardian )
复制代码
project 项目名
add_contract 用于构建智能合约并生成 ABI
第一个参数是合约名称;
第二个参数是 cmake 目标名称;
其余是构建合约所需的 CPP 文件(如本例中的 addressbook.cpp)。
target_include_directories 用于将头文件所在的目录添加到特定的 cmake 目标,指向 include 文件夹
target_ricardian_directory 用于将李嘉图合约所在的目录添加到特定的 cmake 目标,指向 ricardian 文件夹
(四)李嘉图合约目录 ricardian
在基于 EOSIO 的区块链环境中,李嘉图合约是一个伴随智能合约的数字文档,定义了智能合约与其用户之间交互的条款和条件,以人类可读的文本编写,然后对其进行加密签署和验证。对于人和程序来说,它都很容易阅读,并有助于为智能合约和其用户之间的交互中可能出现的任何情况提供清晰的理解。
本例中存在 upsert 和 erase 两个动作,可以按如下编写 addressbook.contracts.md 文件:
<h1 class="contract">upsert</h1>
---
spec-version: 0.0.1
title: Upsert
summary: This action will either insert or update an entry in the address book. If an entry exists with the same name as the specified user parameter, the record is updated with the first_name, last_name, street, city, and state parameters. If a record does not exist, a new record is created. The data is stored in the multi index table. The ram costs are paid by the smart contract.
icon:
<h1 class="contract">erase</h1>
---
spec-version: 0.0.1
title: Erase
summary: This action will remove an entry from the address book if an entry in the multi index table exists with the specified name.
icon:
复制代码
描述了每个动作的具体解释。
(五)CMakeLists.txt 文件
根目录下自动生成的 CMakeLists.txt 文件内容如下:
include(ExternalProject)
# if no cdt root is given use default path
if(EOSIO_CDT_ROOT STREQUAL "" OR NOT EOSIO_CDT_ROOT)
find_package(eosio.cdt)
endif()
ExternalProject_Add(
addressbook_project
SOURCE_DIR ${CMAKE_SOURCE_DIR}/src
BINARY_DIR ${CMAKE_BINARY_DIR}/addressbook
CMAKE_ARGS -DCMAKE_TOOLCHAIN_FILE=${EOSIO_CDT_ROOT}/lib/cmake/eosio.cdt/EosioWasmToolchain.cmake
UPDATE_COMMAND ""
PATCH_COMMAND ""
TEST_COMMAND ""
INSTALL_COMMAND ""
BUILD_ALWAYS 1
)
复制代码
文件中包含对 eosio.cdt 的导入,以及项目源文件、资源文件目录的导航。
(六)README.txt 文件
--- addressbook Project ---
- How to Build -
- cd to 'build' directory
- run the command 'cmake ..'
- run the command 'make'
- After build -
- The built smart contract is under the 'addressbook' directory in the 'build' directory
- You can then do a 'set contract' action with 'cleos' and point in to the './build/addressbook' directory
- Additions to CMake should be done to the CMakeLists.txt in the './src' directory and not in the top level CMakeLists.txt
复制代码
该文档的初始化内容中描述了如何编译并部署合约项目,根据实际的开发情况,可以在 README.txt 文件中对项目的结构、主要功能、使用方法进行描述,供参与开发或使用项目代码的人参考。
05
开发中的操作
(一)编译部署
在终端中使用如下命令:
cd ~/biosboot/genesis/addressbook
cd build/
cmake ..
make
复制代码
得到的最终结果后有如下提示,则为编译成功:
[100%] Linking CXX executable addressbook.wasm
[100%] Built target addressbook
[ 77%] No install step for 'addressbook_project'
[ 88%] No test step for 'addressbook_project'
[100%] Completed 'addressbook_project'
[100%] Built target addressbook_project
复制代码
此时,bulid 文件夹中已生成该合约的.wasm 和.abi 文件,使用 cleos set contract 部署合约:
cleos set contract addressbook /home/xxx/biosboot/genesis/addressbook/build/addressbook -p addressbook@active
复制代码
Reading WASM from /home/xxx/biosboot/genesis/addressbook/build/addressbook/addressbook.wasm...
Skipping set abi because the new abi is the same as the existing abi
Publishing contract...
executed transaction: 19d0acfa59ba196c4d355c95c7fd420f0e2343967540cff6ef4c5aa73c25cc33 17672 bytes 3566 us
# eosio <= eosio::setcode {"account":"addressbook","vmtype":0,"vmversion":0,"code":"0061736d01000000019a022a60000060037f7f7f01...
<4>warn 2023-01-18T07:16:08.250 cleos main.cpp:615 print_rwarning: transaction executed locally, but may not be confirmed by the network yet
复制代码
(二)添加源文件
如果项目中声明的方法增多,且隶属不同种类的功能,可能会用到多个源文件。将同类型或共同实现一类功能的方法实现编写在同一个源文件当中。
当项目有多个源文件时,应当按照如下方法进行添加:
例如:
add_contract( addressbook addressbook addressbook.cpp xxx.cpp)
复制代码
(三)添加头文件
如果项目中声明的方法增多,且隶属不同种类的功能,或者引入了单独的工具方法,可能会用到多个头文件。
当项目有多个头文件时,只需要将.hpp 文件放入 include 文件夹中即可。src 文件夹中的 CMakeLists.txt 中有如下语句:
add_contract( addressbook addressbook addressbook.cpp xxx.cpp)
复制代码
该语句会将 include 文件夹中的所有文件作为头文件资源。
电脑访问 DDC 网络门户
ddc.bsnbase.com
-END-
评论