31 12 25
Сделал что бы запускалось. Поправил мелкие ошибки
This commit is contained in:
parent
62ea49d1fc
commit
f17d077f25
95
DOC/doc-BD.md
Normal file
95
DOC/doc-BD.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
Документ: shine-server-bd — таблицы и структура
|
||||||
|
Таблицы (что хранят)
|
||||||
|
|
||||||
|
solana_users — локальная таблица пользователей (логин + ключ устройства + опциональный solanaKey).
|
||||||
|
|
||||||
|
active_sessions — активные сессии пользователей (секреты сессии, время, push-подписки, IP/клиент-инфо).
|
||||||
|
|
||||||
|
users_params — сохранённые параметры пользователя (например, состояние просмотренности лент и прочие настройки), с подписью.
|
||||||
|
|
||||||
|
ip_geo_cache — кэш геолокации по IP (чтобы не дергать внешние сервисы каждый раз).
|
||||||
|
|
||||||
|
blockchain_state — агрегатное “текущее состояние” блокчейна по blockchainName (лимит, размер, последние хэши/номера).
|
||||||
|
|
||||||
|
blocks — сохранённые блоки/сообщения блокчейна (байты блока + связи “кому/куда” для адресации).
|
||||||
|
|
||||||
|
solana_users — пользователи и их ключи
|
||||||
|
|
||||||
|
login TEXT — логин пользователя (PRIMARY KEY)
|
||||||
|
deviceKey TEXT — публичный ключ устройства (обязателен)
|
||||||
|
solanaKey TEXT — дополнительный ключ Solana (может быть NULL)
|
||||||
|
|
||||||
|
active_sessions — активные сессии пользователя
|
||||||
|
|
||||||
|
sessionId TEXT — идентификатор сессии (PRIMARY KEY)
|
||||||
|
login TEXT — логин владельца сессии (FK → solana_users.login)
|
||||||
|
sessionPwd TEXT — пароль/секрет сессии (используется сервером)
|
||||||
|
storagePwd TEXT — пароль/секрет для шифрования хранилища/данных
|
||||||
|
sessionCreatedAtMs INTEGER — время создания сессии (ms)
|
||||||
|
lastAuthirificatedAtMs INTEGER — время последней успешной аутентификации/refresh (ms)
|
||||||
|
pushEndpoint TEXT — endpoint для web-push (nullable)
|
||||||
|
pushP256dhKey TEXT — ключ p256dh для web-push (nullable)
|
||||||
|
pushAuthKey TEXT — auth key для web-push (nullable)
|
||||||
|
clientIp TEXT — IP клиента при auth/refresh (nullable)
|
||||||
|
clientInfoFromClient TEXT — строка client-info от клиента (nullable)
|
||||||
|
clientInfoFromRequest TEXT — строка client-info, собранная сервером (nullable)
|
||||||
|
userLanguage TEXT — предпочитаемый язык пользователя (nullable)
|
||||||
|
|
||||||
|
users_params — параметры/состояния пользователя
|
||||||
|
|
||||||
|
login TEXT — логин владельца параметра (FK → solana_users.login)
|
||||||
|
param TEXT — имя параметра (в паре с login образует UNIQUE)
|
||||||
|
bch_channel_id INTEGER — id канала/ленты/контекста блокчейна (по умолчанию 0)
|
||||||
|
value TEXT — значение параметра (nullable)
|
||||||
|
time_ms INTEGER — время обновления параметра (ms)
|
||||||
|
pubkey_num INTEGER — номер/индекс публичного ключа, которым подписано значение
|
||||||
|
signature TEXT — подпись значения параметра (строка)
|
||||||
|
|
||||||
|
ip_geo_cache — кэш геоданных по IP
|
||||||
|
|
||||||
|
ip TEXT — IP-адрес (PRIMARY KEY)
|
||||||
|
geo TEXT — строка гео-описания (“Country, City” и т.п.) (nullable)
|
||||||
|
updated_at_ms INTEGER — время последнего обновления (ms)
|
||||||
|
|
||||||
|
blockchain_state — текущее состояние блокчейна по blockchainName
|
||||||
|
|
||||||
|
blockchainName TEXT — имя/идентификатор блокчейна (PRIMARY KEY)
|
||||||
|
login TEXT — владелец блокчейна (FK → solana_users.login)
|
||||||
|
blockchainKey TEXT — публичный ключ блокчейна (используется для подписи блоков)
|
||||||
|
size_limit INTEGER — лимит размера блокчейн-файла (байты)
|
||||||
|
file_size_bytes INTEGER — текущий размер файла блокчейна (байты)
|
||||||
|
last_global_number INTEGER — номер последнего глобального блока (genesis обычно -1)
|
||||||
|
last_global_hash TEXT — хэш последнего глобального блока (пусто для “нулевого” состояния)
|
||||||
|
updated_at_ms INTEGER — время обновления состояния (ms)
|
||||||
|
line0_last_number INTEGER — последний номер в линии 0
|
||||||
|
line0_last_hash TEXT — последний хэш в линии 0
|
||||||
|
line1_last_number INTEGER — последний номер в линии 1
|
||||||
|
line1_last_hash TEXT — последний хэш в линии 1
|
||||||
|
line2_last_number INTEGER — последний номер в линии 2
|
||||||
|
line2_last_hash TEXT — последний хэш в линии 2
|
||||||
|
line3_last_number INTEGER — последний номер в линии 3
|
||||||
|
line3_last_hash TEXT — последний хэш в линии 3
|
||||||
|
line4_last_number INTEGER — последний номер в линии 4
|
||||||
|
line4_last_hash TEXT — последний хэш в линии 4
|
||||||
|
line5_last_number INTEGER — последний номер в линии 5
|
||||||
|
line5_last_hash TEXT — последний хэш в линии 5
|
||||||
|
line6_last_number INTEGER — последний номер в линии 6
|
||||||
|
line6_last_hash TEXT — последний хэш в линии 6
|
||||||
|
line7_last_number INTEGER — последний номер в линии 7
|
||||||
|
line7_last_hash TEXT — последний хэш в линии 7
|
||||||
|
|
||||||
|
blocks — блоки/сообщения блокчейна (байтовое тело + адресация)
|
||||||
|
|
||||||
|
login TEXT — владелец (FK → solana_users.login)
|
||||||
|
bchName TEXT — имя блокчейна (FK → blockchain_state.blockchainName)
|
||||||
|
blockGlobalNumber INTEGER — глобальный номер блока
|
||||||
|
blockGlobalPreHashe TEXT — предыдущий глобальный хэш (строка)
|
||||||
|
blockLineIndex INTEGER — индекс линии (0..7)
|
||||||
|
blockLineNumber INTEGER — номер блока внутри линии
|
||||||
|
blockLinePreHashe TEXT — предыдущий хэш внутри линии
|
||||||
|
msgType INTEGER — тип сообщения/записи (код)
|
||||||
|
blockByte BLOB — сырые байты блока (nullable)
|
||||||
|
to_login TEXT — адресат логин (nullable)
|
||||||
|
toBchName TEXT — адресат блокчейн (nullable)
|
||||||
|
toBlockGlobalNumber INTEGER — целевой глобальный номер (nullable)
|
||||||
|
toBlockHashe TEXT — целевой хэш (nullable)
|
||||||
23
DOC/doc_all_libs.md
Normal file
23
DOC/doc_all_libs.md
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
Перечень библиотек и их краткое описание
|
||||||
|
|
||||||
|
shine-server-log
|
||||||
|
Статический “сиренный” метод для максимально заметного критического лога администратору
|
||||||
|
|
||||||
|
shine-server-config
|
||||||
|
Минимальный конфиг-лоадер, который один раз читает application.properties и даёт доступ к параметрам.
|
||||||
|
Внешние настройки (2): server.1port=7070, db.path=data/shine.sqlite.
|
||||||
|
|
||||||
|
shine-server-geo
|
||||||
|
Утилиты, которые вытаскивают IP/язык/UA из Jetty WebSocket и (опционально) резолвят гео по IP с кэшем в БД.
|
||||||
|
|
||||||
|
shine-server-crypto
|
||||||
|
Базовые крипто-утилиты для SHA-256 и Ed25519 (BouncyCastle) + проверка подписи/хэша для .bch сущностей и маленький self-test.
|
||||||
|
|
||||||
|
shine-server-bd
|
||||||
|
Библиотека реалезующая всю работу с БД:
|
||||||
|
|
||||||
|
shine-server-blockchain
|
||||||
|
Библиотека, которая задаёт единый бинарный формат блоков (RAW+signature+hash), парсит/валидирует “тело” блока по type/version, и проверяет целостность/подпись цепочки через SHA-256 + Ed25519 с привязкой к login и предыдущим хэшам.
|
||||||
|
|
||||||
|
shine-server-protocol
|
||||||
|
Библиотека JSON-протокол поверх WebSocket для взаимодействия с клиентами.
|
||||||
17
DOC/libs/shine-server-bd/DOC.md
Normal file
17
DOC/libs/shine-server-bd/DOC.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
shine-server-bd — это библиотека реалезующая всю работу с БД:
|
||||||
|
|
||||||
|
хранит пользователей/сессии/параметры/кэш IP→гео и данные блокчейна (состояние + блоки), предоставляя единый SqliteDbController для соединений, набор DAO под каждую таблицу (Singleton, методы с Connection для транзакций и без Connection — сами открывают/закрывают), и простые entity-модели как контейнеры данных для маппинга ResultSet↔Java.
|
||||||
|
|
||||||
|
Логика структуры классов (в двух словах):
|
||||||
|
|
||||||
|
shine.db.SqliteDbController — один вход в БД: читает db.path, при отсутствии файла создаёт БД, выдаёт новые Connection и настраивает PRAGMA.
|
||||||
|
shine.db.DatabaseInitializer — разовая сборка схемы (таблицы + индексы).
|
||||||
|
|
||||||
|
|
||||||
|
shine.db.entities.* — POJO-модели строк таблиц (без логики, только поля/геттеры/сеттеры + иногда удобные методы вроде getDeviceKeyByte()).
|
||||||
|
shine.db.dao.* — DAO по таблицам: ActiveSessionsDAO, SolanaUsersDAO, UserParamsDAO, IpGeoCacheDAO, BlockchainStateDAO, BlocksDAO; плюс “сервисные” DAO:
|
||||||
|
|
||||||
|
UserCreateDAO — атомарная регистрация пользователя в транзакции (BEGIN IMMEDIATE + rollback/commit).
|
||||||
|
// Временное решение позволяющее регистрировать новых пользователей
|
||||||
|
// атомарно и добавляет запись и в solana_users и в BlockchainState
|
||||||
|
|
||||||
85
DOC/libs/shine-server-blockchain/Doc.md
Normal file
85
DOC/libs/shine-server-blockchain/Doc.md
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
shine-server-blockchain — это библиотека, которая задаёт формат блока, правила парсинга/валидации тела, крипто-проверку (hash+Ed25519) и безопасную работу с файлами блокчейна (data/<name>.bch через временный .tmp_bch).
|
||||||
|
|
||||||
|
Как устроена структура и логика работы
|
||||||
|
|
||||||
|
1) “Блок” как центральный объект (ядро)
|
||||||
|
BchBlockEntry — единая модель блока “как лежит на диске/в сети”:
|
||||||
|
читает/собирает байты в формате RAW + signature64 + hash32
|
||||||
|
сразу парсит body через BodyRecordParser
|
||||||
|
сразу проверяет что lineIndex совпадает с тем, что ожидает конкретный тип body (expectedLineIndex())
|
||||||
|
То есть: всё, что считается “блоком”, обязано быть самодостаточно валидным уже на этапе создания объекта.
|
||||||
|
|
||||||
|
2) “Body” как плагины по типам (расширяемая часть) ! <-- Новые типы записей добавлть сюда !
|
||||||
|
BodyRecord — интерфейс контракта для всех тел:
|
||||||
|
type/version — идентификаторы формата
|
||||||
|
expectedLineIndex() — жёсткое правило “в какой линии может жить”
|
||||||
|
check() — логическая валидация содержимого
|
||||||
|
toBytes() — сериализация обратно в бинарь
|
||||||
|
|
||||||
|
BodyRecordParser — диспетчер: читает первые 4 байта (type+ver) и выбирает нужный класс:
|
||||||
|
HeaderBody (lineIndex=0)
|
||||||
|
TextBody (lineIndex=1)
|
||||||
|
ReactionBody (lineIndex=2)
|
||||||
|
|
||||||
|
Добавление нового типа = добавить новый класс XxxBody + кейс в BodyRecordParser.
|
||||||
|
|
||||||
|
3) Криптография как отдельный слой проверки
|
||||||
|
BchCryptoVerifier отвечает за “как получить хэш и как проверить подпись”:
|
||||||
|
строит preimage = "SHiNE" + login + prevGlobalHash32 + prevLineHash32 + rawBytes
|
||||||
|
считает sha256(preimage) и сравнивает с hash32 внутри блока
|
||||||
|
проверяет Ed25519 подпись над hash32
|
||||||
|
Важно: BchBlockEntry не проверяет подпись — он проверяет структуру блока и правильность body/lineIndex, а криптопроверка вынесена отдельно.
|
||||||
|
|
||||||
|
4) Утилиты вокруг имени и файлов
|
||||||
|
|
||||||
|
BlockchainNameUtil — извлекает login из blockchainName (отрезает 3 символа суффикса).
|
||||||
|
FileStoreUtil — безопасное файловое хранилище:
|
||||||
|
|
||||||
|
|
||||||
|
5) Объяснение структуры работы
|
||||||
|
|
||||||
|
Типичный сценарий: пришёл блок → проверить → принять
|
||||||
|
Шаг 0. Контекст (что у нас уже есть снаружи)
|
||||||
|
|
||||||
|
Снаружи библиотеки (в сервере) у тебя уже известны:
|
||||||
|
userLogin — владелец блокчейна
|
||||||
|
publicKey32 — публичный ключ пользователя
|
||||||
|
prevGlobalHash32 — хэш предыдущего блока по глобальной цепи
|
||||||
|
prevLineHash32 — хэш предыдущего блока по текущей линии
|
||||||
|
|
||||||
|
Библиотека не хранит это сама, она ожидает, что сервер это передаст.
|
||||||
|
|
||||||
|
Шаг 1. Парсинг блока (структура + логика)
|
||||||
|
BchBlockEntry block = new BchBlockEntry(fullBytes);
|
||||||
|
|
||||||
|
|
||||||
|
Что происходит здесь автоматически:
|
||||||
|
проверяется длина блока
|
||||||
|
проверяется recordSize
|
||||||
|
парсится RAW-заголовок
|
||||||
|
парсится body через BodyRecordParser
|
||||||
|
проверяется, что lineIndex соответствует типу body
|
||||||
|
(HEADER → line 0, TEXT → line 1, REACTION → line 2 и т.д.)
|
||||||
|
❗ На этом шаге никакой криптографии ещё нет — только структура и логика формата.
|
||||||
|
|
||||||
|
Если тут не упало исключение → блок структурно корректен.
|
||||||
|
|
||||||
|
Шаг 2. Подготовка данных для криптопроверки (они получаются просто из частей байтов полного блокас подписью)
|
||||||
|
byte[] rawBytes = block.getRawBytes();
|
||||||
|
byte[] signature64 = block.getSignature64();
|
||||||
|
byte[] hash32FromTail = block.getHash32();
|
||||||
|
|
||||||
|
Важно:
|
||||||
|
rawBytes — это ровно те байты, которые участвовали в хэшировании
|
||||||
|
hash32FromTail — это то, что автор блока положил внутрь блока
|
||||||
|
|
||||||
|
Шаг 3. Криптографическая проверка (ключевой вызов)
|
||||||
|
boolean ok = BchCryptoVerifier.verifyAll(
|
||||||
|
userLogin,
|
||||||
|
prevGlobalHash32,
|
||||||
|
prevLineHash32,
|
||||||
|
rawBytes,
|
||||||
|
signature64,
|
||||||
|
publicKey32,
|
||||||
|
hash32FromTail
|
||||||
|
);
|
||||||
48
DOC/libs/shine-server-blockchain/Общая структура блока.md
Normal file
48
DOC/libs/shine-server-blockchain/Общая структура блока.md
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
Общая структура блока
|
||||||
|
|
||||||
|
Блок — это бинарная запись фиксированного формата:
|
||||||
|
|
||||||
|
[ RAW ][ signature64 ][ hash32 ]
|
||||||
|
|
||||||
|
RAW — данные блока (участвуют в хэшировании и подписи)
|
||||||
|
signature64 — Ed25519-подпись над hash32
|
||||||
|
hash32 — SHA-256 от preimage (привязан к цепочке и владельцу)
|
||||||
|
|
||||||
|
RAW-часть (BigEndian)
|
||||||
|
recordSize int32 — размер RAW (без signature+hash)
|
||||||
|
recordNumber int32 — глобальный номер блока
|
||||||
|
timestamp int64 — unix time (seconds)
|
||||||
|
lineIndex int16 — индекс линии
|
||||||
|
lineNumber int32 — номер блока внутри линии
|
||||||
|
bodyBytes bytes — тело блока (type+version+payload)
|
||||||
|
|
||||||
|
Общая структура блокчейна
|
||||||
|
|
||||||
|
Блокчейн — это:
|
||||||
|
линейная цепочка блоков (по recordNumber)
|
||||||
|
внутри неё — параллельные логические линии (lineIndex)
|
||||||
|
каждая линия имеет собственную нумерацию (lineNumber) и prevLineHash
|
||||||
|
вся цепочка связана ещё и prevGlobalHash
|
||||||
|
|
||||||
|
👉 Таким образом, каждый блок:
|
||||||
|
связан с предыдущим глобальным блоком
|
||||||
|
и с предыдущим блоком своей линии
|
||||||
|
|
||||||
|
Это даёт:
|
||||||
|
строгий порядок всей истории
|
||||||
|
и независимую валидацию логических потоков
|
||||||
|
|
||||||
|
Криптографический смысл блока
|
||||||
|
|
||||||
|
Хэш блока считается от:
|
||||||
|
"SHiNE" +
|
||||||
|
login +
|
||||||
|
prevGlobalHash32 +
|
||||||
|
prevLineHash32 +
|
||||||
|
RAW
|
||||||
|
|
||||||
|
Это означает:
|
||||||
|
блок жёстко привязан к владельцу (login)
|
||||||
|
блок невозможно перенести в другую цепочку
|
||||||
|
подмена предыдущего блока ломает всю цепь
|
||||||
|
Подпись Ed25519 делается над этим хэшем.
|
||||||
36
DOC/libs/shine-server-blockchain/Формат существующих Body.md
Normal file
36
DOC/libs/shine-server-blockchain/Формат существующих Body.md
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
Формат и смысл существующих Body
|
||||||
|
|
||||||
|
1) HeaderBody (type=0, ver=1)
|
||||||
|
|
||||||
|
Линия: lineIndex = 0
|
||||||
|
Смысл:
|
||||||
|
Генезис-блок блокчейна, объявляет формат и владельца.
|
||||||
|
|
||||||
|
Содержит:
|
||||||
|
сигнатуру формата "SHiNE"
|
||||||
|
login владельца блокчейна
|
||||||
|
👉 Всегда первый блок, всегда в линии 0.
|
||||||
|
|
||||||
|
2) TextBody (type=1, ver=1)
|
||||||
|
|
||||||
|
Линия: lineIndex = 1
|
||||||
|
Смысл:
|
||||||
|
Основной контент — текстовые записи (посты, сообщения, дневник).
|
||||||
|
|
||||||
|
Содержит:
|
||||||
|
UTF-8 текст произвольной длины
|
||||||
|
👉 Это “основная история” блокчейна пользователя.
|
||||||
|
|
||||||
|
3) ReactionBody (type=2, ver=1)
|
||||||
|
|
||||||
|
Линия: lineIndex = 2
|
||||||
|
Смысл:
|
||||||
|
Связь с другим блокчейном или блоком (реакция, ответ, лайк, ссылка).
|
||||||
|
|
||||||
|
Содержит:
|
||||||
|
код реакции
|
||||||
|
имя целевого блокчейна
|
||||||
|
globalNumber целевого блока
|
||||||
|
hash32 целевого блока
|
||||||
|
👉 Это механизм межблокчейн-связей без изменения чужих цепочек.
|
||||||
|
|
||||||
7
DOC/libs/shine-server-config/doc.md
Normal file
7
DOC/libs/shine-server-config/doc.md
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
shine-server-config
|
||||||
|
|
||||||
|
Минимальная библиотека конфигурации, предоставляющая потокобезопасный singleton-доступ к параметрам из application.properties.
|
||||||
|
|
||||||
|
Настройки:
|
||||||
|
server.port=7070 — порт запуска сервера
|
||||||
|
db.path=data/shine.sqlite — путь к SQLite базе данных
|
||||||
8
DOC/libs/shine-server-crypto/DOC.md
Normal file
8
DOC/libs/shine-server-crypto/DOC.md
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
shine-server-crypto
|
||||||
|
|
||||||
|
О чём: базовые крипто-утилиты для SHA-256 и Ed25519 (BouncyCastle) + проверка подписи/хэша для .bch сущностей и маленький self-test.
|
||||||
|
Внешние методы, которые вызываются:
|
||||||
|
BchCryptoVerifier.verifyAll(), BchCryptoVerifier.buildPreimage(), Ed25519Util.generatePrivateKey(), Ed25519Util.generatePrivateKeyFromString(), Ed25519Util.derivePublicKey(), Ed25519Util.sign(), Ed25519Util.verify(), Ed25519Util.keyToBase64(), Ed25519Util.keyFromBase64(),
|
||||||
|
HashSHA256Util.sha256()
|
||||||
|
|
||||||
|
HashSHA256Util.loginToLoginId(), HashSHA256Util.loginIdFromLogin(),
|
||||||
19
DOC/libs/shine-server-geo/DOC.md
Normal file
19
DOC/libs/shine-server-geo/DOC.md
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
shine-server-geo
|
||||||
|
|
||||||
|
Назначение: утилиты для получения “кто подключился” (IP/UA/язык) и геолокации по IP (с опциональным кэшем в БД).
|
||||||
|
|
||||||
|
Классы:
|
||||||
|
|
||||||
|
ClientInfoService — собирает строку UA/ch-ua/platform/mobile/remoteIP, вытаскивает реальный IP (приоритет: X-Forwarded-For → X-Real-IP → remoteAddress), парсит первый Accept-Language.
|
||||||
|
GeoLookupService — геолокация по IP через внешний API (ip-api.com), умеет вариант без кэша и с кэшем в таблице ip_geo_cache через IpGeoCacheDAO (пишет даже unknown), плюс метод получения внешнего IP через api.ipify.org.
|
||||||
|
GeoLookupTestMain — консольный тест: берёт IP из аргумента или определяет внешний, вызывает геолокацию и печатает результат + время.
|
||||||
|
|
||||||
|
|
||||||
|
Внешние (публично используемые) методы:
|
||||||
|
|
||||||
|
ClientInfoService.buildClientInfoString(Session) — формирует строку с User-Agent, client-hints и реальным IP клиента
|
||||||
|
ClientInfoService.extractClientIp(Session) — извлекает реальный IP (X-Forwarded-For / X-Real-IP / remoteAddress)
|
||||||
|
ClientInfoService.extractPreferredLanguageTag(Session) — возвращает основной язык клиента из Accept-Language
|
||||||
|
GeoLookupService.resolveCountryCityOrIp(String ip) — геолокация по IP без кэша (Country, City или unknown)
|
||||||
|
GeoLookupService.resolveCountryCityOrIpWithCache(String ip) — геолокация по IP с кэшированием в БД
|
||||||
|
GeoLookupService.fetchPublicIpOrDefault(String fallbackIp) — получение внешнего IP текущей машины
|
||||||
10
DOC/libs/shine-server-log/DOC.md
Normal file
10
DOC/libs/shine-server-log/DOC.md
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
shine-log (BlockchainAdminNotifier)
|
||||||
|
|
||||||
|
Суть (1 предложение): единая точка для “красного” оповещения админа о критических проблемах консистентности, сейчас — через максимально заметный log.error, позже — через Telegram/email/webhook и т.п.
|
||||||
|
|
||||||
|
Структура (очень кратко):
|
||||||
|
|
||||||
|
BlockchainAdminNotifier (final utility)
|
||||||
|
|
||||||
|
BlockchainAdminNotifier.critical(String message)
|
||||||
|
BlockchainAdminNotifier.critical(String message, Throwable t)
|
||||||
@ -1,205 +0,0 @@
|
|||||||
формат добавления блоков и из чего реально состоит блок, плюс как именно считаются хэши и подписи.
|
|
||||||
|
|
||||||
1) Формат добавления блоков в файл .bch
|
|
||||||
|
|
||||||
Файл — это просто конкатенация блоков один за другим, без разделителей:
|
|
||||||
|
|
||||||
BLOCK_0 | BLOCK_1 | BLOCK_2 | ...
|
|
||||||
|
|
||||||
|
|
||||||
Чтобы читать файл, ты идёшь по нему последовательно:
|
|
||||||
|
|
||||||
Читаешь первые 4 байта = recordSize
|
|
||||||
|
|
||||||
Понимаешь, сколько всего байт занимает блок:
|
|
||||||
|
|
||||||
fullBlockLen = recordSize + 64 + 32
|
|
||||||
|
|
||||||
|
|
||||||
Считываешь оставшиеся байты блока и парсишь:
|
|
||||||
|
|
||||||
RAW часть длиной recordSize
|
|
||||||
|
|
||||||
затем signature64
|
|
||||||
|
|
||||||
затем hash32
|
|
||||||
|
|
||||||
Это удобно тем, что файл можно дописывать (APPEND) хоть по сети, хоть локально: блоки самодостаточные.
|
|
||||||
|
|
||||||
2) Из чего состоит блок (формат блока)
|
|
||||||
2.1. RAW (участвует в preimage и верификации; подпись/хэш к нему «пришиты»)
|
|
||||||
|
|
||||||
BigEndian, фиксированный заголовок + body:
|
|
||||||
|
|
||||||
RAW:
|
|
||||||
|
|
||||||
[4] recordSize (int) — размер RAW включая эти 4 байта, но без signature+hash
|
|
||||||
|
|
||||||
[4] recordNumber (int) — глобальный номер блока (цепочка)
|
|
||||||
|
|
||||||
[8] timestamp (long) — unix seconds
|
|
||||||
|
|
||||||
[2] line (short) — линия (у вас пока по сути одна)
|
|
||||||
|
|
||||||
[4] lineNumber (int) — номер в линии (у вас пока обычно равен global)
|
|
||||||
|
|
||||||
[N] bodyBytes — тело, начинается с [2] type + [2] version, дальше payload
|
|
||||||
|
|
||||||
RAW_HEADER_SIZE = 4 + 4 + 8 + 2 + 4 = 22 байта
|
|
||||||
|
|
||||||
2.2. TAIL (не входит в recordSize)
|
|
||||||
|
|
||||||
[64] signature64 — Ed25519 подпись
|
|
||||||
|
|
||||||
[32] hash32 — SHA-256 хэш
|
|
||||||
|
|
||||||
Итого полный блок:
|
|
||||||
|
|
||||||
FULL = RAW(recordSize bytes) + signature64 + hash32
|
|
||||||
|
|
||||||
3) Формат body (тела блока)
|
|
||||||
|
|
||||||
У тебя правило железное: каждый body начинается одинаково:
|
|
||||||
|
|
||||||
[2] type
|
|
||||||
|
|
||||||
[2] version
|
|
||||||
|
|
||||||
дальше payload по типу/версии
|
|
||||||
|
|
||||||
Парсер делает ключ:
|
|
||||||
|
|
||||||
key = (type<<16) | version
|
|
||||||
|
|
||||||
3.1 HeaderBody (type=0, ver=1)
|
|
||||||
|
|
||||||
Полный bodyBytes:
|
|
||||||
|
|
||||||
[2] type=0
|
|
||||||
|
|
||||||
[2] ver=1
|
|
||||||
|
|
||||||
[8] tag "SHiNE001" (ASCII)
|
|
||||||
|
|
||||||
[1] loginLength (uint8)
|
|
||||||
|
|
||||||
[N] login UTF-8
|
|
||||||
|
|
||||||
Это «генезис-смысл» для идентичности: в теле блока явно хранится логин.
|
|
||||||
|
|
||||||
3.2 TextBody (type=1, ver=1)
|
|
||||||
|
|
||||||
[2] type=1
|
|
||||||
|
|
||||||
[2] ver=1
|
|
||||||
|
|
||||||
[N] message UTF-8
|
|
||||||
|
|
||||||
4) Как считается хэш (hash32)
|
|
||||||
|
|
||||||
Важно: hash32 в блоке — это не hash(rawBytes).
|
|
||||||
|
|
||||||
Ты считаешь:
|
|
||||||
|
|
||||||
4.1 Preimage
|
|
||||||
preimage =
|
|
||||||
"SHiNE" +
|
|
||||||
[1] loginLen + loginBytes(UTF-8) +
|
|
||||||
prevGlobalHash32 +
|
|
||||||
prevLineHash32 +
|
|
||||||
rawBytes
|
|
||||||
|
|
||||||
|
|
||||||
Где:
|
|
||||||
|
|
||||||
"SHiNE" — доменная метка (защита от «подпиши этим же ключом что-то другое»)
|
|
||||||
|
|
||||||
loginLen + loginBytes — привязка блока к пользователю/цепочке
|
|
||||||
|
|
||||||
prevGlobalHash32 — сцепление по глобальной цепи
|
|
||||||
|
|
||||||
prevLineHash32 — сцепление по линии
|
|
||||||
|
|
||||||
rawBytes — содержимое текущего блока без подписи/хэша
|
|
||||||
|
|
||||||
4.2 Сам хэш
|
|
||||||
hash32 = SHA-256(preimage)
|
|
||||||
|
|
||||||
|
|
||||||
И именно этот hash32 должен лежать в конце блока.
|
|
||||||
|
|
||||||
5) Как считается подпись (signature64)
|
|
||||||
|
|
||||||
Подписывается не rawBytes, а hash32:
|
|
||||||
|
|
||||||
signature64 = Ed25519.sign(hash32, privateKey)
|
|
||||||
|
|
||||||
|
|
||||||
Проверка:
|
|
||||||
|
|
||||||
Пересобираем preimage
|
|
||||||
|
|
||||||
Считаем hash32 = sha256(preimage)
|
|
||||||
|
|
||||||
Сверяем hash32 с тем, что лежит в блоке
|
|
||||||
|
|
||||||
Проверяем подпись:
|
|
||||||
|
|
||||||
Ed25519.verify(hash32, signature64, publicKey32)
|
|
||||||
|
|
||||||
|
|
||||||
Это хорошая схема: подпись всегда фиксированного размера, а хэш — единая точка правды.
|
|
||||||
|
|
||||||
6) Что является «цепочкой» и чем блоки сцеплены
|
|
||||||
|
|
||||||
Сцепление идёт через prevGlobalHash32 и prevLineHash32, которые ты передаёшь в verifyAll(...).
|
|
||||||
|
|
||||||
То есть логика добавления нового блока (в терминах данных) такая:
|
|
||||||
|
|
||||||
берём последний блок
|
|
||||||
|
|
||||||
достаём его hash32
|
|
||||||
|
|
||||||
это и будет prevGlobalHash32 для нового блока
|
|
||||||
|
|
||||||
prevLineHash32 — по той линии, куда добавляем (у вас пока это то же самое, потому что одна линия/нет разветвления)
|
|
||||||
|
|
||||||
У тебя даже отдельно отмечено: пока нет отдельных линий, lastLineHash == lastGlobalHash — значит сейчас обе цепочки фактически совпадают.
|
|
||||||
|
|
||||||
7) Мини-схема “как добавить новый блок” (пошагово)
|
|
||||||
|
|
||||||
Собрать BodyRecord → получить bodyBytes = body.toBytes()
|
|
||||||
|
|
||||||
Собрать RAW:
|
|
||||||
|
|
||||||
recordNumber = lastRecordNumber + 1
|
|
||||||
|
|
||||||
timestamp = nowSeconds
|
|
||||||
|
|
||||||
line (скорее всего 0 или 1 — как вы договорились)
|
|
||||||
|
|
||||||
lineNumber = lastLineNumber + 1 (сейчас равно global)
|
|
||||||
|
|
||||||
recordSize = RAW_HEADER_SIZE + bodyBytes.length
|
|
||||||
|
|
||||||
rawBytes = new BchBlockEntry(...).getRawBytes() (или собрать вручную)
|
|
||||||
|
|
||||||
preimage = buildPreimage(login, prevGlobalHash32, prevLineHash32, rawBytes)
|
|
||||||
|
|
||||||
hash32 = sha256(preimage)
|
|
||||||
|
|
||||||
signature64 = Ed25519.sign(hash32, privateKey)
|
|
||||||
|
|
||||||
Собрать финальный BchBlockEntry(..., signature64, hash32)
|
|
||||||
|
|
||||||
Дописать toBytes() в файл .bch
|
|
||||||
|
|
||||||
8) Важные нюансы (чтобы не словить «тихий баг»)
|
|
||||||
|
|
||||||
recordSize включает себя (первые 4 байта) — это ок, просто помнить.
|
|
||||||
|
|
||||||
bodyLen = recordSize - RAW_HEADER_SIZE — значит RAW_HEADER_SIZE обязан быть всегда синхронен с реальным RAW.
|
|
||||||
|
|
||||||
login в preimage ограничен 255 байт (у тебя [1] loginLen) — это правильно и предсказуемо.
|
|
||||||
|
|
||||||
В HeaderBody tag фиксирован "SHiNE001" — фактически версия протокола body-header’а.
|
|
||||||
@ -11,7 +11,7 @@ import java.util.Objects;
|
|||||||
* Полный bodyBytes:
|
* Полный bodyBytes:
|
||||||
* [2] type=0
|
* [2] type=0
|
||||||
* [2] version=1
|
* [2] version=1
|
||||||
* [8] tag ASCII "SHiNE001"
|
* [8] tag ASCII "SHiNE"
|
||||||
* [1] loginLength=N (uint8)
|
* [1] loginLength=N (uint8)
|
||||||
* [N] login UTF-8
|
* [N] login UTF-8
|
||||||
*
|
*
|
||||||
@ -23,9 +23,9 @@ public final class HeaderBody implements BodyRecord {
|
|||||||
public static final short TYPE = 0;
|
public static final short TYPE = 0;
|
||||||
public static final short VER = 1;
|
public static final short VER = 1;
|
||||||
|
|
||||||
public static final String TAG = "SHiNE001";
|
public static final String TAG = "SHiNE";
|
||||||
|
|
||||||
public final String tag; // "SHiNE001"
|
public final String tag; // "SHiNE"
|
||||||
public final String login;
|
public final String login;
|
||||||
|
|
||||||
/** Десериализация из полного bodyBytes (включая type/version). */
|
/** Десериализация из полного bodyBytes (включая type/version). */
|
||||||
@ -42,7 +42,7 @@ public final class HeaderBody implements BodyRecord {
|
|||||||
if (bb.remaining() < 8 + 1)
|
if (bb.remaining() < 8 + 1)
|
||||||
throw new IllegalArgumentException("Header payload too short");
|
throw new IllegalArgumentException("Header payload too short");
|
||||||
|
|
||||||
byte[] tagBytes = new byte[8];
|
byte[] tagBytes = new byte[5];
|
||||||
bb.get(tagBytes);
|
bb.get(tagBytes);
|
||||||
String t = new String(tagBytes, StandardCharsets.US_ASCII);
|
String t = new String(tagBytes, StandardCharsets.US_ASCII);
|
||||||
if (!TAG.equals(t)) throw new IllegalArgumentException("Bad tag: " + t);
|
if (!TAG.equals(t)) throw new IllegalArgumentException("Bad tag: " + t);
|
||||||
|
|||||||
@ -1,27 +0,0 @@
|
|||||||
# utils.files
|
|
||||||
|
|
||||||
Хранение блокчейнов в виде файлов в папке `data/`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FileStoreUtil
|
|
||||||
Singleton для чтения и записи `.bch` файлов.
|
|
||||||
|
|
||||||
- `newBlockchain(id, data)` — создать новый файл `data/<id>.bch`
|
|
||||||
- `addDataToBlockchain(id, data)` — добавить байты в конец файла
|
|
||||||
- `readAllDataFromBlockchain(id)` — прочитать весь файл как массив байт
|
|
||||||
|
|
||||||
Каждый файл содержит последовательность полных блоков `[RAW][signature64][hash32]...`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## FileStoreUtilSelfTest
|
|
||||||
Тест: создаёт, дописывает и читает файл, чтобы проверить корректность.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Пока используется как временное файловое хранилище.
|
|
||||||
В будущем всё это уйдёт в SQL.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -1,14 +0,0 @@
|
|||||||
# utils.search
|
|
||||||
|
|
||||||
Поиск пользователей по логину среди зарегистрированных блокчейнов.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## UserSearchService
|
|
||||||
Singleton-сервис для поиска первых 5 логинов, содержащих подстроку (без учёта регистра).
|
|
||||||
|
|
||||||
Основные методы:
|
|
||||||
- `searchFirst5(String query)` — вернуть список `Pair(id, login)`
|
|
||||||
- `packPair(Pair p)` — упаковать пару в бинарный ответ `[8]id + [1]len + [len]login`
|
|
||||||
|
|
||||||
Используется сервером в операции `SEARCH_USERS (op=30)` для ответа клиенту.
|
|
||||||
16
shine-server-crypto/concat_to_file.sh
Executable file
16
shine-server-crypto/concat_to_file.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
OUTFILE="all_files.txt"
|
||||||
|
|
||||||
|
# очищаем или создаём файл
|
||||||
|
: > "$OUTFILE"
|
||||||
|
|
||||||
|
# собрать только *.java файлы и вывести их содержимое в файл
|
||||||
|
find . -type f -name "*.java" | sort | while read -r f; do
|
||||||
|
cat "$f" >> "$OUTFILE"
|
||||||
|
echo >> "$OUTFILE" # пустая строка-разделитель
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Готово! Все .java файлы собраны в $OUTFILE"
|
||||||
|
|
||||||
@ -18,42 +18,6 @@ public final class HashSHA256Util {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Получить loginId из строки логина.
|
|
||||||
* Алгоритм:
|
|
||||||
* - login -> UTF-8 bytes
|
|
||||||
* - SHA-256
|
|
||||||
* - берём последние 8 байт (справа)
|
|
||||||
* - интерпретируем как signed long (BigEndian)
|
|
||||||
*/
|
|
||||||
public static long loginToLoginId(String login) {
|
|
||||||
if (login == null || login.isBlank())
|
|
||||||
throw new IllegalArgumentException("login is null or empty");
|
|
||||||
|
|
||||||
byte[] hash = sha256(login.getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
// последние 8 байт SHA-256
|
|
||||||
return ByteBuffer.wrap(hash, 24, 8)
|
|
||||||
.order(ByteOrder.BIG_ENDIAN)
|
|
||||||
.getLong();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* loginId = last 8 bytes of sha256(login UTF-8), big-endian.
|
|
||||||
* (берём 8 байт справа и читаем как unsigned long в BE)
|
|
||||||
*/
|
|
||||||
public static long loginIdFromLogin(String login) {
|
|
||||||
if (login == null || login.isBlank())
|
|
||||||
throw new IllegalArgumentException("login is blank");
|
|
||||||
|
|
||||||
byte[] h = sha256(login.getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
long v = 0;
|
|
||||||
for (int i = 24; i < 32; i++) {
|
|
||||||
v = (v << 8) | (h[i] & 0xFFL);
|
|
||||||
}
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Инкрементальный SHA-256 (если нужно будет кормить по кускам). */
|
/** Инкрементальный SHA-256 (если нужно будет кормить по кускам). */
|
||||||
public static final class Sha256 {
|
public static final class Sha256 {
|
||||||
private final SHA256Digest d = new SHA256Digest();
|
private final SHA256Digest d = new SHA256Digest();
|
||||||
|
|||||||
@ -1,53 +0,0 @@
|
|||||||
package shine.db.dao;
|
|
||||||
|
|
||||||
import shine.db.SqliteDbController;
|
|
||||||
import shine.db.entities.BlockchainStateEntry;
|
|
||||||
|
|
||||||
import java.sql.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SolanaBlockchainsDAO — таблица блокчейнов пользователя.
|
|
||||||
*
|
|
||||||
* Сейчас физически это blockchain_state, потому что:
|
|
||||||
* - у одного login может быть несколько blockchainName
|
|
||||||
* - у каждого blockchainName свой blockchainKey и size_limit
|
|
||||||
*
|
|
||||||
* Правило:
|
|
||||||
* - методы с Connection НЕ закрывают соединение
|
|
||||||
* - методы без Connection сами открывают и закрывают соединение
|
|
||||||
*/
|
|
||||||
public final class SolanaBlockchainsDAO {
|
|
||||||
|
|
||||||
private static volatile SolanaBlockchainsDAO instance;
|
|
||||||
private final SqliteDbController db = SqliteDbController.getInstance();
|
|
||||||
private final BlockchainStateDAO stateDao = BlockchainStateDAO.getInstance();
|
|
||||||
|
|
||||||
private SolanaBlockchainsDAO() {}
|
|
||||||
|
|
||||||
public static SolanaBlockchainsDAO getInstance() {
|
|
||||||
if (instance == null) {
|
|
||||||
synchronized (SolanaBlockchainsDAO.class) {
|
|
||||||
if (instance == null) instance = new SolanaBlockchainsDAO();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockchainStateEntry getByBlockchainName(String blockchainName) throws SQLException {
|
|
||||||
return stateDao.getByBlockchainName(blockchainName);
|
|
||||||
}
|
|
||||||
|
|
||||||
public BlockchainStateEntry getByBlockchainName(Connection c, String blockchainName) throws SQLException {
|
|
||||||
return stateDao.getByBlockchainName(c, blockchainName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Для HEADER: проверка, что blockchain_state существует и last_global_number=-1. */
|
|
||||||
public BlockchainStateEntry requireExistingAtGenesis(Connection c, String blockchainName) throws SQLException {
|
|
||||||
return stateDao.requireExistingAtGenesis(c, blockchainName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Для добавления блока: атомарная проверка лимита + увеличение размера файла. */
|
|
||||||
public boolean tryIncreaseFileSizeWithinLimit(Connection c, String blockchainName, long deltaBytes, long nowMs) throws SQLException {
|
|
||||||
return stateDao.tryIncreaseFileSizeWithinLimit(c, blockchainName, deltaBytes, nowMs);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,44 +0,0 @@
|
|||||||
# Структура БД SHiNE (кратко)
|
|
||||||
|
|
||||||
## Таблица `solana_users`
|
|
||||||
Локальная копия данных о пользователях из Solana.
|
|
||||||
|
|
||||||
Поля:
|
|
||||||
- `login` (TEXT) — логин пользователя.
|
|
||||||
- `loginId` (INTEGER, PK) — ID пользователя, основной ключ.
|
|
||||||
- `bchId` (INTEGER) — ID блокчейна пользователя.
|
|
||||||
- `pubkey0` (TEXT) — первый публичный ключ.
|
|
||||||
- `pubkey1` (TEXT) — второй публичный ключ.
|
|
||||||
- `bchLimit` (INTEGER, NULL) — произвольный лимит для пользователя (опционально).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Таблица `active_sessions`
|
|
||||||
Активные сессии пользователей (WebSocket/WSS + Web Push).
|
|
||||||
|
|
||||||
Поля:
|
|
||||||
- `sessionId` (INTEGER, PK) — ID сессии, генерируется приложением.
|
|
||||||
- `session_pwd` (TEXT) — секрет/пароль сессии.
|
|
||||||
- `loginId` (INTEGER, FK → solana_users.loginId) — владелец сессии.
|
|
||||||
- `time_ms` (INTEGER) — время создания/активности сессии (мс от эпохи).
|
|
||||||
- `pubkey_num` (INTEGER) — номер ключа пользователя, которым подписывались данные.
|
|
||||||
- `push_endpoint` (TEXT, NULL) — endpoint Web Push.
|
|
||||||
- `push_p256dh_key` (TEXT, NULL) — p256dh-ключ Web Push.
|
|
||||||
- `push_auth_key` (TEXT, NULL) — auth-ключ Web Push.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Таблица `users_params`
|
|
||||||
Сохранённые параметры и состояния пользователя (например, до какого сообщения прочитана лента).
|
|
||||||
|
|
||||||
Поля:
|
|
||||||
- `loginId` (INTEGER, FK → solana_users.loginId) — пользователь.
|
|
||||||
- `param` (TEXT) — имя параметра (ключ).
|
|
||||||
- `bch_channel_id` (INTEGER, DEFAULT 0) — ID канала/ленты который просмотрен.
|
|
||||||
- `value` (TEXT, NULL) — значение параметра (строка).
|
|
||||||
- `time_ms` (INTEGER) — время последнего обновления (мс от эпохи).
|
|
||||||
- `pubkey_num` (INTEGER) — номер ключа, которым подписано значение.
|
|
||||||
- `signature` (TEXT, NULL) — подпись значения.
|
|
||||||
|
|
||||||
Ограничения:
|
|
||||||
- `UNIQUE(loginId, param)` — у одного пользователя каждый `param` только один раз.
|
|
||||||
16
shine-server-log/concat_to_file.sh
Executable file
16
shine-server-log/concat_to_file.sh
Executable file
@ -0,0 +1,16 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
OUTFILE="all_files.txt"
|
||||||
|
|
||||||
|
# очищаем или создаём файл
|
||||||
|
: > "$OUTFILE"
|
||||||
|
|
||||||
|
# собрать только *.java файлы и вывести их содержимое в файл
|
||||||
|
find . -type f -name "*.java" | sort | while read -r f; do
|
||||||
|
cat "$f" >> "$OUTFILE"
|
||||||
|
echo >> "$OUTFILE" # пустая строка-разделитель
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "Готово! Все .java файлы собраны в $OUTFILE"
|
||||||
|
|
||||||
@ -1,13 +1,12 @@
|
|||||||
package server.logic.ws_protocol.JSON;
|
package server.logic.ws_protocol.JSON;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_AuthChallenge_Request;
|
import server.logic.ws_protocol.JSON.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CreateAuthSession_Request;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_RefreshSession_Request;
|
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CloseActiveSession_Request;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_ListSessions_Request;
|
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.blockchain.Net_AddBlock_Request;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.tempToTest.Net_AddUser_Request;
|
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.auth.Net_AuthChallenge_Handler;
|
import server.logic.ws_protocol.JSON.handlers.auth.Net_AuthChallenge_Handler;
|
||||||
import server.logic.ws_protocol.JSON.handlers.auth.Net_CreateAuthSession__Handler;
|
import server.logic.ws_protocol.JSON.handlers.auth.Net_CreateAuthSession__Handler;
|
||||||
|
|||||||
@ -2,8 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.auth;
|
|||||||
|
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.*;
|
import server.logic.ws_protocol.JSON.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_AuthChallenge_Request;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_AuthChallenge_Response;
|
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
|||||||
@ -4,8 +4,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CloseActiveSession_Request;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CloseActiveSession_Response;
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
|
|||||||
@ -5,10 +5,9 @@ import org.slf4j.LoggerFactory;
|
|||||||
import org.eclipse.jetty.websocket.api.Session;
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CreateAuthSession_Response;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_CreateAuthSession_Request;
|
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
|||||||
@ -3,13 +3,12 @@ package server.logic.ws_protocol.JSON.handlers.auth;
|
|||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_ListSessions_Request;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_ListSessions_Response;
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_ListSessions_Response.SessionInfo;
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.Net_ListSessions_Response.SessionInfo;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
import shine.db.dao.ActiveSessionsDAO;
|
import shine.db.dao.ActiveSessionsDAO;
|
||||||
import shine.db.entities.ActiveSessionEntry;
|
import shine.db.entities.ActiveSessionEntry;
|
||||||
@ -144,7 +143,7 @@ public class Net_ListSessions_Handler implements JsonMessageHandler {
|
|||||||
// 4) Собираем DTO с геолокацией
|
// 4) Собираем DTO с геолокацией
|
||||||
List<SessionInfo> resultList = new ArrayList<>();
|
List<SessionInfo> resultList = new ArrayList<>();
|
||||||
for (ActiveSessionEntry s : sessions) {
|
for (ActiveSessionEntry s : sessions) {
|
||||||
SessionInfo info = new SessionInfo();
|
SessionInfo info = new Net_ListSessions_Response.SessionInfo();
|
||||||
info.setSessionId(s.getSessionId());
|
info.setSessionId(s.getSessionId());
|
||||||
info.setClientInfoFromClient(s.getClientInfoFromClient());
|
info.setClientInfoFromClient(s.getClientInfoFromClient());
|
||||||
info.setClientInfoFromRequest(s.getClientInfoFromRequest());
|
info.setClientInfoFromRequest(s.getClientInfoFromRequest());
|
||||||
|
|||||||
@ -4,8 +4,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
import server.logic.ws_protocol.JSON.ActiveConnectionsRegistry;
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_RefreshSession_Request;
|
import server.logic.ws_protocol.JSON.handlers.auth.entyties.*;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Auth.Net_RefreshSession_Response;
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.Auth;
|
package server.logic.ws_protocol.JSON.handlers.auth.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -7,8 +7,10 @@ import org.slf4j.LoggerFactory;
|
|||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.entyties.blockchain.Net_AddBlock_Request;
|
import server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler_utils.BlockchainLocks;
|
||||||
import server.logic.ws_protocol.JSON.entyties.blockchain.Net_AddBlock_Response;
|
import server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler_utils.BlockchainWriter;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Request;
|
||||||
|
import server.logic.ws_protocol.JSON.handlers.blockchain.entyties.Net_AddBlock_Response;
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
import shine.db.dao.BlockchainStateDAO;
|
import shine.db.dao.BlockchainStateDAO;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
package server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler_utils;
|
||||||
|
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
package server.logic.ws_protocol.JSON.handlers.blockchain.Net_AddBlock_Handler_utils;
|
||||||
|
|
||||||
import blockchain.BchBlockEntry;
|
import blockchain.BchBlockEntry;
|
||||||
import blockchain.body.ReactionBody;
|
import blockchain.body.ReactionBody;
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.blockchain;
|
package server.logic.ws_protocol.JSON.handlers.blockchain.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.blockchain;
|
package server.logic.ws_protocol.JSON.handlers.blockchain.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -5,8 +5,8 @@ import org.slf4j.LoggerFactory;
|
|||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
import server.logic.ws_protocol.JSON.entyties.tempToTest.Net_AddUser_Request;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Request;
|
||||||
import server.logic.ws_protocol.JSON.entyties.tempToTest.Net_AddUser_Response;
|
import server.logic.ws_protocol.JSON.handlers.tempToTest.entyties.Net_AddUser_Response;
|
||||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.tempToTest;
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||||
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.logic.ws_protocol.JSON.entyties.tempToTest;
|
package server.logic.ws_protocol.JSON.handlers.tempToTest.entyties;
|
||||||
|
|
||||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||||
|
|
||||||
@ -16,7 +16,7 @@
|
|||||||
*
|
*
|
||||||
* | Смещение | Размер | Поле | Формат | Описание |
|
* | Смещение | Размер | Поле | Формат | Описание |
|
||||||
* |-----------|--------|--------------------|---------|-----------|
|
* |-----------|--------|--------------------|---------|-----------|
|
||||||
* | 0x00 | 8 | tag | ASCII | Статическая сигнатура "SHiNE001" |
|
* | 0x00 | 8 | tag | ASCII | Статическая сигнатура "SHiNE" |
|
||||||
*
|
*
|
||||||
* | 0x10 | 1 | userLoginLength=N | uint8 | Длина логина пользователя |
|
* | 0x10 | 1 | userLoginLength=N | uint8 | Длина логина пользователя |
|
||||||
* | 0x11 | N | userLogin | UTF-8 | Логин пользователя |
|
* | 0x11 | N | userLogin | UTF-8 | Логин пользователя |
|
||||||
@ -34,7 +34,7 @@
|
|||||||
* ----------------------------------------------------------------------------
|
* ----------------------------------------------------------------------------
|
||||||
* 💡 Пример структуры в байтах:
|
* 💡 Пример структуры в байтах:
|
||||||
*
|
*
|
||||||
* 0000: 53 48 69 4E 45 30 30 31 "SHiNE001"
|
* 0000: 53 48 69 4E 45 30 30 31 "SHiNE"
|
||||||
* 0008: 00 00 00 00 01 23 45 67 blockchainId
|
* 0008: 00 00 00 00 01 23 45 67 blockchainId
|
||||||
* 0010: 05 userLoginLength = 5
|
* 0010: 05 userLoginLength = 5
|
||||||
* 0011: 41 69 64 61 72 userLogin = "Aidar"
|
* 0011: 41 69 64 61 72 userLogin = "Aidar"
|
||||||
|
|||||||
@ -18,7 +18,7 @@ public final class InboundMessageProcessor {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(InboundMessageProcessor.class);
|
private static final Logger log = LoggerFactory.getLogger(InboundMessageProcessor.class);
|
||||||
|
|
||||||
private static final Map<Integer, MessageHandler> HANDLERS = Map.of(
|
private static final Map<Integer, MessageHandler> HANDLERS = Map.of(
|
||||||
WireCodes.Op.PING, new PingHandler()
|
// WireCodes.Op.PING, new PingHandler()
|
||||||
// WireCodes.Op.ADD_BLOCK, new AddBlockHandler(),
|
// WireCodes.Op.ADD_BLOCK, new AddBlockHandler(),
|
||||||
// WireCodes.Op.GET_BLOCKCHAIN,new GetBlockchainHandler()
|
// WireCodes.Op.GET_BLOCKCHAIN,new GetBlockchainHandler()
|
||||||
// WireCodes.Op.SEARCH_USERS, new SearchUsersHandler(),
|
// WireCodes.Op.SEARCH_USERS, new SearchUsersHandler(),
|
||||||
|
|||||||
@ -1,250 +0,0 @@
|
|||||||
//package server.logic.ws_protocol.binary.handlers;
|
|
||||||
//
|
|
||||||
//import blockchain.BchBlockEntry;
|
|
||||||
//import blockchain.body.BodyRecord;
|
|
||||||
//import blockchain.BodyRecordParser;
|
|
||||||
//import blockchain.body.HeaderBody;
|
|
||||||
//import org.slf4j.Logger;
|
|
||||||
//import org.slf4j.LoggerFactory;
|
|
||||||
//import server.logic.ws_protocol.WireCodes;
|
|
||||||
//import utils.blockchain.BchInfoEntry;
|
|
||||||
//import utils.blockchain.BchInfoManager;
|
|
||||||
//import utils.crypto.BchCryptoVerifier;
|
|
||||||
//import utils.files.FileStoreUtil;
|
|
||||||
//
|
|
||||||
//import java.nio.ByteBuffer;
|
|
||||||
//import java.nio.ByteOrder;
|
|
||||||
//import java.util.Arrays;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * AddBlockHandler — обработчик команды "добавить блок" (ADD_BLOCK)
|
|
||||||
// * ---------------------------------------------------------------
|
|
||||||
// * Принимает бинарное сообщение от клиента и добавляет новый блок в цепочку.
|
|
||||||
// *.
|
|
||||||
// * Формат входного сообщения (msg):
|
|
||||||
// * [0..3] — 4 байта: код операции (WireCodes.ADD_BLOCK)
|
|
||||||
// * [4..11] — 8 байт: blockchainId (уникальный идентификатор цепочки)
|
|
||||||
// * [12..] — байты полного блока .bch:
|
|
||||||
// * ├── 4 байта recordSize = M + 18
|
|
||||||
// * ├── 4 байта recordNumber
|
|
||||||
// * ├── 8 байт timestamp
|
|
||||||
// * ├── 2 байта recordType
|
|
||||||
// * ├── 2 байта recordVersion
|
|
||||||
// * ├── M байт body (содержимое блока)
|
|
||||||
// * ├── 64 байта signature (Ed25519)
|
|
||||||
// * └── 32 байта hash (SHA-256)
|
|
||||||
// *.
|
|
||||||
// * ---------------------------------------------------------------
|
|
||||||
// * Алгоритм работы:
|
|
||||||
// *.
|
|
||||||
// * 1️⃣ Распаковать BchBlockEntry из msg (т.е. выделить тело блока и подписи).
|
|
||||||
// * 2️⃣ Найти описание цепочки (BchInfoEntry) по blockchainId.
|
|
||||||
// *.
|
|
||||||
// * ─ Если описания нет (цепочка ещё не существует):
|
|
||||||
// * • принимаем только блок типа 0 (HeaderBody) и номера 0;
|
|
||||||
// * • парсим его, создаём новый BchInfoEntry на основе данных заголовка;
|
|
||||||
// * • проверяем подпись и хэш;
|
|
||||||
// * • проверяем корректность тела блока (check);
|
|
||||||
// * • сохраняем блок и создаём новый blockchain-файл;
|
|
||||||
// * • добавляем цепочку в менеджер BchInfoManager.
|
|
||||||
// * (💡 временное решение: создание цепочки допустимо только через HeaderBody)
|
|
||||||
// *.
|
|
||||||
// * ─ Если цепочка уже существует:
|
|
||||||
// * • проверяем, что номер блока равен (lastBlockNumber + 1);
|
|
||||||
// * • проверяем подпись и хэш;
|
|
||||||
// * • проверяем тело блока (check);
|
|
||||||
// * • добавляем блок в файл цепочки;
|
|
||||||
// * • обновляем состояние BchInfoEntry (номер, хэш, размер).
|
|
||||||
// *.
|
|
||||||
// * 3️⃣ Если все проверки пройдены — возвращаем статус OK.
|
|
||||||
// *.
|
|
||||||
// * Таким образом, единственное различие между первым блоком и последующими —
|
|
||||||
// * момент инициализации описания цепочки (BchInfoEntry).
|
|
||||||
// * Всё остальное (валидация, подпись, добавление, обновление) выполняется одинаково.
|
|
||||||
// */
|
|
||||||
//public class AddBlockHandler implements MessageHandler {
|
|
||||||
//
|
|
||||||
// private static final Logger log = LoggerFactory.getLogger(AddBlockHandler.class);
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public byte[] handle(byte[] msg) {
|
|
||||||
// try {
|
|
||||||
// // =====================================================================
|
|
||||||
// // 1️⃣ Проверка минимальной длины пакета
|
|
||||||
// // =====================================================================
|
|
||||||
// int minFull = BchBlockEntry.RAW_HEADER_SIZE + BchBlockEntry.SIGNATURE_LEN + BchBlockEntry.HASH_LEN;
|
|
||||||
// // (RAW_HEADER_SIZE = 18 байт, подпись = 64, хэш = 32)
|
|
||||||
// if (msg.length < 4 + 8 + minFull)
|
|
||||||
// return code(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // 2️⃣ Извлекаем blockchainId (8 байт начиная с позиции 4)
|
|
||||||
// // =====================================================================
|
|
||||||
// long blockchainId = ByteBuffer.wrap(msg, 4, 8)
|
|
||||||
// .order(ByteOrder.BIG_ENDIAN)
|
|
||||||
// .getLong();
|
|
||||||
//
|
|
||||||
// // Всё, что дальше, — это бинарное содержимое блока .bch
|
|
||||||
// int offset = 12; // первые 12 байт = код + blockchainId
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // 3️⃣ Парсим блок (RAW + подпись + хэш)
|
|
||||||
// // =====================================================================
|
|
||||||
// byte[] fullBlock = Arrays.copyOfRange(msg, offset, msg.length);
|
|
||||||
// BchBlockEntry block = new BchBlockEntry(fullBlock); // сам распакует RAW-часть и подписи
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // 4️⃣ Получаем текущее описание цепочки (BchInfoEntry)
|
|
||||||
// // =====================================================================
|
|
||||||
// BchInfoManager info = BchInfoManager.getInstance();
|
|
||||||
// BchInfoEntry chain = info.getBchInfo(blockchainId);
|
|
||||||
//
|
|
||||||
// byte[] prevHash32;
|
|
||||||
// int expectedNum;
|
|
||||||
// String userLogin;
|
|
||||||
// byte[] publicKey32;
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // 🧩 СЦЕНАРИЙ 1: цепочка отсутствует — создаём новую
|
|
||||||
// // =====================================================================
|
|
||||||
// if (chain == null) {
|
|
||||||
// // Допускаем только блок-заголовок (type=0, num=0)
|
|
||||||
// if (block.recordType != BchBlockEntry.TYPE_HEADER || block.recordNumber != 0) {
|
|
||||||
// log.warn("Попытка создать новую цепочку без корректного заголовка (type={}, num={})",
|
|
||||||
// block.recordType, block.recordNumber);
|
|
||||||
// return code(WireCodes.Status.BAD_REQUEST);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Парсим тело блока → HeaderBody
|
|
||||||
// BodyRecord body = BodyRecordParser.parse(block.recordType, block.recordTypeVersion, block.body).check();
|
|
||||||
// if (!(body instanceof HeaderBody))
|
|
||||||
// return code(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// HeaderBody hb = (HeaderBody) body;
|
|
||||||
//
|
|
||||||
// // Проверяем, что blockchainId совпадает
|
|
||||||
// if (hb.blockchainId != blockchainId) {
|
|
||||||
// log.warn("Несовпадение blockchainId в заголовке (ожидалось {}, получено {})",
|
|
||||||
// blockchainId, hb.blockchainId);
|
|
||||||
// return code(WireCodes.Status.BAD_REQUEST);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Проверяем подпись и хэш первого блока (предыдущий хэш = 0)
|
|
||||||
// prevHash32 = new byte[32];
|
|
||||||
// boolean verified = BchCryptoVerifier.verifyAll(
|
|
||||||
// hb.userLogin,
|
|
||||||
// blockchainId,
|
|
||||||
// prevHash32,
|
|
||||||
// block.rawBytes,
|
|
||||||
// block.getSignature64(),
|
|
||||||
// block.getHash32(),
|
|
||||||
// hb.publicKey32
|
|
||||||
// );
|
|
||||||
// if (!verified) {
|
|
||||||
// log.warn("❌ Подпись не прошла проверку при создании цепочки blockchainId={}", blockchainId);
|
|
||||||
// return code(WireCodes.Status.UNVERIFIED);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // ✅ Всё хорошо: создаём новую цепочку
|
|
||||||
// info.addBlockchain(blockchainId, hb.userLogin, hb.publicKey32, Integer.MAX_VALUE);
|
|
||||||
// info.updateBlockchainState(blockchainId, block.recordNumber, bytesToHex(block.getHash32()), fullBlock.length);
|
|
||||||
//
|
|
||||||
// FileStoreUtil.getInstance().addDataToBlockchain(blockchainId, fullBlock);
|
|
||||||
//
|
|
||||||
// log.info("✅ Создана новая цепочка blockchainId={}, user={}, blockNum={}",
|
|
||||||
// blockchainId, hb.userLogin, block.recordNumber);
|
|
||||||
//
|
|
||||||
// return code(WireCodes.Status.OK);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // 🧩 СЦЕНАРИЙ 2: цепочка существует — добавляем новый блок
|
|
||||||
// // =====================================================================
|
|
||||||
// expectedNum = chain.lastBlockNumber + 1;
|
|
||||||
//
|
|
||||||
// // Проверка последовательности (и отправка lastBlockNumber)
|
|
||||||
// if (block.recordNumber < expectedNum) {
|
|
||||||
// log.info("🔁 Блок {} уже существует, последний = {}", block.recordNumber, chain.lastBlockNumber);
|
|
||||||
// ByteBuffer out = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
// out.putInt(WireCodes.Status.BLOCK_ALREADY_EXISTS);
|
|
||||||
// out.putInt(chain.lastBlockNumber);
|
|
||||||
// return out.array();
|
|
||||||
// }
|
|
||||||
// if (block.recordNumber > expectedNum) {
|
|
||||||
// log.warn("⚠️ Нарушена последовательность: получен {}, ожидался {}", block.recordNumber, expectedNum);
|
|
||||||
// ByteBuffer out = ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
// out.putInt(WireCodes.Status.OUT_OF_SEQUENCE);
|
|
||||||
// out.putInt(chain.lastBlockNumber);
|
|
||||||
// return out.array();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// userLogin = chain.userLogin;
|
|
||||||
// publicKey32 = chain.getPublicKey32();
|
|
||||||
//
|
|
||||||
// // Хэш предыдущего блока (или 32 нуля, если это первый)
|
|
||||||
// prevHash32 = (chain.lastBlockHash == null || chain.lastBlockHash.isEmpty())
|
|
||||||
// ? new byte[32]
|
|
||||||
// : hexToBytes(chain.lastBlockHash);
|
|
||||||
//
|
|
||||||
// // Проверяем подпись и хэш
|
|
||||||
// boolean verified = BchCryptoVerifier.verifyAll(
|
|
||||||
// userLogin,
|
|
||||||
// blockchainId,
|
|
||||||
// prevHash32,
|
|
||||||
// block.rawBytes,
|
|
||||||
// block.getSignature64(),
|
|
||||||
// block.getHash32(),
|
|
||||||
// publicKey32
|
|
||||||
// );
|
|
||||||
// if (!verified) {
|
|
||||||
// log.warn("❌ Подпись не прошла проверку: chainId={}, blockNum={}", blockchainId, block.recordNumber);
|
|
||||||
// return code(WireCodes.Status.UNVERIFIED);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Проверяем тело блока (например, корректный UTF-8 или структура)
|
|
||||||
// BodyRecord body = BodyRecordParser.parse(block.recordType, block.recordTypeVersion, block.body).check();
|
|
||||||
//
|
|
||||||
// // ✅ Добавляем блок в файл цепочки
|
|
||||||
// FileStoreUtil.getInstance().addDataToBlockchain(blockchainId, fullBlock);
|
|
||||||
//
|
|
||||||
// // Обновляем состояние цепочки (номер, хэш, размер)
|
|
||||||
// int newSize = chain.blockchainSize + fullBlock.length;
|
|
||||||
// info.updateBlockchainState(blockchainId, block.recordNumber, bytesToHex(block.getHash32()), newSize);
|
|
||||||
//
|
|
||||||
// log.info("✅ Блок добавлен: chain={}, num={}, type={}, bytes={}",
|
|
||||||
// blockchainId, block.recordNumber, block.recordType, fullBlock.length);
|
|
||||||
//
|
|
||||||
// return code(WireCodes.Status.OK);
|
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("❌ ADD_BLOCK: внутренняя ошибка при обработке", e);
|
|
||||||
// return code(WireCodes.Status.INTERNAL_ERROR);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // =====================================================================
|
|
||||||
// // Утилиты
|
|
||||||
// // =====================================================================
|
|
||||||
//
|
|
||||||
// /** Преобразовать статус (int) в 4 байта BigEndian. */
|
|
||||||
// private static byte[] code(int status) {
|
|
||||||
// return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(status).array();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /** Конвертация HEX → bytes (для хэшей). */
|
|
||||||
// private static byte[] hexToBytes(String hex) {
|
|
||||||
// int len = hex.length();
|
|
||||||
// byte[] out = new byte[len / 2];
|
|
||||||
// for (int i = 0; i < len; i += 2)
|
|
||||||
// out[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
|
|
||||||
// return out;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// /** Конвертация bytes → HEX (для сохранения в BchInfo). */
|
|
||||||
// private static String bytesToHex(byte[] b) {
|
|
||||||
// StringBuilder sb = new StringBuilder(b.length * 2);
|
|
||||||
// for (byte x : b) sb.append(String.format("%02x", x));
|
|
||||||
// return sb.toString();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
//package server.logic.ws_protocol.binary.handlers;
|
|
||||||
//
|
|
||||||
//import org.slf4j.Logger;
|
|
||||||
//import org.slf4j.LoggerFactory;
|
|
||||||
//import server.logic.ws_protocol.WireCodes;
|
|
||||||
//import utils.files.FileStoreUtil;
|
|
||||||
//
|
|
||||||
//import java.nio.ByteBuffer;
|
|
||||||
//import java.nio.ByteOrder;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Возврат полного содержимого блокчейна (GET_BLOCKCHAIN).
|
|
||||||
// */
|
|
||||||
//public class GetBlockchainHandler implements MessageHandler {
|
|
||||||
// private static final Logger log = LoggerFactory.getLogger(GetBlockchainHandler.class);
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public byte[] handle(byte[] msg) {
|
|
||||||
// try {
|
|
||||||
// if (msg.length < 12)
|
|
||||||
// return intTo4Bytes(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// long id = ByteBuffer.wrap(msg, 4, 8)
|
|
||||||
// .order(ByteOrder.BIG_ENDIAN)
|
|
||||||
// .getLong();
|
|
||||||
//
|
|
||||||
// FileStoreUtil fs = FileStoreUtil.getInstance();
|
|
||||||
// byte[] data = fs.readAllDataFromBlockchain(id);
|
|
||||||
//
|
|
||||||
// return packOk(data);
|
|
||||||
//
|
|
||||||
// } catch (IllegalStateException e) {
|
|
||||||
// log.warn("GET_BLOCKCHAIN: файл не найден ({})", e.getMessage());
|
|
||||||
// return intTo4Bytes(WireCodes.Status.CHAIN_NOT_FOUND);
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("GET_BLOCKCHAIN: ошибка", e);
|
|
||||||
// return intTo4Bytes(WireCodes.Status.INTERNAL_ERROR);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] packOk(byte[] data) {
|
|
||||||
// if (data == null) data = new byte[0];
|
|
||||||
// ByteBuffer out = ByteBuffer.allocate(8 + data.length).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
// out.putInt(WireCodes.Status.OK);
|
|
||||||
// out.putInt(data.length);
|
|
||||||
// out.put(data);
|
|
||||||
// return out.array();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] intTo4Bytes(int code) {
|
|
||||||
// return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(code).array();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,66 +0,0 @@
|
|||||||
//package server.logic.ws_protocol.binary.handlers;
|
|
||||||
//
|
|
||||||
//import org.slf4j.Logger;
|
|
||||||
//import org.slf4j.LoggerFactory;
|
|
||||||
//import server.logic.ws_protocol.WireCodes;
|
|
||||||
//import utils.blockchain.BchInfoEntry;
|
|
||||||
//import utils.blockchain.BchInfoManager;
|
|
||||||
//
|
|
||||||
//import java.nio.ByteBuffer;
|
|
||||||
//import java.nio.ByteOrder;
|
|
||||||
//import java.util.Arrays;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Возврат информации о последнем блоке цепочки (GET_LAST_BLOCK_INFO).
|
|
||||||
// */
|
|
||||||
//public class GetLastBlockInfoHandler implements MessageHandler {
|
|
||||||
// private static final Logger log = LoggerFactory.getLogger(GetLastBlockInfoHandler.class);
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public byte[] handle(byte[] msg) {
|
|
||||||
// try {
|
|
||||||
// if (msg.length < 12)
|
|
||||||
// return intTo4Bytes(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// long blockchainId = ByteBuffer.wrap(msg, 4, 8)
|
|
||||||
// .order(ByteOrder.BIG_ENDIAN)
|
|
||||||
// .getLong();
|
|
||||||
//
|
|
||||||
// BchInfoManager mgr = BchInfoManager.getInstance();
|
|
||||||
// BchInfoEntry entry = mgr.getBchInfo(blockchainId);
|
|
||||||
// if (entry == null)
|
|
||||||
// return intTo4Bytes(WireCodes.Status.CHAIN_NOT_FOUND);
|
|
||||||
//
|
|
||||||
// int lastNum = entry.lastBlockNumber;
|
|
||||||
// byte[] hash = hexToBytes(entry.lastBlockHash);
|
|
||||||
//
|
|
||||||
// ByteBuffer out = ByteBuffer.allocate(4 + 4 + 32).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
// out.putInt(WireCodes.Status.OK);
|
|
||||||
// out.putInt(lastNum);
|
|
||||||
// out.put(hash);
|
|
||||||
// return out.array();
|
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("GET_LAST_BLOCK_INFO: ошибка", e);
|
|
||||||
// return intTo4Bytes(WireCodes.Status.INTERNAL_ERROR);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] intTo4Bytes(int code) {
|
|
||||||
// return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(code).array();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] hexToBytes(String hex) {
|
|
||||||
// if (hex == null || hex.isEmpty()) return new byte[32];
|
|
||||||
// int len = hex.length();
|
|
||||||
// byte[] out = new byte[len / 2];
|
|
||||||
// for (int i = 0; i < len; i += 2)
|
|
||||||
// out[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
|
|
||||||
// if (out.length < 32) { // добиваем нулями
|
|
||||||
// byte[] full = new byte[32];
|
|
||||||
// System.arraycopy(out, 0, full, 32 - out.length, out.length);
|
|
||||||
// return full;
|
|
||||||
// }
|
|
||||||
// return Arrays.copyOf(out, 32);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
package server.logic.ws_protocol.binary.handlers;
|
|
||||||
|
|
||||||
import server.logic.ws_protocol.WireCodes;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Обработчик команды PING.
|
|
||||||
* Возвращает просто статус PONG.
|
|
||||||
*/
|
|
||||||
public class PingHandler implements MessageHandler {
|
|
||||||
@Override
|
|
||||||
public byte[] handle(byte[] msg) {
|
|
||||||
return new byte[]{
|
|
||||||
0, 0, 0, (byte) WireCodes.Status.PONG // проще и быстрее, можно и через ByteBuffer
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
//package server.logic.ws_protocol.binary.handlers;
|
|
||||||
//
|
|
||||||
//import org.slf4j.Logger;
|
|
||||||
//import org.slf4j.LoggerFactory;
|
|
||||||
//import server.logic.ws_protocol.WireCodes;
|
|
||||||
//import utils.search.UserSearchService;
|
|
||||||
//
|
|
||||||
//import java.nio.ByteBuffer;
|
|
||||||
//import java.nio.ByteOrder;
|
|
||||||
//import java.nio.charset.StandardCharsets;
|
|
||||||
//import java.util.List;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * Поиск пользователей по логину (SEARCH_USERS).
|
|
||||||
// */
|
|
||||||
//public class SearchUsersHandler implements MessageHandler {
|
|
||||||
// private static final Logger log = LoggerFactory.getLogger(SearchUsersHandler.class);
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public byte[] handle(byte[] msg) {
|
|
||||||
// try {
|
|
||||||
// if (msg.length < 8)
|
|
||||||
// return intTo4Bytes(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// int N = ByteBuffer.wrap(msg, 4, 4).order(ByteOrder.BIG_ENDIAN).getInt();
|
|
||||||
// if (N < 0 || msg.length < 8 + N)
|
|
||||||
// return intTo4Bytes(WireCodes.Status.BAD_REQUEST);
|
|
||||||
//
|
|
||||||
// String query = new String(msg, 8, N, StandardCharsets.UTF_8);
|
|
||||||
// List<UserSearchService.Pair> found = UserSearchService.getInstance().searchFirst5(query);
|
|
||||||
// return pack(found);
|
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.error("SEARCH_USERS: ошибка", e);
|
|
||||||
// return intTo4Bytes(WireCodes.Status.INTERNAL_ERROR);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] pack(List<UserSearchService.Pair> pairs) {
|
|
||||||
// if (pairs == null) pairs = List.of();
|
|
||||||
// int total = 8;
|
|
||||||
// var chunks = new java.util.ArrayList<byte[]>();
|
|
||||||
// for (var p : pairs) {
|
|
||||||
// byte[] packed = UserSearchService.packPair(p);
|
|
||||||
// chunks.add(packed);
|
|
||||||
// total += packed.length;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ByteBuffer out = ByteBuffer.allocate(total).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
// out.putInt(WireCodes.Status.OK);
|
|
||||||
// out.putInt(pairs.size());
|
|
||||||
// for (var c : chunks) out.put(c);
|
|
||||||
// return out.array();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] intTo4Bytes(int code) {
|
|
||||||
// return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(code).array();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,5 +1,3 @@
|
|||||||
server.1port=7070
|
server.1port=7070
|
||||||
|
|
||||||
|
|
||||||
db.path=data/shine.sqlite
|
db.path=data/shine.sqlite
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ public class IT_03_AddBlock_NoAuth {
|
|||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
int failed = run();
|
int failed = run();
|
||||||
System.exit(failed);
|
// System.exit(failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Запуск одного теста (standalone). Возвращает 0 если ок, 1 если упал. */
|
/** Запуск одного теста (standalone). Возвращает 0 если ок, 1 если упал. */
|
||||||
|
|||||||
@ -66,7 +66,7 @@
|
|||||||
|
|
||||||
// === Построение HEADER ===
|
// === Построение HEADER ===
|
||||||
function buildHeaderBody() {
|
function buildHeaderBody() {
|
||||||
const tag = textUtf8("SHiNE001");
|
const tag = textUtf8("SHiNE");
|
||||||
const id = beLong(blockchainId);
|
const id = beLong(blockchainId);
|
||||||
const login = textUtf8(userLogin);
|
const login = textUtf8(userLogin);
|
||||||
const loginLen = new Uint8Array([login.length]);
|
const loginLen = new Uint8Array([login.length]);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user