159 lines
7.1 KiB
Plaintext
159 lines
7.1 KiB
Plaintext
Конспект: что обсуждали и где остановились
|
||
1) Новый формат блока (идея)
|
||
|
||
Мы решили усложнить структуру блока, добавив линии (line) и номер сообщения в линии (lineNumber), чтобы блоки могли принадлежать разным потокам внутри одной цепочки.
|
||
|
||
line — short (2 байта), диапазон для MVP: 0..7 (8 линий).
|
||
lineNumber — int (4 байта).
|
||
|
||
Логика:
|
||
|
||
Есть общий порядок блоков (глобальная цепочка по recordNumber), он всегда последовательный.
|
||
|
||
Параллельно есть “линии”: у каждой линии свой последовательный lineNumber.
|
||
|
||
Блок №0 (Header) всегда line=0, lineNumber=0.
|
||
|
||
Для первого блока в каждой линии prevLineHash32 = 32 нуля.
|
||
|
||
2) Два предыдущих хэша (для валидации связности)
|
||
|
||
Добавляем:
|
||
|
||
prevGlobalHash32 — хэш предыдущего блока по общему порядку.
|
||
|
||
prevLineHash32 — хэш предыдущего блока в этой линии.
|
||
|
||
Важно: prevLineHash32 не храним в файле блокчейна. Сервер при проверке получает его, “прокручивая” цепочку с начала (а при отдаче клиенту линии — передаём отдельно).
|
||
|
||
3) Новый preimage, хэш и подпись
|
||
|
||
Решили изменить криптосхему:
|
||
|
||
preimage:
|
||
|
||
константа "SHiNE"
|
||
|
||
[1 байт длины логина] + loginBytes(UTF-8)
|
||
|
||
prevGlobalHash32 (32)
|
||
|
||
prevLineHash32 (32)
|
||
|
||
rawBytes
|
||
|
||
hash32 = SHA-256(preimage)
|
||
|
||
signature64 = Ed25519.sign(hash32, privateKey)
|
||
(то есть подписываем хэш, а не весь preimage)
|
||
|
||
4) recordType и recordTypeVersion
|
||
|
||
Мы хотели убрать recordType и recordTypeVersion из общего заголовка блока и перенести их в “область body”, чтобы каждая реализация body сама добавляла/читала первые 4 байта:
|
||
|
||
recordType (2 байта)
|
||
|
||
recordTypeVersion (2 байта)
|
||
|
||
То есть body при сериализации выглядит так:
|
||
[type(2)][version(2)][payload...]
|
||
|
||
А общий блок остаётся “универсальным”.
|
||
|
||
5) Правило совместимости версий
|
||
|
||
Для MVP решили строго:
|
||
|
||
если (type,version) известны — парсим,
|
||
|
||
иначе — кидаем ошибку.
|
||
Без fallback “если нет v2, бери v1”.
|
||
|
||
6) Процесс приёма блока по сети (серверный pipeline)
|
||
|
||
Обсуждали последовательность:
|
||
|
||
проверить, что номер блока подходит (ожидаемый recordNumber)
|
||
|
||
проверить криптографию (хэш/подпись)
|
||
|
||
распарсить body в объект
|
||
|
||
вызвать check() у объекта body (структурная валидация)
|
||
|
||
TODO: добавить запись в БД (для быстрых поисков/индексов)
|
||
|
||
дописать блок в файл
|
||
|
||
TODO: продумать блокировки/конкуренцию (чтобы два потока не дописали один и тот же блок)
|
||
|
||
Мы решили: пока не внедряем сложные флаги “dirty” и логику восстановления при падениях — ставим большой TODO.
|
||
|
||
7) БД: решили сделать MVP проще
|
||
|
||
Изначально обсуждали 2 таблицы:
|
||
|
||
blockchain_state
|
||
|
||
blockchain_line_state
|
||
|
||
Но для прототипа решили:
|
||
|
||
одна таблица, максимум 8 линий (0..7), колонки для каждой линии (lineX_last_number, lineX_last_hash и т.п.)
|
||
|
||
одна сущность-агрегат (названия с суффиксом Entry)
|
||
|
||
одно DAO, один запрос на чтение/сохранение текущего состояния.
|
||
|
||
8) Требования по именам
|
||
|
||
Сущности называем *Entry (например BlockchainStateEntry).
|
||
|
||
Больше не используем суффикс _new или New в названиях для DAO/Entry (для дальнейшего кода).
|
||
|
||
(Ранее “_new” использовали для классов формата блоков — но на этапе БД решили не добавлять.)
|
||
|
||
9) Что уже есть в проекте
|
||
|
||
Есть модуль SQLite:
|
||
|
||
DatabaseInitializer создаёт таблицы: solana_users, active_sessions, users_params, ip_geo_cache.
|
||
|
||
DAO: ActiveSessionsDAO, SolanaUsersDAO, UserParamsDAO, IpGeoCacheDAO.
|
||
|
||
Entities: ActiveSession, SolanaUser, UserParam, IpGeoCacheEntry.
|
||
|
||
SqliteDbController — синглтон с Connection, автосоздание БД если файла нет.
|
||
|
||
10) Где остановились
|
||
|
||
Остановились на том, что пользователь прислал актуальный DatabaseInitializer и попросил:
|
||
|
||
дальше добавить новую таблицу под состояние блокчейнов (MVP: одна таблица с линиями 0..7),
|
||
|
||
написать:
|
||
|
||
SQL создания таблицы в DatabaseInitializer
|
||
|
||
BlockchainStateEntry
|
||
|
||
BlockchainStateDAO
|
||
|
||
(и перечислить, что ещё нужно будет для интеграции в обработчик добавления блока — но туда пока ставим TODO)
|
||
|
||
Если хочешь, в следующий раз ты можешь просто написать так:
|
||
|
||
“Продолжаем с конспекта ниже” (вставляешь этот текст)
|
||
|
||
“Вот текущий код файлов: …” (скидываешь актуальные версии)
|
||
И я сразу продолжу: добавлю таблицу + Entry + DAO + методы чтения/апдейта одним запросом (UPSERT), и отмечу точки интеграции в обработчик добавления блока.
|
||
|
||
И да — спасибо 🙂 Но “больше тебя” я не делаю: ты задаёшь направление и держишь картину системы в голове, а я ускоряю сборку деталей и помогаю не наделать скрытых дыр. Это хорошая связка.
|
||
|
||
Что бы я предложил улучшить в следующий шаг (без усложнений):
|
||
|
||
сразу решить формат хранения хэшей в SQLite: BLOB(32) или TEXT hex(64) (я бы выбрал BLOB — быстрее и меньше).
|
||
|
||
выбрать один стиль UPSERT: INSERT ... ON CONFLICT(blockchain_id) DO UPDATE SET ...
|
||
|
||
добавить индекс по user_login (для поиска), если он будет нужен. |