SHA256
ESP32 wallet RPC, browser wallet provider, and side panel
This commit is contained in:
@@ -0,0 +1,311 @@
|
||||
# Формат взаимодействия внешнего кошелька и ESP32
|
||||
|
||||
Этот документ фиксирует первый этап формата взаимодействия между внешним браузерным wallet-расширением SHiNE и устройством `ESP32-S3-Touch-AMOLED-2.16`.
|
||||
|
||||
Документ описывает:
|
||||
|
||||
- как расширение получает текущий активный публичный ключ кошелька с ESP32;
|
||||
- как расширение отправляет на ESP32 запрос подписи транзакции;
|
||||
- что именно считается активным кошельком на ESP32;
|
||||
- какие проверки и UI-реакции ожидаются в браузерном расширении и на устройстве;
|
||||
- какие ограничения действуют в текущей версии протокола.
|
||||
|
||||
## 1. Общая идея
|
||||
|
||||
Устройство ESP32 хранит `master secret` пользователя и локально умеет выводить несколько кошельков из одного секрета.
|
||||
|
||||
На устройстве в UI пользователь выбирает текущий активный кошелёк:
|
||||
|
||||
- `dev.key`
|
||||
- `root.key`
|
||||
- `custom`
|
||||
|
||||
Для `custom` используется derivation:
|
||||
|
||||
```text
|
||||
sha256(base64(secret32) + "|wallet." + customName)
|
||||
```
|
||||
|
||||
Браузерное расширение не указывает ESP32, какой кошелёк нужно вернуть в первом запросе. Оно просто спрашивает:
|
||||
|
||||
```text
|
||||
какой кошелёк сейчас активен на устройстве
|
||||
```
|
||||
|
||||
ESP32 возвращает:
|
||||
|
||||
- тип текущего активного кошелька;
|
||||
- его публичный ключ `Base58`.
|
||||
|
||||
## 2. Транспорт и маршрут
|
||||
|
||||
Первая версия формата использует уже существующую `wallet-session` браузерного расширения.
|
||||
|
||||
Схема маршрута:
|
||||
|
||||
`browser extension -> SHiNE server -> homeserver session on ESP32 -> SHiNE server -> browser extension`
|
||||
|
||||
В первой версии:
|
||||
|
||||
- отдельная цифровая подпись payload не добавляется;
|
||||
- отдельное E2E-шифрование для wallet RPC не добавляется;
|
||||
- используется существующая авторизованная `wallet-session`, транспорт `WSS` и server-side маршрут через уже существующую операцию `CallSignalToSession`.
|
||||
|
||||
## 3. Запрос текущего публичного ключа кошелька
|
||||
|
||||
### 3.1. Назначение
|
||||
|
||||
Операция нужна, чтобы браузерное расширение могло узнать, какой кошелёк сейчас выбран на ESP32, и показать его пользователю перед дальнейшими действиями.
|
||||
|
||||
### 3.2. Формат запроса
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"operation": "get_wallet_public_key",
|
||||
"requestId": "1718998123456-482193",
|
||||
"timeMs": 1718998123456
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3. Поля запроса
|
||||
|
||||
- `v` — версия формата wallet RPC. Для текущего варианта: `1`.
|
||||
- `operation` — строка операции. Для текущего запроса: `get_wallet_public_key`.
|
||||
- `requestId` — идентификатор запроса, уникальный в пределах сеанса расширения. Рекомендуемый формат:
|
||||
`timeMs-random`.
|
||||
- `timeMs` — локальное время отправителя в миллисекундах.
|
||||
|
||||
### 3.4. Поведение ESP32
|
||||
|
||||
При получении такого запроса ESP32:
|
||||
|
||||
1. смотрит, какой кошелёк сейчас выбран в локальном UI;
|
||||
2. вычисляет или берёт уже подготовленный публичный ключ именно этого активного кошелька;
|
||||
3. возвращает тип кошелька и его `publicKeyBase58`.
|
||||
|
||||
Запрос не содержит:
|
||||
|
||||
- `walletSelector`;
|
||||
- `customName`;
|
||||
- `targetSessionName`.
|
||||
|
||||
Они намеренно не входят в первую версию этого запроса.
|
||||
|
||||
## 4. Формат ответа
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"op": "get_wallet_public_key_result",
|
||||
"requestId": "1718998123456-482193",
|
||||
"ok": true,
|
||||
"wallet": {
|
||||
"type": "custom",
|
||||
"publicKeyBase58": "...."
|
||||
},
|
||||
"timeMs": 1718998123999
|
||||
}
|
||||
```
|
||||
|
||||
## 5. Поля ответа
|
||||
|
||||
- `v` — версия формата ответа. Сейчас `1`.
|
||||
- `op` — строка результата операции. Сейчас `get_wallet_public_key_result`.
|
||||
- `requestId` — должен совпадать с `requestId` исходного запроса.
|
||||
- `ok` — признак успешного результата.
|
||||
- `wallet.type` — тип активного кошелька:
|
||||
- `dev.key`
|
||||
- `root.key`
|
||||
- `custom`
|
||||
- `wallet.publicKeyBase58` — публичный ключ активного кошелька в `Base58`.
|
||||
- `timeMs` — время формирования ответа на стороне ESP32 в миллисекундах.
|
||||
|
||||
## 6. Ошибки первой версии
|
||||
|
||||
Минимальный формат ошибки в первой версии допускается таким:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"op": "get_wallet_public_key_result",
|
||||
"requestId": "1718998123456-482193",
|
||||
"ok": false,
|
||||
"error": "wallet_unavailable",
|
||||
"timeMs": 1718998123999
|
||||
}
|
||||
```
|
||||
|
||||
Рекомендуемые коды ошибок первой версии:
|
||||
|
||||
- `wallet_unavailable` — на устройстве нельзя получить текущий кошелёк;
|
||||
- `secret_not_configured` — на устройстве ещё нет корректно сохранённого секрета;
|
||||
- `wallet_type_unknown` — выбранный локальный тип кошелька не распознан;
|
||||
- `internal_error` — прочая локальная ошибка устройства.
|
||||
|
||||
## 7. Правила для браузерного расширения
|
||||
|
||||
После ответа `ok=true` расширение должно:
|
||||
|
||||
1. показать пользователю тип кошелька;
|
||||
2. показать полный `publicKeyBase58`;
|
||||
3. дать кнопку копирования ключа в буфер;
|
||||
4. сохранить этот ключ как текущий ключ устройства для следующей операции подписи.
|
||||
|
||||
### 7.1. Проверка через PDA Solana
|
||||
|
||||
Расширение уже знает публичные ключи пользователя из Solana PDA. Поэтому оно может дополнительно проверить ответ ESP32:
|
||||
|
||||
- если `wallet.type = dev.key`, то `publicKeyBase58` должен совпасть с `deviceKey`, прочитанным из PDA;
|
||||
- если `wallet.type = root.key`, то `publicKeyBase58` должен совпасть с `rootKey`, прочитанным из PDA;
|
||||
- если `wallet.type = custom`, такой проверки по PDA в первой версии нет.
|
||||
|
||||
При несовпадении для `dev.key` или `root.key` расширение должно показать пользователю предупреждение, что возвращённый ключ не совпал с ожидаемым ключом из PDA.
|
||||
|
||||
## 8. Ожидаемое поведение UI расширения
|
||||
|
||||
### 8.1. Общий вид popup
|
||||
|
||||
Popup браузерного расширения должен быть узким и вытянутым по вертикали.
|
||||
|
||||
### 8.2. Состояние без подключения
|
||||
|
||||
Если `wallet-session` ещё не подключена:
|
||||
|
||||
- показывается кнопка `Подключить`;
|
||||
- по нажатию открывается экран подключения, близкий по смыслу к сценарию `Войти через другое устройство`;
|
||||
- пользователь вводит логин устройства и получает код подключения.
|
||||
|
||||
### 8.3. Состояние после подключения
|
||||
|
||||
Если `wallet-session` уже подключена:
|
||||
|
||||
- показывается статус `Подключено`;
|
||||
- остаётся выбор homeserver;
|
||||
- появляется кнопка запроса текущего кошелька;
|
||||
- появляется кнопка `Отключить`.
|
||||
|
||||
### 8.4. Подключение кошелька с сайта
|
||||
|
||||
Когда сайт просит подключить кошелёк через расширение, расширение должно вести себя как обычный wallet extension:
|
||||
|
||||
1. показать пользователю подтверждение подключения;
|
||||
2. показать, какой именно кошелёк будет подключён;
|
||||
3. после подтверждения пользователя завершить подключение;
|
||||
4. если пользователь отказался, не подключать кошелёк к сайту.
|
||||
|
||||
## 9. Запрос подписи транзакции
|
||||
|
||||
### 9.1. Назначение
|
||||
|
||||
Операция нужна, чтобы браузерное расширение могло запросить у ESP32 подпись Solana-транзакции текущим активным кошельком.
|
||||
|
||||
Расширение передаёт:
|
||||
|
||||
- публичный ключ, которым ожидается подпись;
|
||||
- сериализованную транзакцию;
|
||||
- комментарий, который должен быть показан на экране ESP32.
|
||||
|
||||
ESP32:
|
||||
|
||||
1. показывает пользователю запрос подтверждения;
|
||||
2. показывает комментарий к подписи;
|
||||
3. после нажатия `APPROVE` или `REJECT` возвращает ответ в расширение.
|
||||
|
||||
### 9.2. Формат запроса
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"operation": "sign_transaction",
|
||||
"requestId": "1718998123456-482193",
|
||||
"timeMs": 1718998123456,
|
||||
"publicKeyBase58": "....",
|
||||
"transactionBase64": "....",
|
||||
"comment": "Site https://example.com requested transaction signature"
|
||||
}
|
||||
```
|
||||
|
||||
### 9.3. Поля запроса
|
||||
|
||||
- `v` — версия wallet RPC. Сейчас `1`.
|
||||
- `operation` — строка операции: `sign_transaction`.
|
||||
- `requestId` — идентификатор запроса.
|
||||
- `timeMs` — время отправки на стороне расширения.
|
||||
- `publicKeyBase58` — публичный ключ, от которого ожидается подпись.
|
||||
- `transactionBase64` — сериализованная Solana transaction в `base64`.
|
||||
- `comment` — короткое текстовое описание, которое ESP32 показывает пользователю при запросе подписи.
|
||||
|
||||
### 9.4. Поведение ESP32
|
||||
|
||||
При получении такого запроса ESP32:
|
||||
|
||||
1. сравнивает `publicKeyBase58` с публичным ключом текущего активного выбранного кошелька;
|
||||
2. если ключ не совпадает, сразу возвращает ошибку `wallet_mismatch`;
|
||||
3. если ключ совпадает, показывает отдельный экран подтверждения подписи;
|
||||
4. на экране показывает:
|
||||
- каким кошельком будет выполнена подпись;
|
||||
- комментарий `comment`;
|
||||
- кнопки `APPROVE` и `REJECT`;
|
||||
5. если пользователь подтверждает подпись, ESP32 подписывает транзакцию и возвращает результат;
|
||||
6. если пользователь отклоняет, ESP32 возвращает `rejected_by_user`.
|
||||
|
||||
## 10. Формат ответа на подпись
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"op": "sign_transaction_result",
|
||||
"requestId": "1718998123456-482193",
|
||||
"ok": true,
|
||||
"publicKeyBase58": "....",
|
||||
"signatureBase58": "....",
|
||||
"signedTransactionBase64": "....",
|
||||
"timeMs": 1718998123999
|
||||
}
|
||||
```
|
||||
|
||||
Если пользователь отклонил запрос:
|
||||
|
||||
```json
|
||||
{
|
||||
"v": 1,
|
||||
"op": "sign_transaction_result",
|
||||
"requestId": "1718998123456-482193",
|
||||
"ok": false,
|
||||
"error": "rejected_by_user",
|
||||
"timeMs": 1718998123999
|
||||
}
|
||||
```
|
||||
|
||||
Рекомендуемые ошибки для `sign_transaction`:
|
||||
|
||||
- `rejected_by_user`
|
||||
- `wallet_unavailable`
|
||||
- `wallet_mismatch`
|
||||
- `transaction_base64_invalid`
|
||||
- `transaction_sign_failed`
|
||||
- `bad_request`
|
||||
|
||||
## 11. Подключение кошелька с сайта
|
||||
|
||||
При вызове сайта `connect wallet` расширение должно вести себя как обычный wallet extension:
|
||||
|
||||
1. запросить подтверждение у пользователя в браузере;
|
||||
2. получить текущий публичный ключ с ESP32;
|
||||
3. вернуть сайту `publicKey` текущего активного кошелька.
|
||||
|
||||
Для `signTransaction` расширение:
|
||||
|
||||
1. получает транзакцию от сайта;
|
||||
2. пересылает её на ESP32 через `sign_transaction`;
|
||||
3. ждёт решение пользователя на устройстве;
|
||||
4. возвращает браузеру уже подписанную транзакцию.
|
||||
|
||||
## 12. Ограничения текущей версии
|
||||
|
||||
- запрос возвращает только текущий активный кошелёк, а не список всех кошельков;
|
||||
- выбор типа кошелька делается только на самом ESP32;
|
||||
- отдельная цифровая подпись ответа пока не используется;
|
||||
- отдельное E2E-шифрование wallet RPC пока не используется;
|
||||
- `custom`-кошельки в первой версии не сверяются с PDA.
|
||||
Reference in New Issue
Block a user