Очень сильно переделать формат блоков

Внимание: версия ещё не проверена.
This commit is contained in:
AidarKC
2026-10-01 09:34:19 +03:00
parent 08c7fd7676
commit 3281da7f5c
65 changed files with 1863 additions and 2743 deletions
@@ -114,3 +114,10 @@ Slug входит в подпись DataItem и не может быть изм
7. проверить `blockNumber == last + 1`;
8. проверить `prevHash32 == lastBlockHash`;
9. записать DataItem и новое состояние атомарно в PostgreSQL.
## Нормативное правило версий body
После запуска layout существующей тройки `(type, subType, version)` неизменяем. Любое изменение порядка, размера или смысла подписываемых байтов требует новой `version`. В текущем чистом запуске используется только новый канонический layout; legacy-body не поддерживаются.
Для внешних target-полей fork является исторической меткой, а не частью логической identity. Identity определяется `toLogin + toBlockGlobalNumber + toBlockHash32`.
@@ -5,6 +5,7 @@
Базовый идентификатор цепочки пользователя:
- `blockchainName = <login>-<NNN>`
- `NNN` — реальный номер fork строго `001..999`; `000` и `1000+` недопустимы;
- пример: `alice-001`
Обычно это одна основная цепочка пользователя.
+2 -1
View File
@@ -6,7 +6,8 @@ TECH-тип покрывает системные записи цепочки.
1. `subType=0` — `HEADER_COMPAT`
- стартовый блок цепочки;
- payload: tag `SHiNE` + login владельца.
- payload: tag `SHiNE` + login владельца + `initialBlockchainKey32`;
- `initialBlockchainKey32` — public blockchain-signing key, которым был создан fork №1; это историческая точка происхождения и при последующих fork сохраняется в скопированном HEADER байт-в-байт.
2. `subType=1` — `TECH_CREATE_CHANNEL`
- создание нового канала;
+17 -7
View File
@@ -10,20 +10,20 @@ TEXT-тип хранит сообщения, материалы и редакт
2. `subType=11` — `TEXT_EDIT_POST`
- редактирование поста;
- line-поля + target на оригинальный POST + новый текст.
- line-поля + compact target (`toBlockGlobalNumber`, `toBlockHash32`) на оригинальный POST + новый текст.
3. `subType=20` — `TEXT_REPLY`
- ответ на сообщение;
- target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`) + текст.
- target (`toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`) + текст.
4. `subType=21` — `TEXT_EDIT_REPLY`
- редактирование ответа;
- target на исходный REPLY + новый текст.
- compact target (`toBlockGlobalNumber`, `toBlockHash32`) на исходный REPLY + новый текст.
- допускается пустой `text` для логического удаления сообщения (без физического удаления блока).
5. `subType=30` — `TEXT_RATING`
- target-based отзыв на конкретный блок;
- содержит target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`) + текст отзыва;
- содержит target (`toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`) + текст отзыва;
- не является сообщением линии канала.
6. `subType=50` — `TEXT_REPOST`
@@ -58,18 +58,28 @@ TEXT-тип хранит сообщения, материалы и редакт
Подробная спецификация: [16_TEXT_Channel_Meta.md](./16_TEXT_Channel_Meta.md).
## Общий target-формат TEXT
## Канонический target-формат TEXT
Для `TEXT_EDIT_POST`, `TEXT_REPLY`, `TEXT_EDIT_REPLY`, `TEXT_RATING` и `TEXT_REPOST` ссылка на цель хранится как:
Внешние цели (`TEXT_REPLY`, `TEXT_RATING`, `TEXT_REPOST`) используют:
```text
[1] toLoginLen (uint8)
[N] toLogin UTF-8
[2] toForkNumber (uint16, BigEndian)
[4] toBlockGlobalNumber
[32] toBlockHash32
```
`blockchainName`/номер fork в подписываемые байты target не входит. Одинаковые `login + blockNumber + blockHash` считаются одной логической целью после перепубликации сохранённого префикса при fork.
`toForkNumber`: `1..999` — fork, который видел автор действия; `0` зарезервирован как unknown/legacy. Поле информационное и **не входит в логическую идентичность цели**. Цель определяется только `toLogin + toBlockGlobalNumber + toBlockHash32`; несовпадение текущего fork само по себе не инвалидирует ссылку.
Собственные edit (`TEXT_EDIT_POST`, `TEXT_EDIT_REPLY`) используют компактный target:
```text
[4] toBlockGlobalNumber
[32] toBlockHash32
```
Login/fork для edit не подписываются: edit может ссылаться только на собственный блок текущей цепочки.
## Правило для edit
+4 -5
View File
@@ -4,10 +4,10 @@
1. `subType=1` — `REACTION_LIKE`
- лайк на целевой блок;
- хранит target: `toLogin`, `toBlockGlobalNumber`, `toBlockHash32`.
- хранит target: `toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`.
2. `subType=2` — `REACTION_UNLIKE`
- снятие лайка с целевого блока;
- хранит target: `toLogin`, `toBlockGlobalNumber`, `toBlockHash32`.
- хранит target: `toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`.
## Назначение
@@ -16,13 +16,12 @@
## Формат target
В подписанных байтах target больше не хранит имя fork/blockchain. Формат:
```text
[1] toLoginLen (uint8)
[N] toLogin UTF-8
[2] toForkNumber (uint16, BigEndian)
[4] toBlockGlobalNumber
[32] toBlockHash32
```
Логическая идентичность цели: `toLogin + toBlockGlobalNumber + toBlockHash32`. Поэтому ссылка остаётся той же после fork, если номер и hash исходного блока сохранены.
`toForkNumber` — только историческая метка (`1..999`, `0` reserved unknown/legacy). Логическая идентичность реакции: `toLogin + toBlockGlobalNumber + toBlockHash32`. При fork номер fork может измениться; если number+hash сохранены в новой ветке, LIKE/UNLIKE продолжает указывать на тот же логический блок.
+3 -2
View File
@@ -18,18 +18,19 @@ CONNECTION-тип описывает социальные связи и подп
## Общий формат payload
- line-поля (`lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`)
- target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`)
- target (`toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`)
## Бинарный target
```text
[1] toLoginLen (uint8)
[N] toLogin UTF-8
[2] toForkNumber (uint16, BigEndian)
[4] toBlockGlobalNumber
[32] toBlockHash32
```
Имя fork/blockchain в target не хранится.
`toForkNumber` хранит fork, который видел автор связи (`1..999`), но не участвует в identity. `0` зарезервирован как unknown/legacy; `1000+` недопустим. Для связи на пользователя используется **реальный HEADER hash**: `toBlockGlobalNumber=0`, `toBlockHash32=SHA-256(Frame HEADER)`. Нулевой hash запрещён.
## Правила target
+3 -1
View File
@@ -38,6 +38,7 @@
```text
[1] toLoginLen (uint8)
[N] toLogin UTF-8
[2] toForkNumber uint16 (BigEndian)
[4] toBlockGlobalNumber
[32] toBlockHash32
[2] textLenBytes (uint16)
@@ -46,7 +47,8 @@
Где:
- `toLogin` — login владельца целевого блока; номер fork в target не хранится;
- `toLogin` — login владельца целевого блока;
- `toForkNumber` — историческая метка fork (`1..999`, `0` reserved unknown/legacy), не часть identity;
- `toBlockGlobalNumber` — номер целевого блока;
- `toBlockHash32` — хэш целевого блока;
- `text` — опциональное пояснение пользователя к статусу.
@@ -0,0 +1,77 @@
# Key rotation, fork и логические ссылки
Этот документ фиксирует протокольную семантику fork. API-последовательность ротации описана отдельно в `docs/API/19_Key_Rotation_API.md`.
## 1. Идентичности
- Пользователь: `login`.
- Физическая ветка: `login-forkNumber` (`1..999`, формат имени строго три цифры: `001..999`; fork `1000` и выше запрещён).
- Активный fork определяется текущим PDA.
- Исторические fork остаются проверяемыми.
## 2. HEADER
Block 0 новой пользовательской истории — `HEADER`:
```text
SHiNE + login + initialBlockchainKey32
```
`initialBlockchainKey32` — public blockchain-signing key fork №1. При смене ключа сохранённый префикс, включая HEADER, перепубликуется с тем же Frame; поэтому это поле остаётся исходным ключом, а новый ключ конкретного fork определяется подписью/owner и PDA.
## 3. Внешний target
Для REPLY, RATING, REPOST, LIKE/UNLIKE, CONNECTION, STATUS_ACTION:
```text
[1] toLoginLen
[N] toLogin UTF-8
[2] toForkNumber uint16
[4] toBlockGlobalNumber
[32] toBlockHash32
```
`toForkNumber` — информационная историческая метка: fork, на который смотрел автор действия. `0` зарезервирован как unknown/legacy и не является реальным fork. Поле не участвует в identity и не должно сравниваться с текущим active fork как условие валидности.
Логическая identity цели:
```text
toLogin + toBlockGlobalNumber + toBlockHash32
```
Если сохранённый префикс перепубликован в новом fork байт-в-байт, номер блока и SHA-256(Frame) совпадают, поэтому внешняя ссылка продолжает указывать на тот же логический блок.
## 4. Собственный edit
EDIT_POST и EDIT_REPLY относятся только к собственному blockchain и используют:
```text
[4] toBlockGlobalNumber
[32] toBlockHash32
```
Login/fork не хранятся. Целевой оригинальный блок должен существовать в текущей ветке с тем же hash. Блок, отброшенный rollback и отсутствующий в новом active fork, редактировать из новой ветки нельзя.
## 5. CONNECTION на пользователя
Связь на пользователя всегда указывает на его реальный HEADER:
```text
toLogin = target login
toBlockGlobalNumber = 0
toBlockHash32 = SHA-256(target HEADER Frame)
```
Нулевой hash как sentinel запрещён.
## 6. Что переносится при fork
Для сохранённого префикса Frame не меняется. Могут измениться внешняя ANS-104 подпись, owner/DataItem ID, но SHiNE block hash (`SHA-256(Frame)`) остаётся прежним. Поэтому `number + hash` является устойчивой частью ссылки.
`toForkNumber` старого действия не переписывается после fork: это исторический факт момента создания действия.
## 7. Серверная модель Stage 2
Stage 2 больше не хранит physical target name как identity. `blocks_store` хранит физическую ветку как `(login, fork_number)`, а `reactions_state`, `message_stats`, `connections_state` и просмотры адресуют цель логически через `login + blockNumber + hash`. Поэтому при смене active fork никакого массового rebind `old_bch_name -> new_bch_name` не выполняется.
Если `number + hash` сохранились после fork, внешние состояния продолжают относиться к той же цели автоматически. Если hash изменился после rollback, это новая логическая цель.
@@ -0,0 +1,62 @@
# Stage 2 — logical targets and numeric fork storage
## Canonical identities
Physical fork identity is `(login, fork_number)`, where `fork_number` is strictly `1..999`. `login-NNN` is a derived presentation value only; `1000+` is invalid.
A logical external target is `(to_login, to_block_number, to_block_hash)`. `to_fork_number` remains signed historical metadata but MUST NOT participate in equality, validity, LIKE aggregation, CONNECTION identity, or fork rebinding.
## Tables
- `blocks_store`: stores `login + fork_number`; does not store `bch_name` or `to_bch_name`.
- `blockchain_state_store`: keyed by `(login,fork_number)`.
- compatibility SQL views `blocks` and `blockchain_state` derive `login-NNN` for old read/API paths; the name is not persisted.
- `reactions_state`: current actor LIKE/UNLIKE state keyed by actor login + logical target.
- `message_stats`: logical target counters. It may exist before the target block is locally available.
- `connections_state`: logical targets only.
- `message_views_state`: viewer + logical target.
## LIKE before target
A valid LIKE is projected even when the target block has not arrived. `message_stats` is created for the logical target. When the target later arrives, it reads the already existing counters by `login + blockNumber + hash`.
## Fork behaviour
If a prefix is republished unchanged, its Frame hash remains unchanged, so incoming social state is automatically reused. No rebind is performed.
If rollback replaces a block with the same number but a different hash, the new block has separate stats. Old stats may remain as historical/orphaned state and do not attach to the replacement.
## Atomic cleanup before replay
`REBUILDING_SERVER` is set before cleanup/replay. Cleanup is one SQL transaction:
1. collect active LIKE targets of the actor;
2. remove actor rows from `reactions_state`;
3. recount only affected LIKE counters (all/official/shining);
4. remove actor outgoing `connections_state`, refresh affected target-user/channel stats, and reset actor-derived profile/state;
5. delete physical blocks for `(login,fork_number)`;
6. delete physical blockchain state for `(login,fork_number)`;
7. commit.
Incoming `message_stats` for the user's targets are never deleted. Candidate blocks are then replayed normally. A crash leaves rotation in `REBUILDING_SERVER`, so rebuild can be retried.
## REPLY
Stage 2 does not redesign REPLY. Only the unavoidable database-column adaptation uses the logical target key after `to_bch_name` removal; no new REPLY UI/resolver/cleanup behaviour is introduced.
## EDIT
EDIT remains own-chain only. EDIT blocks disappear with their physical fork and return through replay. `edits_count` is not part of Stage 2 message aggregates.
## Database bootstrap / DB v2 marker
Stage 2 uses a new database generation explicitly marked as **`SHiNE_DB_V2`** (`database_generation=2`) in `shine_database_metadata`. The runtime schema revision is `29`.
Stage 2 is installed only on a clean PostgreSQL schema. The final schema is created directly by `schema_v1.sql`; there is no `migration_v29.sql`. Both the server bootstrapper and the SQL bootstrap script reject a non-empty/legacy database instead of modifying or migrating it.
At every server start, both conditions are required:
- `shine_database_metadata.id=1`, `database_format='SHiNE_DB_V2'`, `database_generation=2`;
- `db_schema_version.id=1`, `schema_version=29`.
If an old SHiNE database is configured accidentally, server startup fails with an explicit legacy/non-v2 database error.