Files
SHiNE-server/shine-solana/shine/doc/formats/shine-user-pda-format-v.1.2.md
T

186 lines
8.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Solana `user_pda`: формат 1.2
Актуальный бинарный формат пользовательской PDA программы `shine_users`.
Формат пользовательских SHiNE-блоков и Arweave этим изменением не меняется.
## Назначение
PDA 1.2 хранит идентичность и authority пользователя, append-only историю blockchain keys, оплаченный лимит и маршрутизацию.
Состояние вершины пользовательского blockchain в Solana больше не хранится.
Из PDA 1.2 удалены `RecoveryKeyBlock`, `SessionsBlock`, `TrustedStateBlock`, `ArchiveHeadBlock`, `sync_servers[]`, а также старые `used_bytes`, `last_block_*`, `arweave_tx_id`, `blockchain_name/type`.
## Общие правила кодирования
- Little Endian;
- `Pubkey` — 32 байта;
- Ed25519 signature — 64 байта;
- hash — 32 байта;
- строка — `len:u8 + UTF-8 bytes`;
- `block_type:u8 + block_version:u8` есть у каждого typed block;
- фиксированные `RootKeyBlock` и `ClientKeyBlock` не содержат длину;
- любой variable block: `block_type:u8 + block_version:u8 + payload_len:u16 + payload`;
- неизвестный variable block можно пропустить по `payload_len`.
Все известные блоки PDA 1.2 имеют `block_version = 0`.
## Header
```text
magic: [u8;5] = "SHiNE"
format_major: u8 = 1
format_minor: u8 = 2
record_len: u16
created_at_ms: u64
updated_at_ms: u64
record_number: u32
prev_record_hash: [u8;32]
login_len: u8
login: [u8;login_len]
blocks_count: u8
blocks: TypedBlock[blocks_count]
signature: [u8;64]
```
`record_len` включает подпись и не включает padding Solana account.
`record_number=0` на create и увеличивается программой на 1 после каждого успешного update.
`prev_record_hash=0` на create; при update это hash unsigned-части предыдущей PDA.
## Типы блоков
| type | блок | статус |
|---:|---|---|
| 1 | `RootKeyBlock` | обязательный |
| 2 | `ClientKeyBlock` | обязательный |
| 3 | `BlockchainRegistryBlock` | обязательный |
| 30 | `ServerProfileBlock` | опциональный |
| 40 | `AccessServersBlock` | опциональный |
## RootKeyBlock
```text
block_type: u8 = 1
block_version: u8 = 0
root_key: [u8;32]
```
`root_key` — холодный recovery authority. Обычные update не используют root. Root участвует только в полной ротации ключей: старый root разрешает переход на одновременно новые `root_key`, `client_key` и blockchain fork.
## ClientKeyBlock
```text
block_type: u8 = 2
block_version: u8 = 0
client_key: [u8;32]
```
Client key не является authority PDA. Он может использоваться клиентским уровнем и как fee payer Solana-транзакции. В текущем протоколе смена `client_key` разрешена только как часть полной ротации вместе с root и новым blockchain fork.
## BlockchainRegistryBlock
```text
block_type: u8 = 3
block_version: u8 = 0
payload_len: u16
fork_count: u16
forks[fork_count]:
blockchain_key: [u8;32]
created_at_ms: u64
paid_limit_bytes: u32
```
Одна fork-запись занимает 44 байта.
Правила:
- `fork_count >= 1`;
- последний fork — активный;
- старые fork-записи append-only: их нельзя удалить, переставить или изменить;
- один blockchain key нельзя добавить повторно;
- `paid_limit_bytes` старых fork неизменяем;
- лимит можно пополнять только у последнего fork;
- новый fork наследует текущий лимит плюс оплаченный top-up;
- обычный blockchain authority может создать новый fork не раньше чем через 72 часа после `created_at_ms` активного fork;
- root recovery может создать fork без 72-часового ожидания.
`created_at_ms` передаёт клиент и включает в подписываемую PDA. Программа принимает его только если он отличается от Solana Clock не более чем на ±5 минут.
## ServerProfileBlock
Блок отсутствует у обычного пользователя. Наличие блока означает, что PDA публикует SHiNE-server endpoint.
```text
block_type: u8 = 30
block_version: u8 = 0
payload_len: u16
address_count: u8
addresses[address_count]:
address_format_type: u8
address_format_version: u8
address_len: u8
address: [u8;address_len]
```
PDA 1.2 разрешает **ровно один адрес**, если `ServerProfileBlock` присутствует. Массив/count сохранён для будущего увеличения лимита без изменения бинарной структуры.
`address_format_type + address_format_version` позволяют позже стандартизовать URL, IPv4, IPv6, Tor/I2P и другие адресные форматы.
## AccessServersBlock
```text
block_type: u8 = 40
block_version: u8 = 0
payload_len: u16
server_count: u8
servers[server_count]:
login_len: u8
login: [u8;login_len]
```
PDA 1.2 разрешает `server_count = 0` или `1`. Count оставлен для будущего расширения.
## Authority и подпись
Новая PDA хранит `signature[64]` authority **нового состояния** по hash unsigned PDA:
- обычный update — активный blockchain key;
- новый fork — новый blockchain key;
- полная ротация root + client + blockchain fork — новый blockchain key.
На update программа дополнительно требует Ed25519-подпись authority предыдущего состояния по тому же hash нового unsigned state. Эта transition-подпись не хранится внутри PDA.
Blockchain-mode не может изменить root. Для полной ротации используется root-mode: текущий root подписывает hash нового unsigned PDA state как разрешение на переход, а новый blockchain key подписывает тот же hash и его `signature[64]` сохраняется в PDA. Поскольку hash считается по всему unsigned state, обе подписи одновременно фиксируют новый root key, новый client key, новый blockchain fork и остальные поля записи.
Смена root считается корректной только как полная ротация: root key изменён, client key изменён и в ту же запись добавлен новый blockchain fork. Такая recovery-ротация не ограничивается обычным 72-часовым cooldown blockchain-authority.
## Create
Создаются только PDA 1.2.
```text
record_number = 0
prev_record_hash = 0x00 * 32
fork_count = 1
fork[0].blockchain_key = initial blockchain key
fork[0].created_at_ms = created_at_ms
fork[0].paid_limit_bytes = start_bonus + paid top-up
```
Root доказывает владение recovery-key отдельной Ed25519 instruction, а `signature[64]` новой PDA делает initial blockchain key.
## Legacy 1.0
Нормальные create/update/read flows нового клиента и сервера поддерживают только PDA 1.2.
Временно существует instruction `close_legacy_pda` для тестовых PDA 1.0. Она:
- принимает только PDA с `format_major=1`, `format_minor=0`;
- не может закрыть 1.2;
- закрывает старый program-owned account;
- переводит его lamports вызывающему signer.
Legacy migration в 1.2 отсутствует. После удаления тестовых PDA временную close-инструкцию можно удалить из программы.