Hei!
В
La oss nå teste det demonterte litt
Trinn 3. Testing av dApp-kontoen
Hvilke problemer skynder seg umiddelbart til stemmene med Alice Dapp Regnskap?
For det første:
Boob og Cooper kan ved et uhell sende penger til en dApp-adresse ved hjelp av en normal overføre transaksjoner og dermed ikke få tilgang til dem tilbake.Dernest:
Vi begrenser ikke på noen måte Alice fra å ta ut penger uten samtykke fra Boob og/eller Cooper. Siden, vær oppmerksom på å bekrefte, vil alle transaksjoner fra Alice bli utført.
La oss fikse 2 ved å utestenge Alice overføre transaksjoner. Distribuer det korrigerte skriptet:

Vi prøver å ta ut mynter med dApp Alice og signaturen hennes. Vi får en feilmelding:
Prøver å trekke ut via uttak:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))
Manuset fungerer og vi fant ut det andre punktet!
Trinn 4. Lag en DAO med stemmegivning
Dessverre gir RIDE-språket ennå ikke muligheten til å jobbe med samlinger (ordbøker, ordbøker, iteratorer, reduseringer, etc.). Dog for eventuelle operasjoner på flate samlinger nøkkelverdi vi kan designe et system for arbeid med strenger, henholdsvis med nøkler og deres dekryptering.
Strenger er veldig enkle å sette sammen, strenger kan skilles med indekser.
La oss samle og analysere strengen som et testtilfelle og sjekke hvordan dette vil påvirke resultatet av transaksjonen.
Vi bestemte oss for at Alice ikke kunne signere overføringstransaksjonen, siden denne muligheten ble blokkert i @verifier for denne typen transaksjoner.
La oss øve med strenger og så løse dette.
RIDE strenger
Transaksjonen er mulig igjen, vi vet hvordan vi jobber med strenger.

Totalt sett har vi alt du trenger for å skrive kompleks logikk DAO dApp.
Datatransaksjoner
Datatransaksjoner:
"Maksimal størrelse for en nøkkel er 100 tegn, og en nøkkel kan inneholde vilkårlige Unicode-kodepunkter inkludert mellomrom og andre ikke-utskrivbare symboler. Strengeverdier har en grense på 32,768 100 byte og maksimalt antall mulige oppføringer i datatransaksjoner er 140. Totalt sett er den maksimale størrelsen på en datatransaksjon rundt XNUMX kb – for referanse, nesten nøyaktig lengden på Shakespeares skuespill 'Romeo and Juliet' '."
Vi oppretter en DAO med følgende betingelser:
For at en startup skal få finansiering ved å ringe getFunds() støtte fra minst 2 deltakere - DAO-investorer kreves. Ta ut det vil være mulig nøyaktig så mye som summen angitt på stemmegivning DAO-eiere.
La oss lage 3 typer nøkler og legge til logikk for arbeid med saldoer i 2 nye funksjoner vote and getFunds:
xx…xx_ia = investorer, tilgjengelig saldo (stemme, innskudd, uttak)
xx…xx_sv = startups, antall stemmer (stemme, getFunds)
xx…xx_sf = startups, antall stemmer (stemme, getFunds)
xx…xx = offentlig adresse (35 tegn)
Merknad i Vote vi trengte å oppdatere flere felt samtidig:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),
WriteSet lar oss lage flere poster samtidig i én invokeScript transaksjoner.
Slik ser det ut i nøkkelverdilageret til DAO dApp etter at Bob og Cooper fylte på ia- innskudd:
Innskuddsfunksjonen vår har endret seg litt:
Nå kommer det viktigste øyeblikket i DAOs aktiviteter - stemme for prosjekter for finansiering.
Bob stemmer for Neli-prosjektet på 500000 XNUMX wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
I datalageret ser vi alle nødvendige oppføringer for Neli-adressen:
Cooper stemte også for Neli-prosjektet.
La oss ta en titt på funksjonskoden getFunds. Neli må samle minst 2 stemmer for å kunne ta ut midler fra DAO.
Neli kommer til å ta ut halvparten av beløpet som er betrodd henne:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Hun lykkes, det vil si at DAO fungerer!
Vi gjennomgikk prosessen med å lage en DAO på språket RIDE4DAPPS.
I de følgende delene skal vi se nærmere på koderefaktorering og casetesting.
Full kode inn
# 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
}
}
Kilde: www.habr.com