EOS智能合约开发(一)

article/2025/9/13 6:34:12

  • 一、 智能合约功能
  • 二、 通讯模式
    • 2.1 运行机制
    • 2.2 内联通信
    • 2.3 延迟通信
    • 2.4 交易和动作
    • 2.5 交易确认
    • 2.6 动作处理程序和动作的apply上下文
    • 2.7 交易限制
  • 三、文件结构
    • 3.1 创建框架
    • 3.2 apply处理程序
    • 3.3 EOSIO_ABI 宏
  • 四、多索引数据库API
    • 4.1 EOSIO多索引API
    • 4.2 EOSIO多索引迭代器
    • 4.3 创建多索引表
    • 4.4 创建多索引表

一、 智能合约功能

EOSIO智能合约是在区块链上注册并在EOSIO节点上执行的软件,实现了“合约”的功能,合约行动请求帐目存储在区块链中。智能合约定义了接口(操作,参数,数据结构)和实现接口的代码。代码被编译成规范的字节码格式,使节点可以检索和执行。区块链存储合约的交易(例如,合法转移,游戏移动)。每份智能合约都必须附有一份李嘉图合约,其中定义了具有法律约束力的条款和条件。

二、 通讯模式

2.1 运行机制

EOSIO智能合约由一组操作和类型定义组成。操作定义指定并实现合约的行为,类型定义指定所需的内容和结构。 操作主要在基于消息的通信体系结构中运行:客户端通过向nodeos发送(推送)消息来调用操作。可以使用cleos命令完成。它也可以使用EOSIO发送方法(例如,eosio :: action :: send)来完成。 nodeos将操作请求分派给实现合约的WASM代码,该代码运行完之后继续处理下一个操作。
EOSIO智能合约可以彼此通信,例如,让另一个合约执行与当前交易的完成相关的某些操作,或者触发当前交易范围之外的未来交易。
EOSIO支持两种基本通信模型,内联和延迟。在当前交易中执行的操作是内联操作的示例,而触发的将来交易是延迟操作的示例。
合约之间的沟通应视为异步发生,异步通信模型可能导致垃圾邮件,资源限制算法将解析垃圾邮件。

2.2 内联通信

内联通信采用请求其他操作的形式,需要作为调用操作的一部分执行。其使用和原始交易的相同作用域和权限进行操作,并保证使用当前交易执行,即调用交易中的嵌套交易。 如果交易的任何部分失败,则内联操作将与交易的其余部分一起展开。无论成功与否,调用内联操作都不会在交易范围之外生成任何通知。

2.3 延迟通信

延迟通信在采用发送到对等交易的动作通知的形式。 根据生产者的判断,延迟的操作最多可以安排在稍后的时间运行,无法保证将执行延期操作。从始发交易的角度来看,即创建延迟交易的交易,它只能确定创建请求是否成功提交或者是否失败(如果失败,它将立即失败)。 延期交易具有发送合约的权限,交易可以取消延期交易。

2.4 交易和动作

动作表示单个操作,而交易是一个或多个动作的集合。 合约和帐户以行动的形式进行沟通。 如果要将操作作为一个整体执行,则可以单独发送操作,也可以以组合形式发送操作。
交易中可以有一个动作也可以有多个 ,当有多个操作时,其中一个操作失败,则这个交易失败。

2.5 交易确认

交易完成后,将生成交易收据,此收据采用散列形式。 接收交易散列并不意味着交易已被确认,它只意味着节点接受它而没有错误,这也意味着其他生产者很可能会接受它。通过确认,在交易历史记录中可以看到包含它的块编号的交易。

2.6 动作处理程序和动作的apply上下文

智能合约提供动作处理程序来执行所请求操作的工作。每次动作运行时,通过在合约实现中运行apply方法“应用”操作,EOSIO会创建一个新的动作“应用”上下文,动作在该上下文中运行。 下图说明了apply上下文动作的关键元素:

