SHA256
Очень сильно переделать формат блоков
Внимание: версия ещё не проверена.
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
# Key Rotation API
|
||||
|
||||
> Протокольная семантика fork и target-ссылок: `docs/Blockchain/18_KEY_ROTATION_AND_FORK_TARGETS.md`.
|
||||
> Физические fork имеют номера только `001..999`; из `*-999` следующая ротация blockchain key запрещена.
|
||||
|
||||
Этот раздел описывает публичные JSON/WebSocket операции мастера смены ключей пользователя.
|
||||
|
||||
Публично доступны:
|
||||
@@ -274,8 +277,8 @@
|
||||
- проверяется, что current PDA действительно указывает на `candidateBlockchainName/newBlockchainKey`;
|
||||
- старая активная цепочка удаляется из рабочих PostgreSQL-таблиц;
|
||||
- candidate-блоки повторно проходят обычный `AddBlock` validation/projection path;
|
||||
- runtime-cache `to_bch_name` у логических ссылок `login + blockNumber + blockHash` перепривязывается к новому fork;
|
||||
- входящие `likes_count/replies_count` пересчитываются;
|
||||
- никакого rebind `to_bch_name` не выполняется: внешняя цель уже идентифицируется как `login + blockNumber + blockHash`;
|
||||
- входящие `message_stats` не удаляются; для активных LIKE удаляемого пользователя точечно пересчитываются только затронутые like-счётчики; REPLY в Stage 2 отдельно не перерабатывается;
|
||||
- после `COMPLETE` временные строки candidate-chain удаляются из PostgreSQL.
|
||||
|
||||
Если rebuild прерывается, `REBUILDING_SERVER` остаётся активным, ошибка записывается в `lastError/retryCount`, а worker безопасно повторяет rebuild.
|
||||
|
||||
@@ -40,7 +40,7 @@ Payload содержит:
|
||||
- `blockHash` / `prevBlockHash`;
|
||||
- `timestampMs`;
|
||||
- `msgType` / `msgSubType` / `msgVersion`;
|
||||
- для target-блоков: `toLogin + toBlockNumber + toBlockHash`;
|
||||
- для target-блоков: `toLogin + toForkNumber + toBlockNumber + toBlockHash` (для compact EDIT `toLogin/toForkNumber` отсутствуют);
|
||||
- `blockBytesB64`, только если запрошен `includeBlockBytes=true`.
|
||||
|
||||
После fork API показывает только новую активную ветку PostgreSQL. Исторические fork при необходимости восстанавливаются из Arweave/PDA history отдельным будущим viewer-механизмом.
|
||||
|
||||
@@ -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`
|
||||
|
||||
Обычно это одна основная цепочка пользователя.
|
||||
|
||||
@@ -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`
|
||||
- создание нового канала;
|
||||
|
||||
@@ -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,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 продолжает указывать на тот же логический блок.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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.
|
||||
@@ -46,6 +46,8 @@
|
||||
- `username-002` - вторая ветка;
|
||||
- `username-003` - третья ветка.
|
||||
|
||||
Номер fork ограничен диапазоном `001..999`; после `username-999` новая ротация blockchain key не создаётся.
|
||||
|
||||
Рабочая логика по умолчанию должна использовать последнюю актуальную ветку. Старые ветки остаются читаемыми и показывают историю смены ключей.
|
||||
|
||||
## `client key`
|
||||
|
||||
@@ -579,7 +579,7 @@ SYNC_POLL_INTERVAL_SECONDS=300
|
||||
- `ServerProfileBlock` в 1.2 допускает один адрес сервера, `AccessServersBlock` — 0 или 1 access server;
|
||||
- в compatibility SQL-поля проецируется **последний** fork как активный `blockchain_key/paid_limit_bytes`;
|
||||
- полный список fork сохраняется в `blockchain_forks_json`, поэтому поиск пользователя по старому blockchain key остаётся возможным;
|
||||
- `blockchain_name` для compatibility view вычисляется как `<normalized_login>-NNN`, где `NNN` соответствует индексу fork + 1;
|
||||
- `blockchain_name` для compatibility view вычисляется как `<normalized_login>-NNN`, где `NNN` соответствует номеру fork `001..999`; PDA с `fork_count > 999` сервером не принимается;
|
||||
- удалённые tip-поля (`used_bytes`, `last_block_*`, Arweave tx id) в PDA 1.2 больше не являются источником истины и в compatibility snapshot заполняются нейтральными значениями;
|
||||
- server profile считается присутствующим, если опубликован один server address; в старые SQL-поля временно проецируется этот адрес.
|
||||
|
||||
@@ -604,7 +604,7 @@ Create/update транзакции больше не реконструирую
|
||||
|
||||
### Candidate blocks ротации (PostgreSQL v27)
|
||||
|
||||
Начиная с migration v27 будущая ветка во время `COPYING_CHAIN` хранится в отдельной таблице `key_rotation_candidate_blocks`. Она не является частью текущего materialized blockchain state и не должна попадать в обычную `blocks` до финального переключения fork.
|
||||
Будущая ветка во время `COPYING_CHAIN` хранится в отдельной таблице `key_rotation_candidate_blocks`. Она не является частью текущего materialized blockchain state и не должна попадать в обычную `blocks` до финального переключения fork.
|
||||
|
||||
Для каждого candidate DataItem сохраняются rotation session, login, candidate blockchain name, block number/hash, полный ANS-104 DataItem, DataItem id и статус публикации. Уникальность `(rotation_session_id, block_number)` запрещает две разные версии одного candidate-блока.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user