
Ciao a tutti!
In parte abbiamo esaminato in dettaglio come creare e lavorare con dApp (applicazioni decentralizzate) in .
Ora proviamo a testare ciò che abbiamo esaminato .
Fase 3. Testare l'account dApp

Quali problemi saltano subito all'occhio con Alice dApp Account?
In primo luogo:
Boob e Cooper potrebbero accidentalmente inviare fondi all'indirizzo dApp tramite una normale transfer transazione e, in tal modo, non poterli recuperare.In secondo luogo:
Non limitiamo in alcun modo Alice nel prelevare fondi senza l'approvazione di Boob o/ed Cooper. Infatti, si noti che tutte le transazioni di Alice saranno eseguite.
Correggiamo il secondo punto, vietando ad Alice transfer le transazioni. Deployiamo lo script corretto:


Provando a prelevare monete con dApp Alice e la sua firma. Otteniamo un errore:

Provando 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 risolto anche il secondo punto!
Fase 4. Creiamo un DAO con voto
Purtroppo, nel linguaggio RIDE non sono ancora disponibili le funzionalità per lavorare con collezioni (dizionari-dizionari, iteratori, riduttori e simili). Tuttavia, per tutte le operazioni con collezioni piatte key-value possiamo progettare un sistema di gestione delle stringhe, inclusi le chiavi e la loro decodifica.
Le stringhe si concatenano facilmente e possono essere suddivise per indici.
Facciamo un esempio pratico raccogliendo e analizzando una stringa per vedere come influisce sulla transazione originale.
Abbiamo scoperto che Alice non poteva firmare la transazione di trasferimento poiché questa possibilità era bloccata in @verifier per questo tipo di transazioni.
Esercitiamoci con le stringhe e poi risolveremo questo problema.
RIDE Strings
La transazione è di nuovo possibile, sappiamo come gestire le stringhe.


In sintesi, abbiamo tutto il necessario per scrivere logiche complesse. DAO dApp.
Transazioni di dati
Transazioni di dati:
“La dimensione massima per una chiave è di 100 caratteri, e una chiave può contenere punti di codice Unicode arbitrari, compresi spazi e altri simboli non stampabili. I valori delle stringhe hanno un limite di 32.768 byte e il numero massimo di voci possibili nelle transazioni di dati è 100. In generale, la dimensione massima di una transazione di dati è di circa 140 kb — per riferimento, quasi esattamente la lunghezza dell'opera di Shakespeare 'Romeo e Giulietta'.”
Creiamo un DAO con le seguenti condizioni:
Perché una startup ottenga finanziamenti, deve attivare getFunds() il supporto di almeno 2 membri — investitori DAO. Visualizza sarà possibile esattamente quanto indicato in totale su votazione i proprietari del DAO.
Creiamo 3 tipi di chiavi e aggiungiamo la logica per gestire i saldi in due 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 immediatamente diversi campi:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)],WriteSet ci consente di effettuare più registrazioni all'interno di un'unica invokeScript transazione.
Ecco come appare nel key-value store dell'app DAO, dopo che Bob e Cooper hanno depositato ia-depositi:

La funzione di deposito è leggermente cambiata:

Adesso arriva il momento più importante per il DAO — votazione per i progetti da finanziare.
Bob vota per il progetto Neli con 500000 wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
Nel data store 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 dal 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 il DAO è attivo!
Abbiamo esaminato il processo di creazione di un DAO in RIDE4DAPPS.
Nelle prossime parti ci occuperemo più dettagliatamente del refactoring del codice e del testing dei casi.
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
