19 02 25
сделал единый формат протокола в случае ошибок (Наверное сделал удобнее)
This commit is contained in:
parent
c7440e2b5c
commit
37c36ffdba
@ -9,6 +9,7 @@ import org.slf4j.Logger;
|
|||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import server.logic.ws_protocol.Base64Ws;
|
import server.logic.ws_protocol.Base64Ws;
|
||||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||||
|
import server.logic.ws_protocol.JSON.entyties.Net_Exception_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;
|
||||||
@ -29,21 +30,10 @@ import java.util.concurrent.locks.ReentrantLock;
|
|||||||
/**
|
/**
|
||||||
* Net_AddBlock_Handler — единый хэндлер добавления блока (JSON).
|
* Net_AddBlock_Handler — единый хэндлер добавления блока (JSON).
|
||||||
*
|
*
|
||||||
* Новый порядок валидации (ТЗ):
|
* Изменение (v3):
|
||||||
* 1) Достаём из blockchain_state: last_block_number, last_block_hash
|
* - ВСЕ ошибки теперь возвращаются в стандартном формате Net_Exception_Response:
|
||||||
* 2) Проверяем:
|
* status != 200, payload: { code, message, serverLastGlobalNumber, serverLastGlobalHash }
|
||||||
* - incoming.blockNumber == last+1
|
* - Успех — как и раньше Net_AddBlock_Response (status=200).
|
||||||
* - incoming.prevHash32 == last_hash (для genesis last_hash = 32 нулей)
|
|
||||||
* 3) Проверяем подпись Ed25519.verify(hash32(preimage), signature64, pubKey)
|
|
||||||
* 4) Если тип имеет линию:
|
|
||||||
* - если prevLineNumber != null:
|
|
||||||
* достаём hash блока prevLineNumber из blocks
|
|
||||||
* сравниваем с prevLineHash32 из body
|
|
||||||
* 5) Сохраняем блок в blocks + обновляем blockchain_state
|
|
||||||
*
|
|
||||||
* Важно:
|
|
||||||
* - Сетевой протокол AddBlock пока оставляем старые поля (globalNumber/prevGlobalHash),
|
|
||||||
* но внутренняя логика использует НОВЫЙ формат блока.
|
|
||||||
*/
|
*/
|
||||||
public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||||
|
|
||||||
@ -70,28 +60,79 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
|||||||
req.getBlockBytesB64()
|
req.getBlockBytesB64()
|
||||||
);
|
);
|
||||||
|
|
||||||
Net_AddBlock_Response resp = new Net_AddBlock_Response();
|
// ✅ УСПЕХ: как раньше
|
||||||
resp.setOp(req.getOp());
|
|
||||||
resp.setRequestId(req.getRequestId());
|
|
||||||
|
|
||||||
if (r.isOk()) {
|
if (r.isOk()) {
|
||||||
|
Net_AddBlock_Response resp = new Net_AddBlock_Response();
|
||||||
|
resp.setOp(req.getOp());
|
||||||
|
resp.setRequestId(req.getRequestId());
|
||||||
resp.setStatus(WireCodes.Status.OK);
|
resp.setStatus(WireCodes.Status.OK);
|
||||||
|
|
||||||
resp.setReasonCode(null);
|
resp.setReasonCode(null);
|
||||||
} else {
|
resp.setServerLastGlobalNumber(r.serverLastBlockNumber);
|
||||||
resp.setStatus(r.httpStatus);
|
resp.setServerLastGlobalHash(r.serverLastBlockHashHex);
|
||||||
resp.setReasonCode(r.reasonCode);
|
|
||||||
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
resp.setServerLastGlobalNumber(r.serverLastBlockNumber);
|
// ✅ ОШИБКА: стандартный формат (code + message) + доп.поля для ресинка
|
||||||
resp.setServerLastGlobalHash(r.serverLastBlockHashHex);
|
return error(req, r.httpStatus, r.reasonCode, r.serverLastBlockNumber, r.serverLastBlockHashHex);
|
||||||
|
|
||||||
return resp;
|
|
||||||
|
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Net_Response error(Net_AddBlock_Request req,
|
||||||
|
int status,
|
||||||
|
String reasonCode,
|
||||||
|
int serverLastNum,
|
||||||
|
String serverLastHashHex) {
|
||||||
|
|
||||||
|
AddBlockExceptionResponse resp = new AddBlockExceptionResponse();
|
||||||
|
resp.setOp(req.getOp());
|
||||||
|
resp.setRequestId(req.getRequestId());
|
||||||
|
resp.setStatus(status);
|
||||||
|
|
||||||
|
// code — машинный
|
||||||
|
resp.setCode(reasonCode != null ? reasonCode : "add_block_error");
|
||||||
|
// message — человеческий (можешь улучшать тексты как угодно)
|
||||||
|
resp.setMessage(humanMessage(reasonCode));
|
||||||
|
|
||||||
|
// полезно клиенту для ресинка
|
||||||
|
resp.setServerLastGlobalNumber(serverLastNum);
|
||||||
|
resp.setServerLastGlobalHash(serverLastHashHex);
|
||||||
|
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String humanMessage(String code) {
|
||||||
|
if (code == null) return "Ошибка добавления блока";
|
||||||
|
|
||||||
|
return switch (code) {
|
||||||
|
case "empty_blockchain_name" -> "Пустое имя блокчейна";
|
||||||
|
case "bad_blockchain_name" -> "Некорректное имя блокчейна";
|
||||||
|
case "db_error" -> "Ошибка базы данных";
|
||||||
|
case "blockchain_state_not_found" -> "Состояние блокчейна не найдено";
|
||||||
|
case "state_last_hash_invalid" -> "Повреждено состояние блокчейна: неверный last_block_hash";
|
||||||
|
case "bad_block_base64" -> "Некорректный base64 блока";
|
||||||
|
case "limit_exceeded" -> "Превышен лимит размера блокчейна";
|
||||||
|
case "limit_check_failed" -> "Ошибка проверки лимита размера";
|
||||||
|
case "bad_block_format" -> "Некорректный формат блока";
|
||||||
|
case "bad_block_body" -> "Некорректное тело блока";
|
||||||
|
case "bad_block_number" -> "Некорректный номер блока";
|
||||||
|
case "req_global_mismatch" -> "Номер блока в запросе не совпадает с номером в блоке";
|
||||||
|
case "bad_prev_hash" -> "Некорректный prevHash (цепочка не совпадает)";
|
||||||
|
case "bad_blockchain_key_len" -> "Некорректный ключ блокчейна в состоянии (ожидалось 32 байта)";
|
||||||
|
case "signature_verify_failed" -> "Ошибка проверки подписи блока";
|
||||||
|
case "bad_signature" -> "Некорректная подпись блока";
|
||||||
|
case "prev_line_block_not_found" -> "Не найден блок prevLineNumber для проверки линии";
|
||||||
|
case "bad_prev_line_hash" -> "Некорректный prevLineHash";
|
||||||
|
case "db_error_prev_line_check" -> "Ошибка БД при проверке prevLine";
|
||||||
|
case "internal_error" -> "Внутренняя ошибка сервера при записи блока";
|
||||||
|
default -> "Ошибка: " + code;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private AddBlockResult addBlock(
|
private AddBlockResult addBlock(
|
||||||
String blockchainName,
|
String blockchainName,
|
||||||
int globalNumberFromReq,
|
int globalNumberFromReq,
|
||||||
@ -127,9 +168,18 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
final int serverLastNum = st.getLastBlockNumber();
|
final int serverLastNum = st.getLastBlockNumber();
|
||||||
final byte[] serverLastHash32 = (serverLastNum < 0)
|
|
||||||
? new byte[32]
|
final byte[] serverLastHash32;
|
||||||
: require32OrThrow(st.getLastBlockHash(), "state.last_block_hash is null/invalid");
|
try {
|
||||||
|
serverLastHash32 = (serverLastNum < 0)
|
||||||
|
? new byte[32]
|
||||||
|
: require32OrThrow(st.getLastBlockHash(), "state.last_block_hash is null/invalid");
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ✅ Раньше тут мог вылететь неожиданный 500 через внешний try/catch.
|
||||||
|
log.error("AddBlock: state_last_hash_invalid (login={}, blockchainName={}, serverLastNum={})",
|
||||||
|
login, blockchainName, serverLastNum, e);
|
||||||
|
return new AddBlockResult(WireCodes.Status.INTERNAL_ERROR, "state_last_hash_invalid", serverLastNum, "");
|
||||||
|
}
|
||||||
|
|
||||||
final String serverLastHashHex = toHex(serverLastHash32);
|
final String serverLastHashHex = toHex(serverLastHash32);
|
||||||
|
|
||||||
@ -215,7 +265,7 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.warn("AddBlock: signature_verify_failed (login={}, blockchainName={}, blockNumber={})",
|
log.warn("AddBlock: signature_verify_failed (login={}, blockchainName={}, blockNumber={})",
|
||||||
login, blockchainName, block.blockNumber, e);
|
login, blockchainName, block.blockNumber, e);
|
||||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_signature", serverLastNum, serverLastHashHex);
|
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "signature_verify_failed", serverLastNum, serverLastHashHex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!sigOk) {
|
if (!sigOk) {
|
||||||
@ -351,6 +401,31 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
|||||||
return new String(out);
|
return new String(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Спец-ответ ошибки AddBlock: стандартный code/message + поля для ресинка.
|
||||||
|
* В wire-формате это окажется внутри payload.
|
||||||
|
*/
|
||||||
|
public static final class AddBlockExceptionResponse extends Net_Exception_Response {
|
||||||
|
private Integer serverLastGlobalNumber;
|
||||||
|
private String serverLastGlobalHash;
|
||||||
|
|
||||||
|
public Integer getServerLastGlobalNumber() {
|
||||||
|
return serverLastGlobalNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerLastGlobalNumber(Integer serverLastGlobalNumber) {
|
||||||
|
this.serverLastGlobalNumber = serverLastGlobalNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getServerLastGlobalHash() {
|
||||||
|
return serverLastGlobalHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setServerLastGlobalHash(String serverLastGlobalHash) {
|
||||||
|
this.serverLastGlobalHash = serverLastGlobalHash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class AddBlockResult {
|
private static final class AddBlockResult {
|
||||||
final int httpStatus;
|
final int httpStatus;
|
||||||
final String reasonCode;
|
final String reasonCode;
|
||||||
@ -366,4 +441,4 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
|||||||
|
|
||||||
boolean isOk() { return httpStatus == WireCodes.Status.OK; }
|
boolean isOk() { return httpStatus == WireCodes.Status.OK; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,52 @@
|
|||||||
|
////package server.logic.ws_protocol.JSON.utils;
|
||||||
|
//
|
||||||
|
//import shine.db.entities.SolanaUserEntry;
|
||||||
|
//import utils.crypto.Ed25519Util;
|
||||||
|
//
|
||||||
|
//import java.nio.charset.StandardCharsets;
|
||||||
|
//import java.util.Base64;
|
||||||
|
//
|
||||||
|
//public final class AuthSignatures {
|
||||||
|
//
|
||||||
|
// private AuthSignatures() {}
|
||||||
|
//
|
||||||
|
// /** preimage для CreateAuthSession(v2): "AUTH_CREATE_SESSION:login:timeMs:authNonce" */
|
||||||
|
// public static byte[] preimageCreateAuthSession(String login, long timeMs, String authNonce) {
|
||||||
|
// String preimageStr = "AUTH_CREATE_SESSION:" + login + ":" + timeMs + ":" + authNonce;
|
||||||
|
// return preimageStr.getBytes(StandardCharsets.UTF_8);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /** Декод base64 / base64url (если надо — подстрой под твой decodeBase64Any) */
|
||||||
|
// public static byte[] decodeBase64Any(String s) throws IllegalArgumentException {
|
||||||
|
// if (s == null) throw new IllegalArgumentException("base64 is null");
|
||||||
|
// String x = s.trim();
|
||||||
|
// if (x.isEmpty()) throw new IllegalArgumentException("base64 is empty");
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// return Base64.getDecoder().decode(x);
|
||||||
|
// } catch (IllegalArgumentException e1) {
|
||||||
|
// // пробуем base64url без паддинга
|
||||||
|
// return Base64.getUrlDecoder().decode(x);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * Проверка подписи CreateAuthSession(v2) по deviceKey пользователя.
|
||||||
|
// * Подпись проверяется над preimageCreateAuthSession(...).
|
||||||
|
// */
|
||||||
|
// public static boolean verifyCreateAuthSessionSignature(
|
||||||
|
// SolanaUserEntry user,
|
||||||
|
// String login,
|
||||||
|
// String authNonce,
|
||||||
|
// long timeMs,
|
||||||
|
// String signatureB64
|
||||||
|
// ) throws IllegalArgumentException {
|
||||||
|
//
|
||||||
|
// // user.getDeviceKey() — base64 публичного ключа (32 байта)
|
||||||
|
// byte[] publicKey32 = decodeBase64Any(user.getDeviceKey());
|
||||||
|
// byte[] signature64 = decodeBase64Any(signatureB64);
|
||||||
|
//
|
||||||
|
// byte[] preimage = preimageCreateAuthSession(login, timeMs, authNonce);
|
||||||
|
// return Ed25519Util.verify(preimage, signature64, publicKey32);
|
||||||
|
// }
|
||||||
|
//}
|
||||||
Loading…
Reference in New Issue
Block a user