28 12 25
Всё ещё не работает проверка линий. Переделываю тесты понял что нетак в сервере. Дальше буду исправлять сервак.
This commit is contained in:
parent
3f374f48e1
commit
526e2d9cc4
@ -1,9 +0,0 @@
|
|||||||
//import shine.db.DatabaseInitializer;
|
|
||||||
//
|
|
||||||
//public class CreateNewDatabase {
|
|
||||||
//
|
|
||||||
// public static void main(String[] args) {
|
|
||||||
// // Просто прокидываем управление в DatabaseInitializer
|
|
||||||
// DatabaseInitializer.createNewDB(args);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,126 +0,0 @@
|
|||||||
//package Test;
|
|
||||||
//
|
|
||||||
//import java.net.URI;
|
|
||||||
//import java.net.http.HttpClient;
|
|
||||||
//import java.net.http.WebSocket;
|
|
||||||
//import java.net.http.WebSocket.Listener;
|
|
||||||
//import java.util.concurrent.CompletionStage;
|
|
||||||
//import java.util.concurrent.CountDownLatch;
|
|
||||||
//
|
|
||||||
//public class TestJsonWsClient2 {
|
|
||||||
//
|
|
||||||
// public static void main(String[] args) throws Exception {
|
|
||||||
// String uri = "ws://localhost:7070/ws";
|
|
||||||
//
|
|
||||||
// String jsonRequestRefreshSession = """
|
|
||||||
// {
|
|
||||||
// "op": "RefreshSession",
|
|
||||||
// "requestId": "test-1",
|
|
||||||
// "payload": {
|
|
||||||
// "sessionId": 123,
|
|
||||||
// "sessionPwd": "test-password"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// """;
|
|
||||||
//
|
|
||||||
// String jsonRequestAddUser = """
|
|
||||||
// {
|
|
||||||
// "op": "AddUser",
|
|
||||||
// "requestId": "test-add-1",
|
|
||||||
// "payload": {
|
|
||||||
// "login": "anya1111",
|
|
||||||
// "loginId": 100211,
|
|
||||||
// "bchId": 4222,
|
|
||||||
// "pubkey0": "PUB0",
|
|
||||||
// "pubkey1": "PUB1",
|
|
||||||
// "bchLimit": 1000000
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// """;
|
|
||||||
//
|
|
||||||
// String jsonRequestAuthChallenge = """
|
|
||||||
// {
|
|
||||||
// "op": "AuthChallenge",
|
|
||||||
// "requestId": "test-auth-1",
|
|
||||||
// "payload": {
|
|
||||||
// "login": "anya1111"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// """;
|
|
||||||
//
|
|
||||||
// // Что тестируем сейчас:
|
|
||||||
// String jsonRequest = jsonRequestAuthChallenge;
|
|
||||||
//// String jsonRequest = jsonRequestRefreshSession;
|
|
||||||
//// String jsonRequest = jsonRequestAddUser;
|
|
||||||
//
|
|
||||||
// System.out.println("Подключаемся к " + uri);
|
|
||||||
//
|
|
||||||
// CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
//
|
|
||||||
// HttpClient client = HttpClient.newHttpClient();
|
|
||||||
//
|
|
||||||
// WebSocket webSocket = client.newWebSocketBuilder()
|
|
||||||
// .buildAsync(URI.create(uri), new Listener() {
|
|
||||||
//
|
|
||||||
// // 0 — ещё ничего не получили
|
|
||||||
// // 1 — получили 1-й ответ, отправили повторно
|
|
||||||
// // 2 — получили 2-й ответ, закрываемся
|
|
||||||
// private int responsesCount = 0;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onOpen(WebSocket webSocket) {
|
|
||||||
// System.out.println("✅ WebSocket подключен");
|
|
||||||
//
|
|
||||||
// System.out.println("📤 Отправляем JSON-запрос (1 раз):");
|
|
||||||
// System.out.println(jsonRequest);
|
|
||||||
//
|
|
||||||
// webSocket.sendText(jsonRequest, true);
|
|
||||||
// Listener.super.onOpen(webSocket);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onText(WebSocket webSocket,
|
|
||||||
// CharSequence data,
|
|
||||||
// boolean last) {
|
|
||||||
// String message = data.toString();
|
|
||||||
// responsesCount++;
|
|
||||||
//
|
|
||||||
// System.out.println("📥 Получен TEXT-ответ #" + responsesCount + " от сервера:");
|
|
||||||
// System.out.println(message);
|
|
||||||
//
|
|
||||||
// if (responsesCount == 1) {
|
|
||||||
// // После первого ответа — отправляем тот же запрос ещё раз
|
|
||||||
// System.out.println("📤 Отправляем JSON-запрос второй раз:");
|
|
||||||
// System.out.println(jsonRequest);
|
|
||||||
// webSocket.sendText(jsonRequest, true);
|
|
||||||
// } else {
|
|
||||||
// // После второго ответа — закрываем соединение
|
|
||||||
// webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "test done");
|
|
||||||
// latch.countDown();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// return Listener.super.onText(webSocket, data, last);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onError(WebSocket webSocket, Throwable error) {
|
|
||||||
// System.out.println("❌ Ошибка WebSocket-клиента: " + error.getMessage());
|
|
||||||
// error.printStackTrace(System.out);
|
|
||||||
// latch.countDown();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onClose(WebSocket webSocket,
|
|
||||||
// int statusCode,
|
|
||||||
// String reason) {
|
|
||||||
// System.out.println("🔚 Соединение закрыто. Код=" + statusCode + ", причина=" + reason);
|
|
||||||
// latch.countDown();
|
|
||||||
// return Listener.super.onClose(webSocket, statusCode, reason);
|
|
||||||
// }
|
|
||||||
// }).join();
|
|
||||||
//
|
|
||||||
// // Ждём, пока получим ответ/ошибку/закрытие
|
|
||||||
// latch.await();
|
|
||||||
// System.out.println("Тест завершён, выходим.");
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,317 +0,0 @@
|
|||||||
//package Test;
|
|
||||||
//
|
|
||||||
//import com.fasterxml.jackson.databind.JsonNode;
|
|
||||||
//import com.fasterxml.jackson.databind.ObjectMapper;
|
|
||||||
//import utils.crypto.Ed25519Util;
|
|
||||||
//import blockchain.body.HeaderBody;
|
|
||||||
//import blockchain.body.TextBody;
|
|
||||||
//import blockchain.BchCryptoVerifier;
|
|
||||||
//import blockchain.BchBlockEntry;
|
|
||||||
//
|
|
||||||
//import java.net.URI;
|
|
||||||
//import java.net.http.HttpClient;
|
|
||||||
//import java.net.http.WebSocket;
|
|
||||||
//import java.nio.ByteBuffer;
|
|
||||||
//import java.nio.ByteOrder;
|
|
||||||
//import java.util.Base64;
|
|
||||||
//import java.util.concurrent.CompletableFuture;
|
|
||||||
//import java.util.concurrent.CompletionStage;
|
|
||||||
//import java.util.concurrent.CountDownLatch;
|
|
||||||
//
|
|
||||||
//public class Test_AddBlock_new_NoAuth {
|
|
||||||
//
|
|
||||||
// private static final String WS_URI = "ws://localhost:7070/ws";
|
|
||||||
// private static final ObjectMapper JSON = new ObjectMapper();
|
|
||||||
//
|
|
||||||
// private static final String TEST_LOGIN = "anya24";
|
|
||||||
// // По твоему правилу: blockchainName = login + 4 цифры
|
|
||||||
// private static final String TEST_BCH_NAME = TEST_LOGIN + "0001";
|
|
||||||
//
|
|
||||||
// private static final byte[] LOGIN_PRIV_KEY;
|
|
||||||
// private static final byte[] LOGIN_PUB_KEY;
|
|
||||||
//
|
|
||||||
// static {
|
|
||||||
// LOGIN_PRIV_KEY = Ed25519Util.generatePrivateKeyFromString("test-ed25519-login-11" + TEST_LOGIN);
|
|
||||||
// LOGIN_PUB_KEY = Ed25519Util.derivePublicKey(LOGIN_PRIV_KEY);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static final byte[] ZERO32 = new byte[32];
|
|
||||||
// private static final String ZERO64 = "0".repeat(64);
|
|
||||||
//
|
|
||||||
// public static void main(String[] args) throws Exception {
|
|
||||||
// CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
// HttpClient client = HttpClient.newHttpClient();
|
|
||||||
//
|
|
||||||
// client.newWebSocketBuilder()
|
|
||||||
// .buildAsync(URI.create(WS_URI), new WebSocket.Listener() {
|
|
||||||
//
|
|
||||||
// private int step = 0;
|
|
||||||
//
|
|
||||||
// // Эти значения обновим ПО ОТВЕТУ сервера на header
|
|
||||||
// private String lastGlobalHashHex = ZERO64;
|
|
||||||
// private String lastLineHashHex = ZERO64;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onOpen(WebSocket ws) {
|
|
||||||
// System.out.println("✅ WS connected: " + WS_URI);
|
|
||||||
// ws.request(1);
|
|
||||||
//
|
|
||||||
// // 1) HEADER (global=0, line=0, lineNumber=0)
|
|
||||||
// byte[] headerFull = buildHeaderBlockFullBytes(
|
|
||||||
// /*global*/0,
|
|
||||||
// /*lineIndex*/(short)0,
|
|
||||||
// /*lineBlock*/0,
|
|
||||||
// /*prevGlobal*/ZERO32,
|
|
||||||
// /*prevLine*/ZERO32
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// String json = buildAddBlockJson(
|
|
||||||
// "test-add-header",
|
|
||||||
// TEST_BCH_NAME,
|
|
||||||
// 0,
|
|
||||||
// ZERO64, // prevGlobalHash для первого блока — нули
|
|
||||||
// base64(headerFull)
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// System.out.println("\n📤 SEND #1 (HEADER):\n" + json);
|
|
||||||
// ws.sendText(json, true);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onText(WebSocket ws, CharSequence data, boolean last) {
|
|
||||||
// String msg = data.toString();
|
|
||||||
// System.out.println("\n📥 RECV:\n" + msg);
|
|
||||||
// System.out.println("-----------------------------------------------------");
|
|
||||||
//
|
|
||||||
// try {
|
|
||||||
// int status = extractStatus(msg);
|
|
||||||
//
|
|
||||||
// if (step == 0) {
|
|
||||||
// if (status != 200) {
|
|
||||||
// System.out.println("❌ HEADER rejected, status=" + status);
|
|
||||||
// ws.sendClose(WebSocket.NORMAL_CLOSURE, "fail");
|
|
||||||
// return CompletableFuture.completedFuture(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // Берём ИМЕННО ТОТ хэш, который сервер сохранил в state
|
|
||||||
// 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()) {
|
|
||||||
// // fallback: пусть будет как global (если сервер так хранит)
|
|
||||||
// serverLastLineHash = serverLastGlobalHash;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// lastGlobalHashHex = serverLastGlobalHash;
|
|
||||||
// lastLineHashHex = serverLastLineHash;
|
|
||||||
//
|
|
||||||
// byte[] prevGlobal32 = hexToBytes32(lastGlobalHashHex);
|
|
||||||
// byte[] prevLine32 = hexToBytes32(lastLineHashHex);
|
|
||||||
//
|
|
||||||
// // 2) TEXT (global=1, line=0, lineNumber=1)
|
|
||||||
// byte[] textFull = buildTextBlockFullBytes(
|
|
||||||
// /*global*/1,
|
|
||||||
// /*lineIndex*/(short)0,
|
|
||||||
// /*lineBlock*/1,
|
|
||||||
// prevGlobal32,
|
|
||||||
// prevLine32,
|
|
||||||
// "Hello from test client"
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// String json2 = buildAddBlockJson(
|
|
||||||
// "test-add-text",
|
|
||||||
// TEST_BCH_NAME,
|
|
||||||
// 1,
|
|
||||||
// lastGlobalHashHex, // prevGlobalHash = хэш header'а из ответа сервера
|
|
||||||
// base64(textFull)
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// System.out.println("\n📤 SEND #2 (TEXT):\n" + json2);
|
|
||||||
// step = 1;
|
|
||||||
// ws.sendText(json2, true);
|
|
||||||
//
|
|
||||||
// } else if (step == 1) {
|
|
||||||
// if (status != 200) {
|
|
||||||
// System.out.println("❌ TEXT rejected, status=" + status);
|
|
||||||
// } else {
|
|
||||||
// System.out.println("✅ Done. Closing.");
|
|
||||||
// }
|
|
||||||
// ws.sendClose(WebSocket.NORMAL_CLOSURE, "ok");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// e.printStackTrace(System.out);
|
|
||||||
// ws.sendClose(WebSocket.NORMAL_CLOSURE, "exception");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// ws.request(1);
|
|
||||||
// return CompletableFuture.completedFuture(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onError(WebSocket ws, Throwable error) {
|
|
||||||
// System.out.println("❌ WS error: " + error.getMessage());
|
|
||||||
// error.printStackTrace(System.out);
|
|
||||||
// latch.countDown();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onClose(WebSocket ws, int statusCode, String reason) {
|
|
||||||
// System.out.println("🔚 WS closed. code=" + statusCode + " reason=" + reason);
|
|
||||||
// latch.countDown();
|
|
||||||
// return CompletableFuture.completedFuture(null);
|
|
||||||
// }
|
|
||||||
// }).join();
|
|
||||||
//
|
|
||||||
// latch.await();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // =================================================================================
|
|
||||||
// // BUILD BLOCKS
|
|
||||||
// // =================================================================================
|
|
||||||
//
|
|
||||||
// private static byte[] buildHeaderBlockFullBytes(int globalNumber,
|
|
||||||
// short lineIndex,
|
|
||||||
// int lineBlockNumber,
|
|
||||||
// byte[] prevGlobalHash32,
|
|
||||||
// byte[] prevLineHash32) {
|
|
||||||
//
|
|
||||||
// HeaderBody body = new HeaderBody(
|
|
||||||
// TEST_LOGIN
|
|
||||||
// );
|
|
||||||
// byte[] bodyBytes = body.toBytes();
|
|
||||||
//
|
|
||||||
// return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] buildTextBlockFullBytes(int globalNumber,
|
|
||||||
// short lineIndex,
|
|
||||||
// int lineBlockNumber,
|
|
||||||
// byte[] prevGlobalHash32,
|
|
||||||
// byte[] prevLineHash32,
|
|
||||||
// String text) {
|
|
||||||
//
|
|
||||||
// TextBody body = new TextBody(text);
|
|
||||||
// byte[] bodyBytes = body.toBytes();
|
|
||||||
//
|
|
||||||
// return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] buildSignedBlockFullBytes(int globalNumber,
|
|
||||||
// short lineIndex,
|
|
||||||
// int lineBlockNumber,
|
|
||||||
// byte[] bodyBytes,
|
|
||||||
// byte[] prevGlobalHash32,
|
|
||||||
// byte[] prevLineHash32) {
|
|
||||||
//
|
|
||||||
// long ts = System.currentTimeMillis() / 1000L;
|
|
||||||
//
|
|
||||||
// int recordSize =
|
|
||||||
// BchBlockEntry.RAW_HEADER_SIZE +
|
|
||||||
// bodyBytes.length +
|
|
||||||
// BchBlockEntry.SIGNATURE_LEN +
|
|
||||||
// BchBlockEntry.HASH_LEN;
|
|
||||||
//
|
|
||||||
// byte[] rawBytes = ByteBuffer.allocate(BchBlockEntry.RAW_HEADER_SIZE + bodyBytes.length)
|
|
||||||
// .order(ByteOrder.BIG_ENDIAN)
|
|
||||||
// .putInt(recordSize)
|
|
||||||
// .putInt(globalNumber)
|
|
||||||
// .putLong(ts)
|
|
||||||
// .putShort(lineIndex)
|
|
||||||
// .putInt(lineBlockNumber)
|
|
||||||
// .put(bodyBytes)
|
|
||||||
// .array();
|
|
||||||
//
|
|
||||||
// byte[] preimage = BchCryptoVerifier.buildPreimage(
|
|
||||||
// TEST_LOGIN,
|
|
||||||
// prevGlobalHash32,
|
|
||||||
// prevLineHash32,
|
|
||||||
// rawBytes
|
|
||||||
// );
|
|
||||||
//
|
|
||||||
// byte[] hash32 = BchCryptoVerifier.sha256(preimage);
|
|
||||||
//
|
|
||||||
// // если у тебя подпись должна быть по preimage — меняй тут
|
|
||||||
// byte[] signature64 = Ed25519Util.sign(hash32, LOGIN_PRIV_KEY);
|
|
||||||
//
|
|
||||||
// return new BchBlockEntry(
|
|
||||||
// globalNumber,
|
|
||||||
// ts,
|
|
||||||
// lineIndex,
|
|
||||||
// lineBlockNumber,
|
|
||||||
// bodyBytes,
|
|
||||||
// signature64,
|
|
||||||
// hash32
|
|
||||||
// ).toBytes();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // =================================================================================
|
|
||||||
// // JSON BUILD
|
|
||||||
// // =================================================================================
|
|
||||||
//
|
|
||||||
// private static String buildAddBlockJson(String requestId,
|
|
||||||
// String blockchainName,
|
|
||||||
// int globalNumber,
|
|
||||||
// String prevGlobalHashHex,
|
|
||||||
// String blockBytesB64) {
|
|
||||||
// return """
|
|
||||||
// {
|
|
||||||
// "op": "AddBlock",
|
|
||||||
// "requestId": "%s",
|
|
||||||
// "payload": {
|
|
||||||
// "login": "%s",
|
|
||||||
// "blockchainName": "%s",
|
|
||||||
// "globalNumber": %d,
|
|
||||||
// "prevGlobalHash": "%s",
|
|
||||||
// "blockBytesB64": "%s"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// """.formatted(requestId, TEST_LOGIN, blockchainName, globalNumber, prevGlobalHashHex, blockBytesB64);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// // =================================================================================
|
|
||||||
// // HELPERS
|
|
||||||
// // =================================================================================
|
|
||||||
//
|
|
||||||
// private static int extractStatus(String json) {
|
|
||||||
// try {
|
|
||||||
// JsonNode root = JSON.readTree(json);
|
|
||||||
// if (root.has("status")) return root.get("status").asInt();
|
|
||||||
// } catch (Exception ignore) {}
|
|
||||||
// return -1;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static String extractPayloadString(String json, String field) {
|
|
||||||
// try {
|
|
||||||
// JsonNode root = JSON.readTree(json);
|
|
||||||
// JsonNode payload = root.get("payload");
|
|
||||||
// if (payload != null && payload.has(field)) {
|
|
||||||
// return payload.get(field).asText();
|
|
||||||
// }
|
|
||||||
// } catch (Exception ignore) {}
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static String base64(byte[] bytes) {
|
|
||||||
// return Base64.getEncoder().encodeToString(bytes);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static byte[] hexToBytes32(String hex) {
|
|
||||||
// if (hex == null) throw new IllegalArgumentException("hex is null");
|
|
||||||
// String s = hex.trim();
|
|
||||||
// if (s.length() != 64) throw new IllegalArgumentException("hex must be 64 chars, got " + s.length());
|
|
||||||
// byte[] out = new byte[32];
|
|
||||||
// for (int i = 0; i < 32; i++) {
|
|
||||||
// int hi = Character.digit(s.charAt(i * 2), 16);
|
|
||||||
// int lo = Character.digit(s.charAt(i * 2 + 1), 16);
|
|
||||||
// if (hi < 0 || lo < 0) throw new IllegalArgumentException("bad hex at pos " + (i * 2));
|
|
||||||
// out[i] = (byte) ((hi << 4) | lo);
|
|
||||||
// }
|
|
||||||
// return out;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
File diff suppressed because it is too large
Load Diff
@ -1,109 +0,0 @@
|
|||||||
//package Test;
|
|
||||||
//
|
|
||||||
//import java.net.URI;
|
|
||||||
//import java.net.http.HttpClient;
|
|
||||||
//import java.net.http.WebSocket;
|
|
||||||
//import java.net.http.WebSocket.Listener;
|
|
||||||
//import java.util.concurrent.CompletableFuture;
|
|
||||||
//import java.util.concurrent.CompletionStage;
|
|
||||||
//import java.util.concurrent.CountDownLatch;
|
|
||||||
//
|
|
||||||
//public class Test_SessionRefreshClient {
|
|
||||||
//
|
|
||||||
// // Адрес сервера
|
|
||||||
// private static final String WS_URI = "ws://localhost:7070/ws";
|
|
||||||
//
|
|
||||||
// // ==== ЗДЕСЬ ПОДСТАВИШЬ СВОИ ДАННЫЕ СЕССИИ ====
|
|
||||||
// private static final long SESSION_ID = 7599553208996461137L; // TODO: подставь реальный sessionId
|
|
||||||
// private static final String SESSION_PWD = "11b3508f37ae7b41816f42031b90"; // TODO: подставь реальный sessionPwd
|
|
||||||
// // =============================================
|
|
||||||
//
|
|
||||||
// public static void main(String[] args) throws Exception {
|
|
||||||
// System.out.println("Подключаемся к " + WS_URI);
|
|
||||||
//
|
|
||||||
// CountDownLatch latch = new CountDownLatch(1);
|
|
||||||
//
|
|
||||||
// HttpClient client = HttpClient.newHttpClient();
|
|
||||||
//
|
|
||||||
// ClientListener listener = new ClientListener(latch);
|
|
||||||
//
|
|
||||||
// client.newWebSocketBuilder()
|
|
||||||
// .buildAsync(URI.create(WS_URI), listener)
|
|
||||||
// .join();
|
|
||||||
//
|
|
||||||
// latch.await();
|
|
||||||
// System.out.println("Тест RefreshSession завершён, выходим.");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static String buildRefreshSessionJson() {
|
|
||||||
// return """
|
|
||||||
// {
|
|
||||||
// "op": "RefreshSession",
|
|
||||||
// "requestId": "test-session-refresh-1",
|
|
||||||
// "payload": {
|
|
||||||
// "sessionId": %d,
|
|
||||||
// "sessionPwd": "%s"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// """.formatted(SESSION_ID, SESSION_PWD);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static class ClientListener implements Listener {
|
|
||||||
//
|
|
||||||
// private final CountDownLatch latch;
|
|
||||||
//
|
|
||||||
// ClientListener(CountDownLatch latch) {
|
|
||||||
// this.latch = latch;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onOpen(WebSocket webSocket) {
|
|
||||||
// System.out.println("✅ WebSocket подключен");
|
|
||||||
//
|
|
||||||
// webSocket.request(1); // разрешаем принимать одно сообщение
|
|
||||||
//
|
|
||||||
// // сразу отправляем запрос RefreshSession
|
|
||||||
// String json = buildRefreshSessionJson();
|
|
||||||
// System.out.println();
|
|
||||||
// System.out.println("📤 Отправляем RefreshSession:");
|
|
||||||
// System.out.println(json);
|
|
||||||
// webSocket.sendText(json, true);
|
|
||||||
//
|
|
||||||
// Listener.super.onOpen(webSocket);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onText(WebSocket webSocket,
|
|
||||||
// CharSequence data,
|
|
||||||
// boolean last) {
|
|
||||||
// System.out.println("📥 Ответ от сервера:");
|
|
||||||
// System.out.println(data.toString());
|
|
||||||
// System.out.println("-----------------------------------------------------");
|
|
||||||
//
|
|
||||||
// // После одного ответа просто закрываем соединение
|
|
||||||
// System.out.println("✅ Получен ответ на RefreshSession, закрываем соединение");
|
|
||||||
// webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "session refresh test done");
|
|
||||||
//
|
|
||||||
// // запрашиваем следующее сообщение на всякий случай (хотя уже закрываемся)
|
|
||||||
// webSocket.request(1);
|
|
||||||
//
|
|
||||||
// return CompletableFuture.completedFuture(null);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public void onError(WebSocket webSocket, Throwable error) {
|
|
||||||
// System.out.println("❌ Ошибка WebSocket-клиента: " + error.getMessage());
|
|
||||||
// error.printStackTrace(System.out);
|
|
||||||
// latch.countDown();
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// public CompletionStage<?> onClose(WebSocket webSocket,
|
|
||||||
// int statusCode,
|
|
||||||
// String reason) {
|
|
||||||
// System.out.println("🔚 Соединение закрыто. Код=" + statusCode + ", причина=" + reason);
|
|
||||||
// latch.countDown();
|
|
||||||
// return CompletableFuture.completedFuture(null);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,5 +0,0 @@
|
|||||||
//package Test;
|
|
||||||
//
|
|
||||||
//public class test1 {
|
|
||||||
//
|
|
||||||
//}
|
|
||||||
@ -20,7 +20,7 @@ public class IT_RunAllCleanMain {
|
|||||||
private static final String DATA_DIR = "data";
|
private static final String DATA_DIR = "data";
|
||||||
|
|
||||||
public static void main(String[] args) {
|
public static void main(String[] args) {
|
||||||
ItRunContext.initIfNeeded();
|
// ItRunContext.initIfNeeded();
|
||||||
|
|
||||||
TestLog.title("IT RUN CLEAN: очистка data/ + запуск всех тестов");
|
TestLog.title("IT RUN CLEAN: очистка data/ + запуск всех тестов");
|
||||||
|
|
||||||
@ -32,8 +32,8 @@ public class IT_RunAllCleanMain {
|
|||||||
System.exit(1);
|
System.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int failed = IT_RunAllMain.runAll();
|
// int failed = IT_RunAllMain.runAll();
|
||||||
System.exit(failed);
|
// System.exit(failed);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void cleanupDataDir(String dirName) throws IOException {
|
private static void cleanupDataDir(String dirName) throws IOException {
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import blockchain.BchCryptoVerifier;
|
|||||||
import blockchain.body.HeaderBody;
|
import blockchain.body.HeaderBody;
|
||||||
import blockchain.body.ReactionBody;
|
import blockchain.body.ReactionBody;
|
||||||
import blockchain.body.TextBody;
|
import blockchain.body.TextBody;
|
||||||
|
import test.it.utils.JsonParsers;
|
||||||
import test.it.utils.TestConfig;
|
import test.it.utils.TestConfig;
|
||||||
import utils.crypto.Ed25519Util;
|
import utils.crypto.Ed25519Util;
|
||||||
|
|
||||||
@ -13,31 +14,35 @@ import java.nio.ByteOrder;
|
|||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
import java.util.Base64;
|
import java.util.Base64;
|
||||||
|
|
||||||
/**
|
import static org.junit.jupiter.api.Assertions.*;
|
||||||
* AddBlockScenarioRunner
|
|
||||||
*
|
|
||||||
* Хранит локальное состояние:
|
|
||||||
* - globalLastHashHex / globalLastNumber
|
|
||||||
* - lineLastNumber[line] / lineLastHashHex[line]
|
|
||||||
* - headerHash32 (нужен как prevLineHash для первых блоков линий)
|
|
||||||
*
|
|
||||||
* Умеет:
|
|
||||||
* - собрать блок (header/text/react)
|
|
||||||
* - отправить AddBlock по сети (каждый запрос = новое WS соединение)
|
|
||||||
* - обновить локальное состояние
|
|
||||||
*/
|
|
||||||
public final class AddBlockScenarioRunner {
|
|
||||||
|
|
||||||
// requestId делаем фиксированный (как ты попросил)
|
/**
|
||||||
private static final String FIXED_REQUEST_ID = "it03";
|
* AddBlockFlow
|
||||||
|
*
|
||||||
|
* Держит локальное состояние цепочки:
|
||||||
|
* - last globalNumber / last globalHash
|
||||||
|
* - last lineNum / last lineHash для каждой линии
|
||||||
|
*
|
||||||
|
* И умеет:
|
||||||
|
* - собрать следующий блок (HEADER / TEXT / REACTION)
|
||||||
|
* - отправить AddBlock в сервер (через WsJsonOneShot)
|
||||||
|
* - проверить serverLastGlobalHash == localHash
|
||||||
|
* - обновить локальное состояние
|
||||||
|
*
|
||||||
|
* Важно:
|
||||||
|
* - Этот класс НЕ занимается красивыми логами. Только логика + проверки.
|
||||||
|
*/
|
||||||
|
public final class AddBlockFlow {
|
||||||
|
|
||||||
private static final byte[] ZERO32 = new byte[32];
|
private static final byte[] ZERO32 = new byte[32];
|
||||||
private static final String ZERO64 = "0".repeat(64);
|
private static final String ZERO64 = "0".repeat(64);
|
||||||
|
|
||||||
private final String wsUri;
|
// линии как у тебя
|
||||||
private final String blockchainName;
|
public static final short LINE_HEADER = 0;
|
||||||
|
public static final short LINE_TEXT = 1;
|
||||||
|
public static final short LINE_REACT = 2;
|
||||||
|
|
||||||
// Локальное состояние (как и было в тесте)
|
// локальное состояние
|
||||||
private final int[] lineLastNumber = new int[8];
|
private final int[] lineLastNumber = new int[8];
|
||||||
private final String[] lineLastHashHex = new String[8];
|
private final String[] lineLastHashHex = new String[8];
|
||||||
|
|
||||||
@ -46,10 +51,7 @@ public final class AddBlockScenarioRunner {
|
|||||||
|
|
||||||
private byte[] headerHash32 = null;
|
private byte[] headerHash32 = null;
|
||||||
|
|
||||||
public AddBlockScenarioRunner(String wsUri, String blockchainName) {
|
public AddBlockFlow() {
|
||||||
this.wsUri = wsUri;
|
|
||||||
this.blockchainName = blockchainName;
|
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) lineLastHashHex[i] = "";
|
for (int i = 0; i < 8; i++) lineLastHashHex[i] = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -57,90 +59,94 @@ public final class AddBlockScenarioRunner {
|
|||||||
// PUBLIC API
|
// PUBLIC API
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
public String getGlobalLastHashHex() {
|
/** Шлём HEADER (global=0, line=0, lineNum=0). Должно быть ПЕРВЫМ. */
|
||||||
return globalLastHashHex;
|
public void sendHeader0(Duration timeout) {
|
||||||
}
|
assertEquals(-1, globalLastNumber, "HEADER должен идти первым: globalLastNumber сейчас уже " + globalLastNumber);
|
||||||
|
|
||||||
public int getGlobalLastNumber() {
|
|
||||||
return globalLastNumber;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getLineLastNumber(int lineIndex) {
|
|
||||||
return lineLastNumber[lineIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLineLastHashHex(int lineIndex) {
|
|
||||||
return lineLastHashHex[lineIndex];
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Добавить HEADER (global=0, line=0, lineNum=0). */
|
|
||||||
public AddBlockResult addHeader(short lineHeader) {
|
|
||||||
BuiltBlock header = buildHeaderBlock(
|
BuiltBlock header = buildHeaderBlock(
|
||||||
0,
|
0,
|
||||||
lineHeader,
|
LINE_HEADER,
|
||||||
0,
|
0,
|
||||||
ZERO32,
|
ZERO32,
|
||||||
ZERO32
|
ZERO32
|
||||||
);
|
);
|
||||||
|
|
||||||
String reqJson = buildAddBlockJson(FIXED_REQUEST_ID, blockchainName, 0, ZERO64, base64(header.fullBytes));
|
String req = buildAddBlockJson(TestConfig.BCH_NAME(), 0, ZERO64, base64(header.fullBytes));
|
||||||
String resp = WsJsonRoundtripClient.sendOnce(wsUri, reqJson, Duration.ofSeconds(8));
|
String resp = WsJsonOneShot.request(req, timeout);
|
||||||
|
|
||||||
|
assert200("AddBlock(HEADER)", resp);
|
||||||
|
|
||||||
|
String serverLastGlobalHash0 = extractPayloadString(resp, "serverLastGlobalHash");
|
||||||
|
assertNotNull(serverLastGlobalHash0, "HEADER: payload.serverLastGlobalHash must not be null");
|
||||||
|
assertEquals(64, serverLastGlobalHash0.trim().length(), "HEADER: serverLastGlobalHash must be 64 hex chars");
|
||||||
|
|
||||||
// локальный hash
|
|
||||||
String localHash0 = bytesToHex64(header.hash32);
|
String localHash0 = bytesToHex64(header.hash32);
|
||||||
|
assertEquals(localHash0, serverLastGlobalHash0, "HEADER: serverLastGlobalHash должен совпасть с локальным hash");
|
||||||
|
|
||||||
// обновляем состояние (как раньше)
|
// обновляем локальное состояние
|
||||||
headerHash32 = header.hash32;
|
headerHash32 = header.hash32;
|
||||||
globalLastNumber = 0;
|
globalLastNumber = 0;
|
||||||
globalLastHashHex = localHash0;
|
globalLastHashHex = localHash0;
|
||||||
lineLastNumber[0] = 0;
|
|
||||||
lineLastHashHex[0] = localHash0;
|
|
||||||
|
|
||||||
return new AddBlockResult(reqJson, resp, localHash0);
|
lineLastNumber[LINE_HEADER] = 0;
|
||||||
|
lineLastHashHex[LINE_HEADER] = localHash0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Добавить TEXT в lineText, следующим lineNum, global=globalNumber. */
|
/** Шлём следующий TEXT блок в line=1. */
|
||||||
public AddBlockResult addText(int globalNumber, short lineText, String text) {
|
public BuiltBlock sendNextText(String text, Duration timeout) {
|
||||||
int lineNum = nextLineNum(lineText);
|
assertNotNull(headerHash32, "TEXT нельзя слать до HEADER (headerHash32 == null)");
|
||||||
byte[] prevLineHash = prevLineHash32(lineText);
|
|
||||||
|
int nextGlobal = globalLastNumber + 1;
|
||||||
|
int lineNum = nextLineNum(LINE_TEXT);
|
||||||
|
byte[] prevLineHash = prevLineHash32(LINE_TEXT);
|
||||||
|
|
||||||
BuiltBlock b = buildTextBlock(
|
BuiltBlock b = buildTextBlock(
|
||||||
globalNumber,
|
nextGlobal,
|
||||||
lineText,
|
LINE_TEXT,
|
||||||
lineNum,
|
lineNum,
|
||||||
hexToBytes32(globalLastHashHex),
|
hexToBytes32(globalLastHashHex),
|
||||||
prevLineHash,
|
prevLineHash,
|
||||||
text
|
text
|
||||||
);
|
);
|
||||||
|
|
||||||
String reqJson = buildAddBlockJson(FIXED_REQUEST_ID, blockchainName, globalNumber, globalLastHashHex, base64(b.fullBytes));
|
String req = buildAddBlockJson(TestConfig.BCH_NAME(), nextGlobal, globalLastHashHex, base64(b.fullBytes));
|
||||||
String resp = WsJsonRoundtripClient.sendOnce(wsUri, reqJson, Duration.ofSeconds(8));
|
String resp = WsJsonOneShot.request(req, timeout);
|
||||||
|
|
||||||
|
assert200("AddBlock(TEXT)", resp);
|
||||||
|
|
||||||
|
String serverLastGlobalHash = extractPayloadString(resp, "serverLastGlobalHash");
|
||||||
|
assertNotNull(serverLastGlobalHash, "TEXT: payload.serverLastGlobalHash must not be null");
|
||||||
|
assertEquals(64, serverLastGlobalHash.trim().length(), "TEXT: serverLastGlobalHash must be 64 hex chars");
|
||||||
|
|
||||||
String localHash = bytesToHex64(b.hash32);
|
String localHash = bytesToHex64(b.hash32);
|
||||||
|
assertEquals(localHash, serverLastGlobalHash, "TEXT: serverLastGlobalHash должен совпасть с локальным hash");
|
||||||
|
|
||||||
// обновляем состояние
|
// обновляем состояние
|
||||||
globalLastNumber = globalNumber;
|
globalLastNumber = nextGlobal;
|
||||||
globalLastHashHex = localHash;
|
globalLastHashHex = localHash;
|
||||||
lineLastNumber[lineText] = lineNum;
|
lineLastNumber[LINE_TEXT] = lineNum;
|
||||||
lineLastHashHex[lineText] = localHash;
|
lineLastHashHex[LINE_TEXT] = localHash;
|
||||||
|
|
||||||
return new AddBlockResult(reqJson, resp, localHash, b.hash32);
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Добавить REACT в lineReact, следующим lineNum, global=globalNumber, ссылка на (toGlobal,toHash32). */
|
/** Шлём следующий REACTION блок в line=2, ссылаясь на конкретный блок. */
|
||||||
public AddBlockResult addReaction(int globalNumber,
|
public BuiltBlock sendNextReaction(int reactionCode,
|
||||||
short lineReact,
|
|
||||||
int reactionCode,
|
|
||||||
String toBlockchainName,
|
String toBlockchainName,
|
||||||
int toBlockGlobalNumber,
|
int toBlockGlobalNumber,
|
||||||
byte[] toBlockHash32) {
|
byte[] toBlockHash32,
|
||||||
|
Duration timeout) {
|
||||||
|
assertNotNull(headerHash32, "REACTION нельзя слать до HEADER (headerHash32 == null)");
|
||||||
|
assertNotNull(toBlockHash32, "toBlockHash32 is null");
|
||||||
|
assertEquals(32, toBlockHash32.length, "toBlockHash32 must be 32 bytes");
|
||||||
|
|
||||||
int lineNum = nextLineNum(lineReact);
|
int nextGlobal = globalLastNumber + 1;
|
||||||
byte[] prevLineHash = prevLineHash32(lineReact);
|
int lineNum = nextLineNum(LINE_REACT);
|
||||||
|
byte[] prevLineHash = prevLineHash32(LINE_REACT);
|
||||||
|
|
||||||
BuiltBlock b = buildReactionBlock(
|
BuiltBlock b = buildReactionBlock(
|
||||||
globalNumber,
|
nextGlobal,
|
||||||
lineReact,
|
LINE_REACT,
|
||||||
lineNum,
|
lineNum,
|
||||||
hexToBytes32(globalLastHashHex),
|
hexToBytes32(globalLastHashHex),
|
||||||
prevLineHash,
|
prevLineHash,
|
||||||
@ -150,51 +156,38 @@ public final class AddBlockScenarioRunner {
|
|||||||
toBlockHash32
|
toBlockHash32
|
||||||
);
|
);
|
||||||
|
|
||||||
String reqJson = buildAddBlockJson(FIXED_REQUEST_ID, blockchainName, globalNumber, globalLastHashHex, base64(b.fullBytes));
|
String req = buildAddBlockJson(TestConfig.BCH_NAME(), nextGlobal, globalLastHashHex, base64(b.fullBytes));
|
||||||
String resp = WsJsonRoundtripClient.sendOnce(wsUri, reqJson, Duration.ofSeconds(8));
|
String resp = WsJsonOneShot.request(req, timeout);
|
||||||
|
|
||||||
|
assert200("AddBlock(REACT)", resp);
|
||||||
|
|
||||||
|
String serverLastGlobalHash = extractPayloadString(resp, "serverLastGlobalHash");
|
||||||
|
assertNotNull(serverLastGlobalHash, "REACT: payload.serverLastGlobalHash must not be null");
|
||||||
|
assertEquals(64, serverLastGlobalHash.trim().length(), "REACT: serverLastGlobalHash must be 64 hex chars");
|
||||||
|
|
||||||
String localHash = bytesToHex64(b.hash32);
|
String localHash = bytesToHex64(b.hash32);
|
||||||
|
assertEquals(localHash, serverLastGlobalHash, "REACT: serverLastGlobalHash должен совпасть с локальным hash");
|
||||||
|
|
||||||
// обновляем состояние
|
// обновляем состояние
|
||||||
globalLastNumber = globalNumber;
|
globalLastNumber = nextGlobal;
|
||||||
globalLastHashHex = localHash;
|
globalLastHashHex = localHash;
|
||||||
lineLastNumber[lineReact] = lineNum;
|
lineLastNumber[LINE_REACT] = lineNum;
|
||||||
lineLastHashHex[lineReact] = localHash;
|
lineLastHashHex[LINE_REACT] = localHash;
|
||||||
|
|
||||||
return new AddBlockResult(reqJson, resp, localHash, b.hash32);
|
return b;
|
||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================================
|
// getters для итогов/логов (если надо)
|
||||||
// RESULT HOLDER
|
public int globalLastNumber() { return globalLastNumber; }
|
||||||
// =================================================================================
|
public String globalLastHashHex() { return globalLastHashHex; }
|
||||||
|
public int lineLastNumber(short line) { return lineLastNumber[line]; }
|
||||||
public static final class AddBlockResult {
|
public String lineLastHashHex(short line) { return lineLastHashHex[line]; }
|
||||||
public final String requestJson;
|
|
||||||
public final String responseJson;
|
|
||||||
|
|
||||||
/** локально вычисленный hash (HEX64) именно для этого блока */
|
|
||||||
public final String localHashHex;
|
|
||||||
|
|
||||||
/** локальный hash32 (если надо ссылаться на блок дальше) */
|
|
||||||
public final byte[] localHash32;
|
|
||||||
|
|
||||||
public AddBlockResult(String requestJson, String responseJson, String localHashHex) {
|
|
||||||
this(requestJson, responseJson, localHashHex, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddBlockResult(String requestJson, String responseJson, String localHashHex, byte[] localHash32) {
|
|
||||||
this.requestJson = requestJson;
|
|
||||||
this.responseJson = responseJson;
|
|
||||||
this.localHashHex = localHashHex;
|
|
||||||
this.localHash32 = localHash32;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// LINE HELPERS
|
// INTERNALS: line helpers
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
/** Следующий lineNum: если в линии было N блоков, новый будет N+1 (для line>0). Для line0 тут только 0. */
|
/** Следующий lineNum: если в линии было N блоков, новый будет N+1 (для line>0). Для line0 здесь не используется. */
|
||||||
private int nextLineNum(short lineIndex) {
|
private int nextLineNum(short lineIndex) {
|
||||||
if (lineIndex < 0 || lineIndex > 7) throw new IllegalArgumentException("lineIndex must be 0..7");
|
if (lineIndex < 0 || lineIndex > 7) throw new IllegalArgumentException("lineIndex must be 0..7");
|
||||||
if (lineIndex == 0) return 0;
|
if (lineIndex == 0) return 0;
|
||||||
@ -228,15 +221,15 @@ public final class AddBlockScenarioRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// BUILD BLOCKS
|
// INTERNALS: build blocks
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
/** Небольшой холдер, чтобы сценарий мог использовать hash32 как prevGlobal/prevLine и как toBlockHash. */
|
/** Небольшой холдер, чтобы flow мог использовать hash32 как prevGlobal/prevLine и как toBlockHash. */
|
||||||
private static final class BuiltBlock {
|
public static final class BuiltBlock {
|
||||||
final byte[] fullBytes;
|
public final byte[] fullBytes;
|
||||||
final byte[] hash32;
|
public final byte[] hash32;
|
||||||
|
|
||||||
BuiltBlock(byte[] fullBytes, byte[] hash32) {
|
public BuiltBlock(byte[] fullBytes, byte[] hash32) {
|
||||||
this.fullBytes = fullBytes;
|
this.fullBytes = fullBytes;
|
||||||
this.hash32 = hash32;
|
this.hash32 = hash32;
|
||||||
}
|
}
|
||||||
@ -264,12 +257,6 @@ public final class AddBlockScenarioRunner {
|
|||||||
TextBody body = new TextBody(text);
|
TextBody body = new TextBody(text);
|
||||||
byte[] bodyBytes = body.toBytes();
|
byte[] bodyBytes = body.toBytes();
|
||||||
|
|
||||||
// ⚠️ ВАЖНО:
|
|
||||||
// У тебя сервер ругается: "Body is in wrong lineIndex expected=1 actual=0 (type=1 ver=1)".
|
|
||||||
// Это значит, что lineIndex хранится ВНУТРИ bodyBytes.
|
|
||||||
// Ниже — безопасный патч: предполагаем формат "type(1) + ver(1) + lineIndex(2)" и проставляем lineIndex.
|
|
||||||
bodyBytes = patchBodyLineIndexIfPresent(bodyBytes, lineIndex);
|
|
||||||
|
|
||||||
return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -292,9 +279,6 @@ public final class AddBlockScenarioRunner {
|
|||||||
|
|
||||||
byte[] bodyBytes = body.toBytes();
|
byte[] bodyBytes = body.toBytes();
|
||||||
|
|
||||||
// Аналогично TextBody — если внутри есть lineIndex, проставляем.
|
|
||||||
bodyBytes = patchBodyLineIndexIfPresent(bodyBytes, lineIndex);
|
|
||||||
|
|
||||||
return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
return buildSignedBlockFullBytes(globalNumber, lineIndex, lineBlockNumber, bodyBytes, prevGlobalHash32, prevLineHash32);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +305,7 @@ public final class AddBlockScenarioRunner {
|
|||||||
|
|
||||||
// Ключевой момент: preimage должен совпасть с серверным правилом.
|
// Ключевой момент: preimage должен совпасть с серверным правилом.
|
||||||
// Сервер НЕ получает prevLineHash по сети — он берёт его из своего состояния линии.
|
// Сервер НЕ получает prevLineHash по сети — он берёт его из своего состояния линии.
|
||||||
// Поэтому мы обязаны передавать сюда ровно тот же prevLineHash32 (см. prevLineHash32()).
|
// Поэтому в тесте мы обязаны передавать сюда ровно тот же prevLineHash32.
|
||||||
byte[] preimage = BchCryptoVerifier.buildPreimage(
|
byte[] preimage = BchCryptoVerifier.buildPreimage(
|
||||||
TestConfig.LOGIN(),
|
TestConfig.LOGIN(),
|
||||||
prevGlobalHash32,
|
prevGlobalHash32,
|
||||||
@ -346,33 +330,11 @@ public final class AddBlockScenarioRunner {
|
|||||||
return new BuiltBlock(full, hash32);
|
return new BuiltBlock(full, hash32);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Патч lineIndex внутри bodyBytes.
|
|
||||||
*
|
|
||||||
* Предположение (по твоей ошибке type=1 ver=1):
|
|
||||||
* bodyBytes[0] = type
|
|
||||||
* bodyBytes[1] = ver
|
|
||||||
* bodyBytes[2..3] = lineIndex (big-endian short)
|
|
||||||
*
|
|
||||||
* Если формат другой — скажешь, поменяю оффсет/проверки.
|
|
||||||
*/
|
|
||||||
private static byte[] patchBodyLineIndexIfPresent(byte[] bodyBytes, short lineIndex) {
|
|
||||||
if (bodyBytes == null) return null;
|
|
||||||
if (bodyBytes.length < 4) return bodyBytes;
|
|
||||||
|
|
||||||
// Патчим только для line>0 (для header line=0 и так норм).
|
|
||||||
if (lineIndex <= 0) return bodyBytes;
|
|
||||||
|
|
||||||
ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN).putShort(2, lineIndex);
|
|
||||||
return bodyBytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// JSON HELPERS
|
// INTERNALS: json helpers
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
private static String buildAddBlockJson(String requestId,
|
private static String buildAddBlockJson(String blockchainName,
|
||||||
String blockchainName,
|
|
||||||
int globalNumber,
|
int globalNumber,
|
||||||
String prevGlobalHashHex,
|
String prevGlobalHashHex,
|
||||||
String blockBytesB64) {
|
String blockBytesB64) {
|
||||||
@ -387,7 +349,24 @@ public final class AddBlockScenarioRunner {
|
|||||||
"blockBytesB64": "%s"
|
"blockBytesB64": "%s"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""".formatted(requestId, blockchainName, globalNumber, prevGlobalHashHex, blockBytesB64);
|
""".formatted(WsJsonOneShot.FIXED_REQUEST_ID, blockchainName, globalNumber, prevGlobalHashHex, blockBytesB64);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assert200(String op, String resp) {
|
||||||
|
int st = JsonParsers.status(resp);
|
||||||
|
assertEquals(200, st, op + ": expected status=200, but got=" + st + ", resp=" + resp);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String extractPayloadString(String json, String field) {
|
||||||
|
try {
|
||||||
|
com.fasterxml.jackson.databind.JsonNode root =
|
||||||
|
new com.fasterxml.jackson.databind.ObjectMapper().readTree(json);
|
||||||
|
com.fasterxml.jackson.databind.JsonNode payload = root.get("payload");
|
||||||
|
if (payload != null && payload.has(field)) {
|
||||||
|
return payload.get(field).asText();
|
||||||
|
}
|
||||||
|
} catch (Exception ignore) {}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String base64(byte[] bytes) {
|
private static String base64(byte[] bytes) {
|
||||||
@ -395,7 +374,7 @@ public final class AddBlockScenarioRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
// HEX HELPERS
|
// INTERNALS: hex helpers
|
||||||
// =================================================================================
|
// =================================================================================
|
||||||
|
|
||||||
private static byte[] hexToBytes32(String hex) {
|
private static byte[] hexToBytes32(String hex) {
|
||||||
@ -48,7 +48,7 @@ public class IT_03_AddBlock_NoAuth {
|
|||||||
// - requestId тут не важен, но пусть будет.
|
// - requestId тут не важен, но пусть будет.
|
||||||
// - отдельная авторизация не нужна, но пользователь должен существовать.
|
// - отдельная авторизация не нужна, но пользователь должен существовать.
|
||||||
String reqJson = JsonBuilders.addUser("it03-adduser-beforeall");
|
String reqJson = JsonBuilders.addUser("it03-adduser-beforeall");
|
||||||
|
/**
|
||||||
String resp = WsJsonOneShot.request(reqJson, Duration.ofSeconds(5));
|
String resp = WsJsonOneShot.request(reqJson, Duration.ofSeconds(5));
|
||||||
int st = JsonParsers.status(resp);
|
int st = JsonParsers.status(resp);
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ public class IT_03_AddBlock_NoAuth {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fail("User precondition failed. status=" + st + ", resp=" + resp);
|
fail("User precondition failed. status=" + st + ", resp=" + resp);
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
62
src/test/java/test/it/ws/WsJsonOneShot.java
Normal file
62
src/test/java/test/it/ws/WsJsonOneShot.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package test.it.ws;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.databind.JsonNode;
|
||||||
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
|
import test.it.utils.TestConfig;
|
||||||
|
import test.it.utils.WsTestClient;
|
||||||
|
|
||||||
|
import java.time.Duration;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WsJsonOneShot
|
||||||
|
*
|
||||||
|
* Утилита "отправил JSON -> получил JSON", строго:
|
||||||
|
* - на каждый request создаём НОВОЕ WS соединение
|
||||||
|
* - отправляем
|
||||||
|
* - ждём ответ
|
||||||
|
* - закрываем соединение
|
||||||
|
*
|
||||||
|
* Важно:
|
||||||
|
* - requestId тут не важен для человека, но важен для WsTestClient, чтобы сопоставить ответ.
|
||||||
|
* - поэтому ставим ВСЕГДА один и тот же requestId (FIXED_REQUEST_ID).
|
||||||
|
* - requestId НЕ логируем.
|
||||||
|
*/
|
||||||
|
public final class WsJsonOneShot {
|
||||||
|
|
||||||
|
private static final ObjectMapper MAPPER = new ObjectMapper();
|
||||||
|
|
||||||
|
/** Всегда один и тот же requestId. */
|
||||||
|
public static final String FIXED_REQUEST_ID = "it";
|
||||||
|
|
||||||
|
private WsJsonOneShot() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Отправить JSON строкой и вернуть JSON ответ строкой.
|
||||||
|
* Соединение создаётся и закрывается ВНУТРИ.
|
||||||
|
*/
|
||||||
|
public static String request(String json, Duration timeout) {
|
||||||
|
String patched = forceRequestId(json, FIXED_REQUEST_ID);
|
||||||
|
|
||||||
|
try (WsTestClient client = new WsTestClient(TestConfig.WS_URI)) {
|
||||||
|
// requestId нам нужен только как ключ ожидания в WsTestClient
|
||||||
|
return client.request(FIXED_REQUEST_ID, patched, timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Гарантируем, что requestId есть и равен FIXED_REQUEST_ID.
|
||||||
|
* Если JSON кривой — вернём как есть (тогда упадёт выше по логике, и это нормально для теста).
|
||||||
|
*/
|
||||||
|
private static String forceRequestId(String json, String requestId) {
|
||||||
|
try {
|
||||||
|
JsonNode root = MAPPER.readTree(json);
|
||||||
|
if (!(root instanceof ObjectNode obj)) return json;
|
||||||
|
|
||||||
|
obj.put("requestId", requestId);
|
||||||
|
return MAPPER.writeValueAsString(obj);
|
||||||
|
} catch (Exception ignore) {
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user