
Hallo zusammen!
Im Teil haben wir ausführlich betrachtet, wie man dApp (dezentralisierte Anwendung) in .
Lass uns jetzt ein wenig den analysierten .
Schritt 3. Testen des dApp-Kontos

Welche Probleme fallen sofort bei Alice ins Auge? dApp Konto?
Erstens:
Boob und Cooper könnten versehentlich Geld an die dApp-Adresse mit einer normalen transfer Transaktion senden und hätten somit keinen Zugriff darauf.Zweitens:
Wir schränken Alice in der Abhebung von Geldern nicht ein, ohne Rücksprache mit Boob oder Cooper. Beachte, dass alle Transaktionen von Alice ausgeführt werden.
Lass uns den zweiten Punkt korrigieren, indem wir Alice transfer Transaktionen verbieten. Wir deployen das korrigierte Skript:


Wir versuchen, Münzen mit Alice und ihrer Unterschrift von der dApp abzuheben. Wir erhalten einen Fehler:

Wir versuchen es mit withdraw:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Das Skript funktioniert und wir haben den zweiten Punkt geklärt!
Schritt 4. Erstellen einer DAO mit Abstimmung
Leider bietet die Sprache RIDE bisher keine Möglichkeiten zur Arbeit mit Sammlungen (Wörterbuch von Wörterbüchern, Iteratoren, Reducer usw.). Für alle Operationen mit flachen Sammlungen key-value können wir jedoch ein System zur Arbeit mit Zeichenfolgen entwerfen, entsprechend mit Schlüsseln und deren Entschlüsselung.
Zeichenfolgen können sehr einfach verkettet werden, sie können nach Indizes aufgeteilt werden.
Lass uns als Testbeispiel eine Zeichenfolge zusammenstellen und analysieren und überprüfen, wie sich das auf das Ergebnis der Transaktion auswirkt.
Wir sind dabei, dass Alice die Transfer-Transaktion nicht unterzeichnen konnte, da diese Möglichkeit im @verifier für diesen Transaktionstyp blockiert war.
Lass uns mit Zeichenfolgen üben und dann dies erlauben.
RIDE Zeichenfolgen
Die Transaktion ist wieder möglich, wir können mit Zeichenfolgen arbeiten.


Zusammengefasst haben wir alles Notwendige für das Schreiben komplexer Logik DAO dApp.
Daten-Transaktionen
Daten-Transaktionen:
„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 Grenze 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 - zum Vergleich, fast genau die Länge von Shakespeares Stück ‚Rom und Julia‘.“
Wir erstellen eine DAO unter folgenden Bedingungen:
Um Finanzierung für ein Start-up zu erhalten, indem wir getFunds() aufrufen, ist die Unterstützung von mindestens 2 Teilnehmern - DAO-Investoren erforderlich. Ausgabe Es wird genau so viel möglich sein, wie insgesamt beim Abstimmung von den DAO-Eigentümern angegeben wurde.
Lassen Sie uns 3 Arten von Schlüsseln erstellen und eine Logik für den Umgang mit Bilanzen in 2 neuen Funktionen vote und getFunds hinzufügen:
xx…xx_ia = Investoren, verfügbarer Betrag (vote, deposit, withdrawal)
xx…xx_sv = Startups, Anzahl der Stimmen (vote, getFunds)
xx…xx_sf = Startups, Anzahl der Stimmen (vote, getFunds)
xx…xx = öffentlicher Schlüssel (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 innerhalb eines invokeScript Transaktionsrahmens zu erstellen.
So sieht es im key-value Speicher der DAO dApp aus, nachdem Bob und Cooper aufgefüllt haben ia- Einlagen:

Die Einzahlungsfunktion hat sich leicht geändert:

Jetzt kommt der wichtigste Moment in der Tätigkeit der DAO — die Abstimmung für Projekte zur Finanzierung.
Bob stimmt für das Projekt Neli mit 500000 wavelets ab:
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 anschauen getFunds. Neli muss mindestens 2 Stimmen sammeln, um Geld aus der 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, das heißt, die DAO funktioniert!
Wir haben den Prozess der Erstellung einer DAO in der Sprache RIDE4DAPPS.
In den nächsten Teilen werden wir uns näher mit der Code-Umstrukturierung und den Testfällen befassen.
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
