SHA256
17 12 25
Промежуточная версия
This commit is contained in:
+16
-16
@@ -1,8 +1,8 @@
|
||||
package server.logic.ws_protocol.JSON;
|
||||
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.ActiveSession;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
import shine.db.entities.ActiveSessionEntry;
|
||||
|
||||
/**
|
||||
* ConnectionContext — контекст состояния одного WebSocket-соединения.
|
||||
@@ -16,10 +16,10 @@ public class ConnectionContext {
|
||||
public static final int AUTH_STATUS_USER = 2; // авторизованный пользователь
|
||||
|
||||
// Полный пользователь из БД (solana_users)
|
||||
private SolanaUser solanaUser;
|
||||
private SolanaUserEntry solanaUserEntry;
|
||||
|
||||
// Активная сессия из БД (active_sessions)
|
||||
private ActiveSession activeSession;
|
||||
private ActiveSessionEntry activeSessionEntry;
|
||||
|
||||
/**
|
||||
* Идентификатор сессии — base64-строка от 32 байт.
|
||||
@@ -61,30 +61,30 @@ public class ConnectionContext {
|
||||
|
||||
// --- SolanaUser / ActiveSession ---
|
||||
|
||||
public SolanaUser getSolanaUser() {
|
||||
return solanaUser;
|
||||
public SolanaUserEntry getSolanaUser() {
|
||||
return solanaUserEntry;
|
||||
}
|
||||
|
||||
public void setSolanaUser(SolanaUser solanaUser) {
|
||||
this.solanaUser = solanaUser;
|
||||
public void setSolanaUser(SolanaUserEntry solanaUserEntry) {
|
||||
this.solanaUserEntry = solanaUserEntry;
|
||||
}
|
||||
|
||||
public ActiveSession getActiveSession() {
|
||||
return activeSession;
|
||||
public ActiveSessionEntry getActiveSession() {
|
||||
return activeSessionEntry;
|
||||
}
|
||||
|
||||
public void setActiveSession(ActiveSession activeSession) {
|
||||
this.activeSession = activeSession;
|
||||
public void setActiveSession(ActiveSessionEntry activeSessionEntry) {
|
||||
this.activeSessionEntry = activeSessionEntry;
|
||||
}
|
||||
|
||||
// --- Удобные геттеры для логина ---
|
||||
|
||||
public String getLogin() {
|
||||
return solanaUser != null ? solanaUser.getLogin() : null;
|
||||
return solanaUserEntry != null ? solanaUserEntry.getLogin() : null;
|
||||
}
|
||||
|
||||
public Long getLoginId() {
|
||||
return solanaUser != null ? solanaUser.getLoginId() : null;
|
||||
return solanaUserEntry != null ? solanaUserEntry.getLoginId() : null;
|
||||
}
|
||||
|
||||
// --- sessionId / sessionPwd ---
|
||||
@@ -134,8 +134,8 @@ public class ConnectionContext {
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
solanaUser = null;
|
||||
activeSession = null;
|
||||
solanaUserEntry = null;
|
||||
activeSessionEntry = null;
|
||||
|
||||
sessionId = null;
|
||||
sessionPwd = null;
|
||||
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
package server.logic.ws_protocol.JSON.entyties.Blockchain;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
|
||||
public class Net_AddBlock_new_Request extends Net_Request {
|
||||
|
||||
private long blockchainId;
|
||||
|
||||
private int globalNumber;
|
||||
private String prevGlobalHash; // HEX(64) or ""
|
||||
|
||||
private int lineNumber; // 0..7
|
||||
private int lineBlockNumber;
|
||||
private String prevLineHash; // HEX(64) or ""
|
||||
|
||||
private String blockBase64; // base64url of raw .bch bytes
|
||||
|
||||
public long getBlockchainId() { return blockchainId; }
|
||||
public void setBlockchainId(long blockchainId) { this.blockchainId = blockchainId; }
|
||||
|
||||
public int getGlobalNumber() { return globalNumber; }
|
||||
public void setGlobalNumber(int globalNumber) { this.globalNumber = globalNumber; }
|
||||
|
||||
public String getPrevGlobalHash() { return prevGlobalHash; }
|
||||
public void setPrevGlobalHash(String prevGlobalHash) { this.prevGlobalHash = prevGlobalHash; }
|
||||
|
||||
public int getLineNumber() { return lineNumber; }
|
||||
public void setLineNumber(int lineNumber) { this.lineNumber = lineNumber; }
|
||||
|
||||
public int getLineBlockNumber() { return lineBlockNumber; }
|
||||
public void setLineBlockNumber(int lineBlockNumber) { this.lineBlockNumber = lineBlockNumber; }
|
||||
|
||||
public String getPrevLineHash() { return prevLineHash; }
|
||||
public void setPrevLineHash(String prevLineHash) { this.prevLineHash = prevLineHash; }
|
||||
|
||||
public String getBlockBase64() { return blockBase64; }
|
||||
public void setBlockBase64(String blockBase64) { this.blockBase64 = blockBase64; }
|
||||
}
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
package server.logic.ws_protocol.JSON.entyties.Blockchain;
|
||||
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
|
||||
public class Net_AddBlock_new_Response extends Net_Response {
|
||||
|
||||
private int serverLastGlobalNumber;
|
||||
private String serverLastGlobalHash;
|
||||
|
||||
private int serverLastLineNumber;
|
||||
private String serverLastLineHash;
|
||||
|
||||
private String reasonCode; // "OUT_OF_SEQUENCE", "HASH_MISMATCH", ...
|
||||
|
||||
public int getServerLastGlobalNumber() { return serverLastGlobalNumber; }
|
||||
public void setServerLastGlobalNumber(int v) { this.serverLastGlobalNumber = v; }
|
||||
|
||||
public String getServerLastGlobalHash() { return serverLastGlobalHash; }
|
||||
public void setServerLastGlobalHash(String v) { this.serverLastGlobalHash = v; }
|
||||
|
||||
public int getServerLastLineNumber() { return serverLastLineNumber; }
|
||||
public void setServerLastLineNumber(int v) { this.serverLastLineNumber = v; }
|
||||
|
||||
public String getServerLastLineHash() { return serverLastLineHash; }
|
||||
public void setServerLastLineHash(String v) { this.serverLastLineHash = v; }
|
||||
|
||||
public String getReasonCode() { return reasonCode; }
|
||||
public void setReasonCode(String reasonCode) { this.reasonCode = reasonCode; }
|
||||
}
|
||||
+4
-4
@@ -8,7 +8,7 @@ import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
@@ -49,9 +49,9 @@ public class Net_AuthChallenge_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// 2) Ищем пользователя в локальной БД
|
||||
SolanaUser solanaUser = SolanaUsersDAO.getInstance().getByLogin(login);
|
||||
SolanaUserEntry solanaUserEntry = SolanaUsersDAO.getInstance().getByLogin(login);
|
||||
|
||||
if (solanaUser == null) {
|
||||
if (solanaUserEntry == null) {
|
||||
return NetExceptionResponseFactory.error(
|
||||
req,
|
||||
WireCodes.Status.UNVERIFIED,
|
||||
@@ -61,7 +61,7 @@ public class Net_AuthChallenge_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// 3) Заполняем контекст пользователем
|
||||
ctx.setSolanaUser(solanaUser);
|
||||
ctx.setSolanaUser(solanaUserEntry);
|
||||
|
||||
// 3.1) Отмечаем, что по этому соединению начата авторификация
|
||||
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_AUTH_IN_PROGRESS);
|
||||
|
||||
+4
-4
@@ -13,8 +13,8 @@ import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import server.ws.WsConnectionUtils;
|
||||
import shine.db.dao.ActiveSessionsDAO;
|
||||
import shine.db.entities.ActiveSession;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.ActiveSessionEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -62,7 +62,7 @@ public class Net_CloseActiveSession_Handler implements JsonMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
SolanaUser user = ctx.getSolanaUser();
|
||||
SolanaUserEntry user = ctx.getSolanaUser();
|
||||
long currentLoginId = user.getLoginId();
|
||||
|
||||
int authStatus = ctx.getAuthenticationStatus();
|
||||
@@ -158,7 +158,7 @@ public class Net_CloseActiveSession_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
ActiveSessionsDAO sessionsDao = ActiveSessionsDAO.getInstance();
|
||||
ActiveSession targetSession;
|
||||
ActiveSessionEntry targetSession;
|
||||
try {
|
||||
targetSession = sessionsDao.getBySessionId(targetSessionId);
|
||||
} catch (SQLException e) {
|
||||
|
||||
+8
-8
@@ -14,8 +14,8 @@ import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import server.ws.WsConnectionUtils;
|
||||
import shine.db.dao.ActiveSessionsDAO;
|
||||
import shine.db.entities.ActiveSession;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.ActiveSessionEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
import shine.geo.ClientInfoService;
|
||||
import shine.geo.GeoLookupService;
|
||||
import utils.crypto.Ed25519Util;
|
||||
@@ -72,7 +72,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
|
||||
* @throws IllegalArgumentException при некорректном base64 ключа/подписи
|
||||
*/
|
||||
public static boolean verifyAuthorificatedSignature(
|
||||
SolanaUser user,
|
||||
SolanaUserEntry user,
|
||||
String authNonce,
|
||||
long timeMs,
|
||||
String signatureB64
|
||||
@@ -108,7 +108,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
|
||||
return err;
|
||||
}
|
||||
|
||||
SolanaUser user = ctx.getSolanaUser();
|
||||
SolanaUserEntry user = ctx.getSolanaUser();
|
||||
Long loginId = user.getLoginId();
|
||||
if (loginId == null) {
|
||||
Net_Response err = NetExceptionResponseFactory.error(
|
||||
@@ -237,10 +237,10 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
|
||||
|
||||
// --- создаём запись ActiveSession и сохраняем в БД ---
|
||||
ActiveSessionsDAO dao = ActiveSessionsDAO.getInstance();
|
||||
ActiveSession activeSession;
|
||||
ActiveSessionEntry activeSessionEntry;
|
||||
|
||||
try {
|
||||
activeSession = new ActiveSession(
|
||||
activeSessionEntry = new ActiveSessionEntry(
|
||||
sessionId,
|
||||
loginId,
|
||||
newSessionPwd, // настоящий секрет сессии
|
||||
@@ -256,7 +256,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
|
||||
userLanguage
|
||||
);
|
||||
|
||||
dao.insert(activeSession);
|
||||
dao.insert(activeSessionEntry);
|
||||
} catch (SQLException e) {
|
||||
log.error("Ошибка БД при создании новой сессии для loginId={}", loginId, e);
|
||||
Net_Response err = NetExceptionResponseFactory.error(
|
||||
@@ -270,7 +270,7 @@ public class Net_CreateAuthSession__Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// --- обновляем контекст ---
|
||||
ctx.setActiveSession(activeSession);
|
||||
ctx.setActiveSession(activeSessionEntry);
|
||||
ctx.setSessionId(sessionId);
|
||||
ctx.setSessionPwd(newSessionPwd); // теперь в контексте хранится секрет сессии
|
||||
ctx.setAuthNonce(null); // одноразовый nonce больше не нужен
|
||||
|
||||
+5
-5
@@ -12,8 +12,8 @@ import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.ActiveSessionsDAO;
|
||||
import shine.db.entities.ActiveSession;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.ActiveSessionEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
import shine.geo.GeoLookupService;
|
||||
|
||||
import java.sql.SQLException;
|
||||
@@ -50,7 +50,7 @@ public class Net_ListSessions_Handler implements JsonMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
SolanaUser user = ctx.getSolanaUser();
|
||||
SolanaUserEntry user = ctx.getSolanaUser();
|
||||
long currentLoginId = user.getLoginId();
|
||||
|
||||
int authStatus = ctx.getAuthenticationStatus();
|
||||
@@ -128,7 +128,7 @@ public class Net_ListSessions_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// 3) Тянем все активные сессии пользователя из БД
|
||||
List<ActiveSession> sessions;
|
||||
List<ActiveSessionEntry> sessions;
|
||||
try {
|
||||
sessions = ActiveSessionsDAO.getInstance().getByLoginId(currentLoginId);
|
||||
} catch (SQLException e) {
|
||||
@@ -143,7 +143,7 @@ public class Net_ListSessions_Handler implements JsonMessageHandler {
|
||||
|
||||
// 4) Собираем DTO с геолокацией
|
||||
List<SessionInfo> resultList = new ArrayList<>();
|
||||
for (ActiveSession s : sessions) {
|
||||
for (ActiveSessionEntry s : sessions) {
|
||||
SessionInfo info = new SessionInfo();
|
||||
info.setSessionId(s.getSessionId());
|
||||
info.setClientInfoFromClient(s.getClientInfoFromClient());
|
||||
|
||||
+7
-7
@@ -13,8 +13,8 @@ import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.ActiveSessionsDAO;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.ActiveSession;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.ActiveSessionEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
import shine.geo.ClientInfoService;
|
||||
import shine.geo.GeoLookupService;
|
||||
|
||||
@@ -63,7 +63,7 @@ public class Net_RefreshSession_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
ActiveSessionsDAO sessionsDao = ActiveSessionsDAO.getInstance();
|
||||
ActiveSession session;
|
||||
ActiveSessionEntry session;
|
||||
try {
|
||||
session = sessionsDao.getBySessionId(sessionId);
|
||||
} catch (SQLException e) {
|
||||
@@ -96,11 +96,11 @@ public class Net_RefreshSession_Handler implements JsonMessageHandler {
|
||||
}
|
||||
|
||||
// --- вытаскиваем пользователя по loginId ---
|
||||
SolanaUser solanaUser = null;
|
||||
SolanaUserEntry solanaUserEntry = null;
|
||||
long loginId = session.getLoginId();
|
||||
try {
|
||||
SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
||||
solanaUser = usersDao.getByLoginId(loginId);
|
||||
solanaUserEntry = usersDao.getByLoginId(loginId);
|
||||
} catch (SQLException e) {
|
||||
log.error("Ошибка БД при поиске пользователя по loginId={} из сессии", loginId, e);
|
||||
return NetExceptionResponseFactory.error(
|
||||
@@ -111,7 +111,7 @@ public class Net_RefreshSession_Handler implements JsonMessageHandler {
|
||||
);
|
||||
}
|
||||
|
||||
if (solanaUser == null) {
|
||||
if (solanaUserEntry == null) {
|
||||
return NetExceptionResponseFactory.error(
|
||||
req,
|
||||
WireCodes.Status.UNVERIFIED,
|
||||
@@ -171,7 +171,7 @@ public class Net_RefreshSession_Handler implements JsonMessageHandler {
|
||||
// --- обновляем контекст соединения ---
|
||||
if (ctx != null) {
|
||||
ctx.setActiveSession(session);
|
||||
ctx.setSolanaUser(solanaUser);
|
||||
ctx.setSolanaUser(solanaUserEntry);
|
||||
ctx.setSessionId(sessionId);
|
||||
ctx.setSessionPwd(sessionPwd);
|
||||
ctx.setAuthenticationStatus(ConnectionContext.AUTH_STATUS_USER);
|
||||
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
||||
|
||||
import blockchain.BchBlockEntry;
|
||||
import blockchain.BodyRecordParser;
|
||||
import blockchain.body.BodyRecord;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import server.logic.ws_protocol.JSON.ConnectionContext;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Request;
|
||||
import server.logic.ws_protocol.JSON.entyties.Net_Response;
|
||||
import server.logic.ws_protocol.JSON.entyties.Blockchain.Net_AddBlock_new_Request;
|
||||
import server.logic.ws_protocol.JSON.entyties.Blockchain.Net_AddBlock_new_Response;
|
||||
import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.BlockchainStateDAO;
|
||||
import shine.db.entities.BlockchainStateEntry;
|
||||
import utils.crypto.BchCryptoVerifier;
|
||||
import utils.files.FileStoreUtil;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
public class Net_AddBlock_new_Handler implements JsonMessageHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(Net_AddBlock_new_Handler.class);
|
||||
|
||||
@Override
|
||||
public Net_Response handle(Net_Request baseReq, ConnectionContext ctx) throws Exception {
|
||||
|
||||
Net_AddBlock_new_Request req = (Net_AddBlock_new_Request) baseReq;
|
||||
|
||||
// 0) базовые проверки
|
||||
if (req.getBlockchainId() <= 0) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_BLOCKCHAIN_ID", "blockchainId <= 0");
|
||||
}
|
||||
if (req.getGlobalNumber() < 0) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_GLOBAL_NUMBER", "globalNumber < 0");
|
||||
}
|
||||
if (req.getLineNumber() < 0 || req.getLineNumber() > 7) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_LINE_NUMBER", "lineNumber must be 0..7");
|
||||
}
|
||||
if (req.getLineBlockNumber() < 0) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_LINE_BLOCK_NUMBER", "lineBlockNumber < 0");
|
||||
}
|
||||
if (req.getBlockBase64() == null || req.getBlockBase64().isBlank()) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "EMPTY_BLOCK", "blockBase64 is empty");
|
||||
}
|
||||
|
||||
// 1) грузим состояние из БД
|
||||
BlockchainStateDAO dao = BlockchainStateDAO.getInstance();
|
||||
BlockchainStateEntry state = dao.getByBlockchainId(req.getBlockchainId());
|
||||
if (state == null) {
|
||||
// на MVP можно: запретить добавление, пока цепочка не создана отдельно
|
||||
// либо разрешить только genesis/header — как ты делал раньше
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.CHAIN_NOT_FOUND, "CHAIN_NOT_FOUND", "chain not found in DB");
|
||||
}
|
||||
|
||||
// 2) быстрые проверки на “подходит ли блок”
|
||||
int expectedGlobal = state.getLastGlobalNumber() + 1;
|
||||
int expectedLine = state.getLastLineNumber(req.getLineNumber()) + 1;
|
||||
|
||||
String dbPrevGlobalHash = nn(state.getLastGlobalHash());
|
||||
String dbPrevLineHash = nn(state.getLastLineHash(req.getLineNumber()));
|
||||
|
||||
if (req.getGlobalNumber() != expectedGlobal) {
|
||||
return outOfSeq(req, state, req.getLineNumber(), "OUT_OF_SEQUENCE_GLOBAL");
|
||||
}
|
||||
if (!eqHash(req.getPrevGlobalHash(), dbPrevGlobalHash)) {
|
||||
return outOfSeq(req, state, req.getLineNumber(), "GLOBAL_HASH_MISMATCH");
|
||||
}
|
||||
if (req.getLineBlockNumber() != expectedLine) {
|
||||
return outOfSeq(req, state, req.getLineNumber(), "OUT_OF_SEQUENCE_LINE");
|
||||
}
|
||||
if (!eqHash(req.getPrevLineHash(), dbPrevLineHash)) {
|
||||
return outOfSeq(req, state, req.getLineNumber(), "LINE_HASH_MISMATCH");
|
||||
}
|
||||
|
||||
// 3) декодируем блок
|
||||
byte[] fullBlockBytes;
|
||||
try {
|
||||
fullBlockBytes = Base64.getUrlDecoder().decode(req.getBlockBase64());
|
||||
} catch (IllegalArgumentException e) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_BASE64", "blockBase64 decode failed");
|
||||
}
|
||||
|
||||
// 4) парсим .bch
|
||||
BchBlockEntry block;
|
||||
try {
|
||||
block = new BchBlockEntry(fullBlockBytes);
|
||||
} catch (Exception e) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.BAD_REQUEST, "BAD_BLOCK_FORMAT", "cannot parse BchBlockEntry");
|
||||
}
|
||||
|
||||
// 5) ПОЛНАЯ валидация: подпись/хэш/тело
|
||||
// ⚠️ ниже я оставляю общий вызов verifyAll как у тебя раньше,
|
||||
// но теперь prevHash берём из БД, а publicKey — из state (или из solana_users).
|
||||
byte[] prevHashGlobal32 = hexToBytes32(dbPrevGlobalHash);
|
||||
|
||||
boolean verified = BchCryptoVerifier.verifyAll(
|
||||
state.getUserLogin(),
|
||||
req.getBlockchainId(),
|
||||
prevHashGlobal32,
|
||||
block.rawBytes,
|
||||
block.getSignature64(),
|
||||
block.getHash32(),
|
||||
Base64.getDecoder().decode(state.getPublicKeyBase64())
|
||||
);
|
||||
|
||||
if (!verified) {
|
||||
return NetExceptionResponseFactory.error(req, WireCodes.Status.UNVERIFIED, "UNVERIFIED", "signature/hash verification failed");
|
||||
}
|
||||
|
||||
// Проверка тела блока
|
||||
BodyRecord body = BodyRecordParser.parse(block.recordType, block.recordTypeVersion, block.body).check();
|
||||
|
||||
// 6) TODO: извлечь lineNumber/lineBlockNumber/prevLineHash из body (если они реально в теле есть)
|
||||
// и сверить с req + DB. Сейчас оставляю как “крючок”.
|
||||
// BlockLineMeta meta = BlockLineMetaExtractor.extract(body);
|
||||
// if (meta.lineNumber != req.getLineNumber()) ...
|
||||
// if (meta.lineBlockNumber != req.getLineBlockNumber()) ...
|
||||
// if (!eqHash(meta.prevLineHashHex, dbPrevLineHash)) ...
|
||||
|
||||
// 7) запись в файл (фактическое хранение блоков)
|
||||
FileStoreUtil.getInstance().addDataToBlockchain(req.getBlockchainId(), fullBlockBytes);
|
||||
|
||||
// 8) TODO: обновление состояния в БД (вместо BchInfoManager)
|
||||
// - state.sizeBytes += fullBlockBytes.length
|
||||
// - state.lastGlobalNumber = req.globalNumber
|
||||
// - state.lastGlobalHash = bytesToHex(block.getHash32())
|
||||
// - state.lineX_last_number/hash обновить по lineNumber
|
||||
// - state.updatedAtMs = now
|
||||
// dao.upsert(state);
|
||||
|
||||
// 9) ответ OK
|
||||
Net_AddBlock_new_Response resp = new Net_AddBlock_new_Response();
|
||||
resp.setOp(req.getOp());
|
||||
resp.setRequestId(req.getRequestId());
|
||||
resp.setStatus(WireCodes.Status.OK);
|
||||
|
||||
// можно вернуть “новое” состояние, но на MVP вернём хотя бы серверные last’ы до апдейта/после апдейта
|
||||
resp.setServerLastGlobalNumber(req.getGlobalNumber());
|
||||
resp.setServerLastGlobalHash(bytesToHex(block.getHash32()));
|
||||
resp.setServerLastLineNumber(req.getLineBlockNumber());
|
||||
resp.setServerLastLineHash(resp.getServerLastGlobalHash());
|
||||
resp.setReasonCode(null);
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static Net_AddBlock_new_Response outOfSeq(Net_AddBlock_new_Request req, BlockchainStateEntry state, int line, String reason) {
|
||||
Net_AddBlock_new_Response resp = new Net_AddBlock_new_Response();
|
||||
resp.setOp(req.getOp());
|
||||
resp.setRequestId(req.getRequestId());
|
||||
resp.setStatus(WireCodes.Status.OUT_OF_SEQUENCE); // или свой статус
|
||||
resp.setReasonCode(reason);
|
||||
|
||||
resp.setServerLastGlobalNumber(state.getLastGlobalNumber());
|
||||
resp.setServerLastGlobalHash(nn(state.getLastGlobalHash()));
|
||||
|
||||
resp.setServerLastLineNumber(state.getLastLineNumber(line));
|
||||
resp.setServerLastLineHash(nn(state.getLastLineHash(line)));
|
||||
|
||||
return resp;
|
||||
}
|
||||
|
||||
private static boolean eqHash(String a, String b) {
|
||||
return nn(a).equalsIgnoreCase(nn(b));
|
||||
}
|
||||
|
||||
private static String nn(String s) { return s == null ? "" : s.trim(); }
|
||||
|
||||
private static byte[] hexToBytes32(String hex) {
|
||||
hex = nn(hex);
|
||||
if (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) return out;
|
||||
byte[] full = new byte[32];
|
||||
int copy = Math.min(out.length, 32);
|
||||
System.arraycopy(out, out.length - copy, full, 32 - copy, copy);
|
||||
return full;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@ import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler;
|
||||
import server.logic.ws_protocol.JSON.utils.NetExceptionResponseFactory;
|
||||
import server.logic.ws_protocol.WireCodes;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.SolanaUser;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
@@ -61,7 +61,7 @@ public class Net_AddUser_Handler implements JsonMessageHandler {
|
||||
try {
|
||||
SolanaUsersDAO dao = SolanaUsersDAO.getInstance();
|
||||
|
||||
SolanaUser user = new SolanaUser(
|
||||
SolanaUserEntry user = new SolanaUserEntry(
|
||||
req.getLoginId(),
|
||||
req.getLogin(),
|
||||
req.getBchId(),
|
||||
|
||||
Reference in New Issue
Block a user