Добавить ListBlockchainHeads для межсерверной сверки
This commit is contained in:
parent
f0e1ab3af8
commit
1f8b20a7d1
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
Этот файл описывает технические WebSocket-запросы, которые нужны для служебной работы клиента с сервером. Часть операций доступна без авторизации, часть требует успешной авторизованной сессии.
|
Этот файл описывает технические WebSocket-запросы, которые нужны для служебной работы клиента с сервером. Часть операций доступна без авторизации, часть требует успешной авторизованной сессии.
|
||||||
|
|
||||||
Сейчас здесь шесть методов:
|
Сейчас здесь семь методов:
|
||||||
|
|
||||||
- `Ping` — keep-alive запрос для поддержания живого WebSocket-соединения;
|
- `Ping` — keep-alive запрос для поддержания живого WebSocket-соединения;
|
||||||
- `GetServerInfo` — запрос базовой публичной информации о сервере для выбора узла в децентрализованной сети;
|
- `GetServerInfo` — запрос базовой публичной информации о сервере для выбора узла в децентрализованной сети;
|
||||||
|
- `ListBlockchainHeads` — краткая сводка по всем локальным блокчейнам сервера для межсерверной синхронизации;
|
||||||
- `GetCallIceConfig` — выдача STUN/TURN конфигурации для звонков;
|
- `GetCallIceConfig` — выдача STUN/TURN конфигурации для звонков;
|
||||||
- `ClientErrorLog` — отправка клиентской ошибки в серверный лог;
|
- `ClientErrorLog` — отправка клиентской ошибки в серверный лог;
|
||||||
- `ClientDebugLog` — отправка клиентского debug-события в серверный буфер;
|
- `ClientDebugLog` — отправка клиентского debug-события в серверный буфер;
|
||||||
@ -15,6 +16,7 @@
|
|||||||
|
|
||||||
- `Ping` нужен для регулярной проверки, что соединение всё ещё живо;
|
- `Ping` нужен для регулярной проверки, что соединение всё ещё живо;
|
||||||
- `GetServerInfo` нужен до авторизации и до работы с данными, чтобы клиент понял, что сервер доступен, и показал пользователю краткую карточку этого узла.
|
- `GetServerInfo` нужен до авторизации и до работы с данными, чтобы клиент понял, что сервер доступен, и показал пользователю краткую карточку этого узла.
|
||||||
|
- `ListBlockchainHeads` нужен для сервер-сервер сверки: партнёр получает список heads по всем цепочкам, сравнивает его со своим состоянием и затем добирает недостающие блоки по диапазону.
|
||||||
|
|
||||||
Ниже сначала описаны назначение методов, затем точные форматы запросов и ответов.
|
Ниже сначала описаны назначение методов, затем точные форматы запросов и ответов.
|
||||||
|
|
||||||
@ -134,7 +136,68 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 3. `GetCallIceConfig`
|
## 3. `ListBlockchainHeads`
|
||||||
|
|
||||||
|
### Назначение
|
||||||
|
|
||||||
|
Запрос краткой сводки по всем локальным блокчейнам сервера.
|
||||||
|
|
||||||
|
Нужен для межсерверной синхронизации. Партнёр может:
|
||||||
|
|
||||||
|
- получить список всех блокчейнов;
|
||||||
|
- сравнить `lastBlockNumber` и `lastBlockHash` со своими значениями;
|
||||||
|
- понять, какие цепочки нужно догонять;
|
||||||
|
- затем отдельно запросить недостающие блоки по диапазону.
|
||||||
|
|
||||||
|
Этот запрос доступен без авторизации.
|
||||||
|
|
||||||
|
### Запрос
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"op": "ListBlockchainHeads",
|
||||||
|
"requestId": "heads-001",
|
||||||
|
"payload": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Успешный ответ
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"op": "ListBlockchainHeads",
|
||||||
|
"requestId": "heads-001",
|
||||||
|
"status": 200,
|
||||||
|
"ok": true,
|
||||||
|
"payload": {
|
||||||
|
"blockchains": [
|
||||||
|
{
|
||||||
|
"blockchainName": "alice_main",
|
||||||
|
"lastBlockNumber": 124,
|
||||||
|
"lastBlockHash": "aabbccdd00112233445566778899aabbccddeeff00112233445566778899aabb",
|
||||||
|
"fileSizeBytes": 58720
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Поля ответа
|
||||||
|
|
||||||
|
- `blockchains` — массив текущих heads всех цепочек сервера.
|
||||||
|
- `blockchainName` — имя блокчейна.
|
||||||
|
- `lastBlockNumber` — последний номер блока в этой цепочке.
|
||||||
|
- `lastBlockHash` — последний хэш блока в HEX-формате `64` символа.
|
||||||
|
- `fileSizeBytes` — текущий размер файла блокчейна в байтах.
|
||||||
|
|
||||||
|
### Специфические коды ошибок `ListBlockchainHeads`
|
||||||
|
|
||||||
|
- У `ListBlockchainHeads` нет специальных прикладных ошибок при штатной работе.
|
||||||
|
- Если произойдёт непредвиденная проблема, сервер вернёт общую ошибку из раздела `00`, обычно `500 / INTERNAL_ERROR`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. `GetCallIceConfig`
|
||||||
|
|
||||||
Доступно только после успешной авторизации.
|
Доступно только после успешной авторизации.
|
||||||
|
|
||||||
@ -184,7 +247,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 4. `ClientErrorLog`
|
## 5. `ClientErrorLog`
|
||||||
|
|
||||||
### Запрос
|
### Запрос
|
||||||
|
|
||||||
@ -231,7 +294,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 5. `ClientDebugLog`
|
## 6. `ClientDebugLog`
|
||||||
|
|
||||||
### Запрос
|
### Запрос
|
||||||
|
|
||||||
@ -269,7 +332,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 6. `CallDeliveryReport`
|
## 7. `CallDeliveryReport`
|
||||||
|
|
||||||
### Запрос
|
### Запрос
|
||||||
|
|
||||||
|
|||||||
@ -34,6 +34,7 @@
|
|||||||
| `AddBlock` | `04_Add_Block_to_Blockchain_API.md` | добавление блока в блокчейн |
|
| `AddBlock` | `04_Add_Block_to_Blockchain_API.md` | добавление блока в блокчейн |
|
||||||
| `Ping` | `05_Technical_Requests_API.md` | keep-alive |
|
| `Ping` | `05_Technical_Requests_API.md` | keep-alive |
|
||||||
| `GetServerInfo` | `05_Technical_Requests_API.md` | публичная информация о сервере |
|
| `GetServerInfo` | `05_Technical_Requests_API.md` | публичная информация о сервере |
|
||||||
|
| `ListBlockchainHeads` | `05_Technical_Requests_API.md` | список heads всех локальных блокчейнов |
|
||||||
| `GetCallIceConfig` | `05_Technical_Requests_API.md` | STUN/TURN конфигурация звонков |
|
| `GetCallIceConfig` | `05_Technical_Requests_API.md` | STUN/TURN конфигурация звонков |
|
||||||
| `ClientErrorLog` | `05_Technical_Requests_API.md` | логирование клиентской ошибки |
|
| `ClientErrorLog` | `05_Technical_Requests_API.md` | логирование клиентской ошибки |
|
||||||
| `ClientDebugLog` | `05_Technical_Requests_API.md` | клиентский debug-лог |
|
| `ClientDebugLog` | `05_Technical_Requests_API.md` | клиентский debug-лог |
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import shine.db.SqliteDbController;
|
|||||||
import shine.db.entities.BlockchainStateEntry;
|
import shine.db.entities.BlockchainStateEntry;
|
||||||
|
|
||||||
import java.sql.*;
|
import java.sql.*;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public final class BlockchainStateDAO {
|
public final class BlockchainStateDAO {
|
||||||
|
|
||||||
@ -53,6 +55,39 @@ public final class BlockchainStateDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Получить все blockchain_state записи. */
|
||||||
|
public List<BlockchainStateEntry> listAll() throws SQLException {
|
||||||
|
try (Connection c = db.getConnection()) {
|
||||||
|
return listAll(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Получить все blockchain_state записи с внешним соединением. Соединение НЕ закрывает. */
|
||||||
|
public List<BlockchainStateEntry> listAll(Connection c) throws SQLException {
|
||||||
|
String sql = """
|
||||||
|
SELECT
|
||||||
|
blockchain_name,
|
||||||
|
login,
|
||||||
|
blockchain_key,
|
||||||
|
size_limit,
|
||||||
|
file_size_bytes,
|
||||||
|
last_block_number,
|
||||||
|
last_block_hash,
|
||||||
|
updated_at_ms
|
||||||
|
FROM blockchain_state
|
||||||
|
ORDER BY blockchain_name COLLATE NOCASE
|
||||||
|
""";
|
||||||
|
|
||||||
|
List<BlockchainStateEntry> result = new ArrayList<>();
|
||||||
|
try (PreparedStatement ps = c.prepareStatement(sql);
|
||||||
|
ResultSet rs = ps.executeQuery()) {
|
||||||
|
while (rs.next()) {
|
||||||
|
result.add(mapRow(rs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/** UPSERT без внешнего соединения. Сам открывает/закрывает. */
|
/** UPSERT без внешнего соединения. Сам открывает/закрывает. */
|
||||||
public void upsert(BlockchainStateEntry e) throws SQLException {
|
public void upsert(BlockchainStateEntry e) throws SQLException {
|
||||||
try (Connection c = db.getConnection()) {
|
try (Connection c = db.getConnection()) {
|
||||||
|
|||||||
@ -106,6 +106,7 @@ import server.logic.ws_protocol.JSON.handlers.system.Net_GetServerInfo_Handler;
|
|||||||
import server.logic.ws_protocol.JSON.handlers.system.Net_GetCallIceConfig_Handler;
|
import server.logic.ws_protocol.JSON.handlers.system.Net_GetCallIceConfig_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.Net_ClientErrorLog_Handler;
|
import server.logic.ws_protocol.JSON.handlers.system.Net_ClientErrorLog_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.Net_ClientDebugLog_Handler;
|
import server.logic.ws_protocol.JSON.handlers.system.Net_ClientDebugLog_Handler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.system.Net_ListBlockchainHeads_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.Net_CallDeliveryReport_Handler;
|
import server.logic.ws_protocol.JSON.handlers.system.Net_CallDeliveryReport_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.Net_Ping_Handler;
|
import server.logic.ws_protocol.JSON.handlers.system.Net_Ping_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_CallDeliveryReport_Request;
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_CallDeliveryReport_Request;
|
||||||
@ -113,6 +114,7 @@ import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ClientErrorLog
|
|||||||
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ClientDebugLog_Request;
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ClientDebugLog_Request;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_GetCallIceConfig_Request;
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_GetCallIceConfig_Request;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_GetServerInfo_Request;
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_GetServerInfo_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ListBlockchainHeads_Request;
|
||||||
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_Ping_Request;
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_Ping_Request;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -193,6 +195,7 @@ public final class JsonHandlerRegistry {
|
|||||||
// --- system ---
|
// --- system ---
|
||||||
Map.entry("Ping", new Net_Ping_Handler()),
|
Map.entry("Ping", new Net_Ping_Handler()),
|
||||||
Map.entry("GetServerInfo", new Net_GetServerInfo_Handler()),
|
Map.entry("GetServerInfo", new Net_GetServerInfo_Handler()),
|
||||||
|
Map.entry("ListBlockchainHeads", new Net_ListBlockchainHeads_Handler()),
|
||||||
Map.entry("GetCallIceConfig", new Net_GetCallIceConfig_Handler()),
|
Map.entry("GetCallIceConfig", new Net_GetCallIceConfig_Handler()),
|
||||||
Map.entry("ClientErrorLog", new Net_ClientErrorLog_Handler()),
|
Map.entry("ClientErrorLog", new Net_ClientErrorLog_Handler()),
|
||||||
Map.entry("ClientDebugLog", new Net_ClientDebugLog_Handler()),
|
Map.entry("ClientDebugLog", new Net_ClientDebugLog_Handler()),
|
||||||
@ -268,6 +271,7 @@ public final class JsonHandlerRegistry {
|
|||||||
// --- system ---
|
// --- system ---
|
||||||
Map.entry("Ping", Net_Ping_Request.class),
|
Map.entry("Ping", Net_Ping_Request.class),
|
||||||
Map.entry("GetServerInfo", Net_GetServerInfo_Request.class),
|
Map.entry("GetServerInfo", Net_GetServerInfo_Request.class),
|
||||||
|
Map.entry("ListBlockchainHeads", Net_ListBlockchainHeads_Request.class),
|
||||||
Map.entry("GetCallIceConfig", Net_GetCallIceConfig_Request.class),
|
Map.entry("GetCallIceConfig", Net_GetCallIceConfig_Request.class),
|
||||||
Map.entry("ClientErrorLog", Net_ClientErrorLog_Request.class),
|
Map.entry("ClientErrorLog", Net_ClientErrorLog_Request.class),
|
||||||
Map.entry("ClientDebugLog", Net_ClientDebugLog_Request.class),
|
Map.entry("ClientDebugLog", Net_ClientDebugLog_Request.class),
|
||||||
|
|||||||
@ -0,0 +1,76 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.system;
|
||||||
|
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ListBlockchainHeads_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.system.entyties.Net_ListBlockchainHeads_Response;
|
||||||
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
import shine.db.dao.BlockchainStateDAO;
|
||||||
|
import shine.db.entities.BlockchainStateEntry;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ListBlockchainHeads — получить краткую сводку по всем blockchain_state.
|
||||||
|
* Используется для межсерверной сверки heads перед догоняющей синхронизацией.
|
||||||
|
*/
|
||||||
|
public class Net_ListBlockchainHeads_Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(Net_ListBlockchainHeads_Handler.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Net_Response handle(Net_Request baseRequest, ConnectionContext ctx) {
|
||||||
|
Net_ListBlockchainHeads_Request req = (Net_ListBlockchainHeads_Request) baseRequest;
|
||||||
|
|
||||||
|
try {
|
||||||
|
List<BlockchainStateEntry> states = BlockchainStateDAO.getInstance().listAll();
|
||||||
|
|
||||||
|
Net_ListBlockchainHeads_Response resp = new Net_ListBlockchainHeads_Response();
|
||||||
|
resp.setOp(req.getOp());
|
||||||
|
resp.setRequestId(req.getRequestId());
|
||||||
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
|
|
||||||
|
List<Net_ListBlockchainHeads_Response.Item> items = new ArrayList<>(states.size());
|
||||||
|
for (BlockchainStateEntry state : states) {
|
||||||
|
Net_ListBlockchainHeads_Response.Item item = new Net_ListBlockchainHeads_Response.Item();
|
||||||
|
item.setBlockchainName(state.getBlockchainName());
|
||||||
|
item.setLastBlockNumber(state.getLastBlockNumber());
|
||||||
|
item.setLastBlockHash(toHex32(state.getLastBlockHash()));
|
||||||
|
item.setFileSizeBytes(state.getFileSizeBytes());
|
||||||
|
items.add(item);
|
||||||
|
}
|
||||||
|
resp.setBlockchains(items);
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("❌ Internal error ListBlockchainHeads", e);
|
||||||
|
return NetExceptionResponseFactory.error(
|
||||||
|
req,
|
||||||
|
WireCodes.Status.INTERNAL_ERROR,
|
||||||
|
"INTERNAL_ERROR",
|
||||||
|
NetExceptionResponseFactory.detailedMessage("Внутренняя ошибка сервера при ListBlockchainHeads", e)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String toHex32(byte[] bytes32) {
|
||||||
|
byte[] b = bytes32;
|
||||||
|
if (b == null || b.length != 32) {
|
||||||
|
b = new byte[32];
|
||||||
|
}
|
||||||
|
final char[] hex = "0123456789abcdef".toCharArray();
|
||||||
|
char[] out = new char[64];
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
int v = b[i] & 0xFF;
|
||||||
|
out[i * 2] = hex[v >>> 4];
|
||||||
|
out[i * 2 + 1] = hex[v & 0x0F];
|
||||||
|
}
|
||||||
|
return new String(out);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.system.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Пустой запрос для получения списка heads всех blockchain_state.
|
||||||
|
*/
|
||||||
|
public class Net_ListBlockchainHeads_Request extends Net_Request {
|
||||||
|
}
|
||||||
@ -0,0 +1,33 @@
|
|||||||
|
package server.logic.ws_protocol.JSON.handlers.system.entyties;
|
||||||
|
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class Net_ListBlockchainHeads_Response extends Net_Response {
|
||||||
|
|
||||||
|
public static final class Item {
|
||||||
|
private String blockchainName;
|
||||||
|
private int lastBlockNumber;
|
||||||
|
private String lastBlockHash;
|
||||||
|
private long fileSizeBytes;
|
||||||
|
|
||||||
|
public String getBlockchainName() { return blockchainName; }
|
||||||
|
public void setBlockchainName(String blockchainName) { this.blockchainName = blockchainName; }
|
||||||
|
|
||||||
|
public int getLastBlockNumber() { return lastBlockNumber; }
|
||||||
|
public void setLastBlockNumber(int lastBlockNumber) { this.lastBlockNumber = lastBlockNumber; }
|
||||||
|
|
||||||
|
public String getLastBlockHash() { return lastBlockHash; }
|
||||||
|
public void setLastBlockHash(String lastBlockHash) { this.lastBlockHash = lastBlockHash; }
|
||||||
|
|
||||||
|
public long getFileSizeBytes() { return fileSizeBytes; }
|
||||||
|
public void setFileSizeBytes(long fileSizeBytes) { this.fileSizeBytes = fileSizeBytes; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Item> blockchains = new ArrayList<>();
|
||||||
|
|
||||||
|
public List<Item> getBlockchains() { return blockchains; }
|
||||||
|
public void setBlockchains(List<Item> blockchains) { this.blockchains = blockchains; }
|
||||||
|
}
|
||||||
@ -1,2 +1,2 @@
|
|||||||
client.version=1.2.269
|
client.version=1.2.270
|
||||||
server.version=1.2.249
|
server.version=1.2.250
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user