Hello World

我们先来编写一份符合以下规范的经典HelloWorld.scilla合约:

  • 有一个由合约的创建者初始化的不变变量 owner。该变量初始化后,其值就不能改变。owner的类型是 Address
  • 有一个初始化为""的可变变量 welcome_msg,该变量为String类型。这里的可变性是指即使在部署合约之后也可以修改变量的值。
  • 只有owner才能通过接口setHello修改welcome_msg。该接口需要msgString类型)作为输入方,并允许owner设置发送给msg的 welcome_msg的值。
  • 有一个getHello接口可获取到welcome_msggetHello不接收任何内容输入。

定义合约及(不)可变字段

使用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_msgString类型),初始化为""

代码如下:

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。这返回布尔值TrueFalse

注意
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_blockgoal

如果所有者在规定时间内募集到目标金额,则该众筹被视为成功。如果众筹不成功,则捐款将会退还给所有项目支持者。合约包含两个可变变量:捐献者的账户地址和捐献金额组成的键值对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入门相关推荐

  1. Zilliqa官方挖矿指南中文版

    挖矿指南 一般信息 欢迎来到代号为猫山王的Zilliqa测试网络-v3.我们邀请所有矿工成为公共节点,加入到猫山王的测试网络中.希望这次能让大家熟悉工作流程,并帮助我们在2019年1月底之前发现主网上 ...

  2. 用Construct 2制作入门小游戏~

    今天在软导课上了解到了Construct 2这个神器,本零基础菜鸟决定尝试做一个简单的小游戏(实际上是入门的教程啊= = 首先呢,肯定是到官网下载软件啊,点击我下载~ 等安装完毕后我便按照新手教程开始 ...

  3. Docker入门六部曲——Swarm

    原文链接:http://www.dubby.cn/detail.html?id=8738 准备工作 安装Docker(版本最低1.13). 安装好Docker Compose,上一篇文章介绍过的. 安 ...

  4. Docker入门六部曲——Stack

    原文链接:http://www.dubby.cn/detail.html?id=8739 准备知识 安装Docker(版本最低1.13). 阅读完Docker入门六部曲--Swarm,并且完成其中介绍 ...

  5. Docker入门六部曲——服务

    原文链接:http://www.dubby.cn/detail.html?id=8735 准备 已经安装好Docker 1.13或者以上的版本. 安装好Docker Compose.如果你是用的是Do ...

  6. 【springboot】入门

    简介: springBoot是spring团队为了整合spring全家桶中的系列框架做研究出来的一个轻量级框架.随着spring4.0推出而推出,springBoot可以説是J2SEE的一站式解决方案 ...

  7. SpringBoot (一) :入门篇 Hello World

    什么是SpringBoot Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程.该框架使用了特定的方式来进行配置,从而使开发人员不 ...

  8. 入门指南目录页 -PaddlePaddle 飞桨 入门指南 FAQ合集-深度学习问题

    入门指南目录页 -PaddlePaddle 飞桨 入门指南 FAQ合集 GT_Zhang关注 0.1012019.08.01 18:43:34字数 1,874阅读 795 Hi,欢迎各位来自Paddl ...

  9. 5 分钟入门 Google 最强NLP模型:BERT

    BERT (Bidirectional Encoder Representations from Transformers) 10月11日,Google AI Language 发布了论文 BERT: ...

  10. 命名实体识别入门教程(必看)

    关于开发自己的命名实体识别先期思路: 虽然网上有很多相关代码,但实际如何入门材料较少,故整理下: CRF:先期可以用人民日报语料库去做,步骤如下: https://blog.csdn.net/hude ...

最新文章

  1. jenkins rec cve-2019-1003000 复现
  2. Can't pickle local object 'CDLL.__init__.locals._FuncPtr'
  3. HTTP协议超级详解
  4. 妄想性仮想人格障害 新手教程 +改动器
  5. strictmath_Java StrictMath ulp()方法与示例
  6. 获取浏览器可视区域、屏幕的宽和高
  7. 数字电视制播设备间的文件交换格式
  8. android 层叠轮播,vue手写一个卡片化层叠轮播(支持滑动,移动端连续滚动,点击)...
  9. 多线程设计模式总结(一)
  10. su和sudo命令详解
  11. 网络流量分析之流量采集到流量还原
  12. Android TV 开发之 TV视频播放器
  13. win10锁屏后默认1分钟进入睡眠状态的解决办法
  14. 高清液晶触控一体机整合传播,优势明显
  15. OKR如何与绩效考核并行?
  16. 全球及中国水解蚕丝蛋白行业研究及十四五规划分析报告
  17. 八. geotrellis使用 矢量数据栅格化
  18. ActionListenner
  19. 宇宙浏览器汉化版——Universe
  20. Unity-技术美术 199-208

热门文章

  1. 很好的一篇讲解sql优化的文章
  2. Linux 配置rdate时间服务器方法
  3. hibernate4.0+版本和3.0+版本的区别总结
  4. 初识Memcache之安装与测试
  5. 间距margin、padding - bootStrap4常用CSS笔记
  6. SQL Server 获取日期是星期周几(默认从周日开始到周六 1-7)
  7. js date 加一天_需要知道的JS的日期的知识,都在这了
  8. 拓端tecdat|R语言和Python用泊松过程扩展:霍克斯过程Hawkes Processes分析比特币交易数据订单到达自激过程时间序列
  9. 拓端tecdat|Matlab马尔可夫区制转换动态回归模型估计GDP增长率
  10. matlab 例题sin,matlab基础练习题(带答案).doc