学习在RIDE和RIDE4DAPPS上编写Waves智能合约。 第2部分(DAO-权力下放的自治组织)


大家好!


第一部分中,我们详细研究了如何在Waves RIDE IDE中创建和使用dApp(去中心化应用程序)。


现在让我们测试一个小例子


阶段3.测试dApp帐户



Alice dApp帐户会立即发现哪些问题?
首先:
Boob和Cooper可能会通过正常的转账交易意外地将资金发送到dApp,因此将无法取回资金。

其次:
未经Boob和/或Cooper的同意,我们不限制Alice提取资金。 由于要注意验证,因此将执行来自Alice的所有事务。

第三:
只需将其publicKey替换为事务,任何人都可以从Alice帐户执行任何操作:
const unsignedTransferTx = transfer({ amount: 1, recipient: '3P6fVra21KmTfWHBdib45iYV6aFduh4WwC2', //senderPublicKey is required if you omit seed senderPublicKey: '6nR7CXVV7Zmt9ew11BsNzSvVmuyM5PF6VPbWHW9BHgPq' }) 

不幸的是,Waves智能合约尚未允许您阻止帐户中的传入交易,因此Boob和Cooper必须自己控制其传出交易。


让我们通过禁用 Alice设置除SetScriptTransaction之外的所有事务的方法来修复第二和第三通过在@Verifier中指定其公共密钥来禁用其余事务。 也就是说,我们仅允许Alice(作为dApp开发人员)在一段时间内更新/更正智能合约。


是的,爱丽丝可以随时更新脚本以获取更多权限并管理“用户”的手段,但是只有她可以做到这一点,所有用户都将看到未经授权更改合同的时刻并可以采取行动。 但是只要未阻止invokeScript以外的其他事务,Alice就需要信任客户端。

部署更正的脚本:


 @Verifier(tx) func verify() = { match tx { case d: SetScriptTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], base58'x51ySWMyhE8G2AqJqmDpe3qoQM2aBwmieiJzZLK33JW') case _ => true } 

我们正在尝试使用dApp Alice及其签名提取硬币。 我们得到错误:



我们尝试通过提款退出:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []})) 

该脚本有效,第二点我们就搞清楚了!


阶段4.我们通过投票创建DAO


不幸的是,RIDE语言尚未提供使用集合(字典,字典,迭代器,归约器等)的功能。 但是,对于具有平坦键值集合的任何操作,我们可以设计一个系统来分别使用带有键及其解密的字符串。
字符串很容易连接;可以按索引拆分字符串。


我们拥有编写复杂的DAO dApp逻辑所需的一切!


数据交易

数据交易:
“密钥的最大大小为100个字符,密钥可以包含任意Unicode代码点,包括空格和其他不可打印的符号。 字符串值的限制为32,768字节,数据事务中最大可能的条目数为100。总的来说,数据事务的最大大小约为140kb-供参考,几乎与莎士比亚戏剧《罗密欧与朱丽叶》的长度完全一样。 ”


创建具有以下条件的DAO:
为了使一家初创公司获得融资,调用getFunds()至少需要2位参与者(DAO投资者的支持。 可以提取与DAO所有者指示的总金额完全相同的金额。


让我们制作3种类型的键,并在2个新功能vote和getFunds中添加用于处理余额的逻辑:
xx ... xx _ia =投资者,可用余额(投票,存款,提款)
xx ... xx _sv =创业公司,投票数(投票,getFunds)
xx ... xx _sf =创业公司,投票数(投票,getFunds)
xx ... xx =公共地址(35个字符)

请注意,在“投票”中,我们需要一次更新几个字段:


 WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]), 

WriteSet允许我们在一个invokeScript事务中一次制作多条记录。


在Bob和Cooper补充存款之后,这就是DAO dApp键值存储中的样子



存款功能略有变化:



现在是DAO活动中最重要的时刻-为融资项目投票


Bob投票支持Neli的500,000个Wavelet项目:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) 

投票功能代码:


 @Callable(i) func vote(amount: Int, address: String) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let xxxStartupFund = address + "_" + "sf" let xxxStartupVotes = address + "_" + "sv" let flagKey = address + "_" + currentKey let flag = match getInteger(this, flagKey) { case a:Int => a case _ => 0 } let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let currentVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let currentFund = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } if (amount <= 0) then throw("Can't withdraw negative amount") else if (amount > currentAmount) then throw("Not enough balance!") else if (flag > 0) then throw("Only one vote per project is possible!") else WriteSet([ DataEntry(xxxInvestorBalance, currentAmount - amount), DataEntry(xxxStartupVotes, currentVotes + 1), DataEntry(flagKey, 1), DataEntry(xxxStartupFund, currentFund + amount) ]) } 

在数据仓库中,我们看到了Neli地址的所有必要条目:



库珀还投票支持Neli项目。



让我们看一下getFunds函数代码 。 Neli必须至少收集2票才能从DAO提取资金。



内莉将提取委托给她的款项的一半:


 broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []})) 


她成功了,那就是DAO的作品!


我们研究了使用RIDE4DAPPS语言创建DAO的过程。
在以下部分中,我们将更详细地讨论代码重构和案例测试。


Waves RIDE IDE中的完整代码版本


 # In this example multiple accounts can deposit their funds to DAO and safely take them back, no one can interfere with this. # DAO participants can also vote for particular addresses and let them withdraw invested funds then quorum has reached. # An inner state is maintained as mapping `address=>waves`. # https://medium.com/waves-lab/waves-announces-funding-for-ride-for-dapps-developers-f724095fdbe1 # You can try this contract by following commands in the IDE (ide.wavesplatform.com) # Run commands as listed below # From account #0: # deploy() # From account #1: deposit funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]})) # From account #2: deposit funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"deposit",args:[]}, payment: [{amount: 100000000, asset:null }]})) # From account #1: vote for startup # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) # From account #2: vote for startup # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []})) # From account #3: get invested funds # broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []})) {-# STDLIB_VERSION 3 #-} {-# CONTENT_TYPE DAPP #-} {-# SCRIPT_TYPE ACCOUNT #-} @Callable(i) func deposit() = { let pmt = extract(i.payment) if (isDefined(pmt.assetId)) then throw("can hodl waves only at the moment") else { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let newAmount = currentAmount + pmt.amount WriteSet([DataEntry(xxxInvestorBalance, newAmount)]) } } @Callable(i) func withdraw(amount: Int) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let newAmount = currentAmount - amount if (amount < 0) then throw("Can't withdraw negative amount") else if (newAmount < 0) then throw("Not enough balance") else ScriptResult( WriteSet([DataEntry(xxxInvestorBalance, newAmount)]), TransferSet([ScriptTransfer(i.caller, amount, unit)]) ) } @Callable(i) func getFunds(amount: Int) = { let quorum = 2 let currentKey = toBase58String(i.caller.bytes) let xxxStartupFund = currentKey + "_" + "sf" let xxxStartupVotes = currentKey + "_" + "sv" let currentAmount = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } let totalVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let newAmount = currentAmount - amount if (amount < 0) then throw("Can't withdraw negative amount") else if (newAmount < 0) then throw("Not enough balance") else if (totalVotes < quorum) then throw("Not enough votes. At least 2 votes required!") else ScriptResult( WriteSet([ DataEntry(xxxStartupFund, newAmount) ]), TransferSet([ScriptTransfer(i.caller, amount, unit)]) ) } @Callable(i) func vote(amount: Int, address: String) = { let currentKey = toBase58String(i.caller.bytes) let xxxInvestorBalance = currentKey + "_" + "ib" let xxxStartupFund = address + "_" + "sf" let xxxStartupVotes = address + "_" + "sv" let flagKey = address + "_" + currentKey let flag = match getInteger(this, flagKey) { case a:Int => a case _ => 0 } let currentAmount = match getInteger(this, xxxInvestorBalance) { case a:Int => a case _ => 0 } let currentVotes = match getInteger(this, xxxStartupVotes) { case a:Int => a case _ => 0 } let currentFund = match getInteger(this, xxxStartupFund) { case a:Int => a case _ => 0 } if (amount <= 0) then throw("Can't withdraw negative amount") else if (amount > currentAmount) then throw("Not enough balance!") else if (flag > 0) then throw("Only one vote per project is possible!") else WriteSet([ DataEntry(xxxInvestorBalance, currentAmount - amount), DataEntry(xxxStartupVotes, currentVotes + 1), DataEntry(flagKey, 1), DataEntry(xxxStartupFund, currentFund + amount) ]) } @Verifier(tx) func verify() = { match tx { case d: SetScriptTransaction => sigVerify(tx.bodyBytes, tx.proofs[0], base58'x51ySWMyhE8G2AqJqmDpe3qoQM2aBwmieiJzZLK33JW') case _ => false } } 



第一部分
Github代码
Waves RIDE IDE
赠款计划公告

Source: https://habr.com/ru/post/zh-CN447808/


All Articles