EOSIO网络中的每个节点都会获得每个合约中的每个动作的副本并运行。一些节点正在执行合约的实际工作,而其他节点正在处理以证明交易块的有效性。因此,重要的是合约能够确定“他们是谁”,或者基本上,他们在哪个环境下运行。在动作上下文中提供上下文标识信息,如上图中 的receiver, code, action。 receiver是当前正在处理该操作的帐户。code 是授权合约的帐户。 action是当前正在运行的操作的ID。
动作在交易中运作;如果交易失败,则必须回滚所有操作的结果。动作上下文的关键部分是当前交易数据,这包含交易头、交易中所有原始操作的有序向量、交易中上下文自由操作的向量,由code定义的可修复的上下文无关数据集(作为blob的向量提供)实现合约,以及blob向量的完整索引。
在处理动作之前,EOSIO会为动作设置一个干净的工作内存。操作的工作内存仅对该操作可用,即使对于同一交易中的操作也是如此。在执行另一个操作时可能已设置的变量在另一个操作的上下文中不可用。在动作之间传递状态的唯一方法是将其持久化并从EOSIO数据库中检索它。动作可能有许多影响, 其中包括:更改状态在EOSIO持久存储中保持不变;通知当前交易的收件人;将内联操作请求发送给新接收方;生成新的(延期)交易;取消现有的延期交易。

2.7 交易限制

每个交易必须在30ms或更短时间内执行。 如果交易包含多个操作,并且这些操作的总和大于30毫秒,则整个交易将失败。 在没有对其操作进行并发性要求的情况下,可以通过在单独的交易中包含CPU消费动作来规避这种情况。

三、文件结构

3.1 创建框架

$ eosiocpp -n ${contract}

以上将在./$ {conttract}目录中创建一个带有两个文件:

${contract}.hpp ${contract}.cpp

3.2 apply处理程序

每个智能合约都必须提供应用操作处理程序。 apply动作处理程序是一个侦听所有传入操作并执行所需行为的函数。 为了响应特定操作,code识别和响应特定操作请求。 apply使用receiver、code、action输入参数作为过滤器,来映射到实现特定操作的所需功能。 apply函数可以使用以下方法过滤code参数:

if (code == N(${contract_name}) {//对特定动作的处理
}

在给定code之后,可以使用如下方法过滤action参数:

if (action == N(${action_name}) {//your handler to respond to a particular action
}

3.3 EOSIO_ABI 宏

EOSIO_ABI宏封装了apply函数的基本动作:

#define EOSIO_ABI( TYPE, MEMBERS ) \
extern "C" { \
   void apply( uint64_t receiver, uint64_t code, uint64_t action ) { \
      auto self = receiver; \
      if( code == self ) { \
         TYPE thiscontract( self ); \
         switch( action ) { \
            EOSIO_API( TYPE, MEMBERS ) \
         } \
         /* does not allow destructor of thiscontract to run: eosio_exit(0); */ \
      } \
   } \
} \

只需要在宏中指定合约中的code和action名称即可,所有底层C代码映射逻辑都由宏生成。 如EOSIO_ABI(hello,(hi))中,hello和hi是来自合约的值。
apply函数记录所交付的行为,不做任何其他检查。 如果块生产者允许,任何人都可以随时提供任何操作。 如果没有任何必要的签名,合约将按消耗的带宽计费。
(1)apply:动作处理程序,它监听所有传入的动作并根据函数中的规范做出反应。 需要两个输入参数code和action。
(2)code filter:

if (code == N(${contract_name}) {// your handler to respond to particular action
}

(3)action filter:

if (action == N(${action_name}) {//your handler to respond to a particular action
}

(4)wast:部署到区块链的程序必须是wast格式,编译方法如下:

$ eosiocpp -o ${contract}.wast ${contract}.cpp

(5)abi:应用程序二进制接口(ABI)是一个JSON,在JSON和二进制之间转换用户操作。 abi还描述了如何将数据库状态转换为JSON或从JSON转换。 通过abi描述合约后,开发人员和用户将能够通过JSON无缝地与所开发的合约进行交互。可以如下编译产生:

$ eosiocpp -g ${contract}.abi ${contract}.hpp

ABI的一个例子:

{
//定义现有类型的别名列表,在这里,将name定义为account_name的别名。"types": [{"new_type_name": "account_name","type": "name"}],"structs": [{//在此处定义了transfer类型"name": "transfer","base": "",//from、to、quantity与"types"相对应"fields": {"from": "account_name","to": "account_name","quantity": "uint64"}},{"name": "account","base": "","fields": {"account": "name","balance": "uint64"}}],//在此定义了action:transfer,类型为transfer。告诉EOSIO遇到${account}->transfer时应该载入trransfer类"actions": [{"action": "transfer","type": "transfer"}],"tables": [{"table": "account","type": "account","index_type": "i64","key_names" : ["account"],"key_types" : ["name"]}]
}

四、多索引数据库API

EOSIO提供了一组服务和接口,使合约开发人员能够跨行动保持状态,从而保持事务边界。 如果没有持久性,当处理超出范围时,在处理操作和事务期间生成的状态将丢失。
对持久性服务的需求
acttion执行EOSIO合同的工作。 action在上下文的环境中运行。 如action apply上下文关系图中所示,操作上下文提供了执行操作所需的一些操作。 在处理操作之前,EOSIO会为操作设置一个干净的工作内存。 在操作之间传递状态的唯一方法是将其持久化并从EOSIO数据库中检索它。
持久性组件包括:
1)在数据库中保持状态的服务
2)增强查询功能以查找和检索数据库内容
3)服务的C ++ API,供合约开发人员使用
4)用于访问核心服务的C API,对库和系统开发人员有用

4.1 EOSIO多索引API

eosio :: multi_index对应传统数据库中的表,其中行是容器中的单个对象,列是容器中对象的成员属性,索引提供了对象的快速查找。 key与对象成员属性兼容。
传统的数据库表允许索引是表中某些列的用户定义函数。 eosio :: multi_index类似地允许索引是任何用户定义的函数(作为元素类型的类/结构的成员函数提供),但其返回值仅限于一组受限的支持键类型。
统的数据库表通常只有一个允许的唯一主键明确标识表中的特定行,并提供表中行的标准排序顺序。 eosio :: multi_index支持类似的语义,但eosio :: multi_index容器中对象的主键必须是唯一的无符号64位整数。 eosio :: multi_index容器中的对象按主键索引按无符号64位整数主键的升序排序。

4.2 EOSIO多索引迭代器

EOSIO持久性服务提供多索引迭代器。 与仅提供键值存储的其他区块链不同,EOSIO多索引表允许合同开发者保留按各种不同键类型排序的对象集合,这些键类型可以从对象内的数据派生。 这实现了丰富的检索功能。 最多可以定义16个二级索引,每个索引都有自己的排序和检索表内容的方式。
EOSIO多索引迭代器遵循C ++迭代器通用的模式。 所有迭代器都是双向const,const_iterator或const_reverse_iterator。 可以取消引用迭代器以提供对多索引表中对象的访问。

4.3 创建多索引表

以下是使用EOSIO Multi-Index表创建自己的持久数据的步骤:
1)使用C ++类或结构体定义对象。每个对象都在其自己的多索引表中。
2)在名为primary_key的类/结构中定义一个const成员函数,该函数返回对象的uint64_t主键值。
3)确定二级指数。最多支持16个附加索引。二级指数支持几种密钥类型:
idx64 - 原始64位无符号整数键
idx128 - 原始128位无符号整数键,或128位固定大小的词典键
idx256 - 256位固定大小的词典键
idx_double - 双精度浮点键
idx_long_double - 四倍精度浮点键
为每个二级索引定义一个密钥提取器。密钥提取器是用于从多索引表的元素获取密钥的函数。

4.4 创建多索引表

(1)实例化多索引表

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope}
}
EOSIO_ABI( addressbook, (myaction) )

(2)插入操作:

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope// add to table, first argument is account to bill for storageaddresses.emplace(_self, [&](auto& address) {address.account_name = N(dan);address.first_name = "Daniel";address.last_name = "Larimer";address.street = "1 EOS Way";address.city = "Blacksburg";address.state = "VA";});}
}
EOSIO_ABI( addressbook, (myaction) )

(2)修改:

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope// add to table, first argument is account to bill for storageaddresses.emplace(_self, [&](auto& address) {address.account_name = N(dan);address.first_name = "Daniel";address.last_name = "Larimer";address.street = "1 EOS Way";address.city = "Blacksburg";address.state = "VA";});auto itr = addresses.find(N(dan));eosio_assert(itr != addresses.end(), "Address for account not found");addresses.modify( itr, account payer, [&]( auto& address ) {address.city = "San Luis Obispo";address.state = "CA";});}
}
EOSIO_ABI( addressbook, (myaction) )

(3)删除

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope// add to table, first argument is account to bill for storageaddresses.emplace(_self, [&](auto& address) {address.account_name = N(dan);address.first_name = "Daniel";address.last_name = "Larimer";address.street = "1 EOS Way";address.city = "Blacksburg";address.state = "VA";});auto itr = addresses.find(N(dan));eosio_assert(itr != addresses.end(), "Address for account not found");addresses.erase( itr );eosio_assert(itr != addresses.end(), "Address not erased properly");}
}
EOSIO_ABI( addressbook, (myaction) )

(5)get
使用主键查找,返回对包含指定主键的对象的常量引用。

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope// add to table, first argument is account to bill for storageaddresses.emplace(_self, [&](auto& address) {address.account_name = N(dan);address.first_name = "Daniel";address.last_name = "Larimer";address.street = "1 EOS Way";address.city = "Blacksburg";address.state = "VA";});auto user = addresses.get(N(dan));eosio_assert(user.first_name == "Daniel", "Couldn't get him.");}
}
EOSIO_ABI( addressbook, (myaction) )

