Уважаемые пользователи Голос!
Сайт доступен в режиме «чтение» до сентября 2020 года. Операции с токенами Golos, Cyber можно проводить, используя альтернативные клиенты или через эксплорер Cyberway. Подробности здесь: https://golos.io/@goloscore/operacii-s-tokenami-golos-cyber-1594822432061
С уважением, команда “Голос”
GOLOS
RU
EN
UA
yudina-cat
6 лет назад

Делегат-гуманитарий учится программировать (часть 1)

Привет, мои хорошие. Все началось с поста @hipster, хотя я и раньше хотела раз и навсегда разобраться с этим вашим программированием, но вот это стало последней каплей )

Сейчас время когда нужно доставлять рациональный код приложений и самое главное - ядра. И правда в том, что гуманитарные делегаты просто не способны выполнять эту работу, т.к. тупо не могут проверить код ядра на соответствие требованиям избираетелей. Такие дела.

Вообще я считаю, что делегату совсем не обязательно глубоко и полностью разбираться в исходном коде нашего блокчейна. Точнее не так: делегатов и простых пользователей, компетенция которых позволяет проводить аудит ядра, у нас достаточно. И делегата можно сравнить скорее с системным архитектором (продвинутым админом), который, используя готовые программные продукты, решает определенную бизнес-задачу для своей компании. Ну то есть администратору сети не обязательно глубоко и полностью владеть исходным кодом продуктов, которые он устанавливает, настраивает и поддерживает в рабочем состоянии.

Безусловно, чтобы поддерживать диалог с разработчиками, высказывать обоснованные мнения по тем или иным предлагаемым изменениям в коде и выдвигать свои предложения – необходимо в достаточной степени представлять процесс изнутри,и чем глубже и полнее это понимание, тем делегат ценнее для Голоса. Хотя на мой взгляд, бесперебойного подписания блоков, как базовой функции делегата вкупе с работой по популяризации Голоса, и активным участием в жизни проекта – уже достаточно для того, чтобы претендовать на место в топ-19.

С другой стороны, почему бы мне не попробовать вникнуть во внутреннюю кухню нашего блокчейна глубже, чем это необходимо. Нафиг лирические отступления! Пора что-нибудь написать.

@chusovitin сказал, что сразу взять и вникнуть в код golosd у меня не получится, требуется определенный и достаточно объемный набор базовых знаний, и поэтому я решила поставить сначала менее масштабную задачу и написать какое-нибудь простое приложение для повседневных нужд, попутно освоив пару языков программирования и логику взаимодействия между серверными и клиентскими частыми приложения.

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

Если кто-то будет читать все, что написано ниже, прошу обратить внимание, что я никогда ранее не программировала, и основная моя задача – не написать само приложение, а заполнить пробелы в своей голове.

Задачи:

  • Понять принципы взаимодействия между фронт-эндом, бэк-эндом и нодой
  • Освоить основы JS, PHP и mySQL (почувствовать себя в шкуре разработчика)
  • Разобраться в структуре данных блокчейна.

Приложение:

  • Вытащить и сохранить в БД из блокчейна данные пользователей: ник, дату регистрации, даты последних поста, лайка и апвота. Количество подписок, количество подписчиков и силу голоса.
  • Анализировать и хранить данные по истории добавления/удаления из подписок.
  • Анализ и интерфейс оставим на следующую часть.

Структурно приложение будет выглядеть вот так:

Открываем страничку в браузере, она обращается к моей API-ноде, последовательно запрашивает блоки и записывает нужные данные в БД. Для начала просто соберем все необходимое для будущего анализа.

Подготовка
Начинаю с того, что разбираюсь в mySQL. В качестве резервной witness-ноды у меня используется сервер, на котором крутится сайт @rblogger и еще десяток проектов, для которых уже руками @chusovitin развернуты Nginx, Apache с PHP и MySQL, поэтому бэк-энд будет на этом сервере, просто получаю доступ к новой БД, а сайт и домен, как и доступ по FTP у меня уже настроены, я туда картинки для Голоса загружаю.

Иду гуглить про MySQL, узнаю, что существует PHPMyAdmin, разбираюсь в создании таблиц в БД и типах данных. Оказывается, в БД нет ничего страшного, за исключение того, что все мануалы написаны просто ужасающим языком, для биомеханоидов. Ну да ладно. Для начала мне нужна всего одна таблица, создаю ее через МайАдмин:

Назовем ее all_users.
id - идентификатор и «праймари-индекс», пригодится, хотя и без него уникальным будет следующее поле.
nic - тут будем хранить имя пользователя в блокчейне.
black_list - тут в будущем будем хранить «1», у всех пользователей, которые похожи на ботов, ну и моих хейтеров не забудем пометить. Узнаю что есть такая штука, как boolean.
register, last_post, last_comment, last_upvote - поля с типом таймштамп, тот же формат, в котором хранятся даты в голосе. Попутно выясняю массу интересных подробностей о проблемах хранения дат (бедные программисты), наберется на целую статью в psk, чего стоит только хранение даты в виде количества секунд с начала эпохи Линукс! Попутно разражаюсь двадцатиминутным диалогом о том, что за все время люди так и не нашли способ унифицировать такие процессы. Кошмар.
golos_power - в будущем будем записывать сюда СГ в виде GESTS конечно же.
subscribers - количество подписанных на пользователя аккаунтов.
followers - количество аккаунтов, на которые подписан пользователь.

Вот такая штука получилась:

Фронт-энд
Приступаем к самому интересному – получение данных из блокчейна. Сначала безуспешно пытаюсь разобраться в готовых js-библиотеках, в итоге нахожу вариант реализации http://ropox.tools/steemjs/api/ GUI для SteemJS, который запилил @ropox. Но у него в коде используется какая-то странная библиотека main.1537f926.js – долго копаюсь в этом всем, но так и не могу заставить ее работать у себя локально, злюсь, пью 10 чашку кофе и вспоминаю, что есть замечательная штука chain.cf https://chain.cf/ex.html (спасибо, @vik), с помощью которой я в свое время проверяла работу своей API-ноды. Беру за основу этот скрипт.

Сначала выкидываю из него всё лишнее и мешающее пониманию. Разбираюсь, как соединиться с нодой: все очень просто:
var socket = new WebSocket('wss://ws.golos.io')
Подставляю сюда свой IP, и меняю wss на просто ws (потому что я еще не разобралась, как настроить сертификат SSL, чтобы было как у взрослых).

Изучаю скрипт дальше и нахожу то, что мне нужно – запрос на получение блока по номеру:

socket.send(JSON.stringify({
id: 3,
method: 'get_block',
'params': [lastblock]// Отправляем запрос на получение текущего блока
}));

У @vik’а в коде есть подробнейшие комментарии – это сэкономило мне уйму времени, еще раз спасибо. Пробую соединиться с нодой и получить один блок с номером, выбранным наугад. Попутно разбираюсь в том, что в JS у многих объектов есть события, например, после socket.send, если все сложилось удачно, происходит событие socket.onmessage. Тут отвлекаюсь надолго и читаю тонну мануалов про то, как работаю эти события, что такое функции и чем глобальные переменные отличаются от используемых внутри функций.

В конце концов вывожу на страничку код блока, и оказывается, что он записан в совершенно неудобоваримом для обычного человека формате.

Вот так, например, выглядит блок 11568295, подписанный делегатом @anyx. Брррррр.

Это никуда не годится, Иду читать про JSON. Нахожу онлайн-просмотрщик данных в формате JSON. Вот так уже гораздо понятнее:

Выделенная строчка, это я удаляю из друзей @rblogger, чтобы посмотреть, как событие запишется в блокчейне, вы так не делайте, вы наоборот подписывайтесь.

Опять из кода @vik’a выясняю, как обращаться к переменным внутри объектов JSON. И превращать строку в объект и объект в строку. Попутно выясняю, что скрипт @vik’a показывает только первое событие в блоке, и мне приходится изобрести вот такую конструкцию, чтобы в цикле в цикле (разбираюсь, как работают циклы) перебрать все transactions и operations внутри них. Заодно шутка из «Кремниевой долины» про пробелы и табуляции становится понятной.

В результате получаю в переменной trn_type тип транзакции и иду гуглить, какие вообще бывают события в блокчейне. Выясняю, что с документацией у Голоса полный провал – вот уж где непаханное поле для постов и апгрейдов нашей вики. Натыкаюсь на пост @on1x, где хоть и без разъяснений, но есть список типов. Предполагаю, что искомые регистрации это account_create, а из комментов все того же скрипта выясняю, что добавление/удаление в друзья это custom_json, и разница между ними в том, что при удалении params[1].what пусто, а при добавлении там значение ‘blog’.

Получается вот такой код на js:

Бэк-энд
Теперь нужно как-то все это хозяйство сохранить в БД. Для серверной части выбираю PHP, потому что синтаксис вроде бы похож на C++ (а нам-то в итоге нужно код голоса аудировать ;)), попутно читаю статью про Быдлокодеров на Лурке, ржу. Читаю пару статей про Hello world на php и понимаю, что сейчас придется тяжко, но раз уж назвался груздем…

Базу данных мы уже создали, осталось как-то начать общаться с ней из скрипта на php. Выясняю, что есть масса фреймворков и классов для работы с БД, и все это как-то чрезмерно сложно для моих нужд, поэтому выбираю путь «в лоб»:

И в этот момент @chusovitin рассказывает мне о проблемах безопасности и предлагает изобрести своими силами велосипед. Потому что из фронт-энеда я буду посылать запросы на запись в базу, и было бы здорово, если бы это была только я.

Создаю в mySQL еще одну табличку с именем config и двумя полями name и value. Заодно буду хранить тут номер последнего проанализированного блока. Но сейчас создам руками запись name = key и запишу туда значение секретного ключа. Параллельно разбираюсь, зачем тут нужен md5, и как обычно хранятся пароли в базах данных.

Пробую сначала просто отправить запрос в БД из php-скрипта, прямо такой же, какой генерирует PhpMyAdmin, если добавить что-нибудь в таблицу all_users через его интерфейс.

ОМГ! Почти с первой попытки теряю программисткую девственность и начинаю понимать, почему так много людей испытывают восторг от программирования. Запись появляется в таблице. А я иду читать про XMLHttpRequest() в js и глобальный массив $_GET в php. Тут прошло достаточно много времени, прежде чем я научилась передавать параметры через GET, но в итоге процедура добавления пользователя заработала: фронт-энд перебирает последовательно блоки и находит события account_create, после чего передает бэк-энду имя пользователя, дату создания блока, а так же наш волшебный секретный ключ.

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

Со стороны бэкэнда происходит следующее: из url через get забираем все переменные и записываем их куда положено:

Теперь прописываем все то же самое во фронт-энде и бэк-энде для событий добавления и удаления из друзей. А чтобы блоки перебирались последовательно читаем про рекурсию и вставляем вызов основной функции в конец процедуры обработки блока, добавив к номеру блока единичку.

На фронт-энде:

На бэкэнде:

(При отладке долго не могла понять, откуда у меня плодятся пустые записи в таблице followings, оказывается внутри switch каждый кейс должен заканчиваться на break, чтобы не выполнялись все кейсы, которые идут ниже)

И события удаления/добавления в друзья начинают собираться в табличку followings

В этот момент вспоминаю, что нужно еще собирать данные по последним апвотам, комментам и постам ((( устраиваем перекур, и тем же самым методом реализуем обработку еще трех событий: vote, comment и post… Внезапно оказывается, что тип транзакции и у поста, и у комментария comment, разница лишь в том, что в data.result.transactions[].operations[][1].parent_author у поста будет пусто, а у комментария будет имя автора поста, к которому отправлен комментарий.

Отладка
Внезапно обнаружилась куча пустых записей о добавлении в друзья. Долго не могла разобраться, как так получается, что пользователи добавляют друг друга, при этом никаких записей в блокчейне о создании аккаунтов в прошлых блоках нет.

У меня например, все начинается с блока №6030582 : «golosio create account yudina-cat» все по-православному, а вот допустим @lehard добавляет в друзья @on0tole где-то во второй тысяче блоков, но этому не предшествуют записи о создании аккаунтов. Видимо, форк Стима был сделан таким образом, что все эти блоки были удалены.

Например первое упоминание Лехарда в блоке №631 и сразу перевод 0.100 GOLOS от @testz. Это весьма осложняет создание пользователей в моей БД, пришлось неэлегантно выкручиваться из этой ситуации.

И вставляю вызов этой функции перед всеми операциями добавления записей о апвотах и прочей активности.

Почему-то создалось две записи в all_users для @primus. Оказывается скрипт неправильно обрабатывает ситуацию, когда первое появление пользователя в системе это пост+апвот этого поста, как сделал коварно @primus в 900-м блоке.

Итоги
За два вечера «гуманитарный» делегат освоил на базовом уровне:

  • Логику взаимодействия между бэк-эндом, фронт-эндом и api-нодой.
  • JS.
  • PHP.
  • Чуть-чуть языка SQL-запросов.
  • Логику хранения данных в БЧ и их получение.
  • Синтаксис JSON.
  • Миллион админско-программистских мелочей.

Скрипт «фронт-энда» доступен тут , но там нет моего секретного ключа, так что можно разве что помедитировать на то, как блоки разбираются на операции. «Бэк-энд» вот здесь. Если Когда я осилю все это привести в человеческий вид, открою все для общего доступа.

В следующей серии займусь анализом собранных данных и написанием интерфейса, правда нужно подождать пару дней, пока скрипт пройдет по всем блокам. Ну, а в отдаленных планах C++ и копание в коде самого Голоса. Готовьтесь )



Выбрать меня своим пока еще гуманитарным делегатом можно на странице голосования. Нужно нажать кнопочку рядом с моим ником.

1
228.647 GOLOS
На Golos с May 2017
Комментарии (20)
Сортировать по:
Сначала старые