
Здравейте на всички!
В В частите подробно разгледахме как да създадем и работим с dApp (децентрализирано приложение) в .
Нека сега да тестваме разгледаното .
Етап 3. Тестване на dApp акаунта

Какви проблеми веднага се набиват на очи с Alice dApp Акаунт?
Първо:
Boob и Cooper могат случайно да изпратят средства на адреса на dApp чрез обикновена прехвърляне транзакция и така не могат да получат достъп обратно до тях.На второ място:
Ние никак не ограничаваме Alice в извеждането на средства без съгласие с Boob или(и) Cooper. Както виждате на verify, всички транзакции от Alice ще се изпълняват.
Нека поправим второто, като забраним на Alice прехвърляне транзакции. Деплойваме поправения скрипт:


Опитваме се да изтеглим монети от dApp на Alice с нейния подпис. Получаваме грешка:

Опитваме да изтеглим чрез withdraw:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"withdraw",args:[{type:"integer", value: 1000000}]}, payment: []}))Скриптът работи и с втория пункт сме разбрали!
Етап 4. Създаваме DAO с гласуване
За съжаление, в езика RIDE не са предвидени възможности за работа с колекции (речници-словарници, итератори, редюсери и прочее). Обаче, за всякакви операции с плоски колекции ключ-стойност можем да проектираме система за работа с низове, съответно с ключовете и тяхната декодировка.
Низовете са много лесни за конкатенация, низовете могат да се разделят по индекси.
Нека в качеството на тестов пример да съберем и разгледаме низ и да проверим как това влияе на резултата от транзакцията.
Спираме се на това, че Alice не е могла да подпише транзакция за Transfer, тъй като тази възможност е била блокирана в @verifier за такъв тип транзакции.
Нека да се упражняваме с низовете и след това да разрешим това.
RIDE Низове
Транзакцията отново е възможна, можем да работим с низове.


В крайна сметка имаме всичко необходимо за написването на сложна логика DAO dApp.
Данни за транзакции
Данни за транзакции:
„Максималният размер за ключ е 100 символа, а ключът може да съдържа произволни Unicode кодови точки, включително интервали и други непечатаеми символи. Стойностите на низовете имат ограничение от 32 768 байта, а максималният брой възможни записи в данъчната транзакция е 100. Общият максимален размер на данъчната транзакция е около 140kb — за справка, почти точно дължината на пиесата на Шекспир 'Ромео и Жулиета'.”
Създаваме DAO с следните условия:
За да получи финансиране, стартапът трябва да предизвика getFunds() необходима е подкрепата на минимум 2 участници — инвеститори в DAO. Изход може да бъде точно толкова, колкото е общата сума, посочена на гласуването на притежателите на DAO.
Нека създадем 3 типа ключове и добавим логика за работа с балансите в две нови функции vote и getFunds:
xx…xx_ia = инвеститори, наличен баланс (vote, deposit, withdrawal)
xx…xx_sv = стар startups, брой гласове (vote, getFunds)
xx…xx_sf = стар startups, брой гласове (vote, getFunds)
xx…xx = публичен адрес (35 символа)
Обърнете внимание, че в Vote ни беше необходимо да обновим няколко полета:
WriteSet([DataEntry(key1, value1), DataEntry(key2, value2)]),WriteSet ни позволява да направим няколко записа в рамките на една invokeScript транзакция.
Ето как изглежда в key-value хранилището на DAO dApp, след като Боб и Купър попълниха ia-депозити:

Функцията за депозит малко се промени:

Сега настъпва най-важният момент в дейността на DAO — гласуването за проекти за финансиране.
Боб гласува за проекта Нели с 500000 wavelets:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"vote",args:[{type:"integer", value: 500000}, {type:"string", value: "3MrXEKJr9nDLNyVZ1d12Mq4jjeUYwxNjMsH"}]}, payment: []}))
В хранилището на данни виждаме всички необходими записи за адреса на Нели:

Купър също гласува за проекта Нели.

Нека разгледаме кода на функцията getFunds. Нели трябва да събере поне 2 гласа, за да може да изтегли средства от DAO.

Нели планира да изтегли половината от поверената и сума:
broadcast(invokeScript({dappAddress: address(env.accounts[1]), call:{function:"getFunds",args:[{type:"integer", value: 500000}]}, payment: []}))
Тя успява, т.е. DAO работи!
Разгледахме процеса на създаване на DAO на езика RIDE4DAPPS.
В следващите части ще се задълбочим в рефакторинга на кода и тестването на случаите.
Цялата версия на кода в
# 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
}
}
Източник: habr.com
