AE中国

[教程13]如何为一个简单的投票AEPP创建Sophia合同?

一枝梅 发布于 05月28日 阅读 293 本文共3057个字,预计阅读时间需要8分钟。

教程概述

本教程将介绍一个智能合约,用Sophia ML编写,用于投票,但也提供了对语言本身的一般基础知识的另一个基本和更深入的理解。

Prequisites

  • 已安装docker(如果您没有,请查看此网站
  • 安装aecli(看一下本教程,提醒自己安装aecli的javascript版本)
  • 安装forgae(看看这部分

设置项目和开发环境

首先,我们必须初始化我们编写智能合约的项目。为了做到这一点,我们将使用forgae

智能合约

在Sophia ML中,我们有一个状态,它是存储数据链的地方 – 它是智能合约中唯一可以变异(覆盖)的部分。

我们要做的第一件事是定义我们在智能合约中使用的变量和类型。除此之外,我们将定义init()函数,基本上是构造函数,如果我们将它与Solidity智能合约进行比较。

contract Vote =
   record candidates = {
      voters: list(address) }
   record state = {
      votes: map(address, candidates) }
   public stateful function init() : state = {
votes = { } }

candidate记录将其选民存储在地址列表中。该state记录将所有投票存储在候选记录的地址映射(基本上是键值对)中。

我们从aepp的第一个功能开始 – 添加候选者:

 public stateful function add_candidate(candidate: address) : bool =
      is_candidate(candidate)
      true

这个函数的作用是将候选者传递给is_candiate()函数 – 将候选者的地址作为参数。然后该函数检查是否存在使用此地址定义的候选者并将其保存到具有初始空选民列表的状态中的投票映射(如果不是则)。

以下是我们将要使用的辅助函数:

 private stateful function is_candidate(candidate: address) =
      let candidate_found = lookupByAddress(candidate, state.votes, { voters = [] })
      if (size(candidate_found.voters) == 0)
         put(state{
            votes[candidate] = { voters = [] } })
   private function lookup_by_address(k : address, m, v) =
      switch(Map.lookup(k,m))
         None => v
         Some(x) => x

我们需要这样做,因为在Sophia ML中我们没有默认值0x0 / false,例如Solidity。因此,为了能够投票,我们需要首先添加我们可以投票的候选人。

如果我们在投票前没有先添加候选人,我们将会出现气体错误。

接下来我们创建一个看起来基本上像这样的投票函数:

public stateful function vote(candidate: address) : bool =       is_candidate(candidate)       let new_votes_state = Call.caller :: state.votes[candidate].voters       put(state{          votes[candidate].voters = new_votes_state }) true

我们通过内置访问事务发起者的地址,Call.caller并将其添加::到当前的选民列表中。

最后一步是创建一个get votes count函数。

public function count_votes(candidate: address) : int =       size(state.votes[candidate].voters)

我们需要使用size我们在下面定义为辅助函数的函数。这是代码:

private function size(l : list('a)) : int = size'(l, 0)

这是事情变得更复杂的地方,所以我将尝试解释这里发生的事情。

因为在索菲亚ML中我们没有.count.length没有得到列表的长度,我们需要使自己成为一个辅助函数,它在递增计数器时进行递归并迭代列表。

size函数被定义为接受一个列表,'a该列表是泛型类型的约定和返回类型int。在函数体中,我们size'在传递列表和计数器的初始值时调用函数。

private function size'(l : list('a), x : int) : int =
      switch(l)
         [] => x
         _ :: l' => size'(l', x + 1)

在这里,魔术发生了:我们使用switch带有2种情况的语句 [] => x– 它返回计数器的值,并在列表为空时中断递归。最后一部分_ :: l' => size'(l', x+1)匹配模式,并将第一个元素与列表和剩余部分分开。然后它递归地将列表的余数传递给同一个函数,同时递增计数器。

最终的智能合约代码最终看起来像这样:

contract Vote =
   record candidates = {
      voters: list(address) }
   record state = {
      votes: map(address, candidates) }
   public stateful function init() : state = {
      votes = { } }
   public stateful function vote(candidate: address) : bool =
      is_candidate(candidate)
      let new_votes_state = Call.caller :: state.votes[candidate].voters
      put(state{
         votes[candidate].voters = new_votes_state })
      true

   public function count_votes(candidate : address) : int =
      size(state.votes[candidate].voters)  

   private function size(l : list('a)) : int = size'(l, 0)
   
   private function size'(l : list('a), x : int) : int =
      switch(l)
         [] => x
         _ :: l' => size'(l', x + 1)

   public stateful function add_candidate(candidate: address) : bool =
      is_candidate(candidate)
      true

   private stateful function is_candidate(candidate: address) =
      let candidate_found = lookupByAddress(candidate, state.votes, { voters = [] })
      if (size(candidate_found.voters) == 0)
         put(state{
            votes[candidate] = { voters = [] } })

   private function look_up_by_address(k : address, m, v) =
      switch(Map.lookup(k,m))
         None => v
         Some(x) => x

结论

使用Sophia ML在æternity区块链上创建基本aepp非常简单。在我们使用ae-vote的情况下,我们偶然发现了一些棘手的部分,比如上面我们必须进行的递归迭代。但如果你熟悉这门语言,它会随着时间的推移而变得更容易。如果您遇到任何问题,请随时通过æternity论坛与我们联系。

如果你喜欢这篇文章,可在网站底部地址捐赠AE

喜欢 1
or

相关文章

更多

切换注册

登录

忘记密码 ?

切换登录

注册