Сегодня я опубликовал в Lokinet все свои личные веб-сайты под доменом hloth.dev. Подключитесь, замените .dev на .loki и вы зайдете на зеркало веб-сайта из клирнета! Прочитайте эту статью в Lokinet: http://blog.hloth.loki/blog/now-in-lokinet
Пока я читал о Lokinet и его настройке, мне стало любопытно, возможно ли создать бесплатный домен, как в сети Tor onion. Как оказалось, никто раньше не публиковал подобные инструменты! Я нашел только один репозиторий с программой на Golang, которая не работала. Я также нашел неработающую ссылку на имплементацию на питоне среди комментариев к issues в репозиториях oxen, и ничего более. Так что я решил просто создать свой собственный генератор!

В прошлом я уже публиковал генератор vanity Session ID для мессенджера Session, поэтому мне просто нужно было выяснить, как генерируется адрес loki. Оказалось, это не так просто. Я начал с просмотра документации, но, как и следовало ожидать от продуктов oxen, там не было никакой информации.
Пришлось отправиться в исходный код Lokinet на C++... Последний раз, когда я читал код на C от разработчиков oxen мне хотелось вырвать глаза: у меня не укладывается в голове, что в фукнцию нужно что-то передать и она запишет результат в эту переменную, вместо того, чтобы вернуть результат. Вдобавок C++ импортирует ВСЁ из файла, который вы референсите, а не конкретные перечисленные явно переменные, как в JavaScript. Приходится искать среди тонны файлов-заголовков, несколько из которых просто по приколу будут в соседнем репозитории.

В конце концов я все таки нашел файлы service/info.cpp, service/identity.cpp и constants/proto.hpp, которые и содержат логику по преобразованию файлов .private в адрес loki. На самом деле, адрес — это закодированный алгоритмом z-base32 ключ размером 32 байта. В файле .private хранится этот ключ и секретный seed, из которого его можно получить путем преобразований, а также получить и другие ключи, нужные для Lokinet, но не нужные нам для этой статьи.
Я написал алгоритм на псевдо-языке, чтобы объяснить, как это все работает:
// 32 байта
seed = crypto_random_bytes(32)
// на момент 8 марта 2025
protoVersion = 0
// 64 байта
hash = sha512(seed)
// Clamping curve25519: https://neilmadden.blog/2020/05/28/whats-the-curve25519-clamping-all-about/
hash[0] &= 248
hash[31] &= 63
hash[31] |= 64
// 32 байта
privateKey = hash[0:32]
// 32 байта
signingKey = crypto_scalarmult_ed25519_base_noclamp(privateKey)
// https://en.wikipedia.org/wiki/Base32#z-base-32
// всегда 52 символа
addressName = zbase32(signingKey)
// всегда 57 символов
address = addressName + ".loki"
// == This part is for writing to .private file: ==
// 64 байта
sValue = concat(seed, signingKey)
// https://en.wikipedia.org/wiki/Bencode
serviceInfo = bencode({
"s": sValue, // Не конвертируйте! просто отдайте сырые байты в bencode
"v": protoVersion
})
// Запишите serviceInfo как строку utf-8 в файл .private
Я написал это на JavaScript, провел несколько тестов и оно заработало! Даже не заняло весь день, хотя я ненадолго застрял на том, как генерируется encryptionKey (та программа на go, о которой я говорил, меня запутала: я думал надо использовать хеш blake для адреса, но на самом деле нужен только signing key, encryption key, nonce и прочее не надо)
Если хотите посмотреть на мою реализацию на JS, вот итоговый результат: https://github.com/VityaSchel/lokinet-vanity-address-generator
Я сделал бинарники, так что вам остается только скачать CLI и использовать его. Создание vanity доменов для Lokinet теперь очень просто. И быстро: на моем MacBook M1 Pro с 8 потоками скорость может доходить до 230 тыс генераций в секунду. А на подаренном моему парню MacBook M4 Pro она доходит аж до 450-500 тыс генераций в секунду, это настоящий монстр. Я нашел домен с 5-символьным префиксом "h1oth" всего за пару минут.
Кстати, если hloth.loki истечет, вы можете использовать вот этот домен для просмотра моих сайтов:
http://h1oth5jgjqqc1xji8hq1pjgupjzockn56uja7mfsrjcay63oi5ty.loki/
Было весело! Но если честно, мне нужна работа. К слову, вот моё резюме: http://cv.hloth.loki/ 😁