mirror of https://github.com/voidlizard/hbs2
wip
This commit is contained in:
parent
5b052e8a56
commit
32a7ecfd70
|
@ -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 ведёт именно к периодическому
|
||||||
|
запросу. В принципе, если не обрабатывать те логи,
|
||||||
|
что мы уже обработали, это должно быть достаточно
|
||||||
|
безвредно, если не очень часто.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -45,8 +45,8 @@ import Data.Text qualified as Text
|
||||||
import Data.Text.Encoding qualified as TE
|
import Data.Text.Encoding qualified as TE
|
||||||
import Data.Time.Clock (NominalDiffTime)
|
import Data.Time.Clock (NominalDiffTime)
|
||||||
import Data.Heap qualified as Heap
|
import Data.Heap qualified as Heap
|
||||||
import Data.Heap (Heap,Entry(..))
|
import Data.Heap (Entry(..))
|
||||||
import Data.Time.Clock
|
-- import Data.Time.Clock
|
||||||
|
|
||||||
data PeerInfo e =
|
data PeerInfo e =
|
||||||
PeerInfo
|
PeerInfo
|
||||||
|
|
|
@ -68,6 +68,7 @@ common common-deps
|
||||||
, unliftio
|
, unliftio
|
||||||
, unix
|
, unix
|
||||||
, heaps
|
, heaps
|
||||||
|
, psqueues
|
||||||
|
|
||||||
common shared-properties
|
common shared-properties
|
||||||
ghc-options:
|
ghc-options:
|
||||||
|
|
Loading…
Reference in New Issue