
Ciao a tutti!
In parte abbiamo esaminato in dettaglio come creare e lavorare con dApp (applicazione decentralizzata) in .
Adesso proviamo un po' il dApp analizzato .
Fase 3. Test del conto dApp

Quali problemi emergono subito con Alice dApp Account?
In primo luogo:
Boob e Cooper possono accidentalmente inviare fondi all'indirizzo dApp tramite una trasferire transazione e, in tal modo, non potranno riottenerli.In secondo luogo:
Non poniamo alcun limite ad Alice nel prelevare fondi senza l'approvazione di Boob o (e) Cooper. Come notate in verify, tutte le transazioni di Alice verranno eseguite.
Correggiamo la seconda, vietando ad Alice trasferire le transazioni. Deployiamo lo script corretto:


Proviamo a prelevare monete da dApp con la firma di Alice. Otteniamo un errore:

Proviamo a prelevare tramite withdraw:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Lo script funziona e abbiamo affrontato il secondo punto!
Fase 4. Creiamo un DAO con voto
Sfortunatamente, il linguaggio RIDE attualmente non supporta le operazioni con collezioni (dizionari di dizionari, iteratori, riduttori, ecc.). Tuttavia, per qualsiasi operazione con collezioni piatte key-value possiamo progettare un sistema di gestione delle stringhe, corrispondentemente con le chiavi e la loro decodifica.
Le stringhe possono essere concatenate molto facilmente e possono essere divise per indice.
Creiamo un esempio di test per comporre e analizzare una stringa e vediamo come influisce sull'esito della transazione.
Ci siamo fermati al fatto che Alice non poteva firmare la transazione di Transfer, poiché tale possibilità era bloccata in @verifier per quel tipo di transazioni.
Esercitiamoci con le stringhe e poi permettiamo questo.
RIDE Strings
La transazione è di nuovo possibile, sappiamo come lavorare con le stringhe.


In sintesi, abbiamo tutto il necessario per scrivere logiche complesse DAO dApp.
Data Transactions
Data Transactions:
“La dimensione massima per una chiave è di 100 caratteri, e una chiave può contenere punti di codice Unicode arbitrari, inclusi spazi e altri simboli non stampabili. I valori delle stringhe hanno un limite di 32,768 byte e il numero massimo di voci possibili in una transazione dati è 100. In generale, la dimensione massima di una transazione dati è circa 140kb — per riferimento, quasi esattamente la lunghezza dell'opera di Shakespeare 'Romeo e Giulietta'.”
Creiamo un DAO con le seguenti condizioni:
Affinché una startup possa ricevere finanziamenti, inviando getFunds() è necessaria la partecipazione di almeno 2 membri — investitori del DAO. Visualizza sarà possibile esattamente quanto indicato in totale nel voto dei proprietari del DAO.
Creiamo 3 tipi di chiavi e aggiungiamo la logica per gestire i saldi in 2 nuove funzioni vote e getFunds:
xx…xx_ia = investitori, saldo disponibile (vote, deposit, withdrawal)
xx…xx_sv = startup, numero di voti (vote, getFunds)
xx…xx_sf = startup, numero di voti (vote, getFunds)
xx…xx = indirizzo pubblico (35 caratteri)
Nota che in Vote abbiamo dovuto aggiornare diversi campi:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet ci consente di fare diverse registrazioni all'interno di una sola invokeScript transazione.
Ecco come appare nel database key-value della DAO dApp, dopo che Bob e Cooper hanno effettuato ia-depositi:

La funzione di deposito è cambiata leggermente:

Ora arriva il momento più importante dell'attività della DAO — votazione per i progetti da finanziare.
Bob vota per il progetto Neli per 500000 wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
Nel database vediamo tutte le registrazioni necessarie per l'indirizzo Neli:

Cooper ha anche votato per il progetto Neli.

Diamo un'occhiata al codice della funzione getFunds. Neli deve raccogliere almeno 2 voti per poter prelevare fondi dalla DAO.

Neli intende prelevare metà della somma a lei affidata:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Ci riesce, il che significa che la DAO funziona!
Abbiamo esaminato il processo di creazione della DAO nel linguaggio RIDE4DAPPS.
Nelle prossime parti ci occuperemo più dettagliatamente del refactoring del codice e del testing dei casi.
La versione completa del codice 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
}
}
Fonte: habr.com
