
Ahoj všichni!
V V této části se podrobně podíváme na to, jak vytvořit a pracovat s dApp (decentralizovanou aplikací) v .
Teď si trochu vyzkoušíme rozebrané .
Krok 3. Testování dApp účtu

Jaké problémy se hned nabízejí s Alicí dApp Účet?
Za prvé:
Boob a Cooper mohou omylem poslat na adresu dApp prostředky pomocí běžného přenos transakce a tím pádem se k nim nebudou moci dostat zpět.Za druhé:
Neomezujeme Alici v výběru prostředků bez souhlasu Booba nebo Coopera. Jak si všimněte, všechny transakce od Alici budou prováděny.
Pojďme opravit druhé, zakázáním Alici přenos transakce. Nasazujeme opravený skript:


Zkoušíme vybrat mince z dApp Alice se její podpisem. Dostáváme chybu:

Zkoušíme vybrat přes withdraw:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Skript funguje a s druhým bodem jsme si poradili!
Krok 4. Vytvoření DAO s hlasováním
Bohužel, jazyk RIDE zatím nenabízí možnosti práce s kolekcemi (slovníky-slovníků, iterátory, reduktory atd.). Nicméně pro jakékoli operace s plochými kolekcemi key-value můžeme navrhnout systém práce se řetězci, tedy s klíči a jejich dešifrováním.
Řetězce je velmi snadné spojovat, lze je rozdělit podle indexů.
Pojďme si jako testovací příklad sestavit a rozebrat řetězec a zkontrolovat, jak to ovlivní výstup transakce.
Zastavili jsme se u toho, že Alice nemohla podepsat transakci Transfer, protože tato možnost byla zablokována v @verifier pro tento typ transakcí.
Pojďme se procvičit s řetězci a pak to povolíme.
RIDE Strings
Transakce je opět možná, umíme pracovat s řetězci.


Celkově máme vše potřebné pro napsání složité logiky DAO dApp.
Data Transactions
Data Transactions:
„Maximální velikost pro klíč je 100 znaků a klíč může obsahovat libovolné kódy Unicode, včetně mezer a dalších nepřenosných symbolů. Řetězcové hodnoty mají limit 32,768 bajtů a maximální počet možných položek v datové transakci je 100. Celkově je maximální velikost datové transakce přibližně 140 kB - pro srovnání, téměř přesně délka Shakespearovy hry 'Romeo a Julie'.“
Vytváříme DAO se následujícími podmínkami:
Aby startup získal financování, musí vyvolat getFunds() potřebná je podpora alespoň 2 účastníků – investorů DAO. Vypsat bude možné přesně tolik, kolik dohromady uvedli na hlasování majitelé DAO.
Vytvořme 3 typy klíčů a přidejme logiku pro práci s bilancemi ve 2 nových funkcích vote a getFunds:
xx…xx_ia = investoři, dostupný zůstatek (vote, deposit, withdrawal)
xx…xx_sv = startupy, počet hlasů (vote, getFunds)
xx…xx_sf = startupy, počet hlasů (vote, getFunds)
xx…xx = veřejná adresa (35 znaků)
Všimněte si, že v hlasování potřebujeme aktualizovat hned několik polí:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet nám umožňuje provést několik záznamů v rámci jednoho invokeScript transakce.
Takto to vypadá ve key-value úložišti DAO dApp poté, co Bob a Cooper doplnili ia-depozity:

Funkce pro vklad se mírně změnila:

Teď přichází nejdůležitější okamžik v aktivitě DAO — hlasování o projekty k financování.
Bob hlasuje pro projekt Neli s 500000 wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
Ve sbírce dat vidíme všechny potřebné záznamy pro adresu Neli:

Cooper také hlasoval pro projekt Neli.

Podívejme se na kód funkce getFunds. Neli potřebuje získat alespoň 2 hlasy, aby mohla vybrat prostředky z DAO.

Neli se chystá vybrat polovinu důvěřované částky:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
To se jí daří, což znamená, že DAO funguje!
Podívali jsme se na proces vytváření DAO v jazyce RIDE4DAPPS.
V následujících částech se podrobněji zaměříme na refaktoring kódu a testování případů.
Plná verze kódu v
# 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 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 ScriptResult(
WriteSet([
DataEntry(xxxInvestorBalance, currentAmount - amount),
DataEntry(xxxStartupVotes, currentVotes + 1),
DataEntry(xxxStartupFund, currentFund + amount)
]),
TransferSet([ScriptTransfer(i.caller, amount, unit)])
)
}
@Verifier(tx)
func verify() = {
match tx {
case t: TransferTransaction =>false
case _ => true
}
}
Zdroj: habr.com
