
Hallo zusammen!
In Teil haben wir im Detail besprochen, wie man dApps (dezentralisierte Anwendungen) in .
Lassen Sie uns nun etwas testen, was wir durchgegangen sind. .
Schritt 3. Testen des dApp-Kontos

Welche Probleme fallen sofort auf, wenn wir Alice dApp Konto betrachten?
ZunÀchst:
Boob und Cooper könnten versehentlich Mittel an die dApp-Adresse mit einer normalen Ăberweisung senden und hĂ€tten somit keinen Zugang zu diesen Mitteln.Zweitens:
Wir beschrĂ€nken Alice nicht darin, Mittel ohne Zustimmung von Boob oder Cooper abzuheben. Beachten Sie, dass alle Transaktionen von Alice sofort ausgefĂŒhrt werden.
Lassen Sie uns das Zweite beheben, indem wir Alice Ăberweisung die Transaktionen verbieten. Wir deployen das korrigierte Skript:

ïżŒ
Wir versuchen, Coins von dApp Alice mit ihrer Signatur abzuheben. Wir erhalten einen Fehler:

Wir versuchen es ĂŒber den Abzug:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Das Skript funktioniert und wir haben Punkt 2 geklÀrt!
Schritt 4. Erstellen eines DAO mit Abstimmung
Leider bietet die RIDE-Sprache derzeit keine Möglichkeiten zur Arbeit mit Sammlungen (WörterbĂŒcher von WörterbĂŒchern, Iteratoren, Reduzierer usw.). FĂŒr alle Operationen mit flachen Sammlungen SchlĂŒssel-Wert Wir können ein System zur Arbeit mit Strings entwerfen, einschlieĂlich der SchlĂŒssel und deren Dekodierung.
Strings lassen sich sehr einfach verketten, und sie können an Indizes aufgeteilt werden.
Lass uns ein Beispiel nehmen, um einen String zusammenzustellen und zu analysieren und zu prĂŒfen, wie sich das auf den Transaktionsausgang auswirkt.
Wir haben festgestellt, dass Alice die Transfertransaktion nicht unterzeichnen konnte, da diese Möglichkeit im @verifier fĂŒr diesen Transaktionstyp blockiert war.
Lass uns ein bisschen mit Strings ĂŒben und das Problem dann lösen.
RIDE Strings
Die Transaktion ist wieder möglich, wir können mit Strings arbeiten.

ïżŒ
Zusammenfassend haben wir alles, was wir fĂŒr die Erstellung komplexer Logik benötigen. DAO dApp.
Datentransaktionen
Datentransaktionen:
âDie maximale GröĂe fĂŒr einen SchlĂŒssel betrĂ€gt 100 Zeichen, und ein SchlĂŒssel kann beliebige Unicode-Codepunkte einschlieĂlich Leerzeichen und anderer nicht druckbarer Symbole enthalten. String-Werte haben eine Begrenzung von 32.768 Bytes und die maximale Anzahl möglicher EintrĂ€ge in einer Daten Transaktion betrĂ€gt 100. Insgesamt betrĂ€gt die maximale GröĂe einer Daten Transaktion etwa 140 KBâââvergleichbar mit der LĂ€nge von Shakespeares StĂŒck 'Romeo und Julia'.â
Erstellen Sie ein DAO unter den folgenden Bedingungen:
Damit ein Startup Finanzierung erhalten kann, muss getFunds() mindestens 2 Teilnehmer - DAO-Investoren - unterstĂŒtzen. Ausgeben Es wird genau so viel möglich sein, wie insgesamt angegeben auf der Abstimmung der DAO-EigentĂŒmer.
Lassen Sie uns 3 Arten von SchlĂŒsseln erstellen und die Logik fĂŒr die Arbeit mit den Bilanzen in zwei neuen Funktionen vote und getFunds hinzufĂŒgen:
xxâŠxx_ia = Investoren, verfĂŒgbarer Balance (vote, deposit, withdrawal)
xxâŠxx_sv = Startups, Anzahl der Stimmen (vote, getFunds)
xxâŠxx_sf = Startups, Anzahl der Stimmen (vote, getFunds)
xxâŠxx = öffentliche Adresse (35 Zeichen)
Beachten Sie, dass wir in Vote mehrere Felder gleichzeitig aktualisieren mussten:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet ermöglicht es uns, mehrere EintrÀge in einem einzigen invokeScript Transaktion zu machen.
So sieht es im key-value Speicher des DAO dApp aus, nachdem Bob und Cooper ia- Einzahlungen:

Die Einzahlungsfunktion hat sich leicht geÀndert:

Jetzt kommt der entscheidende Moment in der TĂ€tigkeit der DAO â die Abstimmung ĂŒber Projekte zur Finanzierung.
Bob stimmt fĂŒr das Projekt Neli mit 500000 Wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
Im Datenspeicher sehen wir alle erforderlichen EintrĂ€ge fĂŒr die Adresse Neli:

Cooper hat ebenfalls fĂŒr das Projekt Neli gestimmt.

Lassen Sie uns den Code der Funktion ansehen getFunds. Neli muss mindestens 2 Stimmen sammeln, um Gelder aus dem DAO abheben zu können.

Neli plant, die HĂ€lfte des ihr anvertrauten Betrags abzuheben:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Das gelingt ihr, was beweist, dass das DAO funktioniert!
Wir haben den Prozess zur Erstellung eines DAO in der Sprache betrachtet RIDE4DAPPS.
In den nÀchsten Teilen werden wir uns eingehender mit der Refaktorisierung des Codes und der Testung der FÀlle beschÀftigen.
Die vollstÀndige Version des Codes befindet sich in
# 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
}
}
Quelle: habr.com
