Потч работает добавление линий - ситуация сложная

тест падает
This commit is contained in:
AidarKC
2026-01-21 18:37:05 +03:00
parent 376d42cd79
commit 69cd33479b
13 changed files with 396 additions and 149 deletions
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
OUTFILE="all_files.txt"
# === Список файлов (ТОЛЬКО имена без расширений) ===
# пример: Main значит Main.java, Utils значит Utils.java
NAMES=(
"IT_04_UserParams_NoAuth"
"AddBlockSender"
"ChainState"
"JsonBuilders"
)
# очищаем или создаём файл
: > "$OUTFILE"
# Быстрый фильтр: сделаем хеш-таблицу из имён (ассоц. массив)
declare -A WANT=()
for name in "${NAMES[@]}"; do
WANT["$name"]=1
done
# собрать только нужные *.java по базовому имени
find . -type f -name "*.java" | sort | while read -r f; do
base="$(basename "$f" .java)"
if [[ -n "${WANT[$base]+x}" ]]; then
cat "$f" >> "$OUTFILE"
echo >> "$OUTFILE" # пустая строка-разделитель
fi
done
# скопировать весь файл в буфер обмена (Wayland)
wl-copy < "$OUTFILE"
echo "Готово!"
echo "Выбрано имён: ${#NAMES[@]}"
echo "Все нужные .java файлы собраны в $OUTFILE"
echo "Содержимое скопировано в буфер обмена (Wayland)"
-20
View File
@@ -1,20 +0,0 @@
#!/usr/bin/env bash
set -euo pipefail
OUTFILE="all_files.txt"
# очищаем или создаём файл
: > "$OUTFILE"
# собрать только *.java файлы и вывести их содержимое в файл
find . -type f -name "*.java" | sort | while read -r f; do
cat "$f" >> "$OUTFILE"
echo >> "$OUTFILE" # пустая строка-разделитель
done
# скопировать весь файл в буфер обмена (Wayland)
wl-copy < "$OUTFILE"
echo "Готово!"
echo "Все .java файлы собраны в $OUTFILE"
echo "Содержимое скопировано в буфер обмена (Wayland)"
@@ -1,6 +1,5 @@
package test.it.blockchain;
import blockchain.MsgSubType;
import blockchain.body.BodyRecord;
import blockchain.body.BodyHasLine;
import blockchain.body.CreateChannelBody;
@@ -22,9 +21,12 @@ import java.util.Map;
* - CONNECTION (type=3): одна линия
* - USER_PARAM (type=4): одна линия
*
* Важно:
* ВАЖНО:
* - prevLineNumber — это GLOBAL blockNumber предыдущего блока линии.
* - thisLineNumber — внутренний номер линии (для постов: 0,1,2...; для тех-линии: 1,2,3...)
* - lineCode — код линии:
* * 0 для канала "0" и для "простых" линий (connection/user_param/tech)
* * для каналов !=0: lineCode = blockNumber "заглавия" канала (CREATE_CHANNEL)
*/
public final class ChainState {
@@ -79,14 +81,16 @@ public final class ChainState {
// ---------- TEXT channels ----------
public static final class ChannelLineState {
final int rootBlockNumber;
final int lineCode; // для каналов: = rootBlockNumber; для канала 0: 0
final int rootBlockNumber; // 0 для канала 0, иначе blockNumber CREATE_CHANNEL
final String rootHashHex;
int lastGlobalNumber;
String lastHashHex;
int lastThisLineNumber; // перед первым постом = -1, чтобы первый был 0
ChannelLineState(int rootBlockNumber, String rootHashHex) {
ChannelLineState(int lineCode, int rootBlockNumber, String rootHashHex) {
this.lineCode = lineCode;
this.rootBlockNumber = rootBlockNumber;
this.rootHashHex = rootHashHex;
this.lastGlobalNumber = rootBlockNumber;
@@ -95,7 +99,7 @@ public final class ChainState {
}
}
// rootBlockNumber -> state
// lineCode -> state (для канала 0 lineCode=0)
private final Map<Integer, ChannelLineState> textChannels = new HashMap<>();
public ChainState() {
@@ -134,18 +138,20 @@ public final class ChainState {
// -------------------- line helpers --------------------
public static final class NextLine {
public final int lineCode;
public final int prevLineNumber; // GLOBAL blockNumber
public final byte[] prevLineHash32; // 32 bytes
public final int thisLineNumber; // внутр. номер линии
public NextLine(int prevLineNumber, byte[] prevLineHash32, int thisLineNumber) {
public NextLine(int lineCode, int prevLineNumber, byte[] prevLineHash32, int thisLineNumber) {
this.lineCode = lineCode;
this.prevLineNumber = prevLineNumber;
this.prevLineHash32 = (prevLineHash32 == null ? null : prevLineHash32.clone());
this.thisLineNumber = thisLineNumber;
}
}
/** Следующие line-поля для TECH/CONNECTION/USER_PARAM. */
/** Следующие line-поля для TECH/CONNECTION/USER_PARAM. lineCode=0. */
public NextLine nextLineByType(short type) {
if (!hasHeader()) {
throw new IllegalStateException("Нельзя формировать line-поля до HEADER (нет headerHash32)");
@@ -154,12 +160,15 @@ public final class ChainState {
int t = type & 0xFFFF;
if (t == TYPE_TECH) {
// tech-line: prev = последний TECH; первый CREATE_CHANNEL -> prev = HEADER
if (techLine.lastGlobalNumber == -1) {
// после HEADER мы должны инициализировать techLine (делаем в applyHeader)
throw new IllegalStateException("TECH line is not initialized yet");
}
return new NextLine(techLine.lastGlobalNumber, hexToBytes32(techLine.lastHashHex), techLine.lastThisLineNumber + 1);
return new NextLine(
0,
techLine.lastGlobalNumber,
hexToBytes32(techLine.lastHashHex),
techLine.lastThisLineNumber + 1
);
}
if (t == TYPE_CONNECTION) {
@@ -175,35 +184,55 @@ public final class ChainState {
private NextLine nextSimpleLine(SimpleLineState ls) {
if (ls.lastGlobalNumber == -1) {
// первый блок линии ссылается на HEADER (block#0)
return new NextLine(0, headerHash32.clone(), 1);
return new NextLine(0, 0, headerHash32.clone(), 1);
}
if (ls.lastHashHex == null || ls.lastHashHex.isBlank()) {
throw new IllegalStateException("LineState.lastHashHex пуст, но lastGlobalNumber!=-1");
}
return new NextLine(ls.lastGlobalNumber, hexToBytes32(ls.lastHashHex), ls.lastThisLineNumber + 1);
return new NextLine(0, ls.lastGlobalNumber, hexToBytes32(ls.lastHashHex), ls.lastThisLineNumber + 1);
}
/** Следующие line-поля для TEXT-канала по rootBlockNumber. */
public NextLine nextTextLineByRoot(int rootBlockNumber) {
/**
* Следующие line-поля для TEXT-канала по lineCode.
* Для канала 0: lineCode=0.
* Для других каналов: lineCode = rootBlockNumber (CREATE_CHANNEL blockNumber).
*/
public NextLine nextTextLineByCode(int lineCode) {
if (!hasHeader()) throw new IllegalStateException("No HEADER");
ChannelLineState cs = textChannels.get(rootBlockNumber);
if (cs == null) throw new IllegalStateException("Unknown TEXT channel rootBlockNumber=" + rootBlockNumber);
ChannelLineState cs = textChannels.get(lineCode);
if (cs == null) throw new IllegalStateException("Unknown TEXT channel lineCode=" + lineCode);
return new NextLine(
lineCode,
cs.lastGlobalNumber,
hexToBytes32(cs.lastHashHex),
cs.lastThisLineNumber + 1
);
}
/** Зарегистрировать новый канал TEXT по root = CREATE_CHANNEL block. */
public void registerTextChannelRoot(int rootBlockNumber, byte[] rootHash32) {
if (rootBlockNumber <= 0) throw new IllegalArgumentException("rootBlockNumber must be > 0 for custom channel");
if (rootHash32 == null || rootHash32.length != 32) throw new IllegalArgumentException("rootHash32 invalid");
textChannels.put(rootBlockNumber, new ChannelLineState(rootBlockNumber, bytesToHex64(rootHash32)));
/** Старое имя — оставил для удобства: rootBlockNumber == lineCode для каналов. */
public NextLine nextTextLineByRoot(int rootBlockNumber) {
return nextTextLineByCode(rootBlockNumber);
}
/** root канала "0" (по умолчанию) — это HEADER block#0. */
/**
* Зарегистрировать новый канал TEXT:
* - lineCode = rootBlockNumber (blockNumber CREATE_CHANNEL)
* ИДЕМПОТЕНТНО: если уже зарегистрирован — ничего не делаем.
*/
public void registerTextChannelRoot(int rootBlockNumber, byte[] rootHash32) {
if (rootBlockNumber < 0) throw new IllegalArgumentException("rootBlockNumber must be >= 0");
if (rootHash32 == null || rootHash32.length != 32) throw new IllegalArgumentException("rootHash32 invalid");
if (textChannels.containsKey(rootBlockNumber)) {
return; // уже есть — не трогаем, чтобы не сбросить lastThisLineNumber и т.д.
}
int lineCode = rootBlockNumber;
textChannels.put(lineCode, new ChannelLineState(lineCode, rootBlockNumber, bytesToHex64(rootHash32)));
}
/** root/lineCode канала "0" (по умолчанию) — это HEADER block#0, lineCode=0. */
public int rootChannel0() {
return 0;
}
@@ -240,8 +269,8 @@ public final class ChainState {
techLine.lastHashHex = hex64;
techLine.lastThisLineNumber = 0;
// TEXT channel "0" root = HEADER, первый пост будет thisLineNumber=0
textChannels.put(0, new ChannelLineState(0, hex64));
// TEXT channel "0" root = HEADER, lineCode=0
registerTextChannelRoot(0, hash32);
return;
}
@@ -253,6 +282,11 @@ public final class ChainState {
techLine.lastGlobalNumber = blockNumber;
techLine.lastHashHex = hex64;
techLine.lastThisLineNumber = ccb.thisLineNumber;
// ВАЖНО: CREATE_CHANNEL — это root нового текстового канала:
// lineCode для этого канала = blockNumber CREATE_CHANNEL
registerTextChannelRoot(blockNumber, hash32);
return;
}
@@ -273,10 +307,14 @@ public final class ChainState {
// ---- TEXT channels (POST/EDIT_POST) ----
if (t == TYPE_TEXT && body instanceof TextBody tb) {
if (tb.isLineMessage()) {
// ищем канал по совпадению prevLineNumber с lastGlobalNumber канала
ChannelLineState channel = findTextChannelByLastGlobal(tb.prevLineNumber);
int lineCode = tb.lineCode;
ChannelLineState channel = textChannels.get(lineCode);
if (channel == null) {
throw new IllegalStateException("TEXT line message prevLineNumber=" + tb.prevLineNumber + " не привязан ни к одному каналу (канал root не зарегистрирован?)");
throw new IllegalStateException(
"TEXT line message has unknown lineCode=" + lineCode +
" (канал не зарегистрирован; ждали CREATE_CHANNEL или HEADER)"
);
}
channel.lastGlobalNumber = blockNumber;
@@ -286,13 +324,6 @@ public final class ChainState {
}
}
private ChannelLineState findTextChannelByLastGlobal(int prevLineNumber) {
for (ChannelLineState cs : textChannels.values()) {
if (cs.lastGlobalNumber == prevLineNumber) return cs;
}
return null;
}
// -------------------- utils --------------------
private static byte[] hexToBytes32(String hex) {
@@ -64,13 +64,14 @@ public class IT_03_AddBlock_NoAuth {
assertTrue(st1.hasHeader());
// канал "0" (root=HEADER) — по умолчанию существует
int root0 = st1.rootChannel0();
int root0 = st1.rootChannel0(); // lineCode для канала "0" = 0
// POST в канал "0"
{
var ln = st1.nextTextLineByRoot(root0);
sender1.send(new TextBody(
MsgSubType.TEXT_POST,
root0, // lineCode
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
"U1: story/post in channel 0",
null, null, null
@@ -87,11 +88,12 @@ public class IT_03_AddBlock_NoAuth {
{
var ln = st1.nextLineByType(ChainState.TYPE_TECH);
sender1.send(new CreateChannelBody(
0, // lineCode для TECH линии
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
"News"
), t);
newsRootBlock = st1.lastBlockNumber();
newsRootBlock = st1.lastBlockNumber(); // root канала = blockNumber CREATE_CHANNEL
newsRootHash = st1.getHash32(newsRootBlock);
assertNotNull(newsRootHash);
@@ -106,6 +108,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st1.nextTextLineByRoot(newsRootBlock);
sender1.send(new TextBody(
MsgSubType.TEXT_POST,
newsRootBlock, // lineCode = root блока канала (CREATE_CHANNEL)
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
"U1: News post #0",
null, null, null
@@ -121,18 +124,19 @@ public class IT_03_AddBlock_NoAuth {
var ln = st1.nextTextLineByRoot(newsRootBlock);
sender1.send(new TextBody(
MsgSubType.TEXT_POST,
newsRootBlock, // lineCode
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
"U1: News post #1",
null, null, null
), t);
}
// EDIT_POST (не увеличивает thisLineNumber, но является частью линии)
// EDIT_POST (является частью линии; lineCode обязателен)
{
var ln = st1.nextTextLineByRoot(newsRootBlock);
// edit должен иметь thisLineNumber как у предыдущего сообщения линии (ChainState это уже даёт)
sender1.send(new TextBody(
MsgSubType.TEXT_EDIT_POST,
newsRootBlock, // lineCode
ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
"U1: News post #0 (EDIT)",
null,
@@ -151,14 +155,13 @@ public class IT_03_AddBlock_NoAuth {
assertTrue(st2.hasHeader());
// REPLY (20): ответ на post в чужом блокчейне/канале
// ВАЖНО: REPLY не имеет line-полей вообще, поэтому используем фабрику newReply().
{
sender2.send(new TextBody(
MsgSubType.TEXT_REPLY,
-1, new byte[32], -1, // для replies линии нет
"U2: reply to U1 News post #0 (cross-chain)",
sender2.send(TextBody.newReply(
bch1,
newsPost0Block,
newsPost0Hash
newsPost0Hash,
"U2: reply to U1 News post #0 (cross-chain)"
), t);
}