25 12 25
Добавил логгер в настройки.2
This commit is contained in:
parent
c8ee9925a1
commit
f8cc12560e
@ -23,8 +23,10 @@ dependencies {
|
||||
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1' // шифрование
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' // json
|
||||
|
||||
implementation 'org.slf4j:slf4j-api:2.0.9'
|
||||
// implementation 'org.slf4j:slf4j-api:2.0.9'
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.6'
|
||||
// Logback (реализация SLF4J + классический модуль)
|
||||
runtimeOnly "ch.qos.logback:logback-classic:1.5.6"
|
||||
|
||||
testImplementation platform('org.junit:junit-bom:5.10.0')
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter'
|
||||
@ -32,8 +34,8 @@ dependencies {
|
||||
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
||||
|
||||
|
||||
runtimeOnly "org.apache.logging.log4j:log4j-core:2.24.3" // Реализация: Log4j2 пишет в файл/консоль
|
||||
runtimeOnly "org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3" // Реализация: Log4j2 пишет в файл/консоль
|
||||
// runtimeOnly "org.apache.logging.log4j:log4j-core:2.24.3" // Реализация: Log4j2 пишет в файл/консоль
|
||||
// runtimeOnly "org.apache.logging.log4j:log4j-slf4j2-impl:2.24.3" // Реализация: Log4j2 пишет в файл/консоль
|
||||
|
||||
|
||||
implementation project(':shine-server-config') // модуль настроек из application.properties
|
||||
|
||||
@ -22,9 +22,6 @@ dependencies {
|
||||
|
||||
implementation 'com.fasterxml.jackson.core:jackson-databind:2.17.1' // json
|
||||
|
||||
implementation 'org.slf4j:slf4j-api:2.0.9'
|
||||
implementation 'ch.qos.logback:logback-classic:1.5.6'
|
||||
|
||||
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
||||
|
||||
implementation project(':shine-server-config') // модуль с настройками
|
||||
|
||||
@ -32,6 +32,7 @@ import java.util.Map;
|
||||
* }
|
||||
*/
|
||||
public final class JsonInboundProcessor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(JsonInboundProcessor.class);
|
||||
|
||||
private static final ObjectMapper JSON_MAPPER = new ObjectMapper()
|
||||
@ -51,6 +52,10 @@ public final class JsonInboundProcessor {
|
||||
String op = null;
|
||||
String requestId = null;
|
||||
|
||||
// Для лога полезно знать, кто прислал (хотя бы login/sessionId, если есть)
|
||||
String ctxLogin = safe(ctx != null ? ctx.getLogin() : null);
|
||||
String ctxSessionId = safe(ctx != null ? ctx.getSessionId() : null);
|
||||
|
||||
try {
|
||||
if (json == null || json.isBlank()) {
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
@ -60,13 +65,26 @@ public final class JsonInboundProcessor {
|
||||
"EMPTY_JSON",
|
||||
"Пустое JSON-сообщение"
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
|
||||
// DEBUG: что пришло / что ушло
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON IN (login={}, sessionId={}): <empty>", ctxLogin, ctxSessionId);
|
||||
log.debug("JSON OUT (login={}, sessionId={}): {}", ctxLogin, ctxSessionId, shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// 1. Парсим общий пакет
|
||||
// DEBUG: сырой вход (обрезаем, чтобы не убить лог)
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON IN (login={}, sessionId={}): {}", ctxLogin, ctxSessionId, shorten(json, 1200));
|
||||
}
|
||||
|
||||
// 1) Парсим общий пакет
|
||||
JsonNode root = JSON_MAPPER.readTree(json);
|
||||
|
||||
// 2. op и requestId из корня
|
||||
// 2) op и requestId из корня
|
||||
op = getTextOrNull(root, "op");
|
||||
requestId = getTextOrNull(root, "requestId");
|
||||
|
||||
@ -78,7 +96,13 @@ public final class JsonInboundProcessor {
|
||||
"NO_OP",
|
||||
"Поле 'op' отсутствует или пустое"
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
JsonMessageHandler handler = JSON_HANDLERS.get(op);
|
||||
@ -92,10 +116,16 @@ public final class JsonInboundProcessor {
|
||||
"UNKNOWN_OP",
|
||||
"Неизвестная операция: " + op
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// 3. Берём payload
|
||||
// 3) Берём payload
|
||||
JsonNode payloadNode = root.get("payload");
|
||||
if (payloadNode == null || payloadNode.isNull()) {
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
@ -105,7 +135,13 @@ public final class JsonInboundProcessor {
|
||||
"NO_PAYLOAD",
|
||||
"Поле 'payload' отсутствует"
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
if (!payloadNode.isObject()) {
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
@ -115,7 +151,13 @@ public final class JsonInboundProcessor {
|
||||
"BAD_PAYLOAD",
|
||||
"Поле 'payload' должно быть объектом"
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// 3.1 Собираем "плоский" объект для маппинга в NetRequest:
|
||||
@ -123,26 +165,52 @@ public final class JsonInboundProcessor {
|
||||
ObjectNode merged = JSON_MAPPER.createObjectNode();
|
||||
|
||||
// Добавляем op и requestId, чтобы они попали в NetRequest
|
||||
if (op != null) {
|
||||
merged.put("op", op);
|
||||
}
|
||||
if (requestId != null) {
|
||||
merged.put("requestId", requestId);
|
||||
}
|
||||
merged.put("op", op);
|
||||
if (requestId != null) merged.put("requestId", requestId);
|
||||
|
||||
// Добавляем все поля из payload внутрь
|
||||
merged.setAll((ObjectNode) payloadNode);
|
||||
|
||||
// 4. Маппим в конкретный класс NetRequest
|
||||
Net_Request request = JSON_MAPPER.treeToValue(merged, reqClass);
|
||||
// 4) Маппим в конкретный класс NetRequest
|
||||
Net_Request request;
|
||||
try {
|
||||
request = JSON_MAPPER.treeToValue(merged, reqClass);
|
||||
} catch (Exception mapErr) {
|
||||
// Важно: вот это часто “теряется”, если не логировать отдельно
|
||||
log.error("❌ JSON map error (op={}, requestId={}, login={}, sessionId={}): merged={}",
|
||||
op, safe(requestId), ctxLogin, ctxSessionId, shorten(merged.toString(), 1200), mapErr);
|
||||
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
op,
|
||||
requestId,
|
||||
WireCodes.Status.BAD_REQUEST,
|
||||
"BAD_REQUEST_FORMAT",
|
||||
"Некорректный формат запроса: не удалось распарсить поля payload"
|
||||
);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// DEBUG: нормализованный запрос (уже распарсен)
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("REQ OBJ (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(safeToString(request), 1200));
|
||||
}
|
||||
|
||||
// 5) Вызываем хэндлер
|
||||
Net_Response response;
|
||||
|
||||
// 5. Вызываем хэндлер
|
||||
try {
|
||||
response = handler.handle(request, ctx);
|
||||
} catch (Exception handlerError) {
|
||||
log.error("💥 Ошибка внутри хэндлера '{}'", op, handlerError);
|
||||
// ✅ Вот тут как раз и должны “появляться ошибки в логере”
|
||||
log.error("💥 Handler error (op={}, requestId={}, login={}, sessionId={})",
|
||||
op, safe(requestId), ctxLogin, ctxSessionId, handlerError);
|
||||
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
op,
|
||||
requestId,
|
||||
@ -150,18 +218,37 @@ public final class JsonInboundProcessor {
|
||||
"INTERNAL_HANDLER_ERROR",
|
||||
"Неожиданная ошибка при обработке операции: " + op
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// На всякий случай: если хэндлер не выставил op/requestId
|
||||
if (response.getOp() == null) response.setOp(op);
|
||||
if (response.getRequestId() == null) response.setRequestId(requestId);
|
||||
|
||||
// 6. Универсальная сборка ответа
|
||||
return writeResponse(response);
|
||||
// 6) Универсальная сборка ответа
|
||||
String out = writeResponse(response);
|
||||
|
||||
// DEBUG: ответ ушёл
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("RESP OBJ (login={}, sessionId={}, op={}, requestId={}, status={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), response.getStatus(), shorten(safeToString(response), 1200));
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}, status={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), response.getStatus(), shorten(out, 1200));
|
||||
}
|
||||
|
||||
return out;
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Ошибка при обработке JSON-сообщения", e);
|
||||
// ✅ Любая неожиданная ошибка парсинга/обработки — в лог
|
||||
log.error("❌ JSON processing error (op={}, requestId={}, login={}, sessionId={})",
|
||||
safe(op), safe(requestId), safe(ctxLogin), safe(ctxSessionId), e);
|
||||
|
||||
Net_Exception_Response err = NetExceptionResponseFactory.error(
|
||||
op != null ? op : "Unknown",
|
||||
requestId,
|
||||
@ -169,7 +256,15 @@ public final class JsonInboundProcessor {
|
||||
"INTERNAL_ERROR",
|
||||
"Внутренняя ошибка сервера"
|
||||
);
|
||||
return writeResponse(err);
|
||||
|
||||
String out = writeResponse(err);
|
||||
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("JSON OUT (login={}, sessionId={}, op={}, requestId={}): {}",
|
||||
ctxLogin, ctxSessionId, safe(op), safe(requestId), shorten(out, 1200));
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
}
|
||||
|
||||
@ -215,17 +310,39 @@ public final class JsonInboundProcessor {
|
||||
root.set("payload", full);
|
||||
|
||||
return JSON_MAPPER.writeValueAsString(root);
|
||||
|
||||
} catch (Exception e) {
|
||||
// Совсем аварийный случай — сериализация ответа сломалась.
|
||||
return "{\"op\":\"" + safe(response.getOp()) +
|
||||
"\",\"requestId\":\"" + safe(response.getRequestId()) +
|
||||
"\",\"status\":" + response.getStatus() +
|
||||
",\"payload\":{\"code\":\"SERIALIZATION_ERROR\",\"message\":\"" +
|
||||
"Ошибка сериализации ответа\"}}";
|
||||
log.error("❌ Response serialization error (op={}, requestId={})",
|
||||
safe(response != null ? response.getOp() : null),
|
||||
safe(response != null ? response.getRequestId() : null),
|
||||
e);
|
||||
|
||||
return "{\"op\":\"" + safe(response != null ? response.getOp() : null) +
|
||||
"\",\"requestId\":\"" + safe(response != null ? response.getRequestId() : null) +
|
||||
"\",\"status\":" + (response != null ? response.getStatus() : 500) +
|
||||
",\"payload\":{\"code\":\"SERIALIZATION_ERROR\",\"message\":\"Ошибка сериализации ответа\"}}";
|
||||
}
|
||||
}
|
||||
|
||||
private static String safe(String s) {
|
||||
return s != null ? s : "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String shorten(String s, int max) {
|
||||
if (s == null) return "";
|
||||
if (s.length() <= max) return s;
|
||||
return s.substring(0, Math.max(0, max)) + "...(+" + (s.length() - max) + " chars)";
|
||||
}
|
||||
|
||||
private static String safeToString(Object o) {
|
||||
if (o == null) return "null";
|
||||
try {
|
||||
// Чтобы не плодить огромные логи и не утыкаться в циклические ссылки —
|
||||
// логируем как JSON, если возможно.
|
||||
return JSON_MAPPER.writeValueAsString(o);
|
||||
} catch (Exception ignore) {
|
||||
return String.valueOf(o);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,8 @@
|
||||
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
||||
|
||||
import blockchain.BchBlockEntry;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import shine.db.SqliteDbController;
|
||||
import shine.db.dao.BlockchainStateDAO;
|
||||
import shine.db.dao.BlocksDAO;
|
||||
@ -28,6 +30,8 @@ import java.sql.SQLException;
|
||||
*/
|
||||
public final class BlockchainWriter {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(BlockchainWriter.class);
|
||||
|
||||
private final SqliteDbController db;
|
||||
private final BlocksDAO blocksDAO;
|
||||
private final BlockchainStateDAO stateDAO;
|
||||
@ -86,12 +90,20 @@ public final class BlockchainWriter {
|
||||
try {
|
||||
oldBytes = fs.readBlockchain(blockchainName);
|
||||
} catch (Exception e) {
|
||||
// ✅ Добавили подробный лог: это очень важная точка
|
||||
log.error("Ошибка чтения старого файла блокчейна перед записью tmp (login={}, blockchainName={}, oldFileSize={}, blockNumber={})",
|
||||
login, blockchainName, oldFileSize, block.recordNumber, e);
|
||||
|
||||
// Здесь лучше падать: state говорит, что файл есть, а прочитать нельзя.
|
||||
throw new SQLException("Cannot read old blockchain file for: " + blockchainName, e);
|
||||
}
|
||||
|
||||
// (на будущее) можно проверять согласованность: oldBytes.length == oldFileSize
|
||||
// но ты всё равно будешь делать recovery при старте — оставим как подсказку.
|
||||
if (oldBytes.length != (int) oldFileSize) {
|
||||
log.warn("Несовпадение размера файла блокчейна: state говорит oldFileSize={}, а реально прочитали oldBytes.length={} (login={}, blockchainName={}, blockNumber={})",
|
||||
oldFileSize, oldBytes.length, login, blockchainName, block.recordNumber);
|
||||
}
|
||||
|
||||
tmpBytes = concat(oldBytes, newBlockFullBytes);
|
||||
}
|
||||
@ -101,6 +113,9 @@ public final class BlockchainWriter {
|
||||
try {
|
||||
fs.writeBlockchainTmp(blockchainName, tmpBytes);
|
||||
} catch (Exception e) {
|
||||
// ✅ Добавили подробный лог: это тоже критично
|
||||
log.error("Ошибка записи tmp файла блокчейна (login={}, blockchainName={}, tmpBytesLen={}, oldFileSize={}, newFileSize={}, blockNumber={})",
|
||||
login, blockchainName, tmpBytes.length, oldFileSize, newFileSize, block.recordNumber, e);
|
||||
throw new SQLException("Cannot write tmp blockchain file for: " + blockchainName, e);
|
||||
}
|
||||
|
||||
@ -130,6 +145,10 @@ public final class BlockchainWriter {
|
||||
} catch (Exception e) {
|
||||
try { c.rollback(); } catch (SQLException ignore) {}
|
||||
|
||||
// ✅ ВАЖНО: логируем причину отката + контекст
|
||||
log.error("Ошибка транзакции БД при добавлении блока (rollback выполнен) (login={}, blockchainName={}, blockNumber={}, prevHash={}, newHash={}, oldFileSize={}, newFileSize={})",
|
||||
login, blockchainName, block.recordNumber, prevGlobalHashHex, newHashHex, oldFileSize, newFileSize, e);
|
||||
|
||||
if (e instanceof SQLException se) throw se;
|
||||
throw new SQLException("appendBlockAndState failed (db tx)", e);
|
||||
|
||||
@ -150,6 +169,10 @@ public final class BlockchainWriter {
|
||||
try {
|
||||
fs.atomicReplaceBlockchainFile(blockchainName);
|
||||
} catch (Exception moveError) {
|
||||
// ✅ Очень важная ситуация: БД уже committed, а файл не заменился
|
||||
log.error("БД закоммичена, но атомарная замена файла блокчейна не удалась. tmp оставлен для recovery. (login={}, blockchainName={}, blockNumber={}, newHash={}, tmpBytesLen={})",
|
||||
login, blockchainName, block.recordNumber, newHashHex, tmpBytes.length, moveError);
|
||||
|
||||
// Здесь ВАЖНО: мы уже не можем откатить БД.
|
||||
// Оставляем tmp и даём наверх ошибку — клиент увидит internal_error,
|
||||
// а ты при старте починишь файловую часть.
|
||||
|
||||
@ -2,6 +2,8 @@ package server.logic.ws_protocol.JSON.handlers.blockchain;
|
||||
|
||||
import blockchain.BchBlockEntry;
|
||||
import blockchain.BchCryptoVerifier;
|
||||
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;
|
||||
@ -33,6 +35,8 @@ import java.util.concurrent.locks.ReentrantLock;
|
||||
*/
|
||||
public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Net_AddBlock_Handler.class);
|
||||
|
||||
// DAO (перегрузки сами создают/закрывают Connection внутри)
|
||||
private final BlocksDAO blocksDAO = BlocksDAO.getInstance();
|
||||
private final BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();
|
||||
@ -98,12 +102,15 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
) {
|
||||
// 1) Быстрая валидация входных параметров
|
||||
if (blockchainName == null || blockchainName.isBlank()) {
|
||||
log.warn("AddBlock: пустой blockchainName (globalNumber={})", globalNumber);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "empty_blockchain_name", 0, "");
|
||||
}
|
||||
|
||||
// 2) Из имени блокчейна вытаскиваем login (как ты и хотел — через util)
|
||||
String login = BlockchainNameUtil.loginFromBlockchainName(blockchainName);
|
||||
if (login == null || login.isBlank()) {
|
||||
log.warn("AddBlock: плохой blockchainName='{}' => login не получился (globalNumber={})",
|
||||
blockchainName, globalNumber);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_blockchain_name", 0, "");
|
||||
}
|
||||
|
||||
@ -112,6 +119,8 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
blockBytes = decodeBase64(blockBytesB64);
|
||||
} catch (Exception e) {
|
||||
log.warn("AddBlock: некорректный base64 блока (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber, e);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, "");
|
||||
}
|
||||
|
||||
@ -120,6 +129,8 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
block = new BchBlockEntry(blockBytes);
|
||||
} catch (Exception e) {
|
||||
log.warn("AddBlock: не удалось распарсить BchBlockEntry (login={}, blockchainName={}, globalNumber={}, bytesLen={})",
|
||||
login, blockchainName, globalNumber, blockBytes.length, e);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_format", 0, "");
|
||||
}
|
||||
|
||||
@ -127,11 +138,15 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
block.body.check();
|
||||
} catch (Exception e) {
|
||||
log.warn("AddBlock: body.check() не прошёл (login={}, blockchainName={}, globalNumber={}, bodyType={}, bodyVersion={})",
|
||||
login, blockchainName, globalNumber, safeBodyType(block), safeBodyVersion(block), e);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_body", 0, "");
|
||||
}
|
||||
|
||||
// 6) Защита от рассинхрона: recordNumber внутри блока должен совпадать с заявленным globalNumber
|
||||
if (block.recordNumber != globalNumber) {
|
||||
log.warn("AddBlock: global_number_mismatch (login={}, blockchainName={}, заявлен={}, внутриБлока={})",
|
||||
login, blockchainName, globalNumber, block.recordNumber);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "global_number_mismatch", 0, "");
|
||||
}
|
||||
|
||||
@ -140,15 +155,22 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
u = solanaUsersDAO.getByLogin(login); // перегрузка: сама открывает/закрывает соединение
|
||||
} catch (Exception e) {
|
||||
// ✅ ВОТ ТУТ ТВОЯ ОШИБКА РАНЬШЕ ТЕРЯЛАСЬ: теперь будет stacktrace в логе
|
||||
log.error("AddBlock: ошибка БД при чтении пользователя (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber, e);
|
||||
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "db_error", 0, "");
|
||||
}
|
||||
|
||||
if (u == null) {
|
||||
log.warn("AddBlock: user_not_found (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber);
|
||||
return new AddBlockResult(WireCodes.Status.NOT_FOUND, "user_not_found", 0, "");
|
||||
}
|
||||
|
||||
byte[] loginKey32 = u.getLoginKeyByte();
|
||||
if (loginKey32 == null || loginKey32.length != 32) {
|
||||
log.warn("AddBlock: bad_user_login_key (login={}, blockchainName={}, globalNumber={}, keyLen={})",
|
||||
login, blockchainName, globalNumber, (loginKey32 == null ? -1 : loginKey32.length));
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_user_login_key", 0, "");
|
||||
}
|
||||
|
||||
@ -157,6 +179,9 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
st = stateDAO.getByBlockchainName(blockchainName); // перегрузка: сама открывает/закрывает соединение
|
||||
} catch (Exception e) {
|
||||
// ✅ ВОТ ТУТ ТВОЯ ОШИБКА РАНЬШЕ ТЕРЯЛАСЬ: теперь будет stacktrace в логе
|
||||
log.error("AddBlock: ошибка БД при чтении blockchain_state (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber, e);
|
||||
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "db_error", 0, "");
|
||||
}
|
||||
|
||||
@ -165,6 +190,8 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
final String serverLastHash;
|
||||
if (st == null) {
|
||||
if (globalNumber != 0) {
|
||||
log.warn("AddBlock: blockchain_state_not_found, но globalNumber != 0 (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber);
|
||||
return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, "");
|
||||
}
|
||||
serverLastNum = -1;
|
||||
@ -177,6 +204,8 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
// 10) Проверяем, что клиент присылает следующий блок ровно (last+1)
|
||||
int expected = serverLastNum + 1;
|
||||
if (globalNumber != expected) {
|
||||
log.warn("AddBlock: bad_global_number (login={}, blockchainName={}, пришёл={}, ожидали={}, serverLastNum={}, serverLastHash={})",
|
||||
login, blockchainName, globalNumber, expected, serverLastNum, serverLastHash);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_global_number", serverLastNum, serverLastHash);
|
||||
}
|
||||
|
||||
@ -187,10 +216,14 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
prevGlobalHash32 = hexTo32(nn(prevGlobalHashHex));
|
||||
serverPrevGlobal32 = (st == null) ? new byte[32] : hexTo32(nn(st.getLastGlobalHash()));
|
||||
} catch (Exception e) {
|
||||
log.warn("AddBlock: bad_prev_global_hash_format (login={}, blockchainName={}, globalNumber={}, prevGlobalHashHex='{}')",
|
||||
login, blockchainName, globalNumber, nn(prevGlobalHashHex), e);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_prev_global_hash_format", serverLastNum, serverLastHash);
|
||||
}
|
||||
|
||||
if (!bytesEq(prevGlobalHash32, serverPrevGlobal32)) {
|
||||
log.warn("AddBlock: bad_prev_global_hash (login={}, blockchainName={}, globalNumber={}, clientPrev='{}', serverPrev='{}')",
|
||||
login, blockchainName, globalNumber, nn(prevGlobalHashHex), nn(st != null ? st.getLastGlobalHash() : ""));
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_prev_global_hash", serverLastNum, serverLastHash);
|
||||
}
|
||||
|
||||
@ -209,6 +242,8 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
);
|
||||
|
||||
if (!ok) {
|
||||
log.warn("AddBlock: bad_signature_or_hash (login={}, blockchainName={}, globalNumber={})",
|
||||
login, blockchainName, globalNumber);
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_signature_or_hash", serverLastNum, serverLastHash);
|
||||
}
|
||||
|
||||
@ -226,10 +261,15 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
newHashHex
|
||||
);
|
||||
} catch (Exception e) {
|
||||
// ✅ ВОТ ЭТО САМОЕ ВАЖНОЕ: если упал writer/БД/файлы — теперь будет stacktrace в логах
|
||||
log.error("AddBlock: внутренняя ошибка при записи блока (login={}, blockchainName={}, globalNumber={}, newHash={})",
|
||||
login, blockchainName, globalNumber, newHashHex, e);
|
||||
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "internal_error", serverLastNum, serverLastHash);
|
||||
}
|
||||
|
||||
// 16) Успех
|
||||
log.info("✅ AddBlock ok: login={}, blockchainName={}, globalNumber={}, newHash={}",
|
||||
login, blockchainName, globalNumber, newHashHex);
|
||||
return new AddBlockResult(WireCodes.Status.OK, null, globalNumber, newHashHex);
|
||||
}
|
||||
|
||||
@ -301,4 +341,12 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
}
|
||||
return new String(out);
|
||||
}
|
||||
|
||||
private static String safeBodyType(BchBlockEntry b) {
|
||||
try { return String.valueOf(b.body.type()); } catch (Exception e) { return "unknown"; }
|
||||
}
|
||||
|
||||
private static String safeBodyVersion(BchBlockEntry b) {
|
||||
try { return String.valueOf(b.body.version()); } catch (Exception e) { return "unknown"; }
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user