ru
Feedback
Типы и Архетипы

Типы и Архетипы

Открыть в Telegram

Корректность типами, сложность архетипами Deep Tech | Архитектура | R&D Management

Больше
Страна не указанаКатегория не указана
337
Подписчики
+124 часа
Нет данных7 дней
+2530 день

Загрузка данных...

Похожие каналы
Нет данных
Возникли проблемы? Пожалуйста, обновите страницу или обратитесь к нашему support-менеджеру .
Облако тегов
Нет данных
Возникли проблемы? Пожалуйста, обновите страницу или обратитесь к нашему support-менеджеру .
Входящие и исходящие упоминания
---
---
---
---
---
---
Привлечение подписчиков
июль '26
июль '26
+1
в 0 каналах
июнь '26
+29
в 0 каналах
Get PRO
май '26
+36
в 0 каналах
Get PRO
апрель '26
+255
в 0 каналах
Get PRO
март '260
в 2 каналах
Get PRO
февраль '26
+25
в 2 каналах
Дата
Привлечение подписчиков
Упоминания
Каналы
06 июля0
05 июля+1
04 июля0
03 июля0
02 июля0
01 июля0
Посты канала
[вторник] Голый король и два генерала Стандартная загадка: на неком острове у части жителей голубые глаза. Каждый видит цвет
[вторник] Голый король и два генерала Стандартная загадка: на неком острове у части жителей голубые глаза. Каждый видит цвет глаз всех остальных, но не свой и кто понял, что у него голубые глаза, на следующее утро уезжает. Голубоглазых ровно k и k >= 2, значит, каждый из них и так видит вокруг другие голубые глаза. Все знают, что голубоглазые есть, но годами ничего не происходит. Приезжает гость и при всех говорит: "Здесь есть голубоглазый". Он не сказал ничего нового, все это и так видели. Но на k-е утро ровно k человек собирают вещи и уезжают. Но как объявление давно известного факта меняет поведение? Всё дело в лестнице знания, есть факт X, есть "я знаю X". Есть "все знают X" - это взаимное знание, mutual knowledge. А есть цепочка "все знают, что все знают, что все знают…" - вот её целиком и иногда называют общим знанием. Само понятие вроде введено Lewis в 1969, переиспользовано Aumann в теории игр в 1976, а в распределёнку его перенесли Halpern и Moses. До гостя факт был взаимным знанием высокого порядка, но башня где-то наверху обрывалась. Объявление при всех замкнуло её до бесконечности и синхронизировало отсчёт дней, ну и дальше индукция: будь голубоглазый один, он увидел бы вокруг только не-голубоглазых и уехал бы в первое утро. При k каждый видит k−1 и рассуждает: "если бы их было k−1, они уехали бы на (k−1)-е утро, не уехали - значит лишний это я". Все k уезжают на k-е. В последнее время мы говорили про детекторы, про то, что нужно добавить системе, чтобы она договорилась. А общее знание похожее, это тоже своего рода детектор, но он про границу, про ресурс одновременности, произвести который очень дорого. Точно такая же логика в задаче двух генералов: атаковать двум генерадам надо одновременно, а договориться можно только гонцом через долину с врагом, соответственно, гонец может не дойти, нужно подтверждение. Но и гонец с подтверждением может не дойти - нужно подтверждение подтверждения. Последнее сообщение в цепочке всегда ненадёжно, сколько ни посылай гонцов. Это и доказали Halpern и Moses (JACM, 1990): общее знание достижимо только через одновременное публичное событие, которое все наблюдают и наблюдают, что наблюдают другие. Досылкой по ненадёжному каналу такого не достичь, если ослабить требование, то получим ровно настолько же ослабленную координацию. Эта проблема и интуиция применима не только в распределёнке. Например, в "Голом короле" каждый видит, что король гол, каждый против. Но это взаимное знание, не общее: я не знаю, что и ты знаешь, что знают все. Общее знание нельзя построить из сообщений, его надо синтезировать одновременным публичным событием, которое все наблюдают и видят, что видят другие. Общества изобрели для этого ритуалы: свадебное "говорите сейчас или молчите вовек", коронация, городской глашатай, площадь. И тут как раз блокчейн можно воспринимать как способ производства этого общего знания, инженерную "городскую площадь". "Все знают, что все знают, ... , что транзакция пришла", из-за этого важны финальность и публичный порядок, поэтому консенсус дорог, это синтез того самого одновременного публичного события, которое, по Halpern-Moses, сообщениями не достаётся. Стоимость консенсуса = цена производства общего знания. Итог Если общее знание нельзя досылать, а можно только синтезировать одновременным событием, - то всё, что мы строим, от площади до реестра, это машины одного класса. И тогда: что в вашей системе играет роль общего синхронизирующего события и кто за это платит?

2
[рамка] управление онтологией В истории человечества был пример двух людей, которые независимо друг от друга пришли к своему
[рамка] управление онтологией В истории человечества был пример двух людей, которые независимо друг от друга пришли к своему понимаю того, как именно оперировать с непрерывным изменением. Ньютон и Лейбниц получили, по сути, одну и ту же теорию, но записали его по-разному. Ньютон ставил точки над переменными, флюксии, а Лейбниц придумал dy/dx и знак интеграла как вытянутую S (сумма). И вот здесь начинается самое интересное с нашей точки зрения. У Лейбница цепное правило буквально само намекает на сокращение, как дробь: dy/du * du/dx = dy/dx. Это, конечно, только эвристическая мнемоника, но нотация делает половину работы за математика, она буквально форсит следующий шаг. Британцы из верности Ньютону почти весь 18 век писали флюксиями (а трактат Маклорена 1742-го только закрепил привычку) и перешли на запись Лейбница лишь в начале 19 века, через Analytical Society в Кембридже. "Отставание примерно на век" как тогда писали, а всё из-за того, что был выбран неудобный значок... И это супер важное ощущение: тут буквально одни и те же теоремы, одна и та же теория, но разное обозначение, первое использует уже заложенную интуицию, а второе создаёт новую, которая при этом ни к чему новому не приводит. Кен Айверсон - человек, который сформулировал это явно. В 1979-м ему вручили премию Тьюринга и его тьюринговская лекция называлась "Notation as a Tool of Thought". Его основной тезис, если сжать в одну фразу: нотация - это вообще-то не пассивная запись уже готовой мысли, она определяет, какие мысли вообще ДЁШЕВО думать, какие выводы делаются сами, а какие требуют усилий. Айверсон ставит эпиграфом Уайтхеда (An Introduction to Mathematics, 1911): "Освобождая мозг от всякой ненужной работы, хорошая нотация высвобождает его для более сложных задач и фактически увеличивает умственную мощь человечества". Теперь мы знаем ответ на вопрос: "лучше ли Rust C++?" :) Например, возьмите XLVIII и умножьте на XXIII, прям в столбик, прямо римскими цифрами. Тут нет нашего славянского переноса в разряд, нет "запиши, держи в уме". А римляне считали на абаке, который сам по сути и есть физическая позиционная система. А теперь попробуйте 48 * 23, числа одни и те же, изменилась только нотация и вместе с ней изменилось то, что человек способен вычислить руками, без абака. (конечно, вполне вероятно опытный счётчик на абаке был быстр, тут речь именно про письменный алгоритм без вспомогательных устройств, его в римской записи просто нет, а в позиционной он естественно получается из самой формы записи чисел) APL, собственная нотация Айверсона, - это тот же тезис. Помню, как нам на втором курсе рассказывали про него и мне, увлекавшемуся тогда ассемблером и всяким таким, казалось, что это что-то не то, слишком высокоуровнево, где тут оптимальное размещение в стековом кадре? То, что в обычном коде записывается на страницу, в APL умещается в одну строку и внешний вид функции выдаёт в основном только форму вычисления. Мне кажется, что что-то такое (типизированные DSL) и ожидает нас в будущем на горизонте 2-3 лет, потому что они пригоды для llm. В https://t.me/types_and_archetypes/45 я писал про то, что гений меняет не траектории внутри пространства, а сами правила и так, что на уровень выше снова возникает порядок. А потом я писал про первый признак такого мышления в архитектуре - инверсию контроля там, где она реально нужна. А сегодня хочется ввести второй признак: нейминг. Гениальный нейминг = выбор НОТАЦИИ = выбор онтологии: какие сущности вообще существуют в мире и как мы на них ссылаемся. Забегу вперёд на постов 15, чтобы было интересно, но право на именование и переименование - это высшая форма управления, только на этом уровне это уже не "примитивная" власть, а что-то другое, скорее координация или влияние на мир. Про подобное я писал в "онтологической гравитации" https://t.me/types_and_archetypes/15. Поэтому нейминг никогда не нейтрален! Либо мы сами авторы нотации, либо живём в гравитации той, что выбрал кто-то другой за нас. Последнее происходит почти всегда и это нормально в большинстве случаев.
191
3
[быстрые мысли] код ревью за llm За последние 3 дня мне по разным причинам довелось посмотреть на два весьма серьёзных проекта, связанных с криптографией. И оба были навайбкожены на 101%, причём видно это было сразу. Я всё пытался понять, а почему оно так бросается в глаза. Первое, за что цепляешься, это слишком ровный код и структура папок. Казалось бы, наоборот, всё красиво и идеально. Но дело даже не в самой ровности, её ведь и линтер может сделать, а скорее в том, что эта ровность ничего не сообщает. В человеческом коде структура несёт информацию: видно, как он какое-то время развивался, как менялась мысль и дорабатывались разные его части. И вот тут возникает доверие, потому что видно, что работали кожаные, которые ошибались, исправлялись, делали по-другому. Вот тут чуть не сошлось, вот тут виден недоделанный рефакторинг, тут имя выбивается из общей канвы. Открываешь blame - ага, делал человек из другой подсистемы. Каждый такой след дёшево не подделать: за ним стоит чьё-то реальное решение, которое чего-то стоило. Но, самое главное, было ощущение, что код овнится кем-то кожаным, его реально осмыслили и прожили. И можно сделать палеонтологию https://t.me/types_and_archetypes/37. С ллм такого ощущения нет. Код ровный, его структура не несёт никакой доп информации, просто выровненный текст: слои аккуратно перекладывают поля друг в друга, где-то вычисление, и нигде ни одного решения, которое чего-то стоило и нет никакой истории. И также нет никакого желания читать его самому, потому что не хочется тратить на чтение больше времени, чем автор потратил на промпт. Особенно это касается пулл реквестов. И вот использовать ллм, чтобы написать сайт, one-shot код, некритичный сервис, который перезагружается и прочее, как-будто норм (но осмысленно). А видеть такое в весьма сложных проектах, связанных с криптографией и пользовательскими данными, вызывает очень много вопросов. И дело даже не в том, осознаёт ли ллм, что происходит, - это спор без ответа. Важнее, имхо, что в коде нет ни одного следа, что его модель кто-то вообще построил. А автор промпта, скорее всего, тоже не осознал или как минимум не прожил этот код. И мы приходим к тому, что буквально никто в мире (включая Бориса) не понимает, что этот код делает. Вот представьте: такой код показывают заказчику, к нему возникает вопрос — а ответить на него нельзя без ллм.
197
4
[мысли] А и Б сидели на трубе "А и Б сидели на трубе. А упало, Б пропало - что осталось на трубе?" Помню, что мне задали этот
[мысли] А и Б сидели на трубе "А и Б сидели на трубе. А упало, Б пропало - что осталось на трубе?" Помню, что мне задали этот вопрос в 4 или 5 лет, когда мы шли в деревне на речку. И я тогда через продолжительное размышление ответил, что ответ - любая буква. И, имхо, у этой загадки нет одного правильного ответа и не может быть. Потому что там не сказано главное: кто такие А и Б - буквы? Люди? Цифры? Что значит "упало" - буквально свалилось или просто выбыло из игры? И что вообще такое "осталось" - осталось висеть на трубе или осталось в условии задачи? Почему ответ "слон" неверный? Стандартная история про парадокс И вот недавно наткнулся на видео с объяснением теоремы Байеса https://www.youtube.com/watch?v=oPYu-CulGt4 через медицинский тест на редкую болезнь. Он имеет чувствительность 90%: из ста больных он правильно находит девяносто. Кто-то сдал тест и результат положительный. Какова вероятность, что он действительно болен? Интуиция говорит: ну, около 90%. А правильный ответ при том, что болезнь встречается у 1% людей - около 8%. А дальше всегда следует стандартное объяснение, рисуется такая табличка, говорят: "Cмотрите, болезнь редкая, на 1000 человек больны всего 10. Тест найдёт 9 из них. Но среди 990 здоровых тест ошибётся примерно у 99. Итого положительных - 108, а реально больных среди них только 9. Девять из ста восьми - это и есть те самые 8%". Дальше идёт мораль про базовые частоты, про то, что надо помнить распространённость болезни и вот это всё. Объяснение совершенно верное, но оно описывает (с моей точки зрения) только ЧТО происходит и КАК посчитать и почти ничего не говорит о том, почему это вообще ощущается как парадокс и о ЧЁМ он. Почему наша интуиция так уверенно промахивается мимо? Практически то же самое и у Лагутина в его известной книге. Вернёмся к трубе Помните прыжок между мирами? Вот он тут же, в чистом виде: - вопрос первый: среди больных - у скольких тест положительный? - вопрос второй: среди тех, у кого тест положительный, - сколько больны? Это два разных вопроса, которые живут в двух разных мирах. В первом мы стоим внутри толпы больных и "смотрим" наружу. Во втором - стоим внутри толпы людей с плюсиком на бланке и "смотрим" наружу. 90% - правильный ответ на первый вопрос. 8% - правильный ответ на второй. Просто это ответы из разных пространств. А "парадокс" рождается ровно в тот момент, когда вы спросили одно, а в голове ответили на другое. Когда вопрос задан в мире "положительных тестов", а интуиция ответила из мира "больных". Тот же самый молчаливый прыжок, что и в загадке про А и Б. Я называю эти миры онтологиями - пространствами того, что мы вообще считаем и различаем, когда задаём вопрос. Большинство статистических парадоксов - это как раз подмена онтологии: ответ из одного пространства, а воспринимается как ответ из другого. Парадокс дружбы У наших друзей в среднем больше друзей, чем у нас. Но когда мы берём случайного человека - мы в мире "случайных людей", а когда мы перечисляем своих друзей - мы уже в мире "случайных связей". А популярные, общительные люди попадают в списки друзей гораздо чаще - у них просто больше связей, которыми их можно туда затащить. Выжившие После изучения успешных компании можно сделать вывод: они шли на смелый риск, поэтому и взлетели. Свежее видео на тему https://www.youtube.com/watch?v=C-E-2POE24E, тут автор называет это первой причиной. А тут вопрос "что приводит к успеху?" мы задаём в мире всех компаний, а отвечаем в мире одних только выживших. Заметьте, я ни разу не написал ни одной формулы, но онтология в данном случае будет включать в себя тройку Колмоговора. Вообще, конечно, при создании теории вероятностей стоило бы буквально типизировать события, и тогда P(A_{type_1} | B_{type_2}) - вероятность перехода между событиями, описанных в разных мирах type_1 и type_2, а сам P : Type_1 -> Type_2. Итог Стоит всегда уточнять: в чьём мире мы сейчас находимся и точно ли онтологии правильно сохраняются при переходе? Это фундаментальный принцип, к которому я буду постоянно возвращаться.
267
5
[рамка] escrow - это legal state machine До этого я писал про игру Готику как пример social state machine (https://t.me/types
[рамка] escrow - это legal state machine До этого я писал про игру Готику как пример social state machine (https://t.me/types_and_archetypes/56), а недавно прочитал историю появления escrow паттерна и хочется тоже посмотреть на него в разрезе конечных автоматов. Escrow классически объясняют через такой сценарий: покупатель помещает деньги у третьего лица, продавец выполняет условие, затем деньги уходят продавцу. Это простая и понятная схема на первый взгляд, в ней, по сути, escrow - это юридико-операционная state machine: актив, право или документ попадает в контролируемое состояние, а затем движется по заданным переходам. Юридические определения тоже звучат весьма инженерно. Cornell Wex описывает escrow как размещение денег, ценностей, документов или других активов у escrow агента до выполнения условий в escrow соглашении. Если сжать это в одну формулу, получится что-то такое: escrow state machine - это набор - asset - любые ценные активы, которые участвуют в сделке - custodian - escrow агент, банк, юрист - instructions - кто инициирует переходы автомата - conditions - условия наступления переходов автомата: товар доставлен, инспеция завершён, срок истёк и так далее - evidence - слой свидетельств: акт приёмки, банковская выписка, запись в реестре, подпись, ... - remedies - выходы из соглашения - audit trail - память автомата: кто внёс актив, кто подтвердил событие и так далее Т.е. escrow можно реально рассматривать как представитель класса моделей конечный автомат, переходы которого могут быть, например: AGREEMENT_SIGNED -> FUNDED -> CONDITIONS_PENDING -> {RELEASED, DISPUTED -> PARTIAL_RELEASE, TIMEOUT -> REFUNDED} Есть даже статья Mark Flood и Oliver Goodenough "Contract as Automaton", где показывается, что хорошо написанный финансовый контракт подчиняется логике, внимание, state transition и может быть представлен как deterministic finite automaton с состояниями вроде надлежащего исполнения, просрочки и дефолта, а также событиями вроде поступления платежа или истечения срока платежа. Есть и принятый термин legal state machine. В HL7 FHIR Contract Resource жизненный цикл контракта описан как путь юридического инструмента, это уже область так называемого вычислимого права (computable law), где договор рассматривается как объект данных с юридически значимым жизненным циклом. Это то место, где computer science проник в юридическую науку буквально :). Ещё один интересный пример - Рикардианский контракт (Ricardian Contract) Иэна Григга. Эта идея появилась в 1990-х как способ связать юридический текст, читаемый человеком, параметры финансового инструмента и криптографический идентификатор. С одной стороны, это нормальный текст договора, читаемый человеком, а с другой стороны, внутри есть параметры, которые пригодны для использования в софте: название инструмента, символ, количество знаков после запятой, ставка, даты, адреса серверов, ключи, идентификаторы. Затем весь документ подписывается и хэшируется, а полученный хэш становится уникальной ссылкой на конкретную версию условий. Основная фишка тут как раз в том, что каждая операция может ссылаться на хэш этого контракта. Это буквально модель CID Protocol Labs, только из 1995 года... Итог На escrow можно смотреть как на нечто, большее, чем просто продукт для сделок, это, в некотором смысле, общий паттерн trust engineering. Он превращает социальный риск в процедурный риск: стороны меньше полагаются на намерения друг друга и больше на формальные допустимые переходы, свидетельства, роли, сроки и журнал событий.
331
6
[мысли] reconciliation loop или один из лучших паттернов в инфраструктурных системах Как-то мне довелось писать reconcilation
[мысли] reconciliation loop или один из лучших паттернов в инфраструктурных системах Как-то мне довелось писать reconcilation loop для того, что управлять инфраструктурными системами и, мне кажется, что это идеальный паттерн в таких случаях, особенно если мы говорим про "production-grade" масштабируемые системы. Традиционная request-response модель тут работает достаточно плохо, потому что реальность не синхронная, например, узел может быть недоступен, агент может применить конфиг, но не успеть обновить статус, сеть может разделиться, процесс может умереть между "почти сделал" и "успел сохранить статус". Т.е. класс модели "request-response" имеет слишком бедную онтологию и не учитывает реальные проблемы и состояния системы, которая управляет инфраструктурой. Его применение в некоторых случаях, связанных с управлением инфраструктурой, может быть таким же архитектурным преступлением, как и CORBA https://t.me/types_and_archetypes/34. И вот в таких случаях reconciliation loop оказывается, на мой взгляд, одним из самых правильных. Его идея очень простая: у системы есть desired state - то, как мир должен выглядеть, и actual state - то, как он выглядит сейчас. Контроллер снова и снова смотрит на разницу между желаемым и фактическим состоянием и делает следующий шаг во внутренней стейт машине, пока система не сойдется. Именно в этом, как мне кажется, и состоит настоящая техническая сила Kubernetes, т.е. это не в YAML, не в контейнеры и даже не в оркестрации как таковой, а в том, что вся модель построена вокруг вот такой сходимости. Почему это так хорошо работает? Во-первых, reconciliation loop естественно переживает сбои. Если контроллер упал, ничего страшного: он поднимется, снова посмотрит на мир и продолжит. Если агент не ответил, задача просто осталась в состоянии, из которого можно продолжить. Если кто-то руками поменял конфигурацию на хосте, это а drift, который можно обнаружить и исправить. Во-вторых, этот паттерн очень хорошо сочетается с идемпотентностью. Повторный запуск движка сравнения не создаёт новый стейт или новые состояния, он должен доводить текущий стей до нужной формы. В-третьих, reconciliation loop отлично дисциплинирует архитектуру. Как только эта модель принимается всерьёз, то приходится уже чётко разделять: - что пользователь хочет - что контроллер уже увидел - что реально применено - что только запланировано - и что делать при удалении или откате Отсюда появляются очень понятные и правильные вещи: спецификации, статусы, генерации, инварианты, стейт машины и прочее-прочее. Это делает систему не только надежнее, но и гораздо понятнее в эксплуатации. В-четвёртых, такой паттерн даёт хорошее масштабирование и более строгие инварианты. Но у паттерна есть и минусы: Главный минус в том, что reconciliation loop сложнее, чем условный CRUD. Намного сильнее сложнее. Нужно думать о стейт машинах, о повторных попытках, о статусах и так далее. Второй минус - eventual consistency. Для инженера это нормально, а для пользователя или продакт-логики уже сложновато. Третий важный минус - его легко сделать декоративным. Очень многие системы копируют внешнюю форму кубера: ресурсы, статусы, контроллеры, но не забирают суть. Если в модели нет четкого desired state, нет наблюдаемого actual state, нет идемпотентных действий и нет повторяемого reconcile, то это не reconcilation loop-модель, а какой-то просто более запутанный бекенд. Итог Reconciliation loop особенно хорош там, где система управляет внешними, ненадежными, медленными или частично наблюдаемыми сущностями. Кластеры, виртуальные машины, сетевые туннели, DNS, сертификаты - его естественная среда. Этот паттерн позволяет точнее описать операционную реальность такой, какая она есть: с лагами, сбоями, гонками, повторными попытками и частичной наблюдаемостью. И вместо того чтобы бороться с этой реальностью или заметать её под порог, он делает все эти проблемы first-class citizens, заставляя работать и думать про них. Это и делает reconciliation loop по-настоящему сильным для инфраструктурных задач.
377
7
[вторник] Heartbeat failure detector Предыдущий вторник: https://t.me/types_and_archetypes/61 В прошлый мы говорили про детек
[вторник] Heartbeat failure detector Предыдущий вторник: https://t.me/types_and_archetypes/61 В прошлый мы говорили про детекторы Ω и Σ, а теперь давайте посмотрим на ещё один объект из failure detector theory: HB, или heartbeat failure detector. Он появляется у Aguilera, Chen и Toueg в статье "Heartbeat: A Timeout-Free Failure Detector for Quiescent Reliable Communication", а затем обобщается ими для partitionable networks и quiescent consensus. На практике heartbeat обычно означает простой цикл: ping, ожидание, таймаут или иногда suspect-list. Но в этой статье смысл почти противоположный (и тут инверсия, ага). HB выдаёт процессу вектор монотонных счётчиков: hb_p[q], heartbeat значение процесса q с точки зрения процесса p. Если q остаётся достижимым из p через т.н. fair path, этот счётчик может стать сколь угодно большим. Если же q выходит за компоненту достижимости p или падает, последовательность значений у p становится ограниченной. В partitionable networks это превращается в такую семантику: счётчик q у p растёт для процессов из partition(p) и стабилизируется для процессов вне этой partition. Грубо говоря, HB даёт информацию о том, где ещё наблюдается прогресс связи в системе. Зачем это вообще нужно? В статье основной объект quiescent reliable communication (что-то типа спокойных надёжных коммуникаций). Quiescent алгоритм после ограниченной активности приложения порождает только ограниченный хвост протокольных сообщений. Для одного RPC, одного броадкаста или одного раунда алгоритма это очень естественное требование: приложение сделало какую-то конечную работу, транспортный слой тоже должен прийти к покою через какое-то время. Но потеря связи создаёт именно такой конфликт. Ведь надёжная доставка через канал с потерями обычно достигается ретраями до ACK, при этом сам ACK сам идёт через канал с потерями. Получается потенциально неограниченный хвост служебного трафика ради одного сообщения, в терминах статьи это ровно место, где failure detector становится способом отделить активность приложения от активности системного сервиса. Статья предлагает решить это вопрос через дополнительный слой, который может жить постоянно и рассылать heartbeats, а конкретный алгоритм надёжной передачи использует рост счётчика как триггер повторной отправки. Например, p хочет доставить m процессу q. p отправляет m каждый раз, когда hb_p[q] увеличивается и завершает локальную пересылку после ACK от q. Если q корректен и достижим, его heartbeat у p растёт бесконечно, значит попытки доставки будут возникать снова и снова, а fair path в итоге пропустит m и ACK. Если q выпал из достижимой компоненты, рост счётчика у p ограничен, значит ретраи конечны. HB в partitionable networks по сути кодирует сильно связную компоненту процесса в графе корректных процессов и fair links, но делает это не через явный список вершин, а через монотонные счётчики, где неограниченный рост означает принадлежность той же партиции. В обычных системах heartbeat часто используется как часть знания о недоступности, а в теории HB он становится частью знания о доступности. Это различие особенно важно для partitionable networks: падение процесса и потеря достижимости - разные события с разными последствиями для алгоритма. Мне нравится думать об этом, как о третьей минимальной асимметрии в этой серии: Ω - асимметрия координатора: в пределе все смотрят на одного лидера Σ - асимметрия власти: достаточные множества обязаны пересекаться HB - асимметрия прогресса: рост счётчика задаёт достижимую связь между участниками Итог Failure detector theory интересна тем, что она декомпозирует "надёжность" на минимальные виды информации, которые необходимы системе для работы. И если Ω отвечает на вопрос "кто ведёт?", а Σ "какая коалиция имеет право зафиксировать состояние?", то HB отвечает на вопрос "где коммуникация ещё производит наблюдаемый прогресс?". Т.е. вместо того, чтобы как-то полагаться на ненадёжную информацию о том, что пир недоступен, мы полагаемся на надёжную информацию о том, что он доступен. И это красивая и простая инверсия.
398
8
[мысли] как узнать сильную книгу по криптографии Когда-то во время учёбы в универе я сталкивался с большим количеством книг по криптографии, которые были написаны инженерами или программистами, но не математиками или "настоящими" криптографами. Часто такие книги дают только рецепты вида "делай раз-два-три и получишь подпись", не облажайся с ключами вот тут и тут. И они дают только два варианта работы с ними: как с RFC или просто выучить это просто наизусть. Я бы даже предложил простой вероятностный тест на такую книгу: если в книге в описании протокола в какой-то момент написано возьмём "большое простое число", то она почти наверное не стоит своих денег. Большое простое число - это не понятный и не специфицированный объект. Оно большое как? Если записать в двоичном виде или может на клингонском? В каком кольце? В этом примере хочется сразу понять: почему оно должно быть простым, какая структура появляется, какие операции становятся обратимыми, какая задача становится трудной. А вот с определением эллиптических кривых чуть сложнее, когда автор определяет кривую сразу через короткую форму Вейрштрасса (а так делают почти все книги по криптографии, которые я видел, часто даже не говоря про характеристику поля и прочее), то это, конечно, не точно, но смириться с этим можно. Иначе нужно слишком много знать, чтобы понять "настоящее" определение. А вот в хороших книгах, на мой взгляд, изложение может быть примерно таким: какая-то математическая структура рождает трудную задачу, трудная задача входит в модель атаки, модель атаки связывается с доказательством, доказательство держит протокол и только в конце протокол уже становится инженерией. И сильная книга, имхо, учит видеть инварианты: что скрывается, что связывается, что проверяется, где живут гарантии и чем они обеспечиваются. Такие книги можно понять и не нужно заучивать. После них алгоритмы выглядят уже не просто последовательностью действий, а следствием математических законов и аксиом.
333
9
[мысли] Вайбкодинг как взятие технического долга Сейчас в твиттерах стало модно писать, что код превратился в commodity и поч
[мысли] Вайбкодинг как взятие технического долга Сейчас в твиттерах стало модно писать, что код превратился в commodity и почти ничего не стоит. Мне кажется, это одновременно и так, и не так. Если под кодом понимать первичную текстовую реализацию, то да, её стоимость сократилась до стоимости потраченных токенов и доступа к контексту, в котором этот код генерируется. LLM умеют очень дёшево производить текст, похожий на код. Но если под кодом понимать надёжный, понятный, сопровождаемый и проверенный артефакт, то он дешевле не стал. То есть commodity стала не программа как таковая, а скорее заготовка под программу. Из-за этого, кажется, важнее стало различать не просто "код" и "не код", а хотя бы такие вещи: навайбкоженный код и ненавайбкоженный, проаудированный и непроаудированный, локальный и уже где-то поживший, код как черновик и код как часть долговременной семантики системы. Мне кажется, что в будущем появится экономика внимания. Ценность кода будет определяться не только тем, сколько усилий ушло на его написание, но и тем, сколько качественного внимания в него уже конденсировано. Кто-то его долго думал, кто-то читал, кто-то ронял в проде, кто-то чинил крайние случаи, кто-то проверял инварианты, кто-то проводил аудит. В этом смысле хороший код чем-то похож на криптографический примитив, он становится надёжнее не потому, что его быстро однажды написали, а потому что на него долго и качественно смотрели. А LLM радикально меняет именно это соотношение. Она позволяет очень дёшево получить код, на который ещё не было потрачено соответствующее количество человеческого внимания. И в этом смысле использование LLM очень часто является взятием технического долга. Красивый и опасный момент здесь в том, что раньше такой долг обычно брался через отказ от работы: не успели отрефакторить, не дописали тесты, отложили спецификацию, "потом разберёмся". С LLM происходит та самая инверсия, теперь технический долг можно брать через производство работы. Возникают модули, адаптеры, тесты, миграции, обвязки, куски бизнес-логики. Внешне это выглядит как прогресс, но очень часто это только материализация будущих обязательств: понять, что именно здесь имеется в виду, проверить корректность, выровнять стиль и инварианты, научиться безопасно это менять через полгода. То есть LLM делает технический долг ликвидным. Она позволяет выпустить сейчас объём реализации, который потом придётся выкупать пониманием, аудитом и сопровождением. Но тут важно не впасть и в другую крайность. LLM - это не всегда долг, мне кажется, тут есть как минимум три разных режима. Первый - собственно технический долг. Это случай, когда код попадает в долговременный контур, а понимание и верификация откладываются на потом. Второй - покупка опциона. Это прототипы, одноразовый glue code, черновые интеграции и внутренние утилиты. Здесь систему не столько строится, сколько покупается право быстро проверять гипотезы. Такой код может вообще никогда не стать частью "настоящего" софта, и тогда долг либо не возникнет, либо окажется дешёвым. Третий - усиление уже существующего мышления. Когда LLM используется не как автономный автор, а как очень умный grep`/`awk`/`sed с семантическим анализом: найти место, показать зависимость, проложить путь по call graph, предложить аккуратный рефакторинг. Здесь она может не увеличивать долг, а наоборот уменьшать его, потому что ускоряет понимание (rank drop). Поэтому главный вопрос сейчас, имхо, заменяла ли LLM понимание или усиливала его. Если заменяла, то почти наверняка растёт долг. Если усиливала, то нередко наоборот. Поэтому, имхо, код не стал commodity в сильном смысле, commodity стала его первичная генерация. А вот внимание, аудит, переиспользование и доверие к коду стали ещё более дефицитными и потому ещё более ценными. В этом смысле будущее, возможно, вообще не про экономику кода, а про экономику внимания к коду. И тогда один из главных навыков инженера - это умение отличать три вещи: где LLM помогает взять оправданный долг, где она помогает купить опцион на скорость, а где её надо использовать только как рычаг на понимание.
477
10
[рамка] Оркестрация и хореография: кому принадлежит continuation На мой взгляд, если говорить очень грубо, то задача архитект
[рамка] Оркестрация и хореография: кому принадлежит continuation На мой взгляд, если говорить очень грубо, то задача архитектора при проектировании заключается в том, чтобы понять, среди чего выбирать и, собственно, сделать сам выбор. Для первого как раз важна насмотренность (а для чего она не важна:)) и фундаментальные знания. И некоторые понятия, например, continuation и его принадлежность, являются как-будто не совсем очевидными предметами для выбора. Но при этом это понятие и свойство есть в любой системе, в которое есть понятие вызова или похожее на него и, соответственно, есть какая-то сущность, которую можно вызвать. Например, оно есть у любой распределённой системы: после того как некоторый участник сделал свой шаг, кто конкретно определяет следующий шаг системы? Имхо, именно здесь и проходит граница между оркестрацией и хореографией: - если continuation протокола принадлежит выделенной сущности, это оркестрация - если выделенной сущности нет, а continuation передаётся между самими участниками, это хореография Тут мне стоит сразу сказать, что это вроде бы не распространённый способ различения этих понятий, а только моя рамка мышления о них. Из-за названия "хореография" часто возникает ложная ассоциация, будто речь идёт о жёстко прописанном сценарии, а оркестрация и хореография различаются по степени свободы или импровизации. Это не так. Здесь важно не смешивать три разных вопроса: 1. Кто принимает решение о следующем шаге? Это и есть оркестрация vs хореография. 2. Как вычисляется следующий шаг? Это уже другая ось: простой if, таблица переходов, rules engine, DSL, полноценная вычислимая логика. 3. В каком контексте принимается решение? Какие есть инварианты, общие правила, таймауты, политики, ограничения протокола. То есть, оркестрация может быть как полностью детерминированной, так и сильно динамической. Хореография тоже может быть как строго ограниченной протоколом, так и весьма гибкой. Как-то мы сравнивали это с брейк-данс батлом. Есть общий бит, есть круг, есть правила игры, а тот, кто сейчас в круге, на основании текущего состояния танца фактически влияет на то, кто и как пойдёт дальше. Владение кругом в текущий момент - это и есть владение continuation. Сам танец - локальное вычисление агента. Оценка предыдущего шага и выбор следующего участника - логика маршрутизации. А общий бит и формат батла - это базовый протокол, который не даёт всей системе распасться в шум. С джазом ещё интереснее. Тут тоже важны эти три вопроса: - кто принимает решение - нет единого дирижёра, control распределён - как принимается решение - через импровизацию, перехват соло, локальную реакцию - в каком контексте - в рамках общего ритма, тональности, паттерна, то есть некоторого общего протокола С точки зрения control plane это можно сформулировать это так: оркестрация - centralized control plane хореография - distributed control plane На практике чистые формы встречаются редко. Реальные системы обычно гибридны. У них есть общий слой правил игры, например, контракт протокола, политика маршрутизации, согласованные таймауты, механизм выбора лидера или другой источник асимметрии - а поверх него уже могут существовать и оркестрационные, и хореографические участки. Это важно, потому что хореография не означает анархию. Чтобы распределённая система вообще делала прогресс, ей обычно нужен некоторый общий контекст и хотя бы минимальная асимметрия. Итог В оркестрации continuation принадлежит выделенному координатору. В хореографии continuation не имеет одного владельца и передаётся между участниками по правилам системы. А значит, различие между ними - это по сути различие между двумя способами организации control plane: централизованным и распределённым.
340
11
[кейс] ROP-программирование как IoC В прошлом посте continuation выглядела достаточно абстрактно, как ответ на вопрос что дел
[кейс] ROP-программирование как IoC В прошлом посте continuation выглядела достаточно абстрактно, как ответ на вопрос что делать дальше после текущего вычисления. В этом посте давайте рассмотрим сразу две вещи: почему владелец continuation важен, а также на ещё один пример IoC в виде ROP-программирования. В обычном стековом вызове одной из низкоуровневых материализаций continuation является адрес возврата. Думаю, что почти всем читателям этого блога известен самый простой класс бинарных уязвимостей - переполнение буфера в стеке. Обычно он возникает в ситуации, когда атакующий переполняет расположенный на стеке буфер, изменяя при этом адрес возврата. В простейших случаях (если на дворе были бы 2000-е) атакующий изменяет адрес возврата на шеллкод (то, что атакующий хочет исполнить), расположенный также на стеке. И после окончания выполнения функции ret берёт со стека текущее значение и интерпретирует его как continuation, передавая на него управление. Существует много так называемых exploit mitigations, способов защиты от эксплуатации, для данного класса уязвимостей. Одним из самых первых был способ, основанный на том, чтобы сделать страницы памяти в стеке неисполнямыми. Это достаточно элегантное решение, потому что оно препятствует исполнению шеллкода на стеке и при этом не нарушает работу почти всех легитимных программ. Первым методом обхода такой защиты являлся так называемый return-to-library, методика, в которой атакующий изменяет адрес возврата не на адрес шеллкода в стеке, а на адрес какой-то функции внутри, например, libc. Страницы памяти библиотек помечены флагом x и r и, соответственно, передача управления приведёт к нормальному исполнению этой вызванной функции. В качестве такой функции часто используют функцию system, принимающую аргументом строку команды, которая будет исполнена. Более общим и, на мой взгляд, реально красивым методом является так называемое ROP-программирование. Его идея заключается в том, чтобы рассматривать небольшие фрагменты функций в уже загруженных в процесс исполняемых файлах, оканчивающиеся инструкцией ret, как гаджеты. Каждый такой гаджет делает обычно что-то небольшое, например, перекладывает значение между регистрами, загружает что-то со стека и так далее. Примером гаджетов, например, являются xor eax, eax; ret или pop eax; ret. Одно из ключевое тут - инструкция ret, потому что она берёт адрес с текущего rsp и интерпретирует его как continuation. За счёт чего каждый гаджет вызывает следующий. В такой модели атакующий заранее преобразует свой шеллкод в такую ROP-цепочку, которая состоит из адресов гаджетов в библиотеках. Достаточно богатый набор таких гаджетов позволяет выражать Тьюринг-полную логику без инъекции нового кода. Существуют даже специальные ROP-компиляторы, которые позволяют как раз выразить делаемые действия через последовательность ROP-гаджетов. И вот тут стек, по сути, становится внешней программой управления. То, что в обычном исполненнии было скрытым control plane рантайма, становится управляемым атакующим сценарием дальнейших переходов. В обычном исполнении ret означает "вернись туда, откуда тебя позвал вызывающий код при обычном исполнении", а в ROP означает "возьми следующий шаг исполнения, контролируемого атакующим". Инструкции при этом по-прежнему легитимны, но композиция их уже нет. Именно поэтому удобно думать про ROP как про форму hostile IoC. В нормальной программе текущий код и так не полностью владеет своим будущим: следующее continuation управляется соглашением вызова, стековой дисциплиной, ABI и рантаймом. Т.е. control и без того частично инвертирован, а ROP захватывает эту логику, подменяя поставщика continuation. Там, где раньше слоем управления выступали рантайм и соглашения о вызове, теперь управлением владеет атакущий стек. Итог ROP - наглядный пример того, что control - это отдельный ресурс, который в данном случае выражен во владении continuation. Более явно мы это обсудим в следующем посте, посвящённом отличиям оркестрации от хореографии.
251
12
[рамка] call/cc: проявленный continuation Продолжая тему IoC, хочется сделать её дальнейшее рассмотрение более формальным. Дл
[рамка] call/cc: проявленный continuation Продолжая тему IoC, хочется сделать её дальнейшее рассмотрение более формальным. Для этого давайте поговорим про то, что называется continuation. Вообще у любого формализованного вычисления, как части общего вычисления, есть обычно скрытый параметр: а что делать дальше после того, как оно завершилось. Это как раз упомянутый выше continuation или продолжение вычисления или "остаток" общего вычисления, который ждёт текущий результат. Пусть у нас есть выражение, записанное в такой нотации: (+ 1 (f x)) f в нём всегда вызывается в конкретном контексте, ведь её результат потом будет применён в (+ 1 []). Вот эта "дырка" в, по сути, будущем и есть continuation. В наиболее распространённых парадигмах программирования она скрыта в стеке и способе организации вызовов, и мы почти никогда явно о ней не думаем. И если мы захотим проявить этот continuation, то получим то, что иногда называется call/cc, call with current continuation. Оператор берёт текущее продолжение и передаёт его программе как обычное значение, чаще всего как функцию, делая его first-class citizen. Простейший пример: (+ 1 (call/cc (lambda (с) (c 41)))) В точке вызова call/cc текущий continuation примерно такой: возьми значение v и вычисли (+ 1 v). При выполнении (c 41) мы не продолжаем выполнение тела lambda. Наоборот, текущее продолжение отбрасывается, и управление переносится в сохранённый контекст вызова call/cc. Поэтому 41 подставляется на место всего (call/cc ...), и выражение сводится к (+ 1 41), то есть к 42. В языке с call/cc функция больше не обязана нормально возвращаться своему вызывающему, вместо этого она может: не вернуться вовсе, вернуться не туда, вернуться позже, а в Scheme - даже использовать одно и то же продолжение многократно. То есть стек становится чем-то вроде программируемого графа переходов. В онтологии этого блога: стек - это скрытый control plane программы, а call/cc делает его явным. Поэтому, имхо, call/cc не просто экзотическая фича из Scheme, а очень глубокая идея управления вычислениями. С call/cc программа получает доступ не только к данным, но и к собственному будущему, control становится data. Многие из этих механизмов можно понимать как частные или дисциплинированные формы работы с continuations, в современных языках их часто удобнее моделировать через delimited continuations или effect handlers, а не через полный call/cc. Есть ещё одна красивая перспектива - это CPS. В continuation-passing style каждая функция и так уже получает continuation явно. Если в обычном варианте у нас функция выглядит как f : A -> B, то в CPS f: A -> (B -> R) -> R, где A - вход функции B - результат, который в direct style был бы просто возвращён R - финальный результат всей оставшейся программы (B -> R) - continuation, т.е. что делть дальше с результатом типа B Итог В обычном ЯП continuation обычно скрыт. В CPS continuation вынесена в сигнатуру. В call/cc continuation легализована языком.
0
13
[кейс] SpacetimeDB как IoC бэкенда В посте https://t.me/types_and_archetypes/52 я писал про "природные" проявления инверсии к
[кейс] SpacetimeDB как IoC бэкенда В посте https://t.me/types_and_archetypes/52 я писал про "природные" проявления инверсии контроля. Перед чтением этого поста рекомендую посмотреть визуально на пример озера - острова. SpacetimeDB - очень интересный пример IoC, обычно эта база данных позиционируется как database + server in one. Но, по сути, это инверсия контроля на уровне всего бэкенда. В обычном стеке приложение владеет жизненным циклом данных (вода вокруг острова). Оно читает базу, держит кэш, запускает API слой, отдельно тащит realtime синхронизацию, но SpacetimeDB сама база становится runtime для логики и синхронизации (теперь приложение становится озером, окружённом сушей). Архитектурная инверсия выглядит примерно так: база экспортирует таблицы и reducers, модуль с бизнес логикой живет внутри хоста, а клиент подключается через SDK, вызывает серверные функции и получает реалтайм обновления состояния. Клиентские чтения идут из локального cache, который контролируется с помощью механизма подписок, а изменения состояния проходят через reducer вызовы и фиксируются транзакционно. Т.е. в классической схеме код приложения дергает базу и сам решает, когда слать данные клиентам, а в SpacetimeDB код скорее описывает допустимые переходы состояния, а платформа сама решает, когда открыть транзакцию, когда закоммитить результат, как сохранить состояние и какие строки отправить в подписки. По сути, control flow тут переместился из уровня приложения внутрь рантайма базы данных. Data plane тут, по сути, это таблицы и зеркалируемые строки у клиента. Control plane тут host, транзакционный runtime, reducer entrypoints, identity контекст вызова и движок подписок. Именно там находится контроль над исполнением. Есть еще один важный разворот Клиент мыслится уже как локальная реплика интересующего среза данных, подписка говорит серверу, какой кусок состояния нужен клиенту, сервер присылает начальный набор строк и дальше пушит обновления. Получается формула read = local replica, write = reducer. Для игр, чатов и коллаборативного софта это очень сильный сдвиг модели. Reducer в SpacetimeDB это основной путь мутации таблиц. Он выполняется внутри атомарной транзакции и при ошибке откатывает изменения целиком. Процедуры существуют как более широкий метод взаимодействия с внешним миром, включая HTTP наружу, но там транзакционность уже ручная и сами разработчики рекомендуют оставлять обычные операции изменения состояния на reducers. SpacetimeDB - это другой класс решений по сравнению с ORM и BaaS. База забирает к себе серверную логику, транзакции, правила доступа и realtime раздачу состояния. И красота такого подхода в резком уменьшении числа границ. Цена решения, имхо, в такой же резкой онтологической гравитации (https://t.me/types_and_archetypes/15). Приложение начинает думать в терминах таблиц, reducer, подписок и хоста. То есть платформа убирает огромный объем механики, а взамен задает форму мира и будет являться невороятно жёстким lock-in для всей архитектуры. Тип тут довольно красивый. Если база пассивна, архитектура тянется к слоистому серверу. Если база владеет исполнением, архитектура тянется к state machine с зеркалируемыми клиентскими репликами. Инверсия тут применяется в самом дорогом месте современного бэкенда, в точке, где сходятся слой хранения, бизнес логика и синхронизации.
0
14
В эту пятницу в 18:30 буду рассказывать про свои результаты в том, что я называю "алгебраической теории координации". В частности, расскажу про как обеспечичить гарантии сходимости (convergence) и strong eventually consistency в распределённых системах. https://t.me/c/1970172456/373 Аннотация: Conflict-free Replicated Data Types (CRDT) гарантируют схождение данных без консенсуса, опираясь, в основном, на коммутативность операций слияния. Однако на практике мы часто имеем дело с так называемой наблюдаемой некоммутативностью (NC): это двойные траты UTXO, конкурентные перемещения узлов в деревьях, пересекающиеся права доступа или протоколы MPC. В таких случаях порядок применения операций напрямую определяет финальный результат, но система всё равно должна быть сходимой. В этом докладе мы разберём следующий результат: в присутствии NC гарантии схождения, независимости выполнения и строгой точности спецификации являются недостижимыми одновременно. Чтобы обойти это фундаментальное ограничение, архитектура распределенной системы вынуждена пойти ровно по одному из трех путей: - Commutativize: пожертвовать точностью спецификации, убрав порядок через факторизацию наблюдений (как это делает LWW-регистр, стирая реальную историю событий) - Causalize: отказаться от асинхронной независимости операций, введя строгие зависимости и построив причинно-следственный DAG, увеличивая объем метаданных - Reject: сузить пространство допустимых состояний, генерируя верифицируемые сертификаты конфликтов (например, для double-spend) и делегируя разрешение на уровень приложения
0
15
(быстрые мысли) Возможно, человечество столкнулось с самым лучшим маркетингом современности, основанном на массовой продаже с
(быстрые мысли) Возможно, человечество столкнулось с самым лучшим маркетингом современности, основанном на массовой продаже страха. А всё такое на первом уровне пирамиды Маслоу, как мы увидели, отлично работает. Представляете, ребята просто берут и выпускают списки профессий с вероятностями их исчезновения, которые превышают 90% и все начинают обсуждать в духе "ну да, теперь математики и инженеры больше не нужны". Это просто недостижимый уровень маркетинга, имхо.
0
16
Kelp, LayerZero и ещё одно подтверждение закона Конвея для рисков Когда-то я писал, почему 50 серверов - это не всегда децент
Kelp, LayerZero и ещё одно подтверждение закона Конвея для рисков Когда-то я писал, почему 50 серверов - это не всегда децентрализация: https://t.me/types_and_archetypes/6 История с Kelp, если опираться на их публичное описание инцидента вот тут https://x.com/KelpDAO/status/2046332070277091807, почти каноническое подтверждение этого тезиса. По версии Kelp, в атаке были скомпрометированы два RPC-узла, размещённые LayerZero, а по третьему прошёл DDoS. Этого оказалось достаточно, чтобы через forged cross-chain message вывести rsETH из бридж-адаптера. При этом важно отметить и другое: судя по их описанию, команда Kelp быстро среагировала, поставила контракты на паузу, заблокировала связанные с атакующим адреса и тем самым смогла остановить ещё одну попытку атаки на дополнительные 40 000 rsETH (~$95 млн). И главный урок - в архитектуре доверия. На схеме у нас могут быть несколько узлов, несколько эндпоинтов и даже "децентрализованная" инфраструктура. Но если эти компоненты размещаются, конфигурируются и операционно контролируются внутри одного административного домена, то это не настоящая децентрализация. Это только один логический узел, размноженный на несколько адресов. И ещё показателен момент с 1-of-1 DVN. По словам Kelp, именно такая конфигурация была задокументирована в LayerZero как default для новых OFT-деплоев и отдельно подтверждалась как допустимая при L2-экспансии. И это очень важный архитектурный вывод: default-настройка - это уже зашитая модель доверия и риска! Если default фактически означает, что система опирается на один верификационный домен, то скрытая централизация уже встроена в дизайн. То же самое работает, например, для default фич в проектах на расте - очень малый процент пользователей будет действительно разбираться, а что там вообще настраивается и на что настройки влияют. Вспомните, что мы делаем с инструкцией к телевизору, пока он работает. Поэтому при архитектурировании и аудите таких систем нужно задавать примерно такие вопросы: Сколько у нас реально независимых административных доменов? Кто владеет узлами? Кто держит ключи? Кто выкатывает конфигурацию? Кто подтверждает изменения? И переживёт ли система компрометацию одного оператора без каскадного отказа всего остального? Иначе это не уже децентрализация, а скорее репликация одного и того же доверительного допущения.
0
17
LLM для поиска семантически близких функций в бинарном коде И также хочу рассказать о второй работе, которая была представлен
LLM для поиска семантически близких функций в бинарном коде И также хочу рассказать о второй работе, которая была представлена Кучериным Георгием на той же конференции. Она касается поиска семантических близких функций в бинарном коде с помощью LLM. Это важная и совсем не тривиальная задача, потому что, например, один и тот же исходный код, собранный разными компиляторами или даже одним компилятором с разными флагами, может порождать заметно различающийся бинарный код. При этом в бинаре исчезают имена, типы, структура программы, а декомпилятор строит одну из возможных высокоуровневых интерпретаций. Поэтому прямое сравнение машинных инструкций, графов потока управления или даже декомпилированного псевдокода само по себе не даёт устойчивого критерия семантической близости. У нас на кафедре уже было опробовано несколько подходов к этой задаче. Например, перед этим в до-llm эпоху использовался подход, основанный на работе inst2vec и подход, основанный на Value Set Analysis. И вот в 2025–2026 годах начали появляться работы, где для для этой задачи используют llm. И тут стоит сказать, что эта задача поиска является NP-полной, а llm тут стоит рассматривать как эвристический решатель таких NP-полных задач, который или может с какой-то вероятностью решить её, или дать адекватное приближенное к правильному результату решение. Но если сравнивать каждую пару функций просто напрямую, то это становится слишком дорого и долго. Поэтому ключевая техническая идея и инновация работы состоит в том, чтобы не подавать модели полный декомпилированный код и не сравнивать функции как текст вообще, а сначала переводить каждую функцию в набор компактных DSL-представлений, отражающих разные аспекты её поведения. Т.е. получить то, что было нами названо многовидовой DSL-абстракцией. В работе для каждой декомпилированной функции строятся пять DSL-описаний: - поток данных - поток управления - вызовы вспомогательных функций - константы, используемые в битовых операциях - строки, встречающиеся внутри функции То есть вместо одного полного представления функции, мы получаем несколько "проекций" на разные семантические оси, которые подчёркивают какие-то аспекты. В текущей версии были выбраны вот эти 5 DSL, но их список может быть любым. Теперь к самому предлагаемому алгоритму: 1. Каждое непустое DSL-представление переводится в эмбеддинг. После этого для пары функций f и g вычисляется агрегированная векторная похожесть: - по каждому виду DSL считается косинусная близость - затем эти близости усредняются с весами - пустые представления аккуратно исключаются из оценки Получается быстрая метрика S_vecsim (f,g), которая позволяет для функции f взять только top-k кандидатов 2. Затем llm используется как локальный модуль переранжирования. Для некоторого числа отобранных кандидатов вычисляется дополнительная функция близости J(f,g), а затем финальный score задаётся как S_final(f,g)=(1−λ)*S_vecsim(f,g) + λ*J(f,g). 3. Дополнительно ещё можно использовать адаптивное отсечение кандидатов: если лучший кандидат по быстрой метрике S_vecsim оторвался от следующего кандидата достаточно сильно, то llm вообще можно не вызывать: уже гарантируется, что он останется лучшим и по финальной метрике. Более того, можно не фиксировать k заранее, а выбирать минимальную длину префикса ранжированного списка, достаточную для того, чтобы не потерять точный максимум по S_final. Практический смысл Также мне кажется, что похожий подход можно использоваться и для разработки и написания кода и архитектуры вместо вайбкодинга. В первом приближении он мог выглядеть примерно вот так: естественный язык -> вероятностный аппроксиматор (LLM) -> context-specific DSL/IR -> (детерминированно) код на языке высокого уровня Но тут очень много нюансов с потерей связи разработчика с кодом и необходимостью мелких итераций, чтобы успевать "проживать" проект и понимать, что там вообще происходит. Но как-будто большую часть распределённых систем (а это почти все важные) можно описать алгебраически и на основе этого сделать алгебро-типизированный слой DSL/IR.
0
18
PhantomHash: специализированная zkVM для zk-пруфов SHA-256 На прошлой неделе была конференция "Ломоносовские чтения-2026" (ht
PhantomHash: специализированная zkVM для zk-пруфов SHA-256 На прошлой неделе была конференция "Ломоносовские чтения-2026" (https://cs.msu.ru/news/4390), где Артём Николайчук, студент магистратуры, научным руководителем которого я являюсь, представил нашу работу по оптимизации размера и скорости доказательства sha-256 в STARK-подобных сценариях. По сути, это так называемая специализированная zkVM, оптимизированная только для доказательства работы sha-256 хэш функции. В рассматриваемом в работе сетапе она была оптимизирована в первую очередь по размеру доказательства, хотя эта схема может быть использована и для оптимизации по времени работы (latency/throughput). Sha-256 не является zk-friendly для реализации, потому что её алгоритм определён через 32-битные побитовые операции (XOR, AND, ROTR, SHR и сложение по модулю), а для STARK-систем такие операции являются ненативными и очень дорогими, потому что значение сначала нужно преобразовывать в бинарный вид и попутно доказывать, что это преобразование является верным. Потом делать саму побитовую операцию, а дальше преобразовывать результат обратно. В предлагаемой zkVM ключевым атомарным объектом является байт, что само по себе достаточно необычно по дизайну. Из-за чего каждое 32-битное слово кодируется четырьмя байтами как элементами нашего внутреннего поля Fp, а не тридцатью двумя битами. Основная побитовая логика затем реализуется в фиксированных lookup-таблицах (смотрите картинку): - для XOR и AND используется объединённая таблица BITOP8 - для побайтовых сдвигов и вращений используется таблица SHRPAIR, которая по паре соседних байтов восстанавливает выходной байт после сдвига на заданное число бит 4 столпа архитектурной оптимизации - форма функции Majority через Maj(a,b,c)=(a∧b)⊕(c∧(a⊕b)) Она требует всего двух AND-lookup на байт вместо стандартных трёх и даёт экономию 256 AND8-событий на один блок сжатия. - fan-out-safe access bus: для расписания сообщения W[t] слово записывается один раз, но затем многократно читается и в расписании, и в раундах сжатия, для этого строятся два списка обращений (L1 и L2), а корректность связывания доказывается через permutation/grand-product аргумент в стиле Cairo memory model. - формальная схема byte coverage: позволяет полностью отказаться от отдельного глобального lookup на таблицу U8 (проверка диапазона 0..255). - унификация lookup-архитектуры: таблицы спроектированы так, чтобы уменьшить число interaction arguments и тем самым напрямую снизить размер конечного доказательства. Отмечу ещё два момента: - арифметизация сложения по модулю 2^32: она реализована через байтовые гаджеты ADDk с явными переносами между байтами. В сбалансированном профиле мы используем "склеенные" суммы ADD6/ADD7, что позволяет не материализовывать лишние промежуточные слова (например, T1 и T2) и не делать дополнительные проверки диапазона. - теорема об исключении глобального U8: если байтовая переменная уже участвует в BITOP8 или SHRPAIR, то она неявно сертифицируется как элемент множества {0,…,255}. Для оставшихся "сиротских" столбцов добавляются лишь редкие dummy-checks. В самой работе мы доказываем теорему, что проверку байтовости - это структурное следствие архитектуры трассы. Итоговые параметры и бенчмарки Описанный подход приводит к следующим параметрам на блок: - 137 столбцов в исполнительной таблице - 257 переходных ограничений (включая ограничения шины) - 164 граничных утверждения Для размера входа в 2^20 байт сериализованный размер пруфа составляет 122.5 KB. В качестве ориентиров при том же числе security bits: Winterfell (наша реализация): 152.8 KB Binius: 404 KB Заключение Насколько мне известно, это одна из немногих специализированных zkVM, мне кажется, что в будущем их будет становиться всё больше и больше. Надеюсь, работа по созданию алгебраической основы для этого будет продолжена в аспирантуре. При этом получившийся размер в текущей схеме можно ещё сократить на ~30%, но это в следующих сериях. P.S. Если кому-то будут интересны подробности, могу скинуть пейпер и презентацию в комментариях.
0
19
KAN как IoC Давайте вернёмся к идее инверсии контроля, хочу рассказать ещё про несколько интересных примеров IoC in the wild.
KAN как IoC Давайте вернёмся к идее инверсии контроля, хочу рассказать ещё про несколько интересных примеров IoC in the wild. Неделю назад рассказывал другу про IoC и почему это важно для CS и его первой реакций было, что сети Колмогорова-Арнольда - отличный пример такой инверсии. И, действительно, давайте посмотрим на них под таким углом. В классическом MLP слой нейросети в первом приближении выглядит примерно вот так y = \sigma (\sum wx + b). В этой формуле ребро тупое и "умеет" только умножать сигнал на число, а вся выразительная сила сконцентрирована в вершине: нейрон сначала собирает все входы в одну сумму, а потом общей нелинейностью решает, как искривить пространство признаков. В онтологии моего блога ребро - data plane, который только выполняет работу по переносу и масштабированию результата вершина - control plane, который решает, а как именно искривить сигнал MLP сначала смешивает каналы, а потом делает нелинейное решение уже над агрегатом, т.е. различие между отдельными вкладами стирается рано уже на уровне суммы. При этом существуют разные фишки: learnable activations, gated MLP, spline-активации, PReLU, - но с ними мы только делаем уровень активации умнее, но не меняем место принятия решения, структура зависимости сохраняется: сначала merge, а потом nonlinear decision. А вот в KAN происходит как раз настоящая инверсия, в грубом виде там вычисление выглядит так y = \sum f(x), где f - это обучаемая одномерная функция на ребре (а не вес, как в MLP). Т.е. вместо веса на ребре находится параметризованная кривая (например, B-сплайн), и градиентный спуск оптимизирует её контрольные точки. В KAN нелинейность больше не сконценрирована в вершине, а распределяется по рёбрам, т.е. рёбра становятся умными, а вершины глупыми. С инженерной точки зрения теорема Колмогорова-Арнольда может звучать так: сложное многомерное поведение можно конструировать из суперпозиции более простых одномерных преобразований и сумм. Т.е. вместо mix -> bend в MLP, мы делаем bend per channel -> mix в KAN. Вот тут как раз меняется носитель связности (ну или геометрия модели), о чём я говорил в посте https://t.me/types_and_archetypes/52. Но у любой инверсии есть цена Умножение на скаляр - почти идеальная операция для современного железа. Матрицы прекрасно ложатся на GPU и на весь стек оптимизаций, который человечество десятилетиями строило вокруг них. Набор маленьких функций на каждом ребре - гораздо менее удобный объект, мы получаем лучшую локализацию геометрии, но платим сложностью ядра, векторизацией и памятью. Тип определяет архетип Если параметр на ребре - просто число, архитектура естественно тянется к централизованной нелинейности в узлах. Если параметр на ребре - функция, архитектура естественно тянется к распределённой геометрии взаимодействий. Ну или монолит/централизованный роутер vs edge computing Итог KAN, как мне кажется, один из самых красивых примеров инверсии контроля в современной архитектуре нейросетей. А следующий интересный вопрос отсюда уже такой: какие ещё модели в ML на самом деле являются не новыми "слоями", а скорее удачными инверсиями носителя выразительности? Спойлер: mechanism of attention, LoRa, диффузионные модели
0