SHA256
GPT: сгенерено и не проверено (смена ключей пользователя)
This commit is contained in:
@@ -43,6 +43,8 @@
|
||||
- `bad_block_number` — нарушена последовательность;
|
||||
- `bad_prev_hash` — нарушена SHiNE hash chain;
|
||||
- `bad_block_bytes` — DataItem/Frame не парсится.
|
||||
- `key_rotation_in_progress` (HTTP/status `423`) — для пользователя уже запущена смена ключей; обычный `AddBlock` временно запрещён до завершения/отмены ротации;
|
||||
- `key_rotation_check_failed` — сервер не смог проверить локальный rotation status и отклонил запись fail-closed.
|
||||
|
||||
## Storage/publish
|
||||
|
||||
|
||||
@@ -31,6 +31,14 @@
|
||||
| `CloseActiveSession` | `03_Session_Management_API.md` | закрытие активной сессии |
|
||||
| `AddBlock` | `04_Add_Block_to_Blockchain_API.md` | добавление блока в блокчейн |
|
||||
| `GetBlockchainBlock` | `04_Add_Block_to_Blockchain_API.md` | чтение одного блока блокчейна |
|
||||
| `KeyRotationStart` | `19_Key_Rotation_API.md` | начать смену ключей сразу в состоянии `COPYING_CHAIN` |
|
||||
| `KeyRotationStatus` | `19_Key_Rotation_API.md` | получить текущий этап и прогресс смены ключей |
|
||||
| `KeyRotationAddBlock` | `19_Key_Rotation_API.md` | добавить подписанный новым blockchain key candidate-блок будущего fork |
|
||||
| `KeyRotationFinishChain` | `19_Key_Rotation_API.md` | проверить полную публикацию candidate-chain и перевести её в `CHAIN_READY` |
|
||||
| `KeyRotationRotatePda` | `19_Key_Rotation_API.md` | зафиксировать отправленную Solana-ротацию PDA и ждать подтверждения sync |
|
||||
| `KeyRotationContinue` | `19_Key_Rotation_API.md` | продолжить интерактивный post-PDA этап; сейчас wallet/DM отмечаются как `NOT_IMPLEMENTED` и пропускаются |
|
||||
| `KeyRotationAbort` | `19_Key_Rotation_API.md` | прервать ротацию до отправки Solana PDA-ротации |
|
||||
| `GetMyBlockchain` | `20_Get_My_Blockchain_API.md` | постранично читать текущую активную собственную цепочку для аудита и выбора fork point |
|
||||
| `ServerHello` | `16_Server_Connection_Pool_API.md` | объявление server-to-server соединения и возможностей peer без криптографической проверки |
|
||||
| `Ping` | `05_Technical_Requests_API.md` | keep-alive |
|
||||
| `GetServerInfo` | `05_Technical_Requests_API.md` | публичная информация о сервере |
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
# Key Rotation API
|
||||
|
||||
Этот раздел описывает публичные JSON/WebSocket операции мастера смены ключей пользователя.
|
||||
|
||||
Публично доступны:
|
||||
|
||||
- `KeyRotationStart`
|
||||
- `KeyRotationStatus`
|
||||
- `KeyRotationAddBlock`
|
||||
- `KeyRotationFinishChain`
|
||||
- `KeyRotationRotatePda`
|
||||
- `KeyRotationContinue`
|
||||
- `KeyRotationAbort`
|
||||
|
||||
После `PDA_ROTATED` отдельный фоновый worker автоматически переводит ротацию в `REBUILDING_SERVER`, делает новую candidate-chain единственной рабочей цепочкой PostgreSQL и затем переводит процесс в `WALLET_MIGRATION`.
|
||||
|
||||
## Общие правила
|
||||
|
||||
- операции доступны только авторизованному LOCAL-пользователю своего access server;
|
||||
- login берётся из авторизованной сессии и не передаётся в payload;
|
||||
- приватные ключи и пароли серверу не передаются никогда;
|
||||
- в БД сохраняются только старые/новые публичные ключи;
|
||||
- первая серверная запись появляется сразу в состоянии `COPYING_CHAIN`; состояния `PREPARING` в БД нет;
|
||||
- во время `KeyRotationStart` сервер захватывает тот же per-blockchain lock, что использует обычный `AddBlock`, и фиксирует непротиворечивый source tip.
|
||||
|
||||
## `KeyRotationStart`
|
||||
|
||||
Создаёт новую серверную сессию ротации.
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationStart",
|
||||
"requestId": "kr-1",
|
||||
"payload": {
|
||||
"newRootKey": "<Base58 или Base64 публичного ключа 32B>",
|
||||
"newBlockchainKey": "<Base58 или Base64 публичного ключа 32B>",
|
||||
"newClientKey": "<Base58 или Base64 публичного ключа 32B>",
|
||||
"forkFromBlock": 120,
|
||||
"forkFromHash": "<64 hex>",
|
||||
"reasonCode": 1,
|
||||
"comment": "Плановая смена пароля"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
`reasonCode`:
|
||||
|
||||
1. обычная ротация;
|
||||
2. возможная компрометация;
|
||||
3. подтверждённая компрометация / rollback;
|
||||
4. recovery.
|
||||
|
||||
Сервер самостоятельно берёт из текущего PDA:
|
||||
|
||||
- `oldRootKey`;
|
||||
- `oldBlockchainKey`;
|
||||
- `oldClientKey`;
|
||||
- текущий `sourceBlockchainName`.
|
||||
|
||||
Также сервер самостоятельно фиксирует текущий tip цепочки. Клиент не может подменить эти значения в запросе.
|
||||
|
||||
`forkFromBlock/forkFromHash` обязаны указывать на реально существующий блок текущей активной цепочки.
|
||||
|
||||
Новые root/blockchain/client keys должны быть валидными 32-байтовыми публичными ключами, отличаться от соответствующих старых ключей и друг от друга.
|
||||
|
||||
После успеха создаётся `key_rotation_sessions` со статусом `COPYING_CHAIN`. `progressTotal` равен количеству будущих candidate-блоков: копия `0..forkFromBlock` плюс `TECH_FORK`.
|
||||
|
||||
### Success response
|
||||
|
||||
Ответ использует тот же payload состояния, что и `KeyRotationStatus`.
|
||||
|
||||
## `KeyRotationStatus`
|
||||
|
||||
Возвращает текущее состояние ротации авторизованного пользователя.
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationStatus",
|
||||
"requestId": "kr-status-1",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
Если активной ротации нет:
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationStatus",
|
||||
"requestId": "kr-status-1",
|
||||
"status": 200,
|
||||
"ok": true,
|
||||
"payload": {
|
||||
"rotationStatus": "NONE"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Если ротация есть, payload содержит:
|
||||
|
||||
- `rotationSessionId`;
|
||||
- `rotationStatus`;
|
||||
- `sourceBlockchainName` / `candidateBlockchainName`;
|
||||
- старые и новые публичные root/blockchain/client keys;
|
||||
- `forkFromBlock` / `forkFromHash`;
|
||||
- `sourceTipBlock` / `sourceTipHash`;
|
||||
- `reasonCode` / `comment`;
|
||||
- `progressCurrent` / `progressTotal`;
|
||||
- `pdaRotationSignature` (когда появится на последующем этапе);
|
||||
- `walletMigrationStatus`;
|
||||
- `messageMigrationStatus`;
|
||||
- `lastError` / `retryCount`;
|
||||
- `createdAtMs` / `updatedAtMs`.
|
||||
|
||||
Любая авторизованная сессия пользователя может читать этот статус. Состояние ротации принадлежит аккаунту, а не конкретному WebSocket-сеансу.
|
||||
|
||||
|
||||
## `KeyRotationAddBlock`
|
||||
|
||||
Принимает один ANS-104 DataItem будущего fork на этапе `COPYING_CHAIN`. Candidate-блоки хранятся отдельно от обычной таблицы `blocks`, поэтому до переключения fork они **не создают лайки, ответы, каналы, связи и другие materialized effects**.
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationAddBlock",
|
||||
"requestId": "kr-block-1",
|
||||
"payload": {
|
||||
"blockNumber": 0,
|
||||
"prevBlockHash": "",
|
||||
"blockBytesB64": "<полный ANS-104 DataItem в Base64>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Правила:
|
||||
|
||||
- операция доступна только при `rotationStatus=COPYING_CHAIN`;
|
||||
- блоки принимаются строго последовательно: `0..forkFromBlock`, затем один `TECH_FORK` с номером `forkFromBlock+1`;
|
||||
- каждый DataItem обязан быть подписан `newBlockchainKey`;
|
||||
- для копируемых блоков Frame, ANS-104 tags, target и anchor должны полностью совпадать с исходным блоком; меняются только owner/signature/DataItem id;
|
||||
- последний блок обязан быть `TECH_FORK`, а его parent key, fork point, старый tip, reason/comment и число отброшенных блоков должны совпадать с `KeyRotationStart`;
|
||||
- повтор уже принятого идентичного блока безопасен и возвращает success; другой DataItem/hash на том же номере возвращает conflict;
|
||||
- candidate-блоки попадают в отдельную очередь Arweave/Turbo publisher-а и имеют приоритет перед обычными блоками;
|
||||
- `progressCurrent` увеличивается **только после фактической успешной публикации** DataItem в Arweave/Turbo. Поэтому `KeyRotationStatus` показывает реальный прогресс публикации, а не только приём сервером.
|
||||
|
||||
### Основные ошибки
|
||||
|
||||
- `KEY_ROTATION_NOT_COPYING` — ротация не находится в `COPYING_CHAIN`;
|
||||
- `KEY_ROTATION_BLOCK_OUT_OF_ORDER` — пропущен предыдущий candidate-блок;
|
||||
- `KEY_ROTATION_BLOCK_CONFLICT` — на этом номере уже сохранён другой candidate-блок;
|
||||
- `KEY_ROTATION_BAD_SIGNATURE` — DataItem подписан не новым blockchain key;
|
||||
- `KEY_ROTATION_FRAME_MISMATCH` — копируемый Frame отличается от исходной цепочки;
|
||||
- `KEY_ROTATION_TAGS_MISMATCH` — изменены ANS-104 tags копируемого блока;
|
||||
- `KEY_ROTATION_TECH_FORK_REQUIRED` — вместо финального `TECH_FORK` передан другой блок;
|
||||
- ошибки `KEY_ROTATION_TECH_FORK_*` — поля `TECH_FORK` не соответствуют зафиксированной rotation session.
|
||||
|
||||
## Основные ошибки `KeyRotationStart`
|
||||
|
||||
- `AUTH_REQUIRED` — нет авторизованной сессии;
|
||||
- `KEY_ROTATION_ALREADY_ACTIVE` — для login уже выполняется ротация;
|
||||
- `KEY_ROTATION_BAD_FIELDS` — некорректные public keys/hash;
|
||||
- `KEY_ROTATION_BAD_NEW_KEYS` — ключи не изменились либо новые ключи совпадают друг с другом;
|
||||
- `KEY_ROTATION_BAD_REASON` — reason вне диапазона `1..4`;
|
||||
- `KEY_ROTATION_COMMENT_TOO_LONG` — комментарий больше 1024 UTF-8 байт;
|
||||
- `KEY_ROTATION_BAD_FORK_POINT` — неверная точка fork;
|
||||
- `KEY_ROTATION_FORK_BLOCK_NOT_FOUND` — выбранный блок отсутствует;
|
||||
- `KEY_ROTATION_FORK_HASH_MISMATCH` — переданный hash не совпадает с сервером;
|
||||
- `KEY_ROTATION_SESSION_STALE` — авторизованная сессия содержит уже неактуальный blockchainName.
|
||||
|
||||
|
||||
## `KeyRotationFinishChain`
|
||||
|
||||
Финализирует этап построения candidate-chain. Операция **не меняет PDA** и не переключает активную цепочку пользователя: она только доказывает, что будущий fork уже полностью сохранён и опубликован в Arweave/Turbo.
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationFinishChain",
|
||||
"requestId": "kr-finish-chain-1",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
Перед переходом в `CHAIN_READY` сервер повторно проверяет:
|
||||
|
||||
- количество candidate-блоков равно `progressTotal` (`0..forkFromBlock` + один `TECH_FORK`);
|
||||
- каждый candidate-блок уже подтверждён Arweave/Turbo publisher-ом;
|
||||
- номера идут строго `0,1,2,...` без дырок;
|
||||
- `blockHash` и `DataItem id` совпадают с сохранёнными значениями;
|
||||
- каждый DataItem подписан `newBlockchainKey`;
|
||||
- `block 0` имеет нулевой `prevHash`, а каждый следующий блок ссылается на hash предыдущего Frame;
|
||||
- копируемые блоки всё ещё байт-в-байт совпадают с выбранным префиксом исходной цепочки по Frame, tags, target и anchor;
|
||||
- последний блок является корректным `TECH_FORK` и повторяет зафиксированные fork point, parent tip, reason/comment и число отброшенных блоков.
|
||||
|
||||
Только после успешной финальной проверки выполняется:
|
||||
|
||||
`COPYING_CHAIN -> CHAIN_READY`.
|
||||
|
||||
Повторный `KeyRotationFinishChain`, когда ротация уже находится в `CHAIN_READY`, идемпотентно возвращает success. Это позволяет безопасно вызывать операцию из нескольких сессий или повторить её после потери ответа.
|
||||
|
||||
### Основные ошибки
|
||||
|
||||
- `KEY_ROTATION_NOT_ACTIVE` — активная ротация отсутствует;
|
||||
- `KEY_ROTATION_NOT_COPYING` — текущий этап уже не позволяет завершать candidate-chain;
|
||||
- `KEY_ROTATION_CHAIN_INCOMPLETE` — сервер получил не все candidate-блоки;
|
||||
- `KEY_ROTATION_CHAIN_NOT_PUBLISHED` — не все DataItem подтверждены Arweave/Turbo publisher-ом;
|
||||
- `KEY_ROTATION_CANDIDATE_GAP` / `KEY_ROTATION_CHAIN_HASH_MISMATCH` — нарушена последовательность candidate-chain;
|
||||
- `KEY_ROTATION_BAD_SIGNATURE` — сохранённый candidate не подтверждается новым blockchain key;
|
||||
- `KEY_ROTATION_TECH_FORK_*` — финальный `TECH_FORK` больше не соответствует rotation session;
|
||||
- `KEY_ROTATION_FINISH_CHAIN_RACE` — состояние ротации было одновременно изменено другой операцией.
|
||||
|
||||
|
||||
## `KeyRotationRotatePda`
|
||||
|
||||
Фиксирует, что клиент уже локально подписал и отправил в Solana транзакцию полной ротации PDA.
|
||||
|
||||
Сервер **не получает приватные ключи и не подписывает транзакцию**. Клиент передаёт только публичную Solana transaction signature.
|
||||
|
||||
### Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationRotatePda",
|
||||
"requestId": "kr-rotate-pda-1",
|
||||
"payload": {
|
||||
"pdaRotationSignature": "<Base58 Solana transaction signature>"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Операция разрешена только после `CHAIN_READY`. Сервер атомарно сохраняет signature и переводит:
|
||||
|
||||
`CHAIN_READY -> ROTATING_PDA`.
|
||||
|
||||
После этого отмена ротации уже запрещена: отправленная Solana-транзакция может подтвердиться позже, даже если клиент потерял соединение.
|
||||
|
||||
Подтверждением успешной ротации служит **не сам факт наличия signature**, а фактический current PDA, увиденный Solana sync. Sync требует одновременного совпадения:
|
||||
|
||||
- `rootKey == newRootKey`;
|
||||
- `blockchainKey == newBlockchainKey`;
|
||||
- `clientKey == newClientKey`;
|
||||
- `blockchainName == candidateBlockchainName`.
|
||||
|
||||
Только после этого сервер атомарно переводит:
|
||||
|
||||
`ROTATING_PDA -> PDA_ROTATED`.
|
||||
|
||||
Если Solana sync успел увидеть новое PDA раньше вызова `KeyRotationRotatePda`, он умеет подтвердить ожидаемые ключи прямо из `CHAIN_READY`. Поэтому потеря клиентского запроса после уже подтверждённой Solana-транзакции не оставляет ротацию зависшей. Если API-вызов всё же приходит, handler идемпотентно возвращает текущее подтверждённое состояние.
|
||||
|
||||
Повтор с той же signature идемпотентен. Другая signature для уже начатого `ROTATING_PDA` возвращает conflict.
|
||||
|
||||
### Основные ошибки
|
||||
|
||||
- `KEY_ROTATION_NOT_ACTIVE` — активной ротации нет;
|
||||
- `KEY_ROTATION_NOT_CHAIN_READY` — candidate-chain ещё не подтверждена;
|
||||
- `KEY_ROTATION_BAD_SOLANA_SIGNATURE` — signature отсутствует или не Base58;
|
||||
- `KEY_ROTATION_PDA_SIGNATURE_CONFLICT` — для этой ротации уже сохранена другая signature;
|
||||
- `KEY_ROTATION_ROTATE_PDA_FAILED` — внутренняя ошибка фиксации этапа.
|
||||
|
||||
|
||||
## Автоматический rebuild после `PDA_ROTATED`
|
||||
|
||||
После подтверждения нового current PDA сервер сам выполняет:
|
||||
|
||||
`PDA_ROTATED -> REBUILDING_SERVER -> WALLET_MIGRATION`.
|
||||
|
||||
Во время rebuild:
|
||||
|
||||
- проверяется, что current PDA действительно указывает на `candidateBlockchainName/newBlockchainKey`;
|
||||
- старая активная цепочка удаляется из рабочих PostgreSQL-таблиц;
|
||||
- candidate-блоки повторно проходят обычный `AddBlock` validation/projection path;
|
||||
- runtime-cache `to_bch_name` у логических ссылок `login + blockNumber + blockHash` перепривязывается к новому fork;
|
||||
- входящие `likes_count/replies_count` пересчитываются;
|
||||
- после `COMPLETE` временные строки candidate-chain удаляются из PostgreSQL.
|
||||
|
||||
Если rebuild прерывается, `REBUILDING_SERVER` остаётся активным, ошибка записывается в `lastError/retryCount`, а worker безопасно повторяет rebuild.
|
||||
|
||||
## `KeyRotationContinue`
|
||||
|
||||
Продолжает интерактивные post-PDA этапы. В текущей версии два будущих этапа являются честными заглушками:
|
||||
|
||||
- `WALLET_MIGRATION`: `walletMigrationStatus = NOT_IMPLEMENTED`, затем переход в `MESSAGE_MIGRATION`;
|
||||
- `MESSAGE_MIGRATION`: `messageMigrationStatus = NOT_IMPLEMENTED`, затем `FINALIZING -> COMPLETE`.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationContinue",
|
||||
"requestId": "kr-continue-1",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
`KeyRotationContinue` не переводит деньги и не перешифровывает DM. Это специально оставленные точки расширения для будущей реализации.
|
||||
|
||||
## `KeyRotationAbort`
|
||||
|
||||
Прерывает ротацию только пока Solana-ротация PDA ещё не могла быть отправлена:
|
||||
|
||||
- разрешено из `COPYING_CHAIN`;
|
||||
- разрешено из `CHAIN_READY`;
|
||||
- начиная с `ROTATING_PDA` отмена запрещена, процесс можно только довести вперёд.
|
||||
|
||||
Request:
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "KeyRotationAbort",
|
||||
"requestId": "kr-abort-1",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
При `ABORTED` временные candidate-строки удаляются из PostgreSQL. Уже опубликованные Arweave DataItem остаются неизменяемым сиротским историческим следом и не становятся активной цепочкой, поскольку PDA не переключён.
|
||||
@@ -0,0 +1,46 @@
|
||||
# GetMyBlockchain API
|
||||
|
||||
`GetMyBlockchain` — авторизованное постраничное чтение **только текущей активной версии собственного блокчейна**. API используется постоянным экраном «Мой блокчейн» и мастером смены ключей для выбора последнего доверенного блока.
|
||||
|
||||
Login и активный `blockchainName` сервер определяет из авторизованной сессии/current PDA; клиент не может запросить этим методом чужую цепочку.
|
||||
|
||||
## Request
|
||||
|
||||
```json
|
||||
{
|
||||
"op": "GetMyBlockchain",
|
||||
"requestId": "my-bch-1",
|
||||
"payload": {
|
||||
"beforeBlock": 500,
|
||||
"limit": 50,
|
||||
"includeBlockBytes": false
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Поля:
|
||||
|
||||
- `beforeBlock` — необязательный номер верхней границы страницы; если отсутствует, чтение начинается с current tip;
|
||||
- `limit` — `1..100`, default `50`;
|
||||
- `includeBlockBytes` — при `true` дополнительно вернуть полный ANS-104 DataItem в Base64.
|
||||
|
||||
## Response
|
||||
|
||||
Payload содержит:
|
||||
|
||||
- `login`;
|
||||
- `blockchainName`;
|
||||
- `tipBlockNumber` / `tipBlockHash`;
|
||||
- `nextBeforeBlock` для следующей страницы;
|
||||
- `blocks[]` в порядке от новых к старым.
|
||||
|
||||
Каждый элемент `blocks[]` содержит:
|
||||
|
||||
- `blockNumber`;
|
||||
- `blockHash` / `prevBlockHash`;
|
||||
- `timestampMs`;
|
||||
- `msgType` / `msgSubType` / `msgVersion`;
|
||||
- для target-блоков: `toLogin + toBlockNumber + toBlockHash`;
|
||||
- `blockBytesB64`, только если запрошен `includeBlockBytes=true`.
|
||||
|
||||
После fork API показывает только новую активную ветку PostgreSQL. Исторические fork при необходимости восстанавливаются из Arweave/PDA history отдельным будущим viewer-механизмом.
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
## Быстрая карта типов
|
||||
|
||||
- `type=0` — TECH: HEADER, CREATE_CHANNEL.
|
||||
- `type=0` — TECH: HEADER, CREATE_CHANNEL, FORK.
|
||||
- `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 и обратные операции.
|
||||
|
||||
@@ -12,7 +12,43 @@ TECH-тип покрывает системные записи цепочки.
|
||||
- создание нового канала;
|
||||
- хранит line-поля + `channelName` + `channelDescription` + `channelType` + `channelTypeVersion`.
|
||||
|
||||
3. `subType=2` — `TECH_FORK`
|
||||
- первый новый блок после точной перепубликации выбранного префикса предыдущего fork новым blockchain key;
|
||||
- связывает новую активную цепочку с предыдущей и фиксирует точку rollback/продолжения.
|
||||
|
||||
### `TECH_FORK` body (`version=1`)
|
||||
|
||||
Big-endian:
|
||||
|
||||
- `parentBlockchainKey[32]` — public key предыдущего fork;
|
||||
- `forkPointBlockNumber[4]` — последний блок старой цепочки, сохранённый в новом fork;
|
||||
- `forkPointBlockHash32[32]`;
|
||||
- `forkPointTimestampMs[8]`;
|
||||
- `parentTipBlockNumber[4]` — tip старой цепочки на момент начала ротации;
|
||||
- `parentTipBlockHash32[32]`;
|
||||
- `parentTipTimestampMs[8]`;
|
||||
- `discardedBlocksCount[4]` — `parentTipBlockNumber - forkPointBlockNumber`;
|
||||
- `reasonCode[1]`;
|
||||
- `commentUtf8Length[2]`;
|
||||
- `comment[N]` — произвольный комментарий пользователя, максимум 1024 UTF-8 байт.
|
||||
|
||||
`reasonCode`:
|
||||
|
||||
- `1` — `ROUTINE_ROTATION`: обычная смена пароля/ключей, компрометация не предполагается;
|
||||
- `2` — `POSSIBLE_COMPROMISE`: возможная компрометация, неизвестные записи не подтверждены;
|
||||
- `3` — `CONFIRMED_COMPROMISE_ROLLBACK`: обнаружены нежелательные/чужие записи и выполнен rollback;
|
||||
- `4` — `RECOVERY`: восстановление доступа recovery-механизмом.
|
||||
|
||||
Правила:
|
||||
|
||||
- блоки `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.
|
||||
|
||||
## Назначение
|
||||
|
||||
- инициализация блокчейна;
|
||||
- управление набором каналов пользователя.
|
||||
- управление набором каналов пользователя;
|
||||
- фиксация происхождения нового fork и причины ротации/rollback.
|
||||
|
||||
@@ -14,7 +14,7 @@ TEXT-тип хранит сообщения, материалы и редакт
|
||||
|
||||
3. `subType=20` — `TEXT_REPLY`
|
||||
- ответ на сообщение;
|
||||
- target (`toBlockchainName`, `toBlockGlobalNumber`, `toBlockHash32`) + текст.
|
||||
- target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`) + текст.
|
||||
|
||||
4. `subType=21` — `TEXT_EDIT_REPLY`
|
||||
- редактирование ответа;
|
||||
@@ -23,7 +23,7 @@ TEXT-тип хранит сообщения, материалы и редакт
|
||||
|
||||
5. `subType=30` — `TEXT_RATING`
|
||||
- target-based отзыв на конкретный блок;
|
||||
- содержит target (`toBlockchainName`, `toBlockGlobalNumber`, `toBlockHash32`) + текст отзыва;
|
||||
- содержит target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`) + текст отзыва;
|
||||
- не является сообщением линии канала.
|
||||
|
||||
6. `subType=50` — `TEXT_REPOST`
|
||||
@@ -57,6 +57,20 @@ TEXT-тип хранит сообщения, материалы и редакт
|
||||
|
||||
Подробная спецификация: [16_TEXT_Channel_Meta.md](./16_TEXT_Channel_Meta.md).
|
||||
|
||||
|
||||
## Общий target-формат TEXT
|
||||
|
||||
Для `TEXT_EDIT_POST`, `TEXT_REPLY`, `TEXT_EDIT_REPLY`, `TEXT_RATING` и `TEXT_REPOST` ссылка на цель хранится как:
|
||||
|
||||
```text
|
||||
[1] toLoginLen (uint8)
|
||||
[N] toLogin UTF-8
|
||||
[4] toBlockGlobalNumber
|
||||
[32] toBlockHash32
|
||||
```
|
||||
|
||||
`blockchainName`/номер fork в подписываемые байты target не входит. Одинаковые `login + blockNumber + blockHash` считаются одной логической целью после перепубликации сохранённого префикса при fork.
|
||||
|
||||
## Правило для edit
|
||||
|
||||
`EDIT_POST` и `EDIT_REPLY` должны ссылаться на **оригинальный** блок, а не на предыдущий edit.
|
||||
|
||||
@@ -4,11 +4,25 @@
|
||||
|
||||
1. `subType=1` — `REACTION_LIKE`
|
||||
- лайк на целевой блок;
|
||||
- хранит target: `toBlockchainName`, `toBlockGlobalNumber`, `toBlockHash32`.
|
||||
- хранит target: `toLogin`, `toBlockGlobalNumber`, `toBlockHash32`.
|
||||
2. `subType=2` — `REACTION_UNLIKE`
|
||||
- снятие лайка с целевого блока;
|
||||
- хранит target: `toBlockchainName`, `toBlockGlobalNumber`, `toBlockHash32`.
|
||||
- хранит target: `toLogin`, `toBlockGlobalNumber`, `toBlockHash32`.
|
||||
|
||||
## Назначение
|
||||
|
||||
- реакция на текстовые сообщения (и потенциально другие target-блоки, если это разрешено бизнес-логикой).
|
||||
|
||||
|
||||
## Формат target
|
||||
|
||||
В подписанных байтах target больше не хранит имя fork/blockchain. Формат:
|
||||
|
||||
```text
|
||||
[1] toLoginLen (uint8)
|
||||
[N] toLogin UTF-8
|
||||
[4] toBlockGlobalNumber
|
||||
[32] toBlockHash32
|
||||
```
|
||||
|
||||
Логическая идентичность цели: `toLogin + toBlockGlobalNumber + toBlockHash32`. Поэтому ссылка остаётся той же после fork, если номер и hash исходного блока сохранены.
|
||||
|
||||
@@ -18,7 +18,18 @@ CONNECTION-тип описывает социальные связи и подп
|
||||
## Общий формат payload
|
||||
|
||||
- line-поля (`lineCode`, `prevLineNumber`, `prevLineHash32`, `thisLineNumber`)
|
||||
- target (`toBlockchainName`, `toBlockGlobalNumber`, `toBlockHash32`)
|
||||
- target (`toLogin`, `toBlockGlobalNumber`, `toBlockHash32`)
|
||||
|
||||
## Бинарный target
|
||||
|
||||
```text
|
||||
[1] toLoginLen (uint8)
|
||||
[N] toLogin UTF-8
|
||||
[4] toBlockGlobalNumber
|
||||
[32] toBlockHash32
|
||||
```
|
||||
|
||||
Имя fork/blockchain в target не хранится.
|
||||
|
||||
## Правила target
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@
|
||||
Все `STATUS_ACTION` используют один и тот же бинарный body-формат:
|
||||
|
||||
```text
|
||||
[1] toBlockchainNameLen (uint8)
|
||||
[N] toBlockchainName UTF-8
|
||||
[1] toLoginLen (uint8)
|
||||
[N] toLogin UTF-8
|
||||
[4] toBlockGlobalNumber
|
||||
[32] toBlockHash32
|
||||
[2] textLenBytes (uint16)
|
||||
@@ -46,7 +46,7 @@
|
||||
|
||||
Где:
|
||||
|
||||
- `toBlockchainName` — блокчейн, в котором находится целевой материал;
|
||||
- `toLogin` — login владельца целевого блока; номер fork в target не хранится;
|
||||
- `toBlockGlobalNumber` — номер целевого блока;
|
||||
- `toBlockHash32` — хэш целевого блока;
|
||||
- `text` — опциональное пояснение пользователя к статусу.
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
## 2026-09-27 — TECH_FORK v1
|
||||
|
||||
- Добавлен `type=0 / subType=2 / version=1` (`TECH_FORK`).
|
||||
- `TECH_FORK` фиксирует parent blockchain key, точку сохранённой истории, старый tip, число отброшенных блоков, reason code и пользовательский комментарий.
|
||||
- Новый blockchain key не дублируется в body: он определяется подписью нового ANS-104 DataItem.
|
||||
- Зафиксированы четыре причины: обычная ротация, возможная компрометация, подтверждённая компрометация с rollback и recovery.
|
||||
|
||||
# История изменений документации блокчейна
|
||||
|
||||
## 2026-09-23 — Turbo transport для individual ANS-104 DataItems
|
||||
|
||||
+19
-19
@@ -2,10 +2,10 @@
|
||||
|
||||
> **Статус: ИСТОЧНИК ИСТИНЫ (single source of truth) по конкретной деривации.**
|
||||
> Этот файл описывает, как из пароля получается секрет и как из секрета выводятся
|
||||
> все ключи (root, blockchain, device/Solana, homeserver) — формулами, байт-в-байт.
|
||||
> все ключи (root, blockchain, client, homeserver) — формулами, байт-в-байт.
|
||||
> Если в коде меняется деривация (формула секрета, параметры Argon2id, соль, формула
|
||||
> ключа, разделитель `|`, набор/имена суффиксов, формат homeserver-ключа, связь
|
||||
> dev-ключ ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
|
||||
> blockchain key ↔ Solana-адрес) — **в том же изменении обязательно править этот документ**.
|
||||
> Роли и назначение ключей описаны отдельно в `docs/Keys/README.md` (архитектура).
|
||||
> Здесь — только механика. Документ намеренно краткий.
|
||||
|
||||
@@ -55,29 +55,29 @@ seed(32) = SHA-256(material)
|
||||
| Ключ | Суффикс | Назначение (кратко) |
|
||||
|------|---------|---------------------|
|
||||
| root | `root.key` | Личность. Подписывает unsigned-часть PDA-записи (`RootKeyBlock`). |
|
||||
| blockchain | `bch.key` | Подписывает `LastBlockState` персонального блокчейна (`blockchain_public_key`). |
|
||||
| device / **Solana** | `client.key` | Ключ устройства = Solana-ключ. Fee payer и подпись Solana-транзакций; адрес кошелька = `base58(clientPub)`. См. §3. |
|
||||
| blockchain | `blockchain.key` | Подписывает пользовательские блоки/ANS-104 DataItem и является текущим Solana-wallet/fee payer. |
|
||||
| client | `client.key` | Общий клиентский ключ для DM/устройств и derivation отдельного Arweave SAWD-кошелька; не является текущим Solana-wallet. |
|
||||
| homeserver | `homeserver.key:<имя>` | Ключ homeserver-устройства, по одному на каждый homeserver (различитель — имя). См. §4. |
|
||||
|
||||
Полные роли каждого ключа — в `docs/Keys/README.md`.
|
||||
|
||||
---
|
||||
|
||||
## 3. Solana-ключ
|
||||
## 3. Solana-кошелёк и authority
|
||||
|
||||
Отдельного «солана-ключа» нет. На Solana работают два ключа:
|
||||
Отдельного «solana.key» нет. В актуальной модели Solana-wallet пользователя — **активный `blockchain.key`**:
|
||||
|
||||
- **`client.key` (device) — пополняемый кошелёк и fee payer.** Solana-адрес = `base58(clientPub)`.
|
||||
Этим ключом оплачиваются и подписываются `create_user_pda` / `update_user_pda`.
|
||||
Пополнять SOL нужно именно на этот адрес.
|
||||
- **`root.key` — авторитет записи**, подписывает unsigned-часть PDA через Ed25519-инструкцию, но **не** является fee payer.
|
||||
- адрес кошелька = `base58(activeBlockchainPub)`;
|
||||
- им оплачивается регистрация (`create_user_pda`) и обычные `update_user_pda`;
|
||||
- обычное обновление PDA авторизуется активным blockchain key;
|
||||
- новый fork подписывает новое PDA новым blockchain key, а старый активный blockchain key разрешает обычный переход;
|
||||
- полная смена пароля/ключей — особый recovery-переход: старый `root.key` дополнительно разрешает одно новое unsigned PDA state, а **новый blockchain key** подписывает тот же hash и становится новым wallet/authority.
|
||||
|
||||
Соответствует формату PDA `shine-solana/shine/doc/formats/shine-user-pda-format-v.1.0.md` §2.1
|
||||
(«create/update оплачиваются с `client_key`», «root_key — не fee payer»).
|
||||
`root.key` не является fee payer. Он используется как холодное дополнительное разрешение только для полной ротации root + client + blockchain fork.
|
||||
|
||||
Кратко про роли на Solana: `root.key` — это **главный (master) ключ**: им управляют PDA-записью
|
||||
(`create/update`) и через это можно заменить все остальные ключи; `client.key` — это **пополняемый
|
||||
кошелёк и плательщик комиссий**. Полное описание ролей — `docs/Keys/README.md`.
|
||||
`client.key` больше не используется как Solana-wallet/fee payer. Он остаётся клиентским криптографическим ключом (DM/устройства) и входом в отдельный протокол derivation Arweave-кошелька.
|
||||
|
||||
При fork Solana-адрес меняется вместе с blockchain key. Перенос SOL со старого blockchain-wallet на новый является отдельным необязательным этапом ротации; в текущей первой реализации этот этап оставлен явной заглушкой.
|
||||
|
||||
---
|
||||
|
||||
@@ -117,9 +117,9 @@ homeserver.key:home-b -> ключ B
|
||||
- `shine-UI/server-ui/js/server-ui-shared.js` — те же root/bch/dev для серверного UI (~147–160).
|
||||
|
||||
### Solana-ключ / адрес кошелька (UI)
|
||||
- `shine-UI/js/pages/registration-payment-view.js` — `deriveUserWalletAddress`: адрес = `base58(clientPub)` (~113).
|
||||
- `shine-UI/js/pages/topup-view.js` — `clientWalletAddressFromBundle`: тот же канонический адрес из `preGeneratedKeyBundle.clientPair`.
|
||||
Прежний расходящийся путь `deriveWalletFromPassword` (прямой Argon2 по `client.key`, мимо `masterSecret`) удалён.
|
||||
- `shine-UI/js/pages/registration-payment-view.js` — адрес пополнения = `base58(blockchainPub)`.
|
||||
- `shine-UI/js/pages/topup-view.js` — тот же адрес из `preGeneratedKeyBundle.blockchainPair`.
|
||||
- `shine-UI/js/services/solana-wallet-service.js` — текущий пользовательский Solana-wallet загружается из сохранённого `blockchainKey`.
|
||||
|
||||
### Деривация ключей (прошивка ESP32)
|
||||
- `ESP32/esp32/ESP32-S3-Touch-AMOLED-2.16/main-device/shine_homeserver_main/shine_homeserver_main.ino`
|
||||
@@ -147,7 +147,7 @@ homeserver.key:home-b -> ключ B
|
||||
|
||||
1. Этот документ — источник истины по деривации секрета и ключей.
|
||||
2. Любое изменение кода, затрагивающее формулу секрета, параметры Argon2id, соль, формулу ключа,
|
||||
разделитель `|`, набор/имена суффиксов, формат homeserver-ключа или связь dev-ключ ↔ Solana-адрес —
|
||||
разделитель `|`, набор/имена суффиксов, формат homeserver-ключа или связь blockchain key ↔ Solana-адрес —
|
||||
**обязательно** отражать здесь в том же изменении.
|
||||
3. Пункты, помеченные ⚠️, — это долг к устранению, а не норма.
|
||||
4. Нельзя сознательно оставлять код и этот документ в рассинхроне без отдельной явной договорённости.
|
||||
|
||||
+11
-14
@@ -8,9 +8,9 @@
|
||||
|
||||
В SHiNE у пользователя есть несколько уровней ключей:
|
||||
|
||||
- `root key` - главный (master) ключ пользователя: тот, кто им владеет, управляет пользовательской PDA в Solana и может заменить все остальные ключи. Это не пополняемый кошелёк (комиссии платит `client key`).
|
||||
- `blockchain key` - ключ записи в персональный SHiNE-блокчейн пользователя.
|
||||
- `client key` - общий ключ пользовательских устройств для повседневной работы, звонков, DM и мелких платежей.
|
||||
- `root key` - холодный recovery-ключ: при полной ротации дополнительно разрешает замену root + client + blockchain fork. Это не кошелёк.
|
||||
- `blockchain key` - ключ записи в персональный SHiNE-блокчейн и текущий Solana-wallet/fee payer пользователя.
|
||||
- `client key` - общий ключ пользовательских устройств для повседневной работы, звонков и DM; Solana-wallet им больше не является.
|
||||
- `session key` - ключ конкретной сессии или конкретного устройства для авторизации на сервере.
|
||||
|
||||
Главная идея: самые важные ключи можно держать на доверенном серверном или аппаратном устройстве, а обычные клиентские устройства получают только ключи, нужные для текущей работы.
|
||||
@@ -21,16 +21,13 @@
|
||||
|
||||
Назначение:
|
||||
|
||||
- регистрация пользователя в Solana;
|
||||
- создание и обновление пользовательской PDA-записи;
|
||||
- вызов критически важных Solana-функций;
|
||||
- изменение главных настроек пользователя;
|
||||
- управление остальными ключами;
|
||||
- подтверждение операций, которые должны иметь максимальный уровень доверия.
|
||||
- холодное recovery-разрешение для полной смены ключей;
|
||||
- подтверждение атомарной ротации `root + client + новый blockchain fork`;
|
||||
- восстановительные сценарии повышенного уровня доверия.
|
||||
|
||||
`root key` — это **главный (master) ключ** в следующем смысле: зная `root key`, можно управлять пользовательской PDA-записью в Solana (`create_user_pda` / `update_user_pda`) и тем самым **заменить все остальные ключи** пользователя (device, blockchain, homeserver). Поэтому компрометация `root key` равносильна компрометации всей личности пользователя.
|
||||
Обычные PDA-update **не требуют root key**: их выполняет активный blockchain key. При полной ротации старый root подписывает тот же hash нового unsigned PDA state, который подписывает новый blockchain key. Так root разрешает переход, не становясь повседневным ключом.
|
||||
|
||||
Важно не путать авторитет и кошелёк: `root key` — это авторитет над PDA-записью, а **SOL-комиссии за create/update платит `client key`** (он же fee payer и адрес для пополнения). Подробнее о том, какой ключ за что отвечает на Solana, — в `docs/Keys/DERIVATION.md`, §3.
|
||||
Важно не путать recovery-authority и кошелёк: `root key` не является fee payer. Текущий Solana-wallet/fee payer — активный `blockchain key`. Подробнее — `docs/Keys/DERIVATION.md`, §3.
|
||||
|
||||
## `blockchain key`
|
||||
|
||||
@@ -40,7 +37,8 @@
|
||||
|
||||
- подпись записей в персональном блокчейне пользователя;
|
||||
- подтверждение действий, которые должны попасть в SHiNE-блокчейн;
|
||||
- разделение полномочий между главным Solana-ключом и ключом ежедневной записи.
|
||||
- обычные обновления пользовательской PDA;
|
||||
- текущий Solana-wallet/fee payer (`base58(active blockchain public key)`).
|
||||
|
||||
У пользователя может быть несколько персональных блокчейнов или веток. При смене `blockchain key` фактически создаётся новая ветка записи:
|
||||
|
||||
@@ -59,7 +57,6 @@
|
||||
- повседневные входящие и исходящие личные сообщения;
|
||||
- звонки и связанные с ними сообщения;
|
||||
- self-messages, то есть внутренние сообщения пользователя самому себе;
|
||||
- мелкие Solana-расходы на текущие операции;
|
||||
- derivation Arweave-кошелька;
|
||||
- оплата или подготовка добавления данных в Arweave-кошелек по отдельному протоколу.
|
||||
|
||||
@@ -158,7 +155,7 @@ Self-message - это сообщение пользователя самому
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- `docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`bch.key`/`client.key`/`homeserver.key:<имя>`, Solana-ключ, ссылки на код).
|
||||
- `docs/Keys/DERIVATION.md` - **источник истины по конкретной деривации** секрета и ключей (формулы Argon2id, `base64|suffix→SHA-256→Ed25519`, суффиксы `root.key`/`blockchain.key`/`client.key`/`homeserver.key:<имя>`, Solana-wallet, ссылки на код).
|
||||
- `docs/Personal_Messages/Протокол_DM_v1.md` - текущая логическая документация личных сообщений.
|
||||
- `docs/Personal_Messages/Формат_DM_v1.md` - точный байтовый формат личных сообщений.
|
||||
- `docs/Blockchain/README.md` - точка входа по форматам SHiNE-блокчейна.
|
||||
|
||||
@@ -584,3 +584,28 @@ SYNC_POLL_INTERVAL_SECONDS=300
|
||||
- server profile считается присутствующим, если опубликован один server address; в старые SQL-поля временно проецируется этот адрес.
|
||||
|
||||
Create/update транзакции больше не реконструируются байт-в-байт из instruction args. После обнаружения изменения sync-модуль перечитывает фактическую текущую PDA через Solana RPC и декодирует её. Это исключает дублирование on-chain сериализации. `close_legacy_pda` не создаёт новое состояние PDA и для runtime-sync не является пользовательским update.
|
||||
|
||||
---
|
||||
|
||||
## Server-local key rotation state (PostgreSQL v26)
|
||||
|
||||
Начиная с migration v26 сервер хранит локальное состояние длительной смены ключей отдельно от данных Solana PDA.
|
||||
|
||||
В `solana_user_pda_current` добавлены локальные поля:
|
||||
|
||||
- `rotation_status` — быстрый текущий статус (`NONE` в обычном режиме);
|
||||
- `rotation_session_id` — ссылка на текущую запись `key_rotation_sessions`.
|
||||
|
||||
Эти поля **не являются частью PDA**, не приходят из Solana и не должны перезаписываться обычным Solana sync upsert-ом.
|
||||
|
||||
Подробный прогресс хранится в `key_rotation_sessions`: только публичные old/new root/blockchain/client keys, выбранная точка fork, старый tip, reason/comment, прогресс, ошибка/retry и статусы необязательных wallet/DM этапов. Пароли и приватные ключи в PostgreSQL не сохраняются.
|
||||
|
||||
Первая серверная запись создаётся сразу в `COPYING_CHAIN`; состояния `PREPARING` в БД нет. Завершённые `COMPLETE`/`ABORTED` sessions остаются как журнал, а `solana_user_pda_current.rotation_status` возвращается в `NONE`.
|
||||
|
||||
### Candidate blocks ротации (PostgreSQL v27)
|
||||
|
||||
Начиная с migration v27 будущая ветка во время `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-блока.
|
||||
|
||||
Arweave/Turbo publisher обрабатывает candidate-блоки приоритетно. `key_rotation_sessions.progress_current` отражает число DataItem, уже реально опубликованных publisher-ом, а не число принятых API-сервером.
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
Основные данные:
|
||||
|
||||
- `RootKeyBlock` — cold recovery authority;
|
||||
- `ClientKeyBlock` — клиентский/кошелёчный ключ;
|
||||
- `ClientKeyBlock` — клиентский ключ для DM/устройств; Solana-wallet/fee payer — активный blockchain key;
|
||||
- `BlockchainRegistryBlock` — append-only список fork: `blockchain_key[32] + created_at_ms:u64 + paid_limit_bytes:u32`; последний fork активен;
|
||||
- необязательный `ServerProfileBlock` — в 1.2 ровно один адрес сервера;
|
||||
- необязательный `AccessServersBlock` — в 1.2 максимум один access server.
|
||||
|
||||
@@ -90,14 +90,14 @@
|
||||
|
||||
## Кто оплачивает create/update user_pda
|
||||
|
||||
- И обычная регистрация `create_user_pda`, и последующее `update_user_pda` оплачиваются с `clientKey`.
|
||||
- В UI это означает, что Solana fee payer всегда берётся из `device`-ключа пользователя или сервера.
|
||||
- `rootKey` нужен для подписи unsigned PDA-записи, но не оплачивает транзакцию.
|
||||
- Для server UI это особенно важно: перед `create` и `update` нужно пополнять именно Solana-адрес `clientKey`.
|
||||
- И обычная регистрация `create_user_pda`, и последующее `update_user_pda` оплачиваются с активного `blockchainKey`.
|
||||
- В UI это означает, что Solana fee payer всегда берётся из текущего активного `blockchain key` пользователя или сервера.
|
||||
- `rootKey` не оплачивает транзакцию; он дополнительно авторизует только полную ротацию ключей.
|
||||
- Для server UI это особенно важно: перед `create` и `update` нужно пополнять именно Solana-адрес текущего `blockchainKey`.
|
||||
|
||||
## Важно
|
||||
|
||||
- `init_users_economy_config` выполняется один раз на программу. Если PDA уже создан, повторный вызов вернёт ошибку `already initialized`.
|
||||
- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: транзакцию оплачивает `clientKey`, а содержимое записи подписывает `rootKey`.
|
||||
- Серверные приватные ключи для Solana не используются как отдельный backend-wallet: транзакцию оплачивает `blockchainKey`. Обычное состояние PDA подписывает blockchain key; root дополнительно участвует только в полной ротации ключей.
|
||||
- `shine_users` внутри `create_user_pda` требует корректный адрес `shine_login_guard` для CPI-классификации логина.
|
||||
- При новом devnet deploy планируется использовать те же program keypair, чтобы `program id` на devnet совпадали с mainnet.
|
||||
|
||||
Reference in New Issue
Block a user