From 5fe41c7656d7235278787894f617bf6d1bc567236209f756a112bfb94e76ef48 Mon Sep 17 00:00:00 2001 From: AidarKC Date: Tue, 13 Jan 2026 17:54:10 +0300 Subject: [PATCH] 13 01 25 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit мелкие исправления. Убрал оставшиеся странные связи линии --- .../src/main/java/blockchain/LineIndex.java | 34 ++--- .../test/it/blockchain/AddBlockSender.java | 26 ++-- .../java/test/it/blockchain/ChainState.java | 131 +++++++++--------- .../test/it/cases/IT_03_AddBlock_NoAuth.java | 37 ++--- 4 files changed, 116 insertions(+), 112 deletions(-) diff --git a/shine-server-blockchain/src/main/java/blockchain/LineIndex.java b/shine-server-blockchain/src/main/java/blockchain/LineIndex.java index f2f5cfb..0638b42 100644 --- a/shine-server-blockchain/src/main/java/blockchain/LineIndex.java +++ b/shine-server-blockchain/src/main/java/blockchain/LineIndex.java @@ -1,17 +1,17 @@ -package blockchain; - -/** - * LineIndex — канонические номера линий блокчейна. - * - * Линия = независимая последовательность блоков внутри одного блокчейна. - */ -public final class LineIndex { - - private LineIndex() {} - - public static final short HEADER = 0; // genesis / идентификация - public static final short TEXT = 1; // сообщения да надо - public static final short REACTION = 2; // реакции не надо - public static final short CONNECTION = 3; // связи (friend/contact/follow) да надо - public static final short USER_PARAM = 4; // параметры профиля да надо -} \ No newline at end of file +//package blockchain; +// +///** +// * LineIndex — канонические номера линий блокчейна. +// * +// * Линия = независимая последовательность блоков внутри одного блокчейна. +// */ +//public final class LineIndex { +// +// private LineIndex() {} +// +// public static final short HEADER = 0; // genesis / идентификация +// public static final short TEXT = 1; // сообщения да надо +// public static final short REACTION = 2; // реакции не надо +// public static final short CONNECTION = 3; // связи (friend/contact/follow) да надо +// public static final short USER_PARAM = 4; // параметры профиля да надо +//} \ No newline at end of file diff --git a/src/test/java/test/it/blockchain/AddBlockSender.java b/src/test/java/test/it/blockchain/AddBlockSender.java index 68cd8cf..80e77cb 100644 --- a/src/test/java/test/it/blockchain/AddBlockSender.java +++ b/src/test/java/test/it/blockchain/AddBlockSender.java @@ -19,6 +19,10 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; * - block хранит только preimage + signature * - hash32 вычисляется как sha256(preimage) * - signature = Ed25519.sign(hash32) + * + * ВАЖНО: + * - Линии (prevLine/thisLine) по ТЗ нужны только для TEXT/CONNECTION/USER_PARAM. + * - Здесь НЕТ обращения к blockchain.LineIndex. */ public final class AddBlockSender { @@ -38,6 +42,7 @@ public final class AddBlockSender { this.blockchainName = blockchainName; this.loginPrivKey = (loginPrivKey == null ? null : loginPrivKey.clone()); if (this.ws == null) throw new IllegalArgumentException("ws == null"); + if (this.state == null) throw new IllegalArgumentException("state == null"); if (this.loginPrivKey == null) throw new IllegalArgumentException("loginPrivKey == null"); } @@ -97,7 +102,7 @@ public final class AddBlockSender { String serverLastHash = JsonMini.extractPayloadString(resp, "serverLastBlockHash"); if (serverLastHash == null) { - // на случай старого имени, но по твоей просьбе мы на это больше не опираемся + // на всякий случай, но ты говорил старое не поддерживаем — оставил мягко serverLastHash = JsonMini.extractPayloadString(resp, "serverLastGlobalHash"); } @@ -113,13 +118,13 @@ public final class AddBlockSender { assertEquals(localHashHex, serverLastHash, op + ": serverLastBlockHash must match local hash"); + // фиксируем в state глобальную цепочку + (если нужно) line-state по TYPE state.applyAppendedBlock(blockNumber, entry.getHash32(), isHeader, type); - // если это line-body — обновим thisLineNumber в state (для nextLine()) + // если это line-body — обновим thisLineNumber в state (для nextLineByType()) if (body instanceof BodyHasLine hl) { - short lineIndex = lineIndexByType(type); - if (lineIndex != -1) { - state.applyThisLineNumber(lineIndex, hl.thisLineNumber()); + if (ChainState.isLineType(type)) { + state.applyThisLineNumberByType(type, hl.thisLineNumber()); } } @@ -219,15 +224,4 @@ public final class AddBlockSender { return bb.array(); } - - private static short lineIndexByType(short type) { - int t = type & 0xFFFF; - return switch (t) { - case 0 -> blockchain.LineIndex.HEADER; - case 1 -> blockchain.LineIndex.TEXT; - case 3 -> blockchain.LineIndex.CONNECTION; - case 4 -> blockchain.LineIndex.USER_PARAM; - default -> (short) -1; - }; - } } \ No newline at end of file diff --git a/src/test/java/test/it/blockchain/ChainState.java b/src/test/java/test/it/blockchain/ChainState.java index 0b76f75..ad8e411 100644 --- a/src/test/java/test/it/blockchain/ChainState.java +++ b/src/test/java/test/it/blockchain/ChainState.java @@ -1,29 +1,36 @@ package test.it.blockchain; -import blockchain.LineIndex; - import java.util.Arrays; import java.util.HashMap; import java.util.Map; /** - * ChainState — состояние цепочки + состояние линий (только тех, где они нужны): + * ChainState — состояние глобальной цепочки + состояние линий (только тех, где они нужны). * * Глобальная цепочка: * - lastBlockNumber / lastBlockHashHex * - map blockNumber -> hash32 (для ссылок reply/edit/reaction) * - * Линии (по ТЗ нужны): - * - TEXT (1) - * - CONNECTION (3) - * - USER_PARAM (4) + * Линии по ТЗ нужны только для: + * - TEXT (type=1) + * - CONNECTION (type=3) + * - USER_PARAM (type=4) * * prevLineNumber по ТЗ — это GLOBAL blockNumber предыдущего блока линии. * thisLineNumber — внутренний номер линии (мы ведём локально: 1,2,3...) + * + * ВАЖНО: + * - Здесь НЕТ обращения к blockchain.LineIndex. + * - Линии адресуются по msg_type (type). */ public final class ChainState { - public static final int LINES_MAX = 8; + // какие msg_type имеют линейную цепочку по ТЗ + public static final short TYPE_HEADER = 0; + public static final short TYPE_TEXT = 1; + public static final short TYPE_REACTION = 2; + public static final short TYPE_CONNECTION = 3; + public static final short TYPE_USER_PARAM = 4; private static final byte[] ZERO32 = new byte[32]; private static final String ZERO64 = "0".repeat(64); @@ -35,17 +42,34 @@ public final class ChainState { // header (block#0) private byte[] headerHash32 = null; - // per-line state (только для LineIndex.TEXT/CONNECTION/USER_PARAM) - private final int[] lineLastGlobalNumber = new int[LINES_MAX]; // последний GLOBAL номер блока в линии - private final String[] lineLastHashHex = new String[LINES_MAX]; // hash последнего блока линии - private final int[] lineLastThisLineNumber = new int[LINES_MAX]; // последний thisLineNumber (внутренний) + /** + * line state per TYPE (только для TEXT/CONNECTION/USER_PARAM): + * - lastGlobalNumber: последний GLOBAL blockNumber в линии + * - lastHashHex: hash последнего блока линии + * - lastThisLineNumber: последний thisLineNumber (внутренний) + */ + private static final class LineState { + int lastGlobalNumber = -1; + String lastHashHex = ""; + int lastThisLineNumber = 0; + + void reset() { + lastGlobalNumber = -1; + lastHashHex = ""; + lastThisLineNumber = 0; + } + } + + private final LineState textLine = new LineState(); + private final LineState connectionLine = new LineState(); + private final LineState userParamLine = new LineState(); private final Map hash32ByNumber = new HashMap<>(); public ChainState() { - Arrays.fill(lineLastGlobalNumber, -1); - Arrays.fill(lineLastHashHex, ""); - Arrays.fill(lineLastThisLineNumber, 0); + textLine.reset(); + connectionLine.reset(); + userParamLine.reset(); } // -------------------- global getters -------------------- @@ -89,30 +113,33 @@ public final class ChainState { } } - /** Следующие line-поля для указанной линии (только TEXT/CONNECTION/USER_PARAM). */ - public NextLine nextLine(short lineIndex) { - checkLine(lineIndex); - if (!isLineUsed(lineIndex)) { - throw new IllegalArgumentException("Line " + lineIndex + " не используется для BodyHasLine по ТЗ"); + /** Является ли type "линейным" по ТЗ (т.е. нужно вести prevLine/thisLine). */ + public static boolean isLineType(short type) { + int t = type & 0xFFFF; + return t == TYPE_TEXT || t == TYPE_CONNECTION || t == TYPE_USER_PARAM; + } + + /** Следующие line-поля для указанного TYPE (только TEXT/CONNECTION/USER_PARAM). */ + public NextLine nextLineByType(short type) { + if (!isLineType(type)) { + throw new IllegalArgumentException("Type " + (type & 0xFFFF) + " не использует line-поля по ТЗ"); } if (!hasHeader()) { throw new IllegalStateException("Нельзя формировать line-поля до HEADER (нет headerHash32)"); } - int lastGlobal = lineLastGlobalNumber[lineIndex]; - int lastThis = lineLastThisLineNumber[lineIndex]; + LineState ls = lineStateByType(type); - if (lastGlobal == -1) { + if (ls.lastGlobalNumber == -1) { // первый блок линии ссылается на HEADER (block#0) return new NextLine(0, headerHash32.clone(), 1); } - String lastHex = lineLastHashHex[lineIndex]; - if (lastHex == null || lastHex.isBlank()) { - throw new IllegalStateException("lineLastHashHex[" + lineIndex + "] пуст, но lastGlobal!=-1"); + if (ls.lastHashHex == null || ls.lastHashHex.isBlank()) { + throw new IllegalStateException("LineState.lastHashHex пуст, но lastGlobalNumber!=-1 (type=" + (type & 0xFFFF) + ")"); } - return new NextLine(lastGlobal, hexToBytes32(lastHex), lastThis + 1); + return new NextLine(ls.lastGlobalNumber, hexToBytes32(ls.lastHashHex), ls.lastThisLineNumber + 1); } // -------------------- apply -------------------- @@ -140,52 +167,32 @@ public final class ChainState { hash32ByNumber.put(blockNumber, hash32.clone()); - // обновляем line-state только для линий, которые "надо" по ТЗ - short lineIndex = lineIndexByType(type); - if (lineIndex != -1 && isLineUsed(lineIndex)) { - lineLastGlobalNumber[lineIndex] = blockNumber; - lineLastHashHex[lineIndex] = hex64; - - // thisLineNumber мы берём из тела, но здесь его нет. - // Поэтому thisLineNumber должен обновляться там, где формируются тела (в тестах), - // либо AddBlockSender может прокинуть его отдельно. - // Чтобы не дублировать контракт — здесь оставляем как есть. + // обновляем line-state только если этот type по ТЗ линейный + if (isLineType(type)) { + LineState ls = lineStateByType(type); + ls.lastGlobalNumber = blockNumber; + ls.lastHashHex = hex64; + // thisLineNumber обновляется отдельным вызовом (см. applyThisLineNumberByType) } } /** В тестах удобно явно обновлять thisLineNumber после успешной отправки line-body. */ - public void applyThisLineNumber(short lineIndex, int thisLineNumber) { - checkLine(lineIndex); - if (!isLineUsed(lineIndex)) return; - lineLastThisLineNumber[lineIndex] = thisLineNumber; + public void applyThisLineNumberByType(short type, int thisLineNumber) { + if (!isLineType(type)) return; + LineState ls = lineStateByType(type); + ls.lastThisLineNumber = thisLineNumber; } - // -------------------- mapping -------------------- - - /** По type блока определяем lineIndex. Reaction line по твоему ТЗ "не надо". */ - private static short lineIndexByType(short type) { + private LineState lineStateByType(short type) { int t = type & 0xFFFF; return switch (t) { - case 0 -> LineIndex.HEADER; - case 1 -> LineIndex.TEXT; - case 3 -> LineIndex.CONNECTION; - case 4 -> LineIndex.USER_PARAM; - default -> (short) -1; // reaction/unknown => line state not used + case TYPE_TEXT -> textLine; + case TYPE_CONNECTION -> connectionLine; + case TYPE_USER_PARAM -> userParamLine; + default -> throw new IllegalArgumentException("Type " + t + " не имеет LineState по ТЗ"); }; } - private static boolean isLineUsed(short lineIndex) { - return lineIndex == LineIndex.TEXT - || lineIndex == LineIndex.CONNECTION - || lineIndex == LineIndex.USER_PARAM; - } - - private static void checkLine(short lineIndex) { - if (lineIndex < 0 || lineIndex >= LINES_MAX) { - throw new IllegalArgumentException("lineIndex must be 0.." + (LINES_MAX - 1)); - } - } - // -------------------- utils -------------------- private static byte[] hexToBytes32(String hex) { diff --git a/src/test/java/test/it/cases/IT_03_AddBlock_NoAuth.java b/src/test/java/test/it/cases/IT_03_AddBlock_NoAuth.java index 37c7fd9..dc4c500 100644 --- a/src/test/java/test/it/cases/IT_03_AddBlock_NoAuth.java +++ b/src/test/java/test/it/cases/IT_03_AddBlock_NoAuth.java @@ -1,6 +1,5 @@ package test.it.cases; -import blockchain.LineIndex; import blockchain.body.*; import blockchain.MsgSubType; import test.it.blockchain.AddBlockSender; @@ -16,6 +15,10 @@ import static org.junit.jupiter.api.Assertions.*; /** * IT_03_AddBlock_NoAuth — обновлён под новый формат блоков (ТЗ). + * + * ВАЖНО: + * - НЕТ обращения к blockchain.LineIndex (можно удалить LineIndex.java). + * - Линии берём через ChainState.nextLineByType(TYPE_...). */ public class IT_03_AddBlock_NoAuth { @@ -59,7 +62,7 @@ public class IT_03_AddBlock_NoAuth { // TEXT_NEW x3 (с line) { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_NEW, "Hello #1 (NEW) from IT_03 test", @@ -67,7 +70,7 @@ public class IT_03_AddBlock_NoAuth { ), t); } { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_NEW, "Hello #2 (NEW) from IT_03 test", @@ -75,7 +78,7 @@ public class IT_03_AddBlock_NoAuth { ), t); } { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_NEW, "Hello #3 (NEW) from IT_03 test", @@ -92,7 +95,7 @@ public class IT_03_AddBlock_NoAuth { // TEXT_REPLY x2 (с line + target) { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_REPLY, "Reply to TEXT#1", @@ -100,7 +103,7 @@ public class IT_03_AddBlock_NoAuth { ), t); } { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_REPLY, "Reply to TEXT#3", @@ -114,7 +117,7 @@ public class IT_03_AddBlock_NoAuth { // TEXT_EDIT x3 (с line + target) { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_EDIT, "Hello #2 (EDIT#1) from IT_03 test", @@ -122,7 +125,7 @@ public class IT_03_AddBlock_NoAuth { ), t); } { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_EDIT, "Hello #2 (EDIT#2) from IT_03 test", @@ -130,7 +133,7 @@ public class IT_03_AddBlock_NoAuth { ), t); } { - var ln = st1.nextLine(LineIndex.TEXT); + var ln = st1.nextLineByType(ChainState.TYPE_TEXT); sender1.send(new TextBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.TEXT_EDIT, "Hello #3 (EDIT#1) from IT_03 test", @@ -149,7 +152,7 @@ public class IT_03_AddBlock_NoAuth { // USER_PARAM (с line) { - var ln = st2.nextLine(LineIndex.USER_PARAM); + var ln = st2.nextLineByType(ChainState.TYPE_USER_PARAM); sender2.send(new UserParamBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, "Anya", "Amsterdam, Example street 10" ), t); @@ -171,7 +174,7 @@ public class IT_03_AddBlock_NoAuth { // u1 -> follow u2 { - var ln = st1.nextLine(LineIndex.CONNECTION); + var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_FOLLOW, u2, bch2, 0, new byte[32] @@ -180,7 +183,7 @@ public class IT_03_AddBlock_NoAuth { // u1 -> follow u3 { - var ln = st1.nextLine(LineIndex.CONNECTION); + var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_FOLLOW, u3, bch3, 0, new byte[32] @@ -189,7 +192,7 @@ public class IT_03_AddBlock_NoAuth { // u2 -> follow u1 { - var ln = st2.nextLine(LineIndex.CONNECTION); + var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_FOLLOW, u1, bch1, 0, new byte[32] @@ -198,7 +201,7 @@ public class IT_03_AddBlock_NoAuth { // friend/unfriend как было, но тоже по CONNECTION линии { - var ln = st2.nextLine(LineIndex.CONNECTION); + var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_FRIEND, u1, bch1, 0, new byte[32] @@ -207,13 +210,13 @@ public class IT_03_AddBlock_NoAuth { // user1 param + friend to u2 { - var ln = st1.nextLine(LineIndex.USER_PARAM); + var ln = st1.nextLineByType(ChainState.TYPE_USER_PARAM); sender1.send(new UserParamBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, "Anna", "Gareeva" ), t); } { - var ln = st1.nextLine(LineIndex.CONNECTION); + var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_FRIEND, u2, bch2, 0, new byte[32] @@ -221,7 +224,7 @@ public class IT_03_AddBlock_NoAuth { } { - var ln = st2.nextLine(LineIndex.CONNECTION); + var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, MsgSubType.CONNECTION_UNFRIEND, u1, bch1, 0, new byte[32]