Oчень добрый день. Меня зовут Тимур и я программист.
В прошлой своей статье я вкратце описал как можно внести минорные правки в код хромиума, собрать его и подтянуть в собственную сборку электрона. Статья имела оглушительный успех и вызвала бурные дебаты (34 комментария знаете ли) и, как только головокружение от успеха прошло, я немедленно бросился писать следующую. Собственно не прошло и трех лет (всего два так то) и вот она готова. Давайте взглянем.
Сильно растекаться мыслью по древу я сегодня не буду, это статья скорее анонс, подробности будут в следующих статьях, сегодня пойдем немного тезисно.
В двух словах я прикрутил поддержку CAS в хромиум и теперь в нем можно запрашивать контент по его хэшу, что то вроде:
hash://sha512/b8c87ccafa32720483d4456df4f8300f9c5603095e760ffd5f7bed381f9186a3979c5ad56b142e504629988ec44fe37b306401c93c81f356c30340b62502f70c
причем ссылки такого рода поддерживаются как с навигации так и в fetch. Под капотом это все трансформируется в обычные http запросы и происходят обращения к #Net нодам (агентам) список которых можно указать в настройках. Для hash:// запросов все заканчивается как только получен ответ тело которого соответствует хэшу. То есть мы можем запрашивать контент по http, не переживая что его подменят по пути, и мы можем получить ответ от любого подозрительного хоста не заморачиваясь на его подозрительность — если хэш совпал то мы получили именно то что мы хотели. Не смущайтесь что под капотом обычный http — это можно положить на любой уровень, это все таки прототип, не более.
Поддерживаются хэши sha1, sha256, sha512. Добавить поддержку нового хэша несложно, на первое время и этих хватит. sha1 поддерживается потому что торренты и git, по торрентам уже есть черновики которые равняют #Net агента c webtorrent нодой, но об этом тоже отдельно, не в этой статье.
На одних хэшах web3.0 особо не построишь (да, я претендую). Как только мы поменяем хотя бы один бит информации — хэш тоже изменится. А как нам сообщить миру о том что адрес главной страницы поменялся? Тут на сцену выходит signed:// схема (да, я её тоже впилил в хромиум, там все уже работает) Это тоже разновидность CAS — мы заранее знаем какое то свойство контента и когда мы этот контент получаем — проверяем его на наличие этого свойства. Только в этом случае свойством будет не хэш контента а подпись. В ссылке (и в запросе) мы указываем публичный ключ на чью подпись мы рассчитываем и, получив ответ, проверяем сигнатуру. Если подпись соответствует — то мы получили то что хотели.
Но как быть с изменениями — мы же хотели порешать эту проблему? Мы делаем запрос и получаем два сообщения — подписанный первый вариант и подписанный второй вариант, какой из них выбрать? Тут тоже все простенько, в сообщении есть параметр nonce, у кого nonce больше — тот и наиболее свежий.
Если в hash:// запросе мы опрашиваем агентов до тех пор пока не получим первый устраивающий нас ответ, то в signed:// запросе опрашиваются все агенты из списка указанного в настройках и все их ответы суммируются. Для навигационного запроса выбирается один ответ с максимальным nonce, для fetch запроса — отдается весь массив. На самом деле под капотом навигационный запрос делается обычным js который делает обычный fetch и это тоже очень интересная тема, но мы же договорились — yep, об этом будет совсем отдельная статья.
signed:// ссылка имеет вид:
signed://secp256k1.sha256/hex public key/label
Поддерживаются подписи secp256k1 и secp256r1, хэш функции те же что и в списке для hash:// урлов. Так то это URN а не URL поскольку вопрос где? мы выносим за скобки и отдаем на откуп браузеру, в этом вся суть происходящего, но статья вводная, давайте не пугать неокрепшие умы. Итак. Ссылки. Вот например как выглядит ссылка на мой бложик в #Net (тот самый что на кавере):
signed://secp256k1.sha256/02b0d85dd3634384d7fe148b4087cdf29fa24105f33228bf7787b7aaecb60bdd85/index.html
Signed сообщения это обычный json-чик вот такого плана:
{
publicKey: "secp256r1.sha256:22c59bde2043c29a348478306ae9abf5f6503ba8eda08babbd5fae14a56f78b31185977253b4866306cabc63b28ca0713278a3850e89c31f96b436f89a0f043c",
hash: "sha256:6eff0a97ba81d80c1f58adc9fd814a1b77f26c27aa71e89ee87ef5e952e685d7",
signature: "3046022100c5572cc5a2acfb9306f9f92513a4622d5b63590a75dcce4248d97b8101c1edf6022100a05d115d0e8c3f90a532859e33ed4301a36d926883287b12c2d7d01e4fccd150",
label: "/my/first/web3.0/page/ever",
nonce: "42"
relatedTo: "signed://secp256k1.sha256/02b0d85dd3634384d7fe148b4087cdf29fa24105f33228bf7787b7aaecb60bdd85/index.html"
}
Данные указаны произвольные, не пытайтесь проверять подпись конкретно этого сообщения.
После того как все варианты signed сообщения получены и выбрано сообщение с нужным nonce — мы на основании поля hash делаем hash:// запрос и получаем подписанный этим сообщением контент. То есть signed:// это такая надстройка над hash://.
Поле relatedTo не обязательное, его можно не указывать. Но его наличие дает интересную возможность. Например можно постить комментарии к статьям, совершенно не заморачиваясь на то какой именно сервер будет эти комментарии хостить. Движок комментариев при этом запрашивает все сообщения related к данному урлу и выводит их пользователю. Все это можно сделать при помощи related:// запроса (guess what, да, это тоже уже реализовано и можно щупать). related запросы могут быть сделаны только при помощи fetch, в навигации они не участвуют. Более того, related:// schema это всего лишь демонстрация возможностей, я думаю что если идея стрельнет оно транформируется в некий QL (query language).
Да, а поскольку сам комментарий является signed сообщением и имеет собственный url, то можно создать еще один комментарий related к первому и таким образом получить ветку обсуждений, прям как тут на хабре.
Ну и на фоне этого я естественно написал движок комментариев который утилизирует все эти возможности и прикрутил его к своему бложику. Код бложика с движком можно посмотреть тут, #Net ссылку на бложик я давал выше. Часть кода я вынес в либу hashnet-client, желающие могут взглянуть и туда.
Вот скриншот того как я болтаю сам с собой:
Как можно заметить — поддерживаются голосования, emoji, все там стильно модно молодежно. Сообщения можно редактировать (тогда в сети появится новое сообщение с тем же label но бОльшим nonce и естественно другим хэшем), но в отличие от обычных болталок — видно все версии сообщения. Более того, можно отвечать на совершенно конкретный вариант сообщения, сделав свой комментарий relatedTo не к signed:// сообщению а к hash:// тела сообщения. В самом движке это еще не реализовано но технически такая возможность есть и это совсем не трудно.
Когда приходит signed:// или related:// ответ код хромиума смотрит каким ключом оно подписано и если ключ совпадает с приватным ключом в настройках профиля — выставляет в сообщении флаг editable — таким образом код js в песочнице браузера понимает какие сообщения можно редактировать. js не имеет доступа к приватному ключу, подписывание происходит внутри хромиума при отправке сообщения.
На самом деле js код может сделать getHashNetPublicKey() и сравнивать публичный ключ из настроек с ключом из сообщения, но я не уверен что getHashNetPublicKey пройдет проверку временем. Пока что он нужен только для того что бы правильно составить урл для post signed, вполне возможно что эта часть будет меняться и потребность в getHashNetPublicKey отпадет.
Все комментарии, голосования, emoji являются signed сообщениями, соответственно могут хоститься любым #Net агентом. #Net агенты пока не умеют общаться между собой, тут я решил не изобретать велосипед а спокойно пройтись по тому что уже сделано в других сетях, в первую очередь покурить что они там с DHT в торрентах намутили. Кроме того — независимо от того какие протоколы будут использованы — на клиентскую часть это никак не влияет, на клиенте все равно это будет список агентов и обход их.
Фактически этот код — зародыш одновременно движка комментариев к статьям, децентрализованных мессенджеров (там только сверху помазать каким нибудь PGP) и #Net аналога электронной почты.
Распишу тут еще как происходит регистрация на #Net сайтах. Никак. Регистрации как таковой нет. В хромиуме в настройках прописываете свой приватный ключ, что то вроде:
secp256k1:cb426f0b98a95057f2ed46766798e621ec1d68c8c912212322e28e88ca53bbce
и с этого момента вы готовы. Движок комментов, встретив ваше сообщение попытается скачать ваш профиль, если найдет — выведет аватарку указанную в профиле, не повезет — выведет анонимуса. Профиль является signed сообщением, его можно редактировать по адресу:
signed://secp256k1.sha256/02b0d85dd3634384d7fe148b4087cdf29fa24105f33228bf7787b7aaecb60bdd85/add-profile.html
На самом деле никакой привязки к моему публичному ключу нет. Любой желающий может опубликовать эту страницу под своим ключом (да, ключ у нас выступает в роли домена) и соответственно любой пользователь может поправить свой профиль на новой опубликованной странице — эффект будет тот же — профиль будет виден всем. Соотв. подключив движок комментариев к любому другому документу в обсуждении будет видно профили пользователей — без смс и регистрации.
Естественно это приведет к дикому спаму но тут все решаемо. Можно ввести аналог регистрации — владелец ресурса публикует список пользователей, код показывает комментарии только тех пользователей которые указаны в списке. Список может быть signed сообщением, а значит можно будет добавлять/удалять пользователей. Кривое решение, но для небольших комьюнити вполне сойдет. Для чего то более серьезного можно заморочиться на серьезную криптографию, например владелец ресурса подписывает профиль пользователя а код отображает только сообщения от подписанных пользователей, честно говоря я пока в этом направлении особо не думал, we'll cross that bridge when we come to it.
Штош. Это была клиентская часть. Что там с нодами? Ну тут совсем простенько, я сваял express.js сервачок с пятью энпоинтами — get/post hash, get/post signed и get related. Код можно посмотреть тут, честно говоря тут особо рассказывать нечего, обычный web сервачок. Качаем, поднимаем, делаем шаблон урла на базе его адреса и вбиваем этот шаблон в настройки хромиума. Все! А, не, там есть один нюанс, я прикрутил signed сообщения поверх JSON RPC, в итоге можно поднять свой #Net agent указав ему в настройках свой публичный ключ и слать ему сообщения без авторизаций и прочих https, но это уже технические детали, об этом будет отдельная статья.
Шаблон адреса выглядит вот так:
http://172.86.96.172/{{request}}/{{function}}/{{path}}
Ip реальный, кстати, это первая #Net нода которую я поднял. Как будет выглядеть шаблон зависит от реализации #Net ноды и настроенных на ней роутов. При отправке запроса хромиум подставит в плейсхолдеры:
- {{request}} hash/signed/related соответственно
- {{function}} — хэш функция для hash:// запросов, sign формула (secp256k1.sha256 например) для signed запроса. Для related запроса значение будет стоять от урла к которому мы хотим найти related сообщения, то есть это может быть и хэш функция и sign формула
- {{path}} — для hash:// запроса это хэш в hex, для signed это label, для related — так же зависит от того related к чему мы делаем выборку.
Вот так выглядят настройки у меня:
Адреса можно комментировать, любая строка которую нельзя распарсить как url будет просто игнорироваться.
Ок. Теперь посмотрим на девелоперов. После столь пронзительной речи мало кто устоит перед соблазном и многие рванут публиковать свои мысли в #Net. Как это сделать? Тут тоже все очень простенько. Нам нужно пододвинуть к себе поближе hashnet utils, все две — prepare и publish, и подготовить набор статики — html, js, css, вот это вот все. Но при этом в html ссылки должны быть сформированы особым образом. Если ссылка на hash:// артефакт то мы указываем что то вроде:
<img
id="headerImg"
src="hash://sha512/badger.jpeg"
>
Где badger.jpeg это локальный путь до файла. hashnet prepare сходит по этому пути, увидит файл, посчитает его хэш (функцию хэша мы указали в ссылке), положит файлик к артефактам и поправит ссылку в html заменив путь на хэш.
Если нам требуется указать signed ссылку то делаем что то вроде:
<p>
you can add or update your #Net profile here:
<a href="signed://*/*/add-profile.html">add profile</a>
</p>
Если вместо sign.hash функций и ключа указаны * то значения берутся из параметров вызова hashnet prepare (то есть ссылка выше может быть подписана любым ключом который указан при вызове). Можно заменить * на конкретные значения и тогда ссылка будет обработана только в том случае если приватный ключ указанный при вызове соответствует публичному ключу в ссылке. Путь в ссылке должен указывать на существующий файл и он же используется в label сообщения.
Сами утилитки мы можем взять тут hashnet-utils. Кроме hashnet prepare там еще есть hashnet publish — она отправляет все то что hashnet prepare приготовила на указанный #Net сервер (агент).
Для экпериментов можете использовать мою ноду (в статье есть ip), имейте в виду — там ограничение на 50mb на аплоад, на днях подниму еще пару нод — выложу адреса. Ну или можно поднять локально, там завернуто в докер, все простенько.
В общих чертах все с анонсом. Код хромиума лежит тут, как его собирать можно посмотреть в моей предыдущей статье (ссылка в начале этой статьи), с нетерпением жду комментарии собравших в своем бложике.
Народ знающий с++ и готовый ревьювить и контрибьютить приму с обьятиями, рад буду любой помощи. В js я себя чувствую немного поувереннее так что могу в ответ и огрызаться, но тем не менее — жесткому ревью буду рад несмотря на то что это все на стадии mvp. В общем если чувствуете желание поучаствовать — ни в чем себе не отказывайте. Про то каким вообще будет проект и как ему можно будет помочь — да, да, будет отдельная статья.
А теперь коротенько — а нафига оно нужно? Ну вот смотрите. Это все я же не первый придумал, те кто в теме сейчас могут по памяти перечислить десяток уже готовых сетей претендующих на децентрализованный web. И под капотом у них и кода побольше и сам код будет поинтереснее. Я поверхностно посматривал на многие из них, top 10 точно. Беда у них у всех одна — по какой то неведомой причине они все начинают писать клиентский софт с нуля. А потом начинают придумывать новые протоколы, поддержка которых есть только у их софта. Ну и поверх этого на десерт подъезжают новые форматы данных… В общем думаю картина понятная вырисовывается.
А что у меня? А у меня обычный браузер, (да, необычная сборка и ее еще надо собрать и делать это будет пока что только полтора гика дочитавших эту статью, я понимаю это но ключевое слово — пока), обычный html, обычные web сервера. А на выходе это все складывается в децентрализованный web. Да, пока только для статики. И участвовать в этом будут только энтузиасты. Но это уже что то. Что то работающее, что то интересное.
А проблемы — проблемы я вижу и часть из них уже знаю как решать. Решение проблемы энтузиастов есть, простое и понятное — теория игр и крипта, но это совсем потом. Решение статики — сложное и не совсем готовое — это гомоморфные вычисления, но не те надежные гомоморфные вычисления над которыми страдают лучшие математики, в случае web3.0 на самом деле можно существенно снизить требования к стойкости. Есть вопросы с cors и origin, но не серьезные, там надо просто сесть и накидать код. И много много другого, то что я тут выложил это одна маленькая снежинка на вершине айсберга.
Сейчас мне надо прийти немного в себя, собраться с мыслями и мы продолжим. Я намерен запустить народный браузер (да, на базе хромиума но там будут большие изменения), намерен усовершенствовать #Net, в первую очередь научить агентов общаться между собой, текущая сборка хромиума тоже ждет фиксов. Например на данный момент hash ответы не кешируются а надо бы, так же можно задействовать integrity аттрибут и добавить его вообще ко всем тегам имеющим под капотом запрос какого либо ресурса. Надо обновить сам хромиум, я форкнулся довольно давно и так и остался на версии в которой учился. Много всего, одни только планы потянут на отдельную статью. А пока я предлагаю обсудить что есть, на основе этого я накидаю подробностей в следующей статье.
Все. Новостей на сегодня больше нет, с вами был Тимур, хорошего настроения!
diakin
del
diakin
вообще не туда запулил, сорри.
gonzazoid Автор
ничего, бывает. Сюда народ комментить вообще остерегается :)