Pozdrav!
В
Idemo sada malo testirati rastavljeno
Faza 3. Testiranje dApp računa
Kakvi problemi odmah hrle na glasove s Alice dApp Račun?
Prvo:
Boob i Cooper mogu slučajno poslati sredstva na dApp adresu koristeći normalnu Transfer transakcije i stoga im neće moći pristupiti natrag.Drugo:
Ni na koji način ne ograničavamo Alice u povlačenju sredstava bez pristanka Booba i/ili Coopera. Budući da će, obratite pozornost na provjeru, sve transakcije od Alice biti izvršene.
Popravimo 2 banovanjem Alice Transfer transakcije. Implementirajte ispravljenu skriptu:

Pokušavamo povući kovanice s dApp Alice i njezinim potpisom. Dobivamo pogrešku:
Pokušaj povlačenja putem povlačenja:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))
Scenarij radi i skužili smo 2. točku!
Faza 4. Kreirajte DAO s glasanjem
Nažalost, jezik RIDE još ne pruža mogućnost rada s kolekcijama (rječnici, rječnici, iteratori, reduktori itd.). Međutim, za sve operacije na ravnim zbirkama ključ-vrijednost možemo dizajnirati sustav za rad sa stringovima, odnosno ključevima i njihovim dešifriranjem.
Stringovi se vrlo lako spajaju, stringovi se mogu odvojiti indeksima.
Prikupimo i raščlanimo niz kao testni slučaj i provjerimo kako će to utjecati na ishod transakcije.
Složili smo se s činjenicom da Alice ne može potpisati Transfer transakciju, jer je ta mogućnost bila blokirana u @verifier za ovu vrstu transakcije.
Vježbajmo sa žicama i onda riješimo ovo.
RIDE Žice
Transakcija opet moguća, znamo raditi sa stringovima.

Ukupno, imamo sve što vam je potrebno za pisanje složene logike DAO dApp.
Podatkovne transakcije
Podatkovne transakcije:
“Maksimalna veličina ključa je 100 znakova, a ključ može sadržavati proizvoljne Unicode kodne točke uključujući razmake i druge simbole koji se ne mogu ispisati. Vrijednosti niza imaju ograničenje od 32,768 bajtova, a maksimalan broj mogućih unosa u podatkovnoj transakciji je 100. Sveukupno, maksimalna veličina podatkovne transakcije je oko 140 kb — za referencu, gotovo točno koliko traje Shakespeareova drama 'Romeo i Julija' '.”
Stvaramo DAO uz sljedeće uvjete:
Kako bi startup dobio sredstva pozivom getFunds() potrebna je podrška od najmanje 2 sudionika - DAO investitora. povući bit će moguće točno onoliko koliko je zbroj naznačen na glasovanje Vlasnici DAO-a.
Napravimo 3 vrste ključeva i dodamo logiku za rad sa bilansima u 2 nove funkcije vote i getFunds:
xx…xx_ia = investitori, raspoloživo stanje (glasovanje, uplata, isplata)
xx…xx_sv = startupi, broj glasova (glasaj, getFunds)
xx…xx_sf = startupi, broj glasova (glasaj, getFunds)
xx…xx = javna adresa (35 znakova)
Obavijest u Voteu da smo morali ažurirati nekoliko polja odjednom:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),
WriteSet nam omogućuje da napravimo nekoliko zapisa odjednom unutar jednog invokeScript transakcije.
Ovako to izgleda u pohrani ključeva i vrijednosti DAO dApp-a nakon što su Bob i Cooper nadopunili ia- depoziti:
Naša funkcija depozita se malo promijenila:
Sada dolazi najvažniji trenutak u aktivnostima DAO-a - glasanje za projekte za financiranje.
Bob glasa za projekt Neli na 500000 valića:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
U pohrani podataka vidimo sve potrebne unose za Neli adresu:
Za projekt Neli glasao je i Cooper.
Pogledajmo kod funkcije getFunds. Neli mora skupiti najmanje 2 glasa da bi mogla povući sredstva iz DAO-a.
Neli će podići polovinu iznosa koji joj je povjeren:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Ona uspijeva, odnosno DAO radi!
Pregledali smo proces stvaranja DAO-a u jeziku RIDE4DAPPS.
U sljedećim dijelovima pobliže ćemo pogledati refaktoriranje koda i testiranje slučajeva.
Potpuni kod u
# 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
}
}
Izvor: www.habr.com