From 80ea016687f18e622c561dc2722cb2cbbee1456b00ea03d7efe4f9b44a57f058 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Wed, 24 Dec 2025 14:22:50 +0300 Subject: [PATCH] =?UTF-8?q?24=2012=2025=20=D0=94=D0=BE=D1=80=D0=B0=D0=B1?= =?UTF-8?q?=D0=B0=D1=82=D1=8B=D0=B2=D0=B0=D1=8E=20=D0=B4=D0=BE=D0=B1=D0=B0?= =?UTF-8?q?=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B1=D0=BB=D0=BE=D0=BA?= =?UTF-8?q?=D0=BE=D0=B2.=20=D0=9F=D1=80=D0=BE=D0=BC=D0=B5=D0=B6=D1=83?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BD=D1=8B=D0=B9=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8=D1=8F=20(=D0=BD=D0=B5=20?= =?UTF-8?q?=D1=80=D0=B0=D0=B1=D0=BE=D1=82=D0=B0=D1=8E=D1=82)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../blockchain/Net_AddBlock_Request.java | 4 -- .../blockchain/BlockchainStateService.java | 42 +++++++------- .../blockchain/Net_AddBlock_Handler.java | 57 +++++++++++-------- ..._NoAuth.java => Test_AddBlock_NoAuth.java} | 16 +----- 4 files changed, 57 insertions(+), 62 deletions(-) rename src/test/java/test/it/ws/{Test_AddBlock_new_NoAuth.java => Test_AddBlock_NoAuth.java} (93%) diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/entyties/blockchain/Net_AddBlock_Request.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/entyties/blockchain/Net_AddBlock_Request.java index 36a4227..dac0a81 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/entyties/blockchain/Net_AddBlock_Request.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/entyties/blockchain/Net_AddBlock_Request.java @@ -4,15 +4,11 @@ import server.logic.ws_protocol.JSON.entyties.Net_Request; public final class Net_AddBlock_Request extends Net_Request { - private String login; // обязателен private String blockchainName; // обязателен private int globalNumber; // обязателен private String prevGlobalHash; // HEX(64) или "" для нулевого private String blockBytesB64; // байты FULL-блока (raw+sig+hash) в Base64 - public String getLogin() { return login; } - public void setLogin(String login) { this.login = login; } - public String getBlockchainName() { return blockchainName; } public void setBlockchainName(String blockchainName) { this.blockchainName = blockchainName; } diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/BlockchainStateService.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/BlockchainStateService.java index e15ea91..6faa0e6 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/BlockchainStateService.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/BlockchainStateService.java @@ -2,6 +2,7 @@ package server.logic.ws_protocol.JSON.handlers.blockchain; import blockchain.BchBlockEntry; import blockchain.BchCryptoVerifier; +import blockchain.body.BodyRecordParser; import server.logic.ws_protocol.WireCodes; import shine.db.SqliteDbController; import shine.db.dao.BlockchainStateDAO; @@ -68,7 +69,6 @@ public final class BlockchainStateService { } public AddBlockResult addBlockAtomically( - String login, String blockchainName, int globalNumber, String prevGlobalHashHex, @@ -84,18 +84,8 @@ public final class BlockchainStateService { if (loginFromBlockchainName == null || loginFromBlockchainName.isBlank()) return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_blockchain_name", 0, ""); - // todo действительно давай прото брать логин из имени блокчена и не передавать его отдельно в запросе! - if (login == null || login.isBlank()) { - // раз уж у нас есть loginFromName — можно принимать login пустым, - // но ты явно передаёшь login, поэтому пока так: - return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "empty_login", 0, ""); - } - - // (опционально) сверка - if (!loginFromBlockchainName.equals(login)) { - return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "login_not_match_blockchain_name", 0, ""); - } + String login = loginFromBlockchainName; byte[] blockBytes; try { @@ -104,13 +94,14 @@ public final class BlockchainStateService { return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, ""); } -// todo ну и ещё тут нужно проверить что не только сам формат блока верный, но и запись в этом блоке верная - тоесть что её можно распарсить! - - - + // todo ну и ещё тут нужно проверить что не только сам формат блока верный, но и запись в этом блоке верная - тоесть что её можно распарсить! final BchBlockEntry block; try { block = new BchBlockEntry(blockBytes); + + // проверяем, что body распарсится и валидируется + BodyRecordParser.parse(block.bodyBytes).check(); + } catch (Exception e) { return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_format", 0, ""); } @@ -141,9 +132,20 @@ public final class BlockchainStateService { BlockchainStateEntry st = stateDAO.getByBlockchainName(c, blockchainName); //todo тут надо учесть тот случай что если это 0 блок тоесть начало блокчейна то логично что ещё нет самого файла блокчейна и по этому нет и BlockchainStateEntry + boolean isGenesis = (globalNumber == 0); + if (st == null) { - c.rollback(); - return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, ""); + if (!isGenesis) { + c.rollback(); + return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, ""); + } + st = new BlockchainStateEntry(); + st.setBlockchainName(blockchainName); + st.setLogin(login); + st.setLastGlobalNumber(-1); + st.setLastGlobalHash(""); + st.setLastLineNumber(0, -1); + st.setLastLineHash(0, ""); } // 3) проверяем последовательность глобального номера @@ -175,8 +177,8 @@ public final class BlockchainStateService { prevLineHash32, block.getRawBytes(), block.getSignature64(), - block.getHash32(), - loginKey32 + loginKey32, + block.getHash32() ); if (!ok) { diff --git a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java index 057b36f..6885e1c 100644 --- a/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java +++ b/shine-server-net-protocol/src/main/java/server/logic/ws_protocol/JSON/handlers/blockchain/Net_AddBlock_Handler.java @@ -8,6 +8,8 @@ import server.logic.ws_protocol.JSON.entyties.blockchain.Net_AddBlock_Response; import server.logic.ws_protocol.JSON.handlers.JsonMessageHandler; import server.logic.ws_protocol.WireCodes; +import java.util.concurrent.locks.ReentrantLock; + public final class Net_AddBlock_Handler implements JsonMessageHandler { @Override @@ -15,36 +17,41 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler { Net_AddBlock_Request req = (Net_AddBlock_Request) baseReq; - var r = BlockchainStateService.getInstance().addBlockAtomically( - req.getLogin(), - req.getBlockchainName(), - req.getGlobalNumber(), - req.getPrevGlobalHash(), - req.getBlockBytesB64() - ); - // todo если пришёл запрос на добавление то надо блочить работу с этим блокчейном по req.getBlockchainName(), // с помощью класса BlockchainLocks и разлочивать работу только в конце завершения работы этого хэндлера, что бы не случилось паралельной работы двух потоков с одним и тем же блокчейном! + ReentrantLock lock = BlockchainLocks.lockFor(req.getBlockchainName()); + lock.lock(); + try { + var r = BlockchainStateService.getInstance().addBlockAtomically( + req.getBlockchainName(), + req.getGlobalNumber(), + req.getPrevGlobalHash(), + req.getBlockBytesB64() + ); - Net_AddBlock_Response resp = new Net_AddBlock_Response(); - resp.setOp(req.getOp()); - resp.setRequestId(req.getRequestId()); + Net_AddBlock_Response resp = new Net_AddBlock_Response(); + resp.setOp(req.getOp()); + resp.setRequestId(req.getRequestId()); - if (r.isOk()) { - resp.setStatus(WireCodes.Status.OK); - resp.setReasonCode(null); - } else { - resp.setStatus(r.httpStatus); - resp.setReasonCode(r.reasonCode); + if (r.isOk()) { + resp.setStatus(WireCodes.Status.OK); + resp.setReasonCode(null); + } else { + resp.setStatus(r.httpStatus); + resp.setReasonCode(r.reasonCode); + } + + // Даже при ошибке (например bad_global_sequence) можно вернуть “что сервер считает последним” + resp.setServerLastGlobalNumber(r.serverLastGlobalNumber); + if (r.serverLastGlobalHash != null) { + resp.setServerLastGlobalHash(r.serverLastGlobalHash); + } + + return resp; + + } finally { + lock.unlock(); } - - // Даже при ошибке (например bad_global_sequence) можно вернуть “что сервер считает последним” - resp.setServerLastGlobalNumber(r.serverLastGlobalNumber); - if (r.serverLastGlobalHash != null) { - resp.setServerLastGlobalHash(r.serverLastGlobalHash); - } - - return resp; } } \ No newline at end of file diff --git a/src/test/java/test/it/ws/Test_AddBlock_new_NoAuth.java b/src/test/java/test/it/ws/Test_AddBlock_NoAuth.java similarity index 93% rename from src/test/java/test/it/ws/Test_AddBlock_new_NoAuth.java rename to src/test/java/test/it/ws/Test_AddBlock_NoAuth.java index 122ec1e..91b8cee 100644 --- a/src/test/java/test/it/ws/Test_AddBlock_new_NoAuth.java +++ b/src/test/java/test/it/ws/Test_AddBlock_NoAuth.java @@ -19,7 +19,7 @@ import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionStage; import java.util.concurrent.CountDownLatch; -public class Test_AddBlock_new_NoAuth { +public class Test_AddBlock_NoAuth { private static final ObjectMapper JSON = new ObjectMapper(); @@ -36,7 +36,6 @@ public class Test_AddBlock_new_NoAuth { private int step = 0; private String lastGlobalHashHex = ZERO64; - private String lastLineHashHex = ZERO64; @Override public void onOpen(WebSocket ws) { @@ -54,7 +53,6 @@ public class Test_AddBlock_new_NoAuth { String json = buildAddBlockJson( "test-add-header", - TestConfig.TEST_LOGIN, TestConfig.TEST_BCH_NAME, 0, ZERO64, @@ -82,22 +80,17 @@ public class Test_AddBlock_new_NoAuth { } String serverLastGlobalHash = extractPayloadString(msg, "serverLastGlobalHash"); - String serverLastLineHash = extractPayloadString(msg, "serverLastLineHash"); if (serverLastGlobalHash == null || serverLastGlobalHash.isBlank()) { System.out.println("❌ No serverLastGlobalHash in response"); ws.sendClose(WebSocket.NORMAL_CLOSURE, "bad-response"); return CompletableFuture.completedFuture(null); } - if (serverLastLineHash == null || serverLastLineHash.isBlank()) { - serverLastLineHash = serverLastGlobalHash; - } lastGlobalHashHex = serverLastGlobalHash; - lastLineHashHex = serverLastLineHash; byte[] prevGlobal32 = hexToBytes32(lastGlobalHashHex); - byte[] prevLine32 = hexToBytes32(lastLineHashHex); + byte[] prevLine32 = prevGlobal32; // 2) TEXT block: global=1, line=0, lineNumber=1 byte[] textFull = buildTextBlockFullBytes( @@ -111,7 +104,6 @@ public class Test_AddBlock_new_NoAuth { String json2 = buildAddBlockJson( "test-add-text", - TestConfig.TEST_LOGIN, TestConfig.TEST_BCH_NAME, 1, lastGlobalHashHex, @@ -238,7 +230,6 @@ public class Test_AddBlock_new_NoAuth { // ================================================================================= private static String buildAddBlockJson(String requestId, - String login, String blockchainName, int globalNumber, String prevGlobalHashHex, @@ -248,14 +239,13 @@ public class Test_AddBlock_new_NoAuth { "op": "AddBlock", "requestId": "%s", "payload": { - "login": "%s", "blockchainName": "%s", "globalNumber": %d, "prevGlobalHash": "%s", "blockBytesB64": "%s" } } - """.formatted(requestId, login, blockchainName, globalNumber, prevGlobalHashHex, blockBytesB64); + """.formatted(requestId, blockchainName, globalNumber, prevGlobalHashHex, blockBytesB64); } // =================================================================================