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 using namespace eosio;class [[eosio::contract("addressbook")]] addressbook : public eosio::contract {public:addressbook(name receiver, name code,datastream 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>>;};

(三)源文件目录src

src是source的缩写,在目录中是源文件的意思。src文件夹用于存放源文件,即后缀为.cpp的文件,可称为源文件目录。

1、源文件

源文件中编写了合约动作(函数)的实现。

在addressbook项目中,合约的两个动作upsert和erase的实现,都写在addressbook.cpp文件中并放置于源文件目录src.

addressbook.cpp内容如下:

#include 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文件:

upsert

---spec-version: 0.0.1title: Upsertsummary: 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:

erase

---spec-version: 0.0.1title: Erasesummary: 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 pathif(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/addressbookcd 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 abiPublishing contract...executed transaction: 19d0acfa59ba196c4d355c95c7fd420f0e2343967540cff6ef4c5aa73c25cc3317672 bytes3566 us# eosio <= eosio::setcode {"account":"addressbook","vmtype":0,"vmversion":0,"code":"0061736d01000000019a022a60000060037f7f7f01...warn2023-01-18T07:16:08.250 cleos main.cpp:615print_rwarning: transaction executed locally, but may not be confirmed by the network yet

(二)添加源文件

如果项目中声明的方法增多,且隶属不同种类的功能,可能会用到多个源文件。将同类型或共同实现一类功能的方法实现编写在同一个源文件当中。

当项目有多个源文件时,应当按照如下方法进行添加:

  • 将.cpp文件放入src文件夹中

  • 修改src文件夹中的CMakeLists.txt文件,在add_contract后增加新的.cpp文件。

例如:

add_contract( addressbook addressbook addressbook.cpp xxx.cpp)

(三)添加头文件

如果项目中声明的方法增多,且隶属不同种类的功能,或者引入了单独的工具方法,可能会用到多个头文件。

当项目有多个头文件时,只需要将.hpp文件放入include文件夹中即可。src文件夹中的CMakeLists.txt中有如下语句:

target_include_directories( addressbook PUBLIC ${CMAKE_SOURCE_DIR}/../include )

该语句会将include文件夹中的所有文件作为头文件资源。

电脑访问DDC网络门户

ddc.bsnbase.com

-END-