From 32a7ecfd701945d8dae53a45de8a79fa15418f97 Mon Sep 17 00:00:00 2001 From: Dmitry Zuikov Date: Mon, 17 Jul 2023 06:33:16 +0300 Subject: [PATCH] wip --- docs/todo/refchan.txt | 200 +++++++++++++++++++++++++++++++++++++ hbs2-peer/app/PeerTypes.hs | 4 +- hbs2-peer/hbs2-peer.cabal | 1 + 3 files changed, 203 insertions(+), 2 deletions(-) create mode 100644 docs/todo/refchan.txt diff --git a/docs/todo/refchan.txt b/docs/todo/refchan.txt new file mode 100644 index 00000000..2fd91293 --- /dev/null +++ b/docs/todo/refchan.txt @@ -0,0 +1,200 @@ +NOTE: on-refchans-1 + Написал, что фиксация владельцем истории не меняется, и это инвариант. + Смешно получилось. + + Коротко: + + 1) Нам хочется изобразить что-то, желательно CRDT, без + полноценного онлайн-консенсуса, но при этом с возможностью + управлять правами. В идеале и чтения и записи в журнал для + многих авторов. + + 2) (опущены промежуточные выкладки, плоды, между прочим, + дней страданий и выброшенного на помойку кода) + CRDT такой пишется, но даёт возможность зловредным авторам и + корумпированным ими узлам отравлять историю, поскольку даже + если автора удалили из ACL, он может на своём узле не принять + это обновление и несмотря на то, что его обновления не будут + принимать на добросовестных узлах --- если кто-то придёт + за журналом на его узел, он сможет невозбранно писать + "исторические" записи, ссылаясь на любой ACL/HEAD, где он + еще был + + 3) CRDT в целом не стоек к византийскому поведению, атаки + на него придумываются легко и быстро + + 4) Не хочется заставлять владельца канала делать какие-то + действия оперативно, по замыслу -- владелец это просто ключ, + который хранится в сейфе, и на свет выходит, только когда + нужно поменять какую-то существенную метаинформацию, например, + инициирвать форк ну или ACL поменять + + 5) В общем, нам надо придумать какие-то однозначные правила, + которые бы позволили бы принимать транзакции только относительно + валидного "в сейчас" ACL/HEAD и дать какие-то однозначные правила + интерпретации журнала, которые бы могли при прочтении лога + дать возможность установить, что записи были сделаны в соответствии + с актуальными в моменте ACL. + + Решение: + + 1. Для каждой транзакции требовать нескольких подтверждений от разных + пиров (нод). + + 1. Писать транзакции в журнал, если есть несколько + подтверждений. + + 3. ACL устанавливает не только набор авторов, но и набор пиров, + которые могут подтверждать транзакции. Эти пиры подписывают + транзы и постят PROPOSE/ACCEPT. Остальные пиры могут только + транслировать их через Gossip. Неприятное следствие: + журнал будут писать только авторизованные пиры, значит, + количество его экземпляров будет ограниченно ими. Но может, + это и к лучшему. + + 4. Каждая транзакция подписывается и ключом автора, и ключом + пира и содержит ссылку на актуальный ACL/HEAD, подписанный + владельцем канала/журнала/ссылки + + 5. При чтении журнала учитывать только те транзакции, + которые удовлетворяют правилам, установленным в HEAD (число + подтверждений) + + 6. Остаётся проблема, что делать, если приехали записи задним + числом --- если их отвергать, то журналы могут не сойтись, + а если принимать --- то остаётся проблема с отравлением + истории. + + Единственное, что тут вижу --- всем нодам принимать + "исторические" записи в течение какого-то периода после + обновления HEAD и не принимать после. Ну типа, за минуту-то + всяко все транзы разойдутся, а позже не принимаем. + + + Как можно видеть --- этот подход смесь CRDT (однозначные + правила интерпретации журнала) и "онлайн" консенсуса, + тут примерно половина его. "Полусенсус" -- по сути, + есть PROPOSE и VOTE (у нас: ACCEPT), остальные выводы + относительно того, что делать с этими транзакциями + можно делать, или интерпретируя журнал, или вводя + дополнительные транзакции, которые уже обрабатываются + сторонними приложениями. Т.е тут опущены фазы + VALIDATE/PRECOMMIT/COMMIT. Предполагается, что "авторы" + постят уже валидные транзакции, а договориться, какая + транзакция являестся валидной, они должны отдельно, + если им надо, при помощи "эфемерных" транзакций. + + + + +NOTE: refchan-log-syncronization-1 + что мы тут делаем. + 1. Получили merkle лога + 2. Надо читать лог, если мы его еще не обработали. + Каждую транзу из него валидировать, + и если мы его еще не обработали -- + писать вон в refChanWriteQ + Теперь вот прикол: авторы траз будут верифицированы относительно + "сейчас", а не "тогда". То есть, если к нам едут старые записи, + а мы не знаем, в каком они контексте -- то они будут отвергнуты + относительно "сейчас". То есть еще раз: лог едет, допустим, + старый и некоторым авторам в него писать было можно. + А сейчас --- нельзя. Такие транзакции не пройдут валидацию. + Верно и обратное: если добавлены пермишены для каких-то авторов, + но мы эту информацию еще не получили --- то такие транзакции + будут отвергнуты. + Какое видится решение: + 1. Любую метаинформацию обрабатывать отдельно и сразу. + 2. Транзакции откладывать, если они валидные, но не прошли + проверку. + Источник метаинформации всегда один --- это владелец ссылки. + "Рано или поздно" метаинформация у всех обновится, и мы перенакатим + отложенные транзакции. + + Второй вариант. Поскольку лог транзакций приезжает всегда весь, пусть и по + частям. + + Мы можем обрабатывать транзакции из него в контексте тех ACL, что есть в нём + же, а не глобальных. Соответственно, сначала принимаем только те + транзакции, что соответствуют логу. + + принимаем все транзакции на обновление метаинформации. Объединяем этот лог и + свой. + + +Какую мы задачу решаем: + +Как принимать или отвергать транзакции в условиях частичной несинхронности +состояния? + +Метаинформация (ACL) не имеет контекста сама по себе. + +Но всегда имеет контекст в плане принятого журнала -- т.е та +информация, что там определена, та и влияет на приём транзакций +из этого журнала. + +Таким образом, у нас есть следующие варианты обработки транзакций. + +Вариант 1 + +1. Принять журнал + +2. Обновить сначала метаинформацию из него + +3. С обновленной метаинформацией принимать транзакции из него + +4. Непринятые транзакции откладывать и пытаться принять их потом + +Замечание: если узел прислал журнал, в котором есть транзакции, +которые не соответствуют метаинформации (acl) --- это вызывает +вопросы к этому узлу. Такие транзации нужно отвергать. + +Таким образом, мы: + +В контексте одного присланного лога: + + - Извлекаем метаинформацию + - Обрабатываем транзакции с **этой** метаинформацией + - Добавляем только транзации, которые прошли верификацию + - Помечаем лог обработанным + - Конец. + +Итого, как же нам обработать присланный журнал? + +1. Скачали журнал + +2. Поставили в очередь на обработку, если еще не обработан + +3. В процессе обработки: + +3.1 Достаём метаинформацию и обновляем стейт + +3.2 С сохранённой метаинформацией (из стейта) проигрываем + журнал транзакций. + +Что тут плохо: + + Стейт глобальный. Если метаинформацию держать глобально, + то она не будет соответствовать тому контексту, в котором существует присланный лог. + + Нужно каждый лог обрабатывать отдельно в его контексте. + + В чём минусы: он может быть огромный и нас можно легко + зафлудить. + + Как решать? + + 1. Не обрабатывать то состояние, которое у нас уже было. + Мы должны вести историю. + + 2. Не запрашивать (fetch) лог просто так. + + Сейчас инструкция poll ведёт именно к периодическому + запросу. В принципе, если не обрабатывать те логи, + что мы уже обработали, это должно быть достаточно + безвредно, если не очень часто. + + + + + diff --git a/hbs2-peer/app/PeerTypes.hs b/hbs2-peer/app/PeerTypes.hs index fa37718d..6e995956 100644 --- a/hbs2-peer/app/PeerTypes.hs +++ b/hbs2-peer/app/PeerTypes.hs @@ -45,8 +45,8 @@ import Data.Text qualified as Text import Data.Text.Encoding qualified as TE import Data.Time.Clock (NominalDiffTime) import Data.Heap qualified as Heap -import Data.Heap (Heap,Entry(..)) -import Data.Time.Clock +import Data.Heap (Entry(..)) +-- import Data.Time.Clock data PeerInfo e = PeerInfo diff --git a/hbs2-peer/hbs2-peer.cabal b/hbs2-peer/hbs2-peer.cabal index f088af5c..e785a5b0 100644 --- a/hbs2-peer/hbs2-peer.cabal +++ b/hbs2-peer/hbs2-peer.cabal @@ -68,6 +68,7 @@ common common-deps , unliftio , unix , heaps + , psqueues common shared-properties ghc-options: