01
目的
本文档结合系统合约,介绍了合约部署的核心概念和步骤,包括合约部署过程以及setcode和setabi方法,帮助初学者和开发者快速了解和掌握EOS系统合约部署的相关知识。
02
概述
“eosio.system”智能合约是eos的系统命令合约。这个合约几乎实现了eos全部的系统命令,包括创建账户、资源质押、超级节点投票、域名竞拍等功能,它定义了区块链核心功能所需的结构和操作。
下文将介绍合约部署的相关知识。
03
环境准备
(一)一条正在运行且可访问的区块链
中移链(基于EOS)测试环境搭建
(二)确保本地钱包已打开并解锁
如何创建钱包:
https://developers.eos.io/manuals/eos/latest/cleos/how-to-gui...
04
合约部署介绍
(一)合约部署过程
假设您要将合约部署到的帐户名称是addressbook,请执行以下命令:
cleos set contract addressbook you_local_path_to/addressbook/ -p addressbook@active
命令详解:
set contract:cleos 工具的子命令,用于部署合约
addressbook:要部署合约的账户名称,即目标账户。请将 addressbook 替换为您自己的账户名称
you_local_path_to/addressbook/:合约的本地路径,指定了合约的位置。请将 /your_local_path_to/替换为您本地的实际路径,最好是绝对路径
-p addressbook@active:指定操作的权限。在这种情况下,-p参数后面的addressbook表示使用 addressbook 账户的active权限进行操作。请确保addressbook账户具有足够的权限来部署合约
通过运行这个命令,合约将被部署到addressbook账户。
得到如下结果:
Reading WASM from /home/xxx/biosboot/genesis/test/addressbook/addressbook.wasm...
Publishing contract...
executed transaction: ea09081dc5e42bd1f2b5abe619a7388e1e52ec16a91adb221f3ecb11fa566dde 17840 bytes 16689 us
# eosio <= eosio::setcode {"account":"addressbook","vmtype":0,"vmversion":0,"code":"0061736d01000000019a022a60000060037f7f7f01...
# eosio <= eosio::setabi {"account":"addressbook","abi":"0e656f73696f3a3a6162692f312e32000305657261736500010475736572046e616d...
部署EOSIO合约时,需经历以下步骤:
读取WAST/WASM文件:读取存放在build目录下的WAST/WASM文件,这些文件是要部署的合约的编译结果;
装配WASM:对于读取的WAST/WASM文件,进行装配,将合约的二进制代码准备好供后续操作使用。
将合约发布到区块链上
执行交易(合约也是一个交易),需要执行两个关键动作:
setcode:该动作用于在区块链上部署或更新账户的合约代码。
setabi:该动作用于为通过account名称标识的合约设置ABI(ApplicationBinaryInterface)。尽管在技术上ABI是可选的,但所有EOSIO工具都依赖于它以提供更便捷的合约交互体验。
执行完以上操作后,合约已成功部署到EOS区块链中的addressbook账户上。
从结果可以看出,调用
cleossetcontractaddressbookyou_local_path_to/addressbook/-paddressbook
等价于调用:
cleos push action addressbook setcode '[addressbook.wasm]' -p addressbook
cleos push action addressbook setabi '[addressbook.abi]' -p addressbook
(二)setcode方法介绍
setcode操作是EOSIO中的一种操作,用于在区块链上部署或更新账户的合约代码。它用于部署或更新与账户关联的智能合约。在使用setcode操作部署或更新合约时,会检查合约是否已经在运行代码。
具体而言,当执行setcode操作时,EOSIO会获取合约的wasm文件,并对其进行处理。在处理过程中,会计算wasm文件的哈希值,以表示该合约的唯一标识。这个哈希值可以用于比对合约的版本和完整性,以确保在部署或更新合约时没有出现错误或篡改。
1、setcode源码介绍
/**
* Set code action sets the contract code for an account.
*
* @param account - the account for which to set the contract code.
* @param vmtype - reserved, set it to zero.
* @param vmversion - reserved, set it to zero.
* @param code - the code content to be set, in the form of a blob binary..
*/
[[eosio::action]]
void setcode( name account, uint8_t vmtype, uint8_t vmversion, const std::vector& code ) {}
参数详解:
account:要部署或更新代码的账户名称
vmtype:此参数保留,应设置为零
vmversion:此参数保留,应设置为零
code:合约代码的二进制表示
2、setcode操作的实现
读取合约的WAST或WASM文件:
合约的开发人员提供合约代码的文件,这可以是WAST(WebAssemblyText)或WASM(WebAssemblyBinary)格式。
装配合约代码:
读取的合约文件需要进行装配,即将其转换为内部可执行的格式,以便区块链节点能够理解和执行。
存储合约代码:
装配后的合约代码将被存储在区块链上,并与指定的账号关联起来。这样,当其他用户或合约需要调用该合约时,可以通过发送交易指向该账号,并执行相应的合约操作。
通过setcode操作,合约的代码被安全地存储在区块链上,并与账号关联。这样,所有参与者都可以通过调用合约的操作来执行合约的功能,而节点可以验证合约的代码哈希值,确保合约的代码没有被篡改,从而保障合约在执行过程中的安全性和可靠性。
(三)setabi方法介绍
setabi操作用于为通过account名称标识的合约设置ABI(ApplicationBinaryInterface)。它在abi_hash_table索引中创建一个条目,使用account名称作为键,如果尚不存在,并将其值设置为ABI的哈希值。如果已存在,则更新现有account键的当前ABI哈希值。
1、setabi源码介绍
源码:
/**
* Set abi action sets the abi for contract identified by `account` name. Creates an entry in the abi_hash_table
* index, with `account` name as key, if it is not already present and sets its value with the abi hash.
* Otherwise it is updating the current abi hash value for the existing `account` key.
*
* @param account - the name of the account to set the abi for
* @param abi - the abi hash represented as a vector of characters
*/
[[eosio::action]]
void setabi( name account, const std::vector& abi )
{
abi_hash_table table(get_self(), get_self().value);
auto itr = table.find( account.value );
if( itr == table.end() ) {
table.emplace( account, [&]( auto& row ) {
row.owner = account;
row.hash = eosio::sha256(const_cast(abi.data()), abi.size());
});
} else {
table.modify( itr, eosio::same_payer, [&]( auto& row ) {
row.hash = eosio::sha256(const_cast(abi.data()), abi.size());
});
}
}
参数详解:
account:要部署ABI的目标账户的名称。它是一个name类型的参数,表示EOSIO中的账户名
abi:要设置的合约的ABI数据,以std::vector
源码详解:
通过abi_hash_table类创建一个名为table的表对象。该表用于存储账户的abi哈希值。
使用table.find()函数查找表中是否已经存在与目标账户相关的记录。
如果在表中没有找到与目标账户相关的记录(即itr==table.end()),则执行table.emplace()操作。
在table.emplace()中,通过lambda表达式将新的记录插入到表中。lambda表达式接收一个row参数,用于访问新插入的记录。在lambda表达式中,将row.owner设置为目标账户的名称,将row.hash设置为传入的ABI数据的SHA256哈希值。
如果在表中找到了与目标账户相关的记录(即itr!=table.end()),则执行table.modify()操作。
在table.modify()中,通过lambda表达式修改找到的记录。这里使用eosio::same_payer权限,确保修改操作的付款账户与原始记录的付款账户相同。在lambda表达式中,将row.hash更新为传入的ABI数据的SHA256哈希值。
ABI的实际存储方式在EOSIO中与常见的JSON文件存储方式存在较大的差异。相反,ABI被以一种被称为"原始ABI"的打包方式进行存储。
传统的开发环境中通常将ABI表示为JSON格式的文件,其中包含了合约的接口和数据结构定义。然而,在EOSIO中为了减少存储空间和提高效率,ABI被以一种更紧凑的格式进行存储。
“原始ABI”是指将ABI数据按照一定的规则进行打包和编码,以减少其存储空间。这种打包方式不同于常见的JSON文件格式,它更加紧凑,节省了存储空间,并提高了读取和解析的效率。原始ABI存储方式在EOSIO中被使用,以满足区块链中存储资源的限制和性能需求。
每个ABI都需要包含一组特定的字段,这些字段用于描述合约的不同方面,如版本信息、类型定义、结构定义、操作定义、表格定义等。这些字段的内容将被序列化为一种更节省空间的表示形式。
在EOSIO中,ABI用于定义合约的接口和数据结构。为了方便存储和传输,ABI需要被序列化为一种紧凑的二进制格式,以节省存储空间和网络带宽。
具体而言,一个典型的ABI包含以下字段:
version:ABI的版本信息,用于标识ABI的兼容性和支持的特性
types:类型定义,用于描述合约中使用的自定义类型,如结构体、枚举等
structs:结构定义,描述合约中的数据结构,包括结构体的名称、字段和类型等
actions:操作定义,描述合约中可执行的操作,包括操作的名称、参数和返回类型等
tables:表格定义,描述合约中的数据表格,包括表格的名称、字段和索引等
当执行setabi操作时,合约的ABI数据可以以两种形式之一进行提供:二进制文件或JSON文件。二进制文件是指以二进制格式存储的ABI数据文件,而JSON文件则是指以JSON格式存储的文本文件。
setabi操作需要能够处理这两种形式的ABI数据,并进行相应的转换。具体而言,它可以将二进制文件转换为JSON格式的ABI数据,或将JSON格式的ABI数据转换为二进制格式,即序列化和反序列化的过程。这样,合约开发人员可以选择以更适合他们的方式提供ABI数据,并将其转换为适合在区块链上存储和使用的形式。
END