Zilliqa Scilla入门
Hello World
我们先来编写一份符合以下规范的经典HelloWorld.scilla
合约:
- 有一个由合约的创建者初始化的不变变量
owner
。该变量初始化后,其值就不能改变。owner
的类型是Address
。 - 有一个初始化为
""
的可变变量welcome_msg
,该变量为String
类型。这里的可变性是指即使在部署合约之后也可以修改变量的值。 - 只有
owner
才能通过接口setHello
修改welcome_msg
。该接口需要msg
(String
类型)作为输入方,并允许owner
设置发送给msg
的welcome_msg
的值。 - 有一个
getHello
接口可获取到welcome_msg
。getHello
不接收任何内容输入。
定义合约及(不)可变字段
使用contract
声明合约,让合约正式生效。contract
后面跟着合约名称,如例子中的HelloWorld
。因此,以下代码声明了一个HelloWorld
合约。
contract HelloWorld
注意
目前,Scilla合约只能包含单个合约声明,因此contract
后面的任何字符都是合约声明的一部分。换句话说,没有明确的关键字来声明合约定义的结束点。
合约声明后,接着要声明不可变变量,由()
来定义。
每个不可变变量以下列方式声明:vname: vtype
,其中vname
是变量名称,vtype
是变量类型。不可变变量用,
分隔。根据规范,合约将只有一个不可变的类型变量owner
,类型为Address
。参阅如下代码。
(owner: Address)
合约中的可变变量通过field
声明。
每个可变变量都按以下方式声明:
field vname : vtype = init_val
vname
是变量名称,vtype
是它的类型,init_val
是变量的初始值。
HelloWorld
合约带有一个可变参数welcome_msg
(String
类型),初始化为""
。
代码如下:
field welcome_msg : String = ""
至此,HelloWorld.scilla
合约形式如下,其中包含合约名称及其可变/不可变变量:
contract HelloWorld(owner: Address)
field welcome_msg : String = ""
注意
除了这些字段之外,Scilla中的合约都有一个隐式声明的可变字段_balance
(在合约创建时初始化),这代表了合约范围内的资金量。该字段可在合约过程中免费读取,但只能在显式转账时才能修改。
定义接口(即Transitions)
类似setHello
的接口在Scilla中称为transitions。Transitions类似于其他语言中的函数或方法。
注意
术语transition来自Scilla的通信自动机里的基础计算模型。Scilla的合约是一种带有状态的自动机。自动机的状态可以通过transition来改变,即transition接收先前的状态和输入信息,然后变为新的状态。查看维基百科条目 以了解transition系统的更多信息。
transition使用transition
来声明。transition用end
表示结束。transition
后面为transition名称,如例子中的setHello
。名称后面是用()
括起来的参数。每个参数用,
分隔,声明格式如下:vname : vtype
。按照规范,setHello
只需要一个string
类型的msg
参数。代码如下:
transition setHello (msg : String)
然后是transition的主体。例如,在transitionsetHello (msg : String)
中设定welcome_msg
,代码如下:
transition setHello (msg : String)is_owner = builtin eq owner _sender;match is_owner with | False => msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};msgs = one_msg msg;send msgs| True => welcome_msg := msg;msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};msgs = one_msg msg;send msgsend
end
首先看代码第二行,用builtin eq owner _sender
来判断owner
是否正确。builtin为
内置命令。
为了比较两个账户地址,我们使用了builtin
方法的eq
。这返回布尔值True
或False
。
注意
Scilla在内部定义了一些具有特殊语义的变量。这些特殊变量通常以_
为前缀。例如,_sender
指的是当前合约调用的帐户地址。
根据比较结果,transition根据匹配结果进入不同的分支路径,语法如下:
match expr with| x => expr_1
| y => expr_2
end
上面代码检查expr
是符合x
还是y
。如果expr
符合x
,那么下一步将是expr_1
,如果expr
符合y
,那么下一步将是expr_2
。简单地说,上面的代码实现了一条 if-then-else
指令。
调用者不是owner的情况
如果调用者不是owner
,则transition会走 False
分支,此时合约会输出一条消息。Scilla为输出消息定义了一种特殊类型 Message
。输出消息包含需要调用的其他合约信息(作为当前调用的一部分)或需要返回的值。
在这种情况下,输出消息是msg
中的一个错误代码not_owner_code
。具体来说,这种情况下的输出消息是:
msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};
输出消息参数格式为vname : value
组成,用;
分隔,其范围在{}
之内。每个输出消息必须有三个必填字段:_tag
、 _recipient
、_amount
,字段顺序无要求。_recipient
是消息发送地址。_tag
是_recipient
里要调用的transition的名称,_amount
是要转移给_recipient
的ZIL数量。
除了这些必填字段,消息可能还有其他字段。在这个示例中,消息使用code
字段来报告错误消息。
send
指令能承载一系列 Message
类型的内容,因此会用它来发送消息。在当前示例中,项内容仅有一个消息。总而言之,以下代码将创建并发送一条消息。
msgs = one_msg msg;
send msgs
one_msg
是一个实用函数,它允许创建消息列表并插入至msg
列表中。
调用者是owner的情况
如果调用者是owner
,则合约允许调用者设定可变变量welcome_msg
的值,并作为msg
的输入参数。代码如下:
welcome_msg := msg;
注意
写入可变参数是通过:=
完成的。
在上述例子中,合约使用set_hello_code
向调用者发送了一条消息。
库
Scilla合约可能带有一些辅助库,包含一些合约的纯功能(没有状态管理)组件。库会在合约的最前面使用library
加上库名称来声明。库声明如下例所示:
library HelloWorld
在这个示例中,库将使用标准用法let x = y in expr
来定义错误代码。
let not_owner_code = Int32 1let set_hello_code = Int32 2
该库还可以包括实用函数。例如,one_msg
函数创建了一个包含Message
类型项的列表 ,如下所示:
let one_msg =fun (msg : Message) =>let nil_msg = Nil {Message} in Cons {Message} msg nil_msg
在此阶段,合约代码如下:
library HelloWorldlet one_msg =fun (msg : Message) =>let nil_msg = Nil {Message} in Cons {Message} msg nil_msglet not_owner_code = Int32 1 let set_hello_code = Int32 2contract HelloWorld(owner: Address)field welcome_msg : String = ""transition setHello (msg : String)is_owner = builtin eq owner _sender;match is_owner with | False => msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : not_owner_code};msgs = one_msg msg;send msgs| True => welcome_msg := msg;msg = {_tag : "Main"; _recipient : _sender; _amount : Uint128 0; code : set_hello_code};msgs = one_msg msg;send msgsendend
最后
现在可以添加第二个transitiongetHello()
,让所有调用者都能收到welcome_msg
。声明方法类似setHello (msg : String)
,而getHello()
不用带任何参数。
transition getHello ()r <- welcome_msg;msg = {_tag : Main; _recipient : _sender; _amount : 0; msg : r};msgs = one_msg msg;send msgsend
注意
将内容传入可变变量的操作符为<-
。即示例中的r <- welcome_msg
。
下面给出了理想的完整合约范式:
(* HelloWorld contract *)(***************************************************)
(* Associated library *)
(***************************************************)
library HelloWorld
let one_msg =fun (msg : Message) =>let nil_msg = Nil {Message} in Cons {Message} msg nil_msg
let not_owner_code = Int32 1let set_hello_code = Int32 2
(***************************************************)
(* The contract definition *)
(***************************************************)contract HelloWorld
(owner: Address)field welcome_msg : String = ""
transition setHello (msg : String)is_owner = builtin eq owner _sender;match is_owner with | False => msg = {_tag : "Main"; _recipient : _sender; _amount : 0; code : not_owner_code};msgs = one_msg msg;send msgs| True => welcome_msg := msg;msg = {_tag : "Main"; _recipient : _sender; _amount : 0; code : set_hello_code};msgs = one_msg msg;send msgsend
endtransition getHello ()r <- welcome_msg;msg = {_tag : Main; _recipient : _sender; _amount : 0; msg : r};msgs = one_msg msg;send msgs
end
众筹
本节将介绍一个简单众筹活动的合约。在这个众筹中,项目发起人希望通过社区捐款筹集资金。
假设发起人(owner
)希望在预定周期(max_block
)内运行该活动。发起人还希望能设定一个最低筹集金额(goal
),否则项目无法启动。因此合约有三个不可变的变量owner
, max_block
和goal
。
如果所有者在规定时间内募集到目标金额,则该众筹被视为成功。如果众筹不成功,则捐款将会退还给所有项目支持者。合约包含两个可变变量:捐献者的账户地址和捐献金额组成的键值对backer
,以及一个布尔值标志funded
,指示在活动结束后发起人是否已获得了资金。
合约包含三个transition:捐献者为众筹捐款的方法Donate ()
,只允许发起人owner
宣布众筹金额且获取金额的方法GetFunds ()
,允许捐献者在活动不成功时索回捐款的方法ClaimBack()
。
完整的合约如下:
(***************************************************)
(* Associated library *)
(***************************************************)
library Crowdfunding
let andb =fun (b : Bool) =>fun (c : Bool) =>match b with | False => False| True => match c with | False => False| True => Trueendend
let orb =fun (b : Bool) => fun (c : Bool) =>match b with | True => True| False => match c with | False => False| True => Trueendend
let negb = fun (b : Bool) =>match b with | True => False| False => Trueend
let one_msg =fun (msg : Message) =>let nil_msg = Nil {Message} in Cons {Message} msg nil_msg
let check_update =fun (bs : Map Address Int) =>fun (_sender : Address) =>fun (_amount : Int) =>let c = builtin contains bs _sender in match c with | False => let bs1 = builtin put bs _sender _amount in Some {Map Address Int} bs1| True => None {Map Address Int}end
let blk_leq =fun (blk1 : BNum) =>fun (blk2 : BNum) =>let bc1 = builtin blt blk1 blk2 in let bc2 = builtin eq blk1 blk2 in orb bc1 bc2
let accepted_code = 1let missed_deadline_code = 2let already_backed_code = 3let not_owner_code = 4let too_early_code = 5let got_funds_code = 6let cannot_get_funds = 7let cannot_reclaim_code = 8let reclaimed_code = 9
(***************************************************)
(* The contract definition *)
(***************************************************)
contract Crowdfunding(* Parameters *)
(owner : Address,max_block : BNum,goal : Int)(* Mutable fields *)
field backers : Map Address Int = Emp Address Int
field funded : Bool = Falsetransition Donate ()blk <- & BLOCKNUMBER;in_time = blk_leq blk max_block;match in_time with | True => bs <- backers;res = check_update bs _sender _amount;match res with | None => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : already_backed_code};msgs = one_msg msg;send msgs| Some bs1 =>backers := bs1;accept;msg = {_tag : Main; _recipient : _sender; _amount : 0;code : accepted_code};msgs = one_msg msg;send msgsend| False => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : missed_dealine_code};msgs = one_msg msg;send msgsend
endtransition GetFunds ()is_owner = builtin eq owner _sender;match is_owner with | False => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : not_owner_code};msgs = one_msg msg;send msgs| True => blk <- & BLOCKNUMBER;in_time = blk_leq blk max_block;c1 = negb in_time;bal <- balance;c2 = builtin lt bal goal;c3 = negb c2;c4 = andb c1 c3;match c4 with | False => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : cannot_get_funds};msgs = one_msg msg;send msgs| True => tt = True;funded := tt;msg = {_tag : Main; _recipient : owner; _amount : bal;code : got_funds_code};msgs = one_msg msg;send msgsendend
end(* transition ClaimBack *)
transition ClaimBack ()blk <- & BLOCKNUMBER;after_deadline = builtin blt max_block blk;match after_deadline with | False => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : too_early_code};msgs = one_msg msg;send msgs| True => bs <- backers;bal <- _balance;(* Goal has not been reached *)f <- funded;c1 = builtin lt bal goal;c2 = builtin contains bs _sender;c3 = negb f;c4 = andb c1 c2;c5 = andb c3 c4;match c5 with | False => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : cannot_reclaim_code};msgs = one_msg msg;send msgs| True => res = builtin get bs _sender;match res with | None => msg = {_tag : Main; _recipient : _sender; _amount : 0;code : cannot_reclaim_code};msgs = one_msg msg;send msgs| Some v =>bs1 = builtin remove bs _sender;backers := bs1;msg = {_tag : Main; _recipient : _sender; _amount : v;code : reclaimed_code};msgs = one_msg msg;send msgsendendend
end
https://zhuanlan.zhihu.com/p/40044768
Zilliqa Scilla入门相关推荐
- Zilliqa官方挖矿指南中文版
挖矿指南 一般信息 欢迎来到代号为猫山王的Zilliqa测试网络-v3.我们邀请所有矿工成为公共节点,加入到猫山王的测试网络中.希望这次能让大家熟悉工作流程,并帮助我们在2019年1月底之前发现主网上 ...
- 用Construct 2制作入门小游戏~
今天在软导课上了解到了Construct 2这个神器,本零基础菜鸟决定尝试做一个简单的小游戏(实际上是入门的教程啊= = 首先呢,肯定是到官网下载软件啊,点击我下载~ 等安装完毕后我便按照新手教程开始 ...
- Docker入门六部曲——Swarm
原文链接:http://www.dubby.cn/detail.html?id=8738 准备工作 安装Docker(版本最低1.13). 安装好Docker Compose,上一篇文章介绍过的. 安 ...
- Docker入门六部曲——Stack
原文链接:http://www.dubby.cn/detail.html?id=8739 准备知识 安装Docker(版本最低1.13). 阅读完Docker入门六部曲--Swarm,并且完成其中介绍 ...
- Docker入门六部曲——服务
原文链接:http://www.dubby.cn/detail.html?id=8735 准备 已经安装好Docker 1.13或者以上的版本. 安装好Docker Compose.如果你是用的是Do ...
- 【springboot】入门
简介: springBoot是spring团队为了整合spring全家桶中的系列框架做研究出来的一个轻量级框架.随着spring4.0推出而推出,springBoot可以説是J2SEE的一站式解决方案 ...
- SpringBoot (一) :入门篇 Hello World
什么是SpringBoot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不 ...
- 入门指南目录页 -PaddlePaddle 飞桨 入门指南 FAQ合集-深度学习问题
入门指南目录页 -PaddlePaddle 飞桨 入门指南 FAQ合集 GT_Zhang关注 0.1012019.08.01 18:43:34字数 1,874阅读 795 Hi,欢迎各位来自Paddl ...
- 5 分钟入门 Google 最强NLP模型:BERT
BERT (Bidirectional Encoder Representations from Transformers) 10月11日,Google AI Language 发布了论文 BERT: ...
- 命名实体识别入门教程(必看)
关于开发自己的命名实体识别先期思路: 虽然网上有很多相关代码,但实际如何入门材料较少,故整理下: CRF:先期可以用人民日报语料库去做,步骤如下: https://blog.csdn.net/hude ...
最新文章
- jenkins rec cve-2019-1003000 复现
- Can't pickle local object 'CDLL.__init__.locals._FuncPtr'
- HTTP协议超级详解
- 妄想性仮想人格障害 新手教程 +改动器
- strictmath_Java StrictMath ulp()方法与示例
- 获取浏览器可视区域、屏幕的宽和高
- 数字电视制播设备间的文件交换格式
- android 层叠轮播,vue手写一个卡片化层叠轮播(支持滑动,移动端连续滚动,点击)...
- 多线程设计模式总结(一)
- su和sudo命令详解
- 网络流量分析之流量采集到流量还原
- Android TV 开发之 TV视频播放器
- win10锁屏后默认1分钟进入睡眠状态的解决办法
- 高清液晶触控一体机整合传播,优势明显
- OKR如何与绩效考核并行?
- 全球及中国水解蚕丝蛋白行业研究及十四五规划分析报告
- 八. geotrellis使用 矢量数据栅格化
- ActionListenner
- 宇宙浏览器汉化版——Universe
- Unity-技术美术 199-208
热门文章
- 很好的一篇讲解sql优化的文章
- Linux 配置rdate时间服务器方法
- hibernate4.0+版本和3.0+版本的区别总结
- 初识Memcache之安装与测试
- 间距margin、padding - bootStrap4常用CSS笔记
- SQL Server 获取日期是星期周几(默认从周日开始到周六 1-7)
- js date 加一天_需要知道的JS的日期的知识,都在这了
- 拓端tecdat|R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达自激过程时间序列
- 拓端tecdat|Matlab马尔可夫区制转换动态回归模型估计GDP增长率
- matlab 例题sin,matlab基础练习题(带答案).doc