00:00
一起看一下这个ballad,这个投票的合约,前面大家可以看到,他首先上来先定义了一个vote,这个voter的类型比较特别,Structure,这是对结构体,我们可以看这个结构体里边它可以包含各种各样类型元素,我们看这个waterer里边包含什么呢?首先它包含一个这个weight,呃,其实就是权重的,对吧,在这简单的投票应用里边,那其实就不会有这种零点几,零点几的这种权重,那其实就是零和一,我们直接指定它是,呃,如果要是这个人可以投票,我们给他权重是一,不能投票的话,权重值为零,这样就可以了。然后第二个布尔行的叫mod。那其实这个很好理解了,一看就知道这就是标志,这个vote他到底投过票没有,要投过票的话再投,那不行了,不能投了,所以我们用一个标志,还有一个就是一个U8型的vote,那这个vote如果这是一个U8的话,那在我们这个实现里边,就是每一个提案都是用一个。
01:17
数字来表示,我们也不用说,呃,这个某一个提案到底是什么,到底是什么,那可能就是一号提案,二号提案,三号提案,直接上来之后,大家就是选选ABC,选选哪个就完了,所以确实很简单啊。呃,然后接下来是地址代理给的,就是说有没有授权给别人,有没有代理好,呃,接下来还有一个structure proposal,它里面的东西很简单,就一个东西,就是说这个提案他收获了多少票。那当然大家能想到它一个结构体,其实我们如果扩充的话,很容易想到可以把它扩充出来,就是说比方说我们加一个proposal的name呀,我们对吧,我们加一个它到底是什么内容啊,这其实都是可以加进去的,好这个我们就先先不说了啊。
02:12
然后我们看下边,下边就又定义了一个我们所谓的主席address Chairperson,那我们知道肯定这个在呃,初始化的时候,一定是要把合约的创建者。给定到这个person上的,肯定是这样做的。另外这个就非常重要,一个mapping出来了,Waters。大家可以看,就是它是一个映射,是地址到voter的一个映射。这是为什么呢?因为我们在投票的过程当中,在我们子代币就是呃,在我们这个子货币,或者说代币token的这个应用里边,合约里边,我们是每个人直接就用一个address来表示,就用一个地址来表示,然后它只有一个参数就是余额,所以说我们不用考虑别的,但是在这个里边呢。
03:13
他的东西太多了,我们既要有代理,还要有权重,还要表示他是否投过票,这么多东西我们就只好用一个结构体来表示,所以我们这里建立的对应关系不是说一个address对应一个余额那么简单,所以是得一个对应一个voter的结构体,所以这个就相当于是存储了我们所有的voter信息,大家可以理解成就像我们在传统的数据库里边建表一样,建了一个water表,在water表里面包含什么呢?它的所以的键值就是它的P值,就是地址。然后他后边的这个表里面的选项还有什么呢?还有他的权重,他是否投过票,他投给谁,还有就是他的代理,所有的这些信息都在里面对应起来一个映射关系,这是这样的一个关系啊,然后下面还有一个数据结构是叫proposal,那就是。
04:15
我们前面定义了一个结构体叫proposal,那我们既然有多个proposal,那当然就是要定义一个proposal的一个数组了,但是在这里大家看里边没有指定长度,这个就叫一个变长数组,所以就是说它的长度是可变的,我们可以调整好,这是基本的数据结构,我们可以看到啊,呃,就是这样的一个状况,然后接下来我们就看它的constructor,也就是我们所说的这个构造函数,它这里还是老式的写法,是用这个合约名来写的,它在构造的时候,我们看它传入了一个什么东西。它传入的东西很简单,就传入了一个number proposals,所以他是说一共有几个提案,他就传了这么一个数。
05:09
那大家想这个是不是会有问题啊。这有问题吗?或者大家再结合前面的这个数据结构看一看,我们如果要是想要一个投票的应用的话,首先得有投票人voter,哦,它是定义了,然后他还定义了voters,这个数据结构都有了没问题,那VOTER11定要能给提案投票,那也没问题,Vote可以去vote去指定他投,投给哪个,那每个提案呢,他也有一个,呃,Vote count就是去记录他一共收获了几票,那这个也是没问题的。大家是不是觉得他还缺什么东西吗?
06:08
可不可以多个提案投票,呃,在这个里边它的设计肯定是不可以的,因为对他只有一票,投完了之后,他就会把这个voteti肯定会就改变了嘛,对吧,所以你只只能改变一次,要不然你投完了之后他还没改,那就没有意义了,所以在咱们这个应用里面简单投票,一人一票,他这里边可能大家会发现就是说。我提案的这个proposal。它是利用一个proposal这个结构体类型的数组来保存的。那这样一个数组里边呢,它只保留了一个vote count,就是他得票得几票,那我怎么知道这个pro到底是哪个呢?啊对,所以大家看这就是为什么它这里没有定义一个mapping对吧,它没有没有定义说我从一个东西到这个proposal的一个一个映射,它是直接用数组,那其实也就是说我们直接用下标就好了,对吧,就比方说我们就甚至我们都可以不做转换,就直接说零号提案,一号提案,那就是它的第一个元素,第二个元素,这样拿出来就可以了,啊大概我们已经搞清楚它的这个数据结构了。
07:29
那在这种状况下,我们就可以知道,你既然是用下标做它的这个编号,然后呢,又不需要传入它的名称,我们只要知道一号二号提案就可以,所以呢,是不是我们只要初始化的它一共有几个就可以了?对吧,一号二号那都不用你再初始化了,那个下标本来就在那里的,你只要告诉我长度有几个就可以了,到时候我投几号我传几,你把那个对应下标的给我改就完了,所以他是这样的一个想法,所以他说简单投票真是简单到不能再简单啊。好,我们看一下这个构造函数里边他还做了什么事情,构造函数里边首先哦,那我们知道他会把这个message sender就是付给我们的Chairperson。
08:17
就是我们所谓的主席,最有权利可以分配票的这个人,就是一开始的创建者,然后之后他要把哦,就是主席的权重先制为一,对吧,大家看看得懂这个意思吧,就是voters,它是一个映射,是从一个address到vote的这个结构体的映射,所以主席的地址传进来votes Chairperson,那就是主席对应的这个结构体关系,对吧?呃,结构体的这个数,数据那点位,那就是主席的权重。初始化的时候大家注意啊,如果没有指定特殊的值,在扫里面初始化的时候,像这些东西都是零,所以上来之后要把这个给成一。
09:05
首先主席要有权利去投票嘛,然后proposals.lengths等于我们初始化的这个长度,所以这就相当于大家看这个变长的数组,它是通过什么方式去给它的长度的,它有一个length属性,然后直接给这个赋值就可以了。还用这种方式去把它确定长度。好,接下来我们就看它的四个主要的方法啊,四个主要的函数,首先看这个比较简单,就是give right to vote,就是主题有权利给别人权利去投票。那首先他给的参数呢,就是你想给谁这个权利对吧?给谁这个权利,首先我们要确定调用这个方法的人必须是主席,所以是message sender必须要等于主席,你要不等于的话,Return啊,当然它这个是用的if return的方式,我们等一下可以把它改写成require,对吧?然后还有一个就是说,那在什么情况下可以给他投票的权利呢?我还得保证他的。
10:09
没有投过票。就是votes to water这个地址上的motor结构,它的voted是保证它是零,他如果要是已经投过的话。所以是这样的一个要求,接下来呢,就给它voters to vote点位负权重一,只要是一,他就有权利投票,就拿这个来控制。所以这就是很简单的,这个给权利给他,那有些同学可能看的比较细的话,就会想到,那你voters里边它这么多参数,你判断了这个vote的,呃,他首先不能是投过票的,那假如说他的weight原先本来就是一,你还有必要再付一下一吗?呃,当然其实也是,也是没必要的,只不过在这里也不会影响我们的结果,对吧,顶多就是多做一个复制操作,咱们如果要是想要把这个也排除掉的话,那可以等一下我们再来一个require,就是防止这个多做不必要的操作也是可以的。
11:14
好,接下来我们就看这个delegate delegate就是我们所说的代理一个用户,他可以调这个方法,把自己的投票权委托给另外一个人,所以后面他要传进来一个参数就是他委托给谁。那我们看了下面这个,就稍微有点麻烦啊,就稍微有点麻烦。他首先先定义了一个vote型的变量。后边有一个storage,它是指明了这个结构体变量要存储在storage里面,就是storage,大家还记得吗?是我们的永久性的对存储空间,它指明它要存在这里。然后他定义了这个standard。
12:00
它等于谁呢?就等于votes mes sender,所以这其实就是说从这个地址里边把它转换成它对应的这个voter,对吧?从voters里面把它拿出来。是这样找到它对应的那个water的结构体。然后大家就看,如果说他是投票已经投过的,那就得return。你已经投过了,你还要把自己的投票权委托给别人,那不是那那那那不是这个对吧,已经投过了,你怎么还能干做做这这种事情呢,所以这个是要排除掉的,接下来我们看啊,如果说。这个稍微有点复杂啊,如果说voters to the delegate。不等于ADDRESS0。这首先是要保证就是。我们要委托的那个人,他的delegate。
13:02
不是零。然后需要voters to的delegate不是三。这是什么意思,大家觉得?先说votes two是谁?就是他要去委托给的那个人对吧。是不是这个意思?他要委托给的那个人,那他要委托给的那个人不能等于零,地址是什么意思?啊对,你肯定不能委托给零地址对吧,那林地址你委托给林地址去投票,这是这就是作废的一票了,所以说呃,这这是当然的一个事情,那另外后面就是说他委托给的那个人,他的委托人不能又是这个调用的发起人,也就是说对这是什么,这就是我委托给你,结果你又委托给了我,那咱们俩就别投了,对吧,死循环了,这就成了。
14:13
嗯,所以就成了这个样子,所以。它是这样的一个保证啊,就是不能允许这样。所以大家可以看它这里是还用了一个while,其实似乎我们也不需要用while是吧。大家觉得呢?大家觉得这里可以用衣服吗?这里可以用衣服吗?来,大家看一下,下面他是要干什么。他是要看我委托的那个人的委托有没有委委托人对吧。
15:00
他有没有委托人?如果说。那如果说他没有委托人的话,默认咱们不是说都是零吗?那他就委托给零地址了,所以说这是要求他不能委托给零地址,也就是说他得有委托人。有委托人,而且不是自己就是不不能返回来委托到我,那接下来就把这这个定义好的to。对,变成委托人的delegate。这这这是这是什么意思。我这里是删掉了什么东西吗?好像没有删掉什么东西是吧。对,但其实可以看到就是在这个过程当中。
16:01
其实在这个过程当中,他是要说这个代理权是可以传递的,他想说的是这件事情。就是说假如我委托的那个人,他又委托了人的话。我要把他委托的那个人现在也加成我的委托人。对吧,他是说委托可以传递,是这个意思。那大家可以理解,就是说相当于你委托了别人之后。你委托别人投票,那自己就不能再投票了,对吧,那自己如果要再投票的话,你这不就相当于重复投票投了两票了,所以说你一旦是委托了别人,而别人又委托了别人的话,那他其实你委托那个人,他是不能投票,他是委托给了另外一个人,所以相当于你的这一票也加到了就是另外一个人身上。是这样的意思,那所以想到这一点的话,大家就能理解这个well是什么意思了。对吧,这个意思就是说,假如说你的这个过程,所以大家看他是把to付给了这个votes to的delegate。
17:11
所以就是说我这里的to,等一下well回来之后还要再放到这里来voters to。对吧,本来这个to是传进来的,我要我要给的代理的那个人,然后现在呢,有一波,假如说他还有代理,那么现在的to就变成了他委托给的那个委托人。然后我再把这个土再来一圈,再返回去,再看他委托的这个委托人有没有委托别人,对,所以说这是个循环对吧,就是一层一层掉下去,所以这稍微有点绕,大家就是稍微理解一下这个逻辑。所以有有些地方可能就是逻辑稍微会会比较复杂一点啊,但其实大家想清楚的话,它代码就很简单,就这么两行,就就把这个委托人的委托人把这个事情搞定了,好接下来,所以他最后在这个well跑完之后,这个to应该是谁了,就不是传进来的to了,对吧,假如说呃,这里才有委托人的话。
18:20
对,这个图其实就已经变了,就变成了一直传递下去,委托人的委托人的委托人的委托人,到最后是一个没有委托人委托人对吧,就是很有点绕啊,就一层一层传递下去的投票权,最后归结到谁,这个图就是谁。是这样,他要确定这个to,然后他就确定这个to,假如说又回到自己了,他为什么还要又判断就是回到自己呢。对对对,最后那个人尽管说,呃,就是说对对对一直往下传递的话,其实还是有可能要要传回到自己的,对吧,就是这样的话,那也是不行的,你最后不能说这个投票又绕回来嘛,所以就return这,然后接下来所有的这些条件都满足了,好,那这个我们的这个to他到底要委托给谁,终于确定了,这几行绕这么半天,他就要做这件事情。
19:19
然后就3.mod等于处,我就先已经确定他投票了。已经投完了,然后他的delegate,但他不是真正的自己投,他是delegate给了别人,给到了to。好,那接下来我们就看到又定义了一个water的类型的变量,叫delegate to。他是谁呢?就是真正拿出to这个地址对应的votes里边的那个voter结构体,对应它的这个vote信息。嗯,这里就还得再看,如果说。这个人已经投过票的话。
20:00
那我就要把我委托过来的这这个票加到他的上面去,对吧,大家应该能能想到这一点对吧。对,但是这里大家注意看啊,就是他还没有就是proposals,这是他点vote,这是他投了哪个提案,所以大家看直接就对应它的下标。所以proposals里边的这个提案的what count就要加,但它不是直接的加一。而是说它要加3.wait。啊,这一方面也是比较通用,另外一方面也是为了防止出现就是异常,因为我们前面没有判断它的,就是三点位一定要是一对吧。有可能它是零。他本来就没有权利去投票,但是他随便调了一个这个方法,那我们不能给他加上。所以如果它位置是零的话,它就没有。走到这儿还是零。
21:01
那如果说他没有投过票的话,那当然了,就是他的位置再加上。就本来他是一,现在如果说我把我的这一票又给到你的话,你就变成两票了,到时候你把你的这个都投上去就可以。所以大家搞清楚就是这个delegate的这个方法,就是这是所有里边最最绕最复杂的一个方法,嗯,大家如果把这个搞清楚的话,应该别的都很好实现好,接下来我们看一下这个vote投票的这个环节,投票环节呢,肯定就是我们。传递进去我要给谁投票,一个用户调这个方法发起投票,然后你到底要给谁传递一个有吧,就是那个方案,呃,方案的序号。同样,我们还是定义一个vote,就是sander,把这个me sander的对应的那个voter结构体拿出来。然后我们要拿它的里面的属性对吧,看他是否投过票,如果已经投过票,那肯定就不能投了。
22:07
然后再看他的要选的这一个方案是不是超出了我们proposal的。那就是不存在的一个提案,那当然就没有用了,对吧?我们不能让它越界,所以如果要超出去的话,我们就return,大家注意看,这里是大于等于啊,就等于也要也要。对,所以说有零下标,所以说咱们要的其实就是就是呃,从零开始的那个下标,你不能直接就是前面十个提案,你就给十,那就已经越界了,好,那接下来他要投的是这个,那就vote等于处投投完了,然后他的vote等于这个提案。然后proposal里面的这个加上一票,加上它的with。不管谁投,都是这样。如果是他已经有代理的人的话,前面大家看到代理人的位置都已经加上了,对吧,所以说代理人一投就把这些票全投到这个人上面。
23:09
所以代理人其实他相当于也是投投一个方案,但是他投的时候可以把自己叠加的那些票全投到这个上面,一次性全投上面,所以这就是一个这样的一个事情。最后就简单了,那就是winning proposal,就是我们最后选取出来,到一定时间的时候,我们选取出来到底哪个方案获胜。那这个就不需要有什么有什么参数了,我们直接从里面选就可以对吧?那其实也很简单,大家一看就是一个for循环,我们就是把这个所有的proposal便利意思小于proposals.length的时候,然后把它把它一个一个拿出来。这这个大家要注意,就是这是因为这个数组本身是有烂的。所以我们可以便利,如果说它本来没有lengths,那我们真的就就就没办法去便利它了,所以这个是大家需要注意的,所以下面的这个选取的时候呢,那便利的过程当中呢,就是如果说proposals,当前的这个proposal,它的vote count,如果比。
24:16
就是当前,当前获胜的这个model com要大的话,这个默认是零啊,初始是零对吧,所以第一个上来之后,它肯定就是先先是获胜的,如果说后来我们看到的某一个提案,它的work count比这个要大,那我们就把winning world count制成当前它的这个work count,然后我们把winning proposal。来制成当前的这个序号,最终我们会return返回这个winning。所以大家可以看到这里定义的参数在这里是可以用的。对吧。但大家注意看这样的一个用法。好,呃,这个还是挺费劲的啊,我们直接把这个这么一看,其实确实还是稍微有点复杂的,我们把把它大概的过了一遍。
我来说两句