
Ahojte všetci!
V V tejto časti podrobne preskúmame, ako vytvárať a pracovať s dApp (decentralizovanou aplikáciou) v .
Teraz si trochu otestujeme rozobratý .
Krok 3. Testovanie účtu dApp

Aké problémy okamžite padnú do očí s Alicou dApp Účet?
Po prvé:
Boob a Cooper môžu náhodou poslať prostriedky na adresu dApp pomocou bežnej transfer transakcie a tým pádom k nim nebudú môcť získať prístup späť.Po druhé:
Alicu nijako neobmedzujeme pri výbere prostriedkov bez súhlasu Booba alebo Coobera. Ako si všimnite, všetky transakcie od Alice sa budú vykonávať.
Poďme opraviť druhú vec a zakázať Alici transfer transakcie. Nasadíme opravený skript:


Skúšame vybrať mince z dApp Alici a jej podpisu. Dostávame chybu:

Skúšame vybrať cez withdraw:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Skript funguje a s druhým bodom sme sa vyrovnali!
Krok 4. Vytvárame DAO s hlasovaním
Bohužiaľ, jazyk RIDE zatiaľ nepodporuje možnosti práce s kolekciami (slovníky-slovníkov, iterátory, reduktory a iné). Avšak pre akékoľvek operácie s plochými kolekciami key-value môžeme navrhnúť systém práce s reťazcami, a teda s kľúčmi a ich dešifrovaním.
Reťazce sa veľmi jednoducho konkatenujú, reťazce sa dajú deliť podľa indexov.
Poďme na testovacom príklade zostaviť a analyzovať reťazec a zistiť, ako to ovplyvní výsledok transakcie.
Zastavili sme sa pri tom, že Alica nemohla podpísať Transfer transakciu, pretože táto možnosť bola zablokovaná v @verifier pre takýto typ transakcií.
Poďme si precvičiť reťazce a potom to povolíme.
RIDE Reťazce
Transakcia je znova možná, vieme pracovať s reťazcami.


Celkovo máme všetko potrebné na napísanie zložitej logiky DAO dApp.
Dátové transakcie
Dátové transakcie:
„Maximálna veľkosť kľúča je 100 znakov a kľúč môže obsahovať ľubovoľné Unicode znaky vrátane medzier a iných neviditeľných symbolov. Reťazcové hodnoty majú limit 32 768 bajtov a maximálny počet možných položiek v dátovej transakcii je 100. Celková maximálna veľkosť dátovej transakcie je okolo 140 kB — pre porovnanie, takmer presne dĺžka hry Shakespeara ‘Romeo a Júlia’.“
Vytvárame DAO za nasledujúcich podmienok:
Aby startup získal financovanie, vyžaduje si getFunds() podporu minimálne 2 účastníkov — investorov DAO. Zobraziť bude možné presne toľko, koľko celkovo uviedli na hlasovaní vlastníci DAO.
Vytvorme 3 typy kľúčov a pridajme logiku na prácu s zostatkami v 2 nových funkciách vote a getFunds:
xx…xx_ia = investori, dostupný zostatok (vote, deposit, withdrawal)
xx…xx_sv = startupy, počet hlasov (vote, getFunds)
xx…xx_sf = startupy, počet hlasov (vote, getFunds)
xx…xx = verejná adresa (35 znakov)
Všimnite si, že v Vote sme museli aktualizovať viacero polí naraz:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet nám umožňuje robiť viacero záznamov v rámci jednej invokeScript transakcie.
Takto to vyzerá v key-value úložisku DAO dApp, po tom, čo Bob a Cooper doplnili ia- vklady:

Funkcia vkladu sa u nás mierne zmenila:

Teraz prichádza najdôležitejší moment v činnosti DAO — hlasovanie o projektoch na financovanie.
Bob hlasuje za projekt Neli s 500000 waveletmi:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
V databáze vidíme všetky potrebné záznamy pre adresu Neli:

Cooper tiež hlasoval za projekt Neli.

Pozrime sa na kód funkcie getFunds. Neli musí získať minimálne 2 hlasy, aby mohla vybrať prostriedky z DAO.

Neli sa chystá vybrať polovicu zverenej sumy:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Podarí sa jej to, to znamená, že DAO funguje!
Skontrolovali sme proces vytvorenia DAO v jazyku RIDE4DAPPS.
V nasledujúcich častiach sa podrobnejšie zaoberáme refaktoringom kódu a testovaním prípadov.
Úplná verzia 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
