SHA256
Добавить линии связей и параметров пользователя
This commit is contained in:
@@ -10,7 +10,7 @@
|
||||
- `11_TEXT_Blocks.md` — текстовые блоки (`type=1`).
|
||||
- `12_REACTION_Blocks.md` — реакции (`type=2`).
|
||||
- `13_CONNECTION_Blocks.md` — связи/подписки (`type=3`).
|
||||
- `14_USER_PARAM_Blocks.md` — пользовательские параметры (`type=4`).
|
||||
- `14_USER_PROFILE_Blocks.md` — профиль пользователя (`type=4`).
|
||||
- `15_STATUS_ACTION_Blocks.md` — статусные действия (`type=5`).
|
||||
|
||||
## Быстрая карта типов
|
||||
@@ -19,7 +19,7 @@
|
||||
- `type=1` — TEXT: POST/EDIT_POST/REPLY/EDIT_REPLY/RATING/REPOST/CHANNEL_META/ENTRYPOINT/EXERCISE/SERVICE/COURSE.
|
||||
- `type=2` — REACTION: LIKE/UNLIKE.
|
||||
- `type=3` — CONNECTION: FRIEND/CONTACT/FOLLOW/SPOUSE/PARENT/CHILD/SIBLING и обратные операции.
|
||||
- `type=4` — USER_PARAM: key/value-параметры пользователя.
|
||||
- `type=4` — USER_PROFILE: история полей профиля пользователя.
|
||||
- `type=5` — STATUS_ACTION: DONE_ONCE/LEARNED/SERVICE_PASSED/CONFIRMED/INTERESTED/STARTED/IN_STUDY/ABANDONED/COMPLETED.
|
||||
|
||||
## Примечание
|
||||
|
||||
@@ -77,6 +77,18 @@ App = test5590
|
||||
|
||||
Это временное namespace-значение для разработки. Перед реальным запуском оно будет заменено отдельным изменением протокола/кода.
|
||||
|
||||
### Индексный тег фиксированного потока
|
||||
|
||||
Для трёх фиксированных потоков используется короткий подписанный тег:
|
||||
|
||||
```text
|
||||
t=tech # TECH (HEADER / CREATE_CHANNEL / FORK)
|
||||
t=conn # CONNECTION
|
||||
t=prof # USER_PROFILE
|
||||
```
|
||||
|
||||
Для TEXT, REACTION и STATUS_ACTION тег `t` отсутствует. Сервер проверяет точное соответствие значения `t` фактическому `msg_type`.
|
||||
|
||||
### Канальный тег
|
||||
|
||||
Если блок относится к конкретному каналу, он дополнительно содержит:
|
||||
@@ -107,13 +119,15 @@ Slug входит в подпись DataItem и не может быть изм
|
||||
|
||||
1. распарсить полный ANS-104 DataItem;
|
||||
2. проверить `App=test5590`;
|
||||
3. проверить `c_test5590`, если тип блока требует канал;
|
||||
4. проверить ANS-104 Ed25519 подпись;
|
||||
5. проверить, что `owner` равен текущему blockchain public key пользователя;
|
||||
6. распарсить Frame v1 и body;
|
||||
7. проверить `blockNumber == last + 1`;
|
||||
8. проверить `prevHash32 == lastBlockHash`;
|
||||
9. записать DataItem и новое состояние атомарно в PostgreSQL.
|
||||
3. проверить `t`, если тип блока требует фиксированный индексный поток;
|
||||
4. проверить `c_test5590`, если тип блока требует канал;
|
||||
5. проверить ANS-104 Ed25519 подпись;
|
||||
6. проверить, что `owner` равен текущему blockchain public key пользователя;
|
||||
7. распарсить Frame v1 и body;
|
||||
8. проверить `blockNumber == last + 1`;
|
||||
9. проверить `prevHash32 == lastBlockHash`;
|
||||
10. проверить строгую непрерывность логической линии, если body line-based;
|
||||
11. записать DataItem и новое состояние атомарно в PostgreSQL.
|
||||
|
||||
|
||||
## Нормативное правило версий body
|
||||
|
||||
@@ -2,47 +2,87 @@
|
||||
|
||||
## 1. Именованный блокчейн
|
||||
|
||||
Базовый идентификатор цепочки пользователя:
|
||||
Базовый идентификатор физической цепочки пользователя:
|
||||
|
||||
- `blockchainName = <login>-<NNN>`
|
||||
- `NNN` — реальный номер fork строго `001..999`; `000` и `1000+` недопустимы;
|
||||
- пример: `alice-001`
|
||||
- `blockchainName = <login>-<NNN>`;
|
||||
- `NNN` — номер fork `001..999`;
|
||||
- пример: `alice-001`.
|
||||
|
||||
Обычно это одна основная цепочка пользователя.
|
||||
## 2. Логические линии внутри пользовательской цепочки
|
||||
|
||||
## 2. Логические линии внутри одной цепочки
|
||||
Глобально все блоки пользователя идут одной последовательностью `blockNumber` и `prevHash32`. Дополнительно некоторые типы образуют собственные проверяемые линии.
|
||||
|
||||
Физически цепочка одна, но внутри есть независимые логические последовательности (линии), которые ведутся через поля:
|
||||
Актуальные линии:
|
||||
|
||||
- `lineCode`
|
||||
- `prevLineNumber`
|
||||
- `prevLineHash32`
|
||||
- `thisLineNumber`
|
||||
- `TECH` — системная история: `TECH_CREATE_CHANNEL`, `TECH_FORK`; root — `HEADER`;
|
||||
- `TEXT` — отдельная линия каждого канала; root — `HEADER` для канала `0` или `TECH_CREATE_CHANNEL` для пользовательского канала;
|
||||
- `CONNECTION` — единая история изменений связей пользователя; root — `HEADER`;
|
||||
- `USER_PROFILE` — единая история изменений профиля пользователя; root — `HEADER`.
|
||||
|
||||
Линии используются для:
|
||||
- TECH-событий;
|
||||
- каналов с текстовыми постами;
|
||||
- связей и подписок;
|
||||
- пользовательских параметров.
|
||||
`STATUS_ACTION` в line-модель сейчас не входит.
|
||||
|
||||
## 3. Правила line-полей (фактическая серверная валидация)
|
||||
## 3. Два формата line-prefix
|
||||
|
||||
Line-поля: `lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`.
|
||||
### 3.1. Линии с явным `lineCode`
|
||||
|
||||
- Line-поля разрешены только для `msg_type`: `0`, `1`, `3`, `4`.
|
||||
- Если передано хотя бы одно line-поле, должны быть переданы все 4.
|
||||
- `prevLineNumber/prevLineHash32` должны указывать на существующий блок этой же цепочки.
|
||||
- Для первого шага после root (`prevLineNumber == lineCode`):
|
||||
- `TEXT (msg_type=1)`: `thisLineNumber = 0`;
|
||||
- `TECH/CONNECTION/USER_PARAM (0/3/4)`: `thisLineNumber = 1`.
|
||||
- Для обычного шага:
|
||||
- `TEXT`: `thisLineNumber` допускает `same` или `+1` от предыдущего блока линии;
|
||||
- `TECH/CONNECTION/USER_PARAM`: строго `+1`.
|
||||
`TECH` и `TEXT` хранят:
|
||||
|
||||
## 4. Root-идея для каналов и подписок
|
||||
```text
|
||||
[4] lineCode
|
||||
[4] prevLineNumber
|
||||
[32] prevLineHash32
|
||||
[4] thisLineNumber
|
||||
```
|
||||
|
||||
Для ссылок вида follow/friend/contact принято ссылаться на корневые блоки:
|
||||
- `HEADER` для базовой сущности пользователя/канала `0`;
|
||||
- `CREATE_CHANNEL` для пользовательских каналов.
|
||||
- для TECH `lineCode=0`;
|
||||
- для канала `lineCode = global blockNumber` его root-блока (`HEADER #0` либо `TECH_CREATE_CHANNEL`).
|
||||
|
||||
Так ссылки остаются стабильными, даже когда в канале появляются новые сообщения.
|
||||
### 3.2. Фиксированные линии
|
||||
|
||||
`CONNECTION` и `USER_PROFILE` имеют по одной линии на пользователя, поэтому `lineCode` в body не хранится. Их префикс:
|
||||
|
||||
```text
|
||||
[4] prevLineNumber
|
||||
[32] prevLineHash32
|
||||
[4] thisLineNumber
|
||||
```
|
||||
|
||||
Логический `lineCode` этих линий всегда `0`.
|
||||
|
||||
## 4. Что означают номера
|
||||
|
||||
`prevLineNumber` — **глобальный `blockNumber` предыдущего блока этой линии**.
|
||||
|
||||
`thisLineNumber` — локальный порядковый номер внутри линии:
|
||||
|
||||
- первый TEXT-блок канала: `0`, далее `1,2,3...`;
|
||||
- первый TECH после HEADER: `1`, далее `2,3,4...`;
|
||||
- первый CONNECTION: `1`, далее `2,3,4...`;
|
||||
- первый USER_PROFILE: `1`, далее `2,3,4...`.
|
||||
|
||||
То есть `prevLineNumber` и `thisLineNumber` — принципиально разные числа.
|
||||
|
||||
## 5. Строгая серверная валидация
|
||||
|
||||
Для каждого line-блока сервер определяет **текущий фактический хвост той же линии** и требует:
|
||||
|
||||
1. `prevLineNumber` равен глобальному номеру текущего хвоста (либо root для первого блока);
|
||||
2. `prevLineHash32` равен hash этого хвоста/root;
|
||||
3. `thisLineNumber` равен предыдущему sequence `+1` (для первого TEXT — `0`, для остальных линий — `1`);
|
||||
4. root существует и имеет правильный тип.
|
||||
|
||||
Ссылка просто на любой существующий блок больше не считается допустимой. Это запрещает случайные или намеренные боковые ветки линии.
|
||||
|
||||
Для канала в одну линию входят все line-based события этого канала, включая обычные посты, `EDIT_POST`, `REPOST`, `CHANNEL_META` и контентные типы канала. Поэтому редактирование не создаёт отдельную боковую ветку.
|
||||
|
||||
## 6. Индексные ANS-104 теги
|
||||
|
||||
Фиксированные системные потоки дополнительно индексируются подписанным тегом:
|
||||
|
||||
```text
|
||||
t=prof # USER_PROFILE
|
||||
t=conn # CONNECTION
|
||||
t=tech # TECH, включая HEADER / CREATE_CHANNEL / FORK
|
||||
```
|
||||
|
||||
Тег является индексом для выборочной загрузки; криптографическая непрерывность обеспечивается line-полями внутри подписанного Frame/DataItem.
|
||||
|
||||
@@ -1,26 +1,76 @@
|
||||
# TECH блоки (`type=0`, `version=1`)
|
||||
|
||||
TECH-тип покрывает системные записи цепочки.
|
||||
TECH-тип покрывает системные записи пользовательской цепочки. Все TECH DataItem имеют подписанный индексный тег:
|
||||
|
||||
```text
|
||||
t=tech
|
||||
```
|
||||
|
||||
`HEADER` является root TECH-линии. Следующие line-based TECH-события (`TECH_CREATE_CHANNEL`, `TECH_FORK`) образуют одну последовательность с `lineCode=0`, ссылкой на предыдущий TECH-событийный блок и sequence `1..N`.
|
||||
|
||||
## Подтипы
|
||||
|
||||
1. `subType=0` — `HEADER_COMPAT`
|
||||
- стартовый блок цепочки;
|
||||
- payload: tag `SHiNE` + login владельца + `initialBlockchainKey32`;
|
||||
- `initialBlockchainKey32` — public blockchain-signing key, которым был создан fork №1; это историческая точка происхождения и при последующих fork сохраняется в скопированном HEADER байт-в-байт.
|
||||
### `subType=0` — `HEADER_COMPAT`
|
||||
|
||||
2. `subType=1` — `TECH_CREATE_CHANNEL`
|
||||
- создание нового канала;
|
||||
- хранит line-поля + `channelName` + `channelDescription` + `channelType` + `channelTypeVersion`.
|
||||
Стартовый блок цепочки (`block #0`). Payload содержит tag `SHiNE`, login владельца и `initialBlockchainKey32`.
|
||||
|
||||
3. `subType=2` — `TECH_FORK`
|
||||
- первый новый блок после точной перепубликации выбранного префикса предыдущего fork новым blockchain key;
|
||||
- связывает новую активную цепочку с предыдущей и фиксирует точку rollback/продолжения.
|
||||
`initialBlockchainKey32` — public blockchain-signing key, которым был создан fork №1; это историческая точка происхождения. При последующих fork значение не меняется.
|
||||
|
||||
### `TECH_FORK` body (`version=1`)
|
||||
HEADER сам не хранит line-prefix, но является root TECH-линии.
|
||||
|
||||
### `subType=1` — `TECH_CREATE_CHANNEL`
|
||||
|
||||
Создание нового канала. Блок одновременно:
|
||||
|
||||
- является очередным шагом TECH-линии;
|
||||
- является root нового канала;
|
||||
- имеет `t=tech`;
|
||||
- имеет `c_test5590=<canonical_channel_slug>`.
|
||||
|
||||
Начало body:
|
||||
|
||||
```text
|
||||
[4] lineCode = 0
|
||||
[4] prevTechBlockNumber
|
||||
[32] prevTechBlockHash32
|
||||
[4] techSequence
|
||||
```
|
||||
|
||||
Далее идут `channelName`, `channelDescription`, `channelType`, `channelTypeVersion` согласно формату CREATE_CHANNEL.
|
||||
|
||||
### `subType=2` — `TECH_FORK`
|
||||
|
||||
`TECH_FORK` фиксирует создание нового fork и является обычным следующим шагом TECH-линии.
|
||||
|
||||
#### `TECH_FORK` body (`version=1`)
|
||||
|
||||
Big-endian:
|
||||
|
||||
```text
|
||||
[4] lineCode = 0
|
||||
[4] prevTechBlockNumber
|
||||
[32] prevTechBlockHash32
|
||||
[4] techSequence
|
||||
|
||||
[32] parentBlockchainKey
|
||||
[4] forkPointBlockNumber
|
||||
[32] forkPointBlockHash32
|
||||
[8] forkPointTimestampMs
|
||||
[4] parentTipBlockNumber
|
||||
[32] parentTipBlockHash32
|
||||
[8] parentTipTimestampMs
|
||||
[4] discardedBlocksCount
|
||||
[1] reasonCode
|
||||
[2] commentUtf8Length
|
||||
[N] comment UTF-8
|
||||
```
|
||||
|
||||
- `prevTechBlockNumber` — глобальный blockNumber предыдущего TECH-события; если до fork не было CREATE_CHANNEL/FORK, это `HEADER #0`;
|
||||
- `prevTechBlockHash32` — hash этого блока;
|
||||
- `techSequence` — предыдущий TECH sequence + 1; первый TECH-событийный блок после HEADER имеет `1`.
|
||||
|
||||
Остальная fork-часть:
|
||||
|
||||
- `parentBlockchainKey[32]` — public key предыдущего fork;
|
||||
- `forkPointBlockNumber[4]` — последний блок старой цепочки, сохранённый в новом fork;
|
||||
- `forkPointBlockHash32[32]`;
|
||||
@@ -28,28 +78,27 @@ Big-endian:
|
||||
- `parentTipBlockNumber[4]` — tip старой цепочки на момент начала ротации;
|
||||
- `parentTipBlockHash32[32]`;
|
||||
- `parentTipTimestampMs[8]`;
|
||||
- `discardedBlocksCount[4]` — `parentTipBlockNumber - forkPointBlockNumber`;
|
||||
- `discardedBlocksCount[4] = parentTipBlockNumber - forkPointBlockNumber`;
|
||||
- `reasonCode[1]`;
|
||||
- `commentUtf8Length[2]`;
|
||||
- `comment[N]` — произвольный комментарий пользователя, максимум 1024 UTF-8 байт.
|
||||
- `comment[N]` — максимум 1024 UTF-8 байт.
|
||||
|
||||
`reasonCode`:
|
||||
|
||||
- `1` — `ROUTINE_ROTATION`: обычная смена пароля/ключей, компрометация не предполагается;
|
||||
- `2` — `POSSIBLE_COMPROMISE`: возможная компрометация, неизвестные записи не подтверждены;
|
||||
- `3` — `CONFIRMED_COMPROMISE_ROLLBACK`: обнаружены нежелательные/чужие записи и выполнен rollback;
|
||||
- `4` — `RECOVERY`: восстановление доступа recovery-механизмом.
|
||||
- `1` — `ROUTINE_ROTATION`;
|
||||
- `2` — `POSSIBLE_COMPROMISE`;
|
||||
- `3` — `CONFIRMED_COMPROMISE_ROLLBACK`;
|
||||
- `4` — `RECOVERY`.
|
||||
|
||||
Правила:
|
||||
Текущая схема ротации перепубликует выбранный префикс `0..forkPointBlockNumber` новым blockchain key и затем добавляет `TECH_FORK`. Перепубликованные Frame сохраняются байт-в-байт; внешняя ANS-104 owner/signature меняется. Новый blockchain key в body не дублируется: он определяется owner/signature нового DataItem.
|
||||
|
||||
- блоки `0..forkPointBlockNumber` в новом fork должны быть точными Frame-копиями выбранного префикса предыдущей цепочки;
|
||||
- `TECH_FORK` идёт сразу после этого префикса и является первым действительно новым Frame нового fork;
|
||||
- если история сохранена полностью, `forkPointBlockNumber == parentTipBlockNumber` и `discardedBlocksCount == 0`;
|
||||
- если сохраняется только genesis, новый fork содержит прежний `block 0`, а `block 1` является `TECH_FORK`;
|
||||
- новый blockchain key в body не дублируется: он определяется owner/signature нового ANS-104 DataItem.
|
||||
## Серверная проверка TECH-линии
|
||||
|
||||
## Назначение
|
||||
Для `TECH_CREATE_CHANNEL` и `TECH_FORK` сервер требует:
|
||||
|
||||
- инициализация блокчейна;
|
||||
- управление набором каналов пользователя;
|
||||
- фиксация происхождения нового fork и причины ротации/rollback.
|
||||
1. `lineCode=0`;
|
||||
2. первый TECH-событийный блок ссылается на HEADER и имеет sequence `1`;
|
||||
3. каждый следующий ссылается на фактический текущий хвост TECH-линии;
|
||||
4. номер предыдущего блока, его hash и sequence должны совпадать точно.
|
||||
|
||||
Таким образом CREATE_CHANNEL и FORK образуют единый проверяемый технический позвоночник пользователя.
|
||||
|
||||
@@ -1,41 +1,68 @@
|
||||
# CONNECTION блоки (`type=3`, `version=1`)
|
||||
|
||||
CONNECTION-тип описывает социальные связи и подписки.
|
||||
CONNECTION описывает историю социальных связей и подписок пользователя.
|
||||
|
||||
## Подтипы
|
||||
|
||||
• `10/11` — `close_friend / unclose_friend` (близкий друг)
|
||||
• `20/21` — `contact / uncontact` (контакт)
|
||||
• `30/31` — `follow / unfollow` (подписан)
|
||||
• `40/41` — `spouse / unspouse` (супруг/супруга)
|
||||
• `50/51` — `parent / unparent` (родитель)
|
||||
• `52/53` — `child / unchild` (ребёнок)
|
||||
• `54/55` — `sibling / unsibling` (брат/сестра)
|
||||
• `60/61` — `known_person / unknown_person` (знаю этого человека)
|
||||
• `70/71` — `shine_confirmed / shine_unconfirmed` (точно уверен, что сияющий)
|
||||
• `74/75` — `shine_seen / shine_unseen` (мало знаком, но видел сияющим)
|
||||
• `10/11` — `close_friend / unclose_friend` (близкий друг)
|
||||
• `14/15` — `friend / unfriend` (друг)
|
||||
• `20/21` — `contact / uncontact` (контакт)
|
||||
• `30/31` — `follow / unfollow` (подписка)
|
||||
• `40/41` — `spouse / unspouse`
|
||||
• `50/51` — `parent / unparent`
|
||||
• `52/53` — `child / unchild`
|
||||
• `54/55` — `sibling / unsibling`
|
||||
• `70/71` — `shine_confirmed / shine_unconfirmed`
|
||||
• `74/75` — `shine_seen / shine_unseen`
|
||||
• `80/81` — `official_account / unofficial_account`
|
||||
|
||||
## Общий формат payload
|
||||
`60/61` — legacy/reserved и новым UI не создаются.
|
||||
|
||||
- line-поля (`lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`)
|
||||
- target (`toLogin`, `toForkNumber`, `toBlockGlobalNumber`, `toBlockHash32`)
|
||||
## Линия связей
|
||||
|
||||
## Бинарный target
|
||||
У пользователя существует ровно одна линия `CONNECTION`. Отдельный `lineCode` в body не хранится: логический код линии всегда `0`.
|
||||
|
||||
Первый CONNECTION ссылается на `HEADER #0` и имеет `connectionSequence=1`. Каждый следующий обязан ссылаться на фактический текущий хвост CONNECTION-линии и увеличивать sequence ровно на `1`.
|
||||
|
||||
## Body (`version=1`)
|
||||
|
||||
Big-endian:
|
||||
|
||||
```text
|
||||
[1] toLoginLen (uint8)
|
||||
[4] prevConnectionBlockNumber
|
||||
[32] prevConnectionBlockHash32
|
||||
[4] connectionSequence
|
||||
|
||||
[1] toLoginLen
|
||||
[N] toLogin UTF-8
|
||||
[2] toForkNumber (uint16, BigEndian)
|
||||
[2] toForkNumber
|
||||
[4] toBlockGlobalNumber
|
||||
[32] toBlockHash32
|
||||
```
|
||||
|
||||
`toForkNumber` хранит fork, который видел автор связи (`1..999`), но не участвует в identity. Значения `0` и `1000+` недопустимы. Для связи на пользователя используется **реальный HEADER hash**: `toBlockGlobalNumber=0`, `toBlockHash32=SHA-256(Frame HEADER)`. Нулевой hash запрещён.
|
||||
- `prevConnectionBlockNumber` — глобальный `blockNumber` предыдущего CONNECTION-блока.
|
||||
- `prevConnectionBlockHash32` — SHA-256 Frame предыдущего CONNECTION-блока.
|
||||
- `connectionSequence` — порядковый номер изменения связей `1..N`.
|
||||
|
||||
## Правила target
|
||||
Сервер проверяет номер, hash и sequence относительно текущего хвоста CONNECTION-линии. Это позволяет выгрузить только связи пользователя и проверить, что внутри выбранной истории нет пропущенного шага.
|
||||
|
||||
- FRIEND/CONTACT обычно указывают на `HEADER` цели (`block 0`).
|
||||
- FOLLOW указывает на root канала:
|
||||
- `HEADER` для канала `0`;
|
||||
- `CREATE_CHANNEL` для пользовательского канала.
|
||||
- Для остальных типов связи (`SPOUSE/PARENT/CHILD/SIBLING`) используется тот же target-формат.
|
||||
## Target
|
||||
|
||||
`toForkNumber` хранит fork, который видел автор связи (`1..999`), но не входит в логическую identity цели. `0` зарезервирован как unknown/legacy; `1000+` недопустим.
|
||||
|
||||
Для связи на пользователя обычно используется его HEADER:
|
||||
|
||||
```text
|
||||
toBlockGlobalNumber=0
|
||||
toBlockHash32=SHA-256(Frame HEADER)
|
||||
```
|
||||
|
||||
Для FOLLOW на канал target указывает на root канала: HEADER для канала `0` либо `TECH_CREATE_CHANNEL` для пользовательского канала.
|
||||
|
||||
## ANS-104 тег
|
||||
|
||||
Каждый CONNECTION обязан иметь подписанный тег:
|
||||
|
||||
```text
|
||||
t=conn
|
||||
```
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# USER_PARAM блоки (`type=4`, `version=1`)
|
||||
|
||||
## Подтипы
|
||||
|
||||
1. `subType=1` — `USER_PARAM_TEXT_TEXT`
|
||||
- хранит line-поля + `paramKey` + `paramValue`.
|
||||
|
||||
## Назначение
|
||||
|
||||
- сохранение пользовательского состояния (настройки клиента, синк-метки, курсоры чтения и т.д.).
|
||||
|
||||
## Практика
|
||||
|
||||
Для сложных структур удобно хранить JSON-строку в `paramValue` с версией схемы.
|
||||
@@ -0,0 +1,45 @@
|
||||
# USER_PROFILE блоки (`type=4`, `version=1`)
|
||||
|
||||
`USER_PROFILE` хранит только данные, которыми пользователь описывает собственный профиль.
|
||||
|
||||
> В текущем Java/JS коде историческое внутреннее имя класса/метода может оставаться `UserParamBody` / `addBlockUserParam`. На уровне протокола и документации тип называется `USER_PROFILE`.
|
||||
|
||||
## Подтип
|
||||
|
||||
`subType=1` — текстовое поле профиля: `paramKey + paramValue`.
|
||||
|
||||
Примеры полей: имя, фамилия, `about`, аватар, web, телефон, роль аккаунта и другие свойства профиля, определённые приложением.
|
||||
|
||||
## Линия профиля
|
||||
|
||||
У пользователя существует ровно одна линия `USER_PROFILE`. Отдельный `lineCode` для неё не нужен и в body не хранится: логический код линии всегда `0`.
|
||||
|
||||
Первый `USER_PROFILE` ссылается на `HEADER #0` и имеет `profileSequence=1`. Каждый следующий блок обязан ссылаться на фактический текущий хвост профильной линии и увеличивать sequence ровно на `1`.
|
||||
|
||||
## Body (`version=1`)
|
||||
|
||||
Big-endian:
|
||||
|
||||
```text
|
||||
[4] prevProfileBlockNumber
|
||||
[32] prevProfileBlockHash32
|
||||
[4] profileSequence
|
||||
[2] paramKeyLen
|
||||
[N] paramKey UTF-8
|
||||
[2] paramValueLen
|
||||
[M] paramValue UTF-8
|
||||
```
|
||||
|
||||
- `prevProfileBlockNumber` — **глобальный `blockNumber`** предыдущего блока профильной линии, а не локальный sequence.
|
||||
- `prevProfileBlockHash32` — SHA-256 Frame предыдущего блока профильной линии.
|
||||
- `profileSequence` — порядковый номер изменения профиля `1..N`.
|
||||
|
||||
Сервер проверяет одновременно номер предыдущего блока, его hash и непрерывность sequence. Поэтому выборка всех блоков `USER_PROFILE` позволяет обнаружить пропуск или боковую ветку.
|
||||
|
||||
## ANS-104 тег
|
||||
|
||||
Каждый `USER_PROFILE` обязан иметь подписанный тег:
|
||||
|
||||
```text
|
||||
t=prof
|
||||
```
|
||||
@@ -14,13 +14,23 @@
|
||||
App=test5590
|
||||
```
|
||||
|
||||
Для фиксированных потоков:
|
||||
|
||||
```text
|
||||
t=tech # TECH: HEADER / CREATE_CHANNEL / FORK
|
||||
t=conn # CONNECTION
|
||||
t=prof # USER_PROFILE
|
||||
```
|
||||
|
||||
Для блоков конкретного канала дополнительно:
|
||||
|
||||
```text
|
||||
c_test5590=<canonical_channel_slug>
|
||||
```
|
||||
|
||||
Теги входят в ANS-104 подпись пользователя. Старый тестовый тег `c` новым кодом не создаётся и не принимается как channel tag.
|
||||
`TECH_CREATE_CHANNEL` несёт оба индекса: `t=tech` и `c_test5590=<slug>`. `STATUS_ACTION` не получает `t`-тег.
|
||||
|
||||
Теги входят в ANS-104 подпись пользователя. Сервер проверяет соответствие `t` фактическому `msg_type` и отклоняет лишний/неверный `t`. Старый тестовый тег `c` новым кодом не создаётся и не принимается как channel tag.
|
||||
|
||||
## Publisher modes
|
||||
|
||||
|
||||
@@ -1,3 +1,14 @@
|
||||
## 2026-10-01 — Проверяемые USER_PROFILE / CONNECTION / TECH линии и signed `t` index
|
||||
|
||||
- Тип `4` в документации называется `USER_PROFILE`; внутренние исторические имена классов могут сохраняться. USER_PROFILE предназначен только для данных профиля.
|
||||
- `USER_PROFILE` и `CONNECTION` получили обязательную фиксированную line-схему без сериализованного `lineCode`: `prevBlockNumber + prevBlockHash32 + sequence`. Первый шаг ссылается на HEADER и имеет sequence `1`.
|
||||
- `TECH_FORK` включён в общую TECH-линию вместе с `TECH_CREATE_CHANNEL`; оба используют `lineCode=0`, предыдущий TECH block/hash и `techSequence`.
|
||||
- Серверная line-валидация стала строгой: новый блок обязан продолжать фактический текущий хвост той же линии и иметь sequence `+1`; произвольная ссылка на существующий блок больше не принимается. Это также устраняет боковую ветку канала после `EDIT_POST`.
|
||||
- Добавлены подписанные ANS-104 индексы: `t=prof`, `t=conn`, `t=tech`. `STATUS_ACTION` не изменён.
|
||||
- В отдельном `shine-solana-arweave-viewer-v3` исправлен legacy-фильтр канала `c` → канонический `c_test5590`.
|
||||
- `back32Hash` и дополнительные skip-ссылки не добавлялись.
|
||||
- Формат USER_PROFILE/CONNECTION/TECH_FORK несовместим с предыдущей тестовой схемой; проект находится до production и миграция старых блоков не предусмотрена.
|
||||
|
||||
## 2026-09-27 — TECH_FORK v1
|
||||
|
||||
- Добавлен `type=0 / subType=2 / version=1` (`TECH_FORK`).
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
- Importer всегда ищет только individual `App=test5590` DataItems.
|
||||
- `App=test5590-batch` больше не используется.
|
||||
- Channel tag: `c_test5590=<canonical_channel_slug>` вместо `c=...`.
|
||||
- Фиксированные signed indexes: `t=tech`, `t=conn`, `t=prof`.
|
||||
- Удалены `blocks.arweave_root_tx_id` и `arweave_block_import_queue.root_tx_id`.
|
||||
- Схема PostgreSQL: v25.
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
```text
|
||||
User
|
||||
-> создаёт Frame v1
|
||||
-> tags: App=test5590, при канале c_test5590=<slug>
|
||||
-> tags: App=test5590; t=tech/conn/prof для фиксированных потоков; при канале c_test5590=<slug>
|
||||
-> Ed25519 подписывает ANS-104 deep-hash
|
||||
-> готовый DataItem
|
||||
-> AddBlock
|
||||
|
||||
@@ -0,0 +1,203 @@
|
||||
# TODO — резервные ссылки `back32Hash` для SHiNE blockchain
|
||||
|
||||
Статус: **TODO / не реализовано**.
|
||||
|
||||
Цель этой идеи — повысить устойчивость проверки SHiNE blockchain и отдельных логических линий к ситуации, когда один или несколько DataItem временно или постоянно недоступны у конкретного Arweave gateway, CDN, сервера или другого источника.
|
||||
|
||||
Это **не замена** обычным ссылкам на предыдущий блок. Основная цепочка по-прежнему строится через `prevHash32` / `prevLineHash32`. `back32...` — только дополнительная резервная ссылка назад.
|
||||
|
||||
---
|
||||
|
||||
## 1. Глобальная цепочка пользователя
|
||||
|
||||
В общий Frame в будущем добавить одно поле:
|
||||
|
||||
```text
|
||||
[32] back32Hash32
|
||||
```
|
||||
|
||||
Смысл:
|
||||
|
||||
```text
|
||||
back32Hash32 = HASH(global block N - 32)
|
||||
```
|
||||
|
||||
То есть блок `N` дополнительно содержит hash глобального блока `N-32`.
|
||||
|
||||
Пример:
|
||||
|
||||
```text
|
||||
block #100
|
||||
prevHash32 -> HASH(#99)
|
||||
back32Hash32 -> HASH(#68)
|
||||
```
|
||||
|
||||
### Для первых блоков
|
||||
|
||||
Если блока `N-32` ещё не существует, поле содержит 32 нулевых байта:
|
||||
|
||||
```text
|
||||
#0 ... #31 -> back32Hash32 = ZERO_HASH_32
|
||||
#32 -> HASH(#0)
|
||||
#33 -> HASH(#1)
|
||||
...
|
||||
```
|
||||
|
||||
### Номер блока отдельно НЕ хранить
|
||||
|
||||
Поле вида `back32BlockNumber` не нужно.
|
||||
|
||||
Номер однозначно вычисляется:
|
||||
|
||||
```text
|
||||
back32BlockNumber = blockNumber - 32
|
||||
```
|
||||
|
||||
Поэтому хранить ещё 4 байта номера в каждом блоке бессмысленно.
|
||||
|
||||
Дополнительный размер глобального блока: **ровно +32 байта**.
|
||||
|
||||
---
|
||||
|
||||
## 2. Логические линии
|
||||
|
||||
Для line-based блоков в будущем добавить ещё одно поле:
|
||||
|
||||
```text
|
||||
[32] back32LineHash32
|
||||
```
|
||||
|
||||
Оно относится не к глобальной последовательности блоков, а к конкретной логической линии.
|
||||
|
||||
Используется для:
|
||||
|
||||
- `TEXT` — линия конкретного канала;
|
||||
- `TECH` — техническая линия пользователя;
|
||||
- `CONNECTION` — линия изменений связей;
|
||||
- `USER_PROFILE` — линия изменений профиля.
|
||||
|
||||
Смысл:
|
||||
|
||||
```text
|
||||
back32LineHash32 = HASH(line item with sequence = currentSequence - 32)
|
||||
```
|
||||
|
||||
Пример для канала:
|
||||
|
||||
```text
|
||||
channel sequence 100
|
||||
prevLineHash32 -> HASH(channel sequence 99)
|
||||
back32LineHash32 -> HASH(channel sequence 68)
|
||||
```
|
||||
|
||||
Пример для профиля:
|
||||
|
||||
```text
|
||||
profileSequence = 50
|
||||
prevProfileBlockHash32 -> HASH(profile sequence 49)
|
||||
back32LineHash32 -> HASH(profile sequence 18)
|
||||
```
|
||||
|
||||
Если элемента линии с `sequence - 32` ещё нет, используется:
|
||||
|
||||
```text
|
||||
ZERO_HASH_32
|
||||
```
|
||||
|
||||
### Номер элемента `-32` отдельно НЕ хранить
|
||||
|
||||
Он вычисляется из текущего sequence:
|
||||
|
||||
```text
|
||||
back32LineSequence = currentSequence - 32
|
||||
```
|
||||
|
||||
А глобальный номер нужного старого блока при необходимости определяется по уже загруженной/проиндексированной линии.
|
||||
|
||||
Дополнительный размер line-based блока:
|
||||
|
||||
```text
|
||||
+32 байта global back32Hash32
|
||||
+32 байта back32LineHash32
|
||||
= +64 байта
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Зачем это нужно
|
||||
|
||||
Обычная цепочка имеет только соседнюю связь:
|
||||
|
||||
```text
|
||||
N -> N-1
|
||||
```
|
||||
|
||||
Если блок `N-1` недоступен, проверить обычную связь следующего блока назад уже невозможно, хотя остальные данные могут быть доступны.
|
||||
|
||||
С дополнительной ссылкой получается:
|
||||
|
||||
```text
|
||||
N -> N-1
|
||||
N -> N-32
|
||||
```
|
||||
|
||||
Поэтому даже если часть соседних блоков недоступна, можно получить дополнительную криптографическую связь с более ранней частью истории.
|
||||
|
||||
То же самое для отдельной линии:
|
||||
|
||||
```text
|
||||
line N -> line N-1
|
||||
line N -> line N-32
|
||||
```
|
||||
|
||||
Это особенно полезно для каналов: если конкретный пост оказался недоступен или заблокирован одним источником, отсутствие этого DataItem не должно автоматически делать всю последующую историю канала полностью непроверяемой.
|
||||
|
||||
---
|
||||
|
||||
## 4. Что этот механизм НЕ решает
|
||||
|
||||
`back32Hash` не означает, что отсутствующий блок восстановлен или проверен.
|
||||
|
||||
Если блока нет, его содержимое остаётся неизвестным.
|
||||
|
||||
Механизм даёт только дополнительный проверяемый мост между доступными частями истории.
|
||||
|
||||
Он также не заменяет:
|
||||
|
||||
- `prevHash32`;
|
||||
- `prevLineHash32`;
|
||||
- sequence логической линии;
|
||||
- подпись ANS-104 DataItem;
|
||||
- проверку текущего хвоста линии;
|
||||
- отдельный механизм доказательства того, что скачан самый последний существующий блок/элемент линии.
|
||||
|
||||
---
|
||||
|
||||
## 5. Почему шаг именно 32
|
||||
|
||||
Шаг `32` выбран как простой компромисс:
|
||||
|
||||
- всего 32 дополнительных байта на глобальную цепочку;
|
||||
- ещё 32 байта только для line-based блоков;
|
||||
- позволяет делать длинный резервный переход без сложной структуры skip-list;
|
||||
- номер ссылки не надо хранить — он вычисляется арифметически;
|
||||
- реализация и проверка остаются очень простыми.
|
||||
|
||||
В будущем шаг можно пересмотреть только через новую версию Frame/body, если появится убедительная причина.
|
||||
|
||||
---
|
||||
|
||||
## 6. План реализации в будущем
|
||||
|
||||
Когда будет принято решение реализовать TODO:
|
||||
|
||||
1. добавить `back32Hash32` в новую версию общего Frame;
|
||||
2. при создании блока вычислять hash глобального блока `N-32`;
|
||||
3. добавить `back32LineHash32` в новые версии всех line-based body;
|
||||
4. вычислять его по элементу той же линии с sequence `currentSequence-32`;
|
||||
5. серверу проверять эти hashes при `AddBlock`;
|
||||
6. импортерам/Arweave sync/viewer использовать резервные ссылки при проверке частично доступной истории;
|
||||
7. обновить тесты бинарного формата и документацию;
|
||||
8. не добавлять отдельные поля номера для ссылок `-32`.
|
||||
|
||||
До выполнения этих пунктов текущий канонический формат SHiNE остаётся без `back32Hash32` и `back32LineHash32`.
|
||||
@@ -55,7 +55,7 @@
|
||||
- `type=1` — `TEXT`
|
||||
- `type=2` — `REACTION`
|
||||
- `type=3` — `CONNECTION`
|
||||
- `type=4` — `USER_PARAM`
|
||||
- `type=4` — `USER_PROFILE`
|
||||
|
||||
## Верхнеуровневые типы первой итерации
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
- `1` — `TEXT`
|
||||
- `2` — `REACTION`
|
||||
- `3` — `CONNECTION`
|
||||
- `4` — `USER_PARAM`
|
||||
- `4` — `USER_PROFILE`
|
||||
- `5` — `STATUS_ACTION`
|
||||
|
||||
Отдельно:
|
||||
@@ -652,7 +652,7 @@
|
||||
| 1 | TEXT | старый код, новое расширенное смысловое описание |
|
||||
| 2 | REACTION | старый |
|
||||
| 3 | CONNECTION | старый |
|
||||
| 4 | USER_PARAM | старый |
|
||||
| 4 | USER_PROFILE | старый |
|
||||
| 5 | STATUS_ACTION | первая итерация |
|
||||
| 6 | CHANNEL_MEMBERSHIP | вынесено в отдельный отложенный черновик |
|
||||
|
||||
|
||||
Reference in New Issue
Block a user