Делегат-гуманитарий учится программировать (часть 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++ и копание в коде самого Голоса. Готовьтесь )
Выбрать меня своим пока еще гуманитарным делегатом можно на странице голосования. Нужно нажать кнопочку рядом с моим ником.