
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