(6)find
使用主键在表中搜索现有对象。如果找不到具有主键primary的对象,则找到的对象的迭代器,其主键等于主键或引用表的结束迭代器。

#include 
using namespace eosio;
using namespace std;
class addressbook: contract {struct address {uint64_t account_name;string first_name;string last_name;string street;string city;string state;uint64_t primary_key() const { return account_name; }EOSLIB_SERIALIZE( address, (account_name)(first_name)(last_name)(street)(city)(state) )};public:addressbook(account_name self):contract(self) {}typedef eosio::multi_index< N(address), address > address_index;void myaction() {address_index addresses(_self, _self); // code, scope// add to table, first argument is account to bill for storageaddresses.emplace(_self, [&](auto& address) {address.account_name = N(dan);address.first_name = "Daniel";address.last_name = "Larimer";address.street = "1 EOS Way";address.city = "Blacksburg";address.state = "VA";});auto itr = addresses.find(N(dan));eosio_assert(itr != addresses.end(), "Couldn't get him.");}
}
EOSIO_ABI( addressbook, (myaction) )

http://chatgpt.dhexx.cn/article/onLQIx1U.shtml

相关文章

EOS系列 - WASM智能合约 - 特性

构造函数 addressbook(name receiver, name code, datastream<const char*> ds):contract(receiver, code, ds) {}#单例表&#xff08;code和scope都用receiver的表&#xff09;也可在初始化列表中实例化 singleton_example( name receiver, name code, datastream<c…

EOS智能合约开发(十五)EOS 状态机架构解析

好久没有写文章了&#xff0c;最近公司事情比较忙。非常感谢上次杭州团队一位负责EOS状态机的同事精彩分享。今天查阅很多资料后&#xff0c;整理这篇文章&#xff0c;希望对大家有所帮助。 EOS状态机是什么&#xff1f; 简单讲就是数据库&#xff0c;是EOS记录智能合约执行结…

接口测试时,输入所有参数的参数值后,接口返回“参数错误:所有参数都不能为空”

问题原因&#xff1a; 参数名称输入有误&#xff0c;前台&#xff08;pwd&#xff09;和后台&#xff08;password&#xff09;的参数名称不一致 &#xff0c;保持一致后即可

matlab 错误使用 connector.internal.autostart.run输入参数的数目不足。

matlab打开后提示&#xff1a; 错误使用 connector.internal.autostart.run输入参数的数目不足 原因&#xff1a;自己写的一个函数和matlab自带的函数重名了 参考&#xff1a;警告: 在 matlabrc 中初始化 Java 首选项失败 – MATLAB中文论坛MATLAB中文论坛MATLAB 基础讨论板块…

Matlab 自带遗传算法函数 ga() 运行报错:“ fitnessfcn() 输入参数的数目不足。”

原始程序 %主程序 m [1 2]; R [1.5 1]; epsilon [2^R(1)-1 2^R(2) - 1]; A [-1 epsilon(1); 0 -1]; b [0 0]; Aeq [1 1]; Beq 1;%matlab自带函数包 [x_optimal, fval] ga(fitnessfcn, 2, A, b, Aeq, Beq, [0;0], [1;1], []); ---------------------------------…

matlab函数参数不足,调用函数显示输入参数不足

问题描述.png (29.7 KB, 下载次数: 1) 2015-1-27 09:34 上传 %Gauss-Newton算法实现如下 function[x,minf] = GN(f,x0,var,eps)formatlong; ifnargin == 3 %如果没有设置eps,则eps=1.0e-6eps = 1.0e-6; end m = 0; S =transpose(f)*f; %trnspose是转…

matlab 函数不定参数,matlab function定义一个函数,但一直出来说输入参数数目不足。我用的是2014版本,不知道数目原因啊?...

www.mh456.com防采集。 h 5;g 1; % g取源0有问题% 被积函数2113可以化5261简成f 4102(a,b,r)(r*h-r^16532*cos(a-b))/(h^2r^2g^2-2*h*r*cos(a-b))^1.5;% 求积分quadv((r)quadv((b)quadv((a)f(a,b,r), 0, 2*pi), 0, 2*pi), 0, h) 错误在于&#xff0c;调用函数f( x )缺一个…

matlab plot输入参数太多,输入参数太多问题

版本&#xff1a;R2016a 利用SCE-UA调用新安江模型进行率定时&#xff0c;显示参数太多&#xff0c;求大神指导。 clear; clc; spathcd P0(textread([spath,\matlabxingshanjiangyu.txt])); PP0(2,:);%降水量(mm) EI0(textread([spath,\matlabxingshanzhengfa.txt])); EIEI0(2,…

参数输入太多

参数输入太多 在向platEMO平台添加新的算法时&#xff0c;可能会出现参数输入太多的错误提示 可能是由于输入的参数内包含逗号&#xff0c;比如 a[1,2],b[3,4] 从而造成”输入参数太多“的错误提示。

MATLAB S-function(教程分享) 报错 flag = 3(output), at time 0.0. 输入参数的数目不足。

前言 从上周开始用S-function做仿真&#xff0c;之前已经学过的&#xff0c;我觉得很好的教程网址是 【0基础直接带你上手matlab simulink仿真&#xff08;不是标题党&#xff0c;讲解超级细致用心&#xff09;&#xff08;非线性系统自适应控制器的搭建&#xff09;】 https…

matlab报错“输入参数的数目不足”

图片源自下方链接 原文链接MATLAB中出现参数数目不足的错误提示 - 爱上对方过后就哭了忆 - 博客园 (cnblogs.com)

错误使用 max 输入参数的数目不足。

相比大多数人熟悉的在调用自己编写的函数时出现的 “输入参数的数目不足” 问题&#xff0c;该情况下&#xff08;即在同一脚本或同一函数中&#xff09;也会出现matlab内部函数调用问题&#xff08;如max、min等&#xff09;&#xff0c;在编程时应注意此点&#xff01;

Matlab运行自定义函数显示“输入参数的数目不足”

问题描述 很多初学者在刚开始学习使用Matlab自定义函数时&#xff0c;可能遇到程序上没有问题&#xff0c;但一运行就显示输入参数的数目不足这个问题。 比如自定义一个函数&#xff1a;查找最小的m&#xff0c;使得m满足12…m > k&#xff08;k为任意一个整数&#xff09;…

浏览器播放视频倍速方法

1.右击播放页面 2.选择检查 4.选择console&#xff0c;如图&#xff1a; 5.在下面输入 &#xff1a;videojs.getPlayers("video-player").html5player.tech_.setPlaybackRate(1.5)&#xff0c;括号里的数字就是要加倍的倍速。

浏览器播放RTSP

浏览器播放RTSP 由于现在浏览器不直接支持RTSP&#xff0c;然后很多监控厂商如海康大华等又不提供RTMP的情况下&#xff0c;项目需要实现HTML中播放监控视频。之前做过转流的方案&#xff0c;在服务器中把RTSP转成RTMP&#xff0c;这种方案开发量大而且转码不稳定&#xff0c;…

0516---win10 chrome 浏览器播放视频没有声音的解决办法

win10 chrome 浏览器播放视频没有声音的解决办法 近期我的Win10 主机在使用 Chrome 浏览器播放搜狐视频的时候没有声音&#xff0c;而用 Edge浏览器播放同一视频就一切正常&#xff0c;初步判断是浏览器的设置引起的。通过以下操作步骤得以解决。 启动 chrome 浏览器&#xf…

html5播放mp4不见画面,浏览器播放mp4格式视频时只有声音看不到画面的原因及解决方法(精)...

做HTML5网页&#xff0c;要播放视频&#xff0c;视频格式为mp4&#xff0c;结果浏览器只能听到声音&#xff0c;看不到画面。 反复测试&#xff0c;多个浏览器均出现此问题。刚开始以为视频的格式不对&#xff0c;我是从MOV转的MP4格式&#xff0c;反复确认嵌入的视频确实是mp4…

360浏览器html5无法播放,win7系统360浏览器播放不了视频的解决方法

今天和大家分享一下win7系统360浏览器播放不了视频问题的解决方法&#xff0c;在使用win7系统的过程中经常不知道如何去解决win7系统360浏览器播放不了视频的问题&#xff0c;有什么好的办法去解决win7系统360浏览器播放不了视频呢&#xff1f;小编教你只需要 1、视频无法正常…

《浏览器播放RTSP方案》之 VLC插件播放RTSP视频流

VLC插件播放RTSP视频流 多版本chrome安装安装vlc软件开启浏览器的NPAPI设置编写测试页插件设置其他问题最后 目前网页对于RTSP流直接播放不支持&#xff0c;目前有插件和转流两种方式&#xff0c;这里针对vlc插件播放做一个简单的梳理。 查看官网教程 vlcWebPlugin, 得知其浏览…