ICON Workshop — Voting DAPP

ibriz inc подготовил еще один обучающий семинар, который не был представлен на ICON workshop во время SF blockchain week. К сожалению, нет никакой презентации, но если вы изучили все предыдущие уроки: часть 1, часть 2 и часть 3, это должно быть довольно легко для вас.

Это простое голосование DAPP с помощью ICONex кошелька, один голос на кошелек, и результаты отображаются на веб-странице. Давайте рассмотрим код в этом уроке, сначала получим файлы проекта из их репозитория gitlab:

$ git clone https://gitlab.com/ibriz/icon-polling.git

SCORE
Взгляните на наш SCORE polling.py

from iconservice import *

TAG = 'PollingScore'


class PollingScore(IconScoreBase):
_VOTE_YES = "VOTE_YES"
_VOTE_NO = "VOTE_NO"
_VOTE_TRACK = "VOTE_TRACK"

@eventlog(indexed=1)
def Vote(self, _by: Address, _vote: int):
pass

def __init__(self, db: IconScoreDatabase) -> None:
super().__init__(db)
self._vote_talley_yes = VarDB(self._VOTE_YES, db, value_type=int)
self._vote_talley_no = VarDB(self._VOTE_NO, db, value_type=int)
self._voteMapping = DictDB(self._VOTE_TRACK, db, value_type=int)

def on_install(self) -> None:
super().on_install()

def on_update(self) -> None:
super().on_update()

@external
def vote(self, _vote: int):

# make sure vote is allowed just once
if self._voteMapping[self.msg.sender] == 0:
self._voteMapping[self.msg.sender] = _vote
self.Vote(self.msg.sender, _vote)
Logger.debug(f'vote of {_vote} registered for {self.msg.sender}', TAG)
# 1 is yes and 2 is no
if _vote == 1:
self._vote_talley_yes.set(self._vote_talley_yes.get() + 1)
elif _vote == 2:
self._vote_talley_no.set(self._vote_talley_no.get() + 1)
else:
Logger.debug(f'vote sent {_vote} is not valid', TAG)
self.revert('invalid vote!')
else:
Logger.debug(f'already voted', TAG)
self.revert('already voted!')

# Check sender's vote
@external(readonly=True)
def get_vote(self) -> dict:
result = self._voteMapping[self.msg.sender]
Logger.debug(f'get_vote for {self.msg.sender} is {result}', TAG)
return {'vote': result}

# Get vote results
@external(readonly=True)
def get_vote_talley(self) -> dict:
result = {"yes": self._vote_talley_yes.get(), "no": self._vote_talley_no.get()}
Logger.debug(f'get_vote_talley {result}', TAG)
return result

Я сделал несколько дополнительных комментариев к исходному файлу, оценка очень проста. Для получения голосов требуется выполнитьvote по контракту, происходит проверка, проголосовал ли отправитель, затем регистрирует результаты, вот и все.

Сначала вы можете протестировать локально, мы пропустим это, и сделаем развертывание непосредственно в testnet.

Необходимо исправить ошибку tbears_cli_config_testnet.json так как он использует некоторые устаревшие коды:

config/tbears_cli_config_testnet.json
{
"uri": "https://bicon.net.solidwallet.io/api/v3",
"nid": "0x3",
"keyStore": null,
"from": "hxe9d75191906ccc604fc1e45a9f3c59fb856c215f",
"to": "cx0000000000000000000000000000000000000000",
"deploy": {
"stepLimit": "0x5a000000",
"contentType": "zip",
"mode": "install",
"scoreParams": {
}
},
"txresult": {},
"transfer": {}
}

затем осуществите развертывание непосредственно в testnet:

$ tbears deploy polling -k keystores/keystore1.json -c config/tbears_cli_config_testnet.json
# password p@ssword1

Проверьте результат как обычно:

$ tbears txresult 0x3948fbf6ca67dd04801283c47123064e54d7324a358add345c8c4fd05fa1d385 -u https://bicon.net.solidwa
llet.io/api/v3

держите scoreAddress где-нибудь, нам нужно вставить адрес в нескольких местах:

"scoreAddress": "cx4e739b203b92498b6b6fb6d205f1cb62a6d4f88d"

Поправьте webapp/main.py и измените default_score на наш новый адрес SCORE, также дайте возможность localhost on port 5000.

# webapp/main.py
default_score = "cx4e739b203b92498b6b6fb6d205f1cb62a6d4f88d"
# Last line
app.run(debug=True, host='localhost', port=5000)

Делаем тестовый запуск и посещаем http://localhost:5000

$ python main.py
# Visit http://localhost:5000

Вы должны увидеть пустую страницу без голосов

Заменить keystore2.json и keystore3.json с файлами хранилища ключей из кошельков testnet, мы будем использовать iconkeystore3 и iconkeystore4. Скопируйте эти файлы из предыдущих руководств в keystores/ папка. Вы также можете использовать любые кошельки с некоторым балансом в тестовой сети.

Сменить пароль под кошельками в строке 23 webapp/main.py соответственно.

# webapp/main.py
wallets = {
'wallet1': KeyWallet.load("../keystores/keystore1.json", "p@ssword1"),
'wallet2': KeyWallet.load("../keystores/iconkeystore3", "@icon333"),
'wallet3': KeyWallet.load("../keystores/iconkeystore4", "@icon444"),
}

Теперь отдайте наш первый голос с keystore1.в JSON (wallet1), "_vote": "0x1" = да, "_vote": "0x2" = нет. В этом примере, мы голосуем, да.

# testcmd/send.json
{
"jsonrpc": "2.0",
"method": "icx_sendTransaction",
"params": {
"version": "0x3",
"from": "hxe9d75191906ccc604fc1e45a9f3c59fb856c215f",
"value": "0x0",
"stepLimit": "0x200000",
"nid": "0x3",
"nonce": "0x2",
"to": "cx4e739b203b92498b6b6fb6d205f1cb62a6d4f88d",
"dataType": "call",
"data": {
"method": "vote",
"params": {
"_vote": "0x1"
}
}
},
"id": 1
}

выполните это:

$ tbears sendtx -k keystores/keystore1.json testcmdline/send.json -u https://bicon.net.solidwallet.io/api/v3

затем проверьте txresult:

$ tbears txresult 0xad6e8058a8f992f6b7312848853437c9639280e436e5391dd6254d8593d637ec -u https://bicon.net.solidwallet.io/api/v3

Посетите наш сайт http://localhost:5000 опять, вы должны увидеть что wallet1 проголосовал “да”.

Вы не можете повторить голосование с одного кошелька на кошельки, которые не проголосовали (iconkeystore3, iconkeystore4), вы можете проголосовать с помощью JSON-RPC, как мы сделали для wallet1, или вы можете просто проголосовать через веб-приложение.

Вы также можете проверить результаты подсчета от T-Bears, вызвав get_vote_talleyот SCORE.

# testcmdline/call.json
{
"jsonrpc": "2.0",
"method": "icx_call",
"params": {
"from": "hxe9d75191906ccc604fc1e45a9f3c59fb856c215f",
"to": "cx4e739b203b92498b6b6fb6d205f1cb62a6d4f88d",
"dataType": "call",
"data": {
"method": "get_vote_talley"
}
},
"id": 1
}

Выполните:

$ tbears call testcmdline/call.json -u https://bicon.net.solidwallet.io/api/v3

Вы должны увидеть тот же результат:

response : {
"jsonrpc": "2.0",
"result": {
"yes": "0x2",
"no": "0x1"
},
"id": 1
}

Web App
В main.py имеется некоторый неиспользованный и нерелевантный код, не беспокойтесь об этом. Основной поток базируется в getAllVotesIcon из polling-script.js, который направляет результат /<wallet> вызовомvotingResults на наш main.py затем рисует диаграмму. Основная функция голосования в нашем веб-приложении создаст подписанную транзакцию и вызовет ваш SCORE’s vote метод, чтобы проголосовать.

При необходимости можно развернуть приложение на рабочем сервере, следуя инструкциям из предыдущего урока

Demo: https://dapps.icon.support/icon-polling


Вот и все для голосования DAPP, это демонстрирует еще один отличный вариант использования для децентрализованных приложений blockchain. Бумажные бюллетени использовались при голосовании со времен Римской Империи, а результаты подсчитываются вручную по сей день. Онлайн-голосование в централизованном приложении имеет несколько фундаментальных недостатков, которые обычно преодолеваются более высокими стандартами безопасности, но голоса, хранящиеся в централизованной базе данных, могут быть нарушены и изменены. Код приложения также может измениться, то есть правила выборов могут измениться. Блокчейны повышают целостность голосования с помощью безопасных транзакций и отслеживаемости, поэтому каждый голос может быть проверен, а записи являются постоянными, прозрачными и неизменяемыми. Пришло время развиваться.

Источник

Присоединяйтесь к нам: ICON Foundation Russia