mirror of https://github.com/voidlizard/hbs2
229 lines
14 KiB
Plaintext
229 lines
14 KiB
Plaintext
NOTE: on-refchans-tldr
|
||
CRDT с контролем прав доступа, но без полного онлайн-консенсуса.
|
||
Проблема: участники могут искажать историю после их удаления из
|
||
списка авторов.
|
||
|
||
Решение:
|
||
|
||
Транзакции требуют подтверждений от нескольких пиров.
|
||
Только авторизованные пиры могут подписывать и публиковать транзакции.
|
||
Транзакции подписываются автором и пиром, ссылаясь на актуальный ACL/HEAD.
|
||
Учитываем только транзакции, удовлетворяющие правилам HEAD.
|
||
Принимаем "запоздалые" записи в течение короткого периода после обновления HEAD.
|
||
|
||
Подход: комбинация CRDT и консенсуса. У нас есть PROPOSE и VOTE,
|
||
решения делаются через интерпретацию журнала или дополнительные
|
||
транзакции. "Авторы" публикуют только валидные транзакции,
|
||
достигая валидности при помощи сообщений эмеферного протокола
|
||
(фазы VALIDATE/PRECOMMIT).
|
||
|
||
|
||
|
||
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 ведёт именно к периодическому
|
||
запросу. В принципе, если не обрабатывать те логи,
|
||
что мы уже обработали, это должно быть достаточно
|
||
безвредно, если не очень часто.
|
||
|
||
TODO: dont-spam-with-notifications
|
||
Не рассылать нотификации, если контрагент отказывается их принимать.
|
||
Для этого: сделать протокол (?) отказа (?) от рассылки --- допустим,
|
||
из одной команды: RejectNotification Key ?
|
||
И в госсипе (?) или рефчане/рефлоге с одной стороны --- присылать команду,
|
||
что бы заткнулся, с другой стороны -- какой-то кэш устанавливать, и если ключ
|
||
не истёк -- то не рассылать сообщения в эту ссылку.
|
||
Потребует модификации gossip/broadcast
|
||
|
||
|
||
|