Переписал код кучи классов перешёл на новый надеюсь теперь подходящий формат блоков

и тесты переделал.

Но пока остались баги и тесты не проходят (в частности пользователи не создаются - ошибка в бд)
This commit is contained in:
AidarKC
2026-01-13 16:18:38 +03:00
parent b7025dde59
commit e9e05c1192
23 changed files with 1516 additions and 2379 deletions
@@ -1,6 +1,9 @@
// =======================
// blockchain/body/ReactionBody.java (ИЗМЕНЁННЫЙ: bodyBytes без type/subType/version, НЕТ линейных полей)
// =======================
package blockchain.body;
import blockchain.LineIndex;
import shine.db.MsgSubType;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
@@ -9,22 +12,18 @@ import java.util.Arrays;
import java.util.Objects;
/**
* ReactionBody — type=2, version=1.
* ReactionBody — type=2, version=1 (в заголовке блока).
*
* Формат bodyBytes (BigEndian):
* [2] type=2
* [2] ver=1
*
* [2] subType (uint16) — подтип реакции
* 1 = LIKE (лайк)
* subType (в заголовке блока):
* 1 = LIKE
*
* bodyBytes (BigEndian), новый формат:
* [1] toBlockchainNameLen (uint8)
* [N] toBlockchainName UTF-8
* [4] toBlockGlobalNumber (int32)
* [32] toBlockHash32 (raw 32 bytes)
*
* ЛИНИЯ:
* - строго lineIndex=2
* ЛИНИИ НЕТ.
*/
public final class ReactionBody implements BodyRecord, BodyHasTarget {
@@ -33,40 +32,34 @@ public final class ReactionBody implements BodyRecord, BodyHasTarget {
public static final int KEY = ((TYPE & 0xFFFF) << 16) | (VER & 0xFFFF);
// subType:
public static final short SUB_LIKE = 1;
public final short subType;
public final short subType; // из header
public final short version; // из header
public final String toBlockchainName;
public final int toBlockGlobalNumber;
public final byte[] toBlockHash32;
/** Десериализация из полного bodyBytes (включая type/version/subType). */
public ReactionBody(byte[] bodyBytes) {
public ReactionBody(short subType, short version, byte[] bodyBytes) {
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
// минимум: type[2]+ver[2]+subType[2]+nameLen[1]+name[1]+global[4]+hash[32]
if (bodyBytes.length < 2 + 2 + 2 + 1 + 1 + 4 + 32) {
throw new IllegalArgumentException("ReactionBody too short");
this.subType = subType;
this.version = version;
if ((this.version & 0xFFFF) != (VER & 0xFFFF)) {
throw new IllegalArgumentException("ReactionBody version must be 1, got=" + (this.version & 0xFFFF));
}
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
short type = bb.getShort();
short ver = bb.getShort();
if (type != TYPE || ver != VER)
throw new IllegalArgumentException("Not ReactionBody: type=" + type + " ver=" + ver);
this.subType = bb.getShort();
if (this.subType != SUB_LIKE) {
if ((this.subType & 0xFFFF) != (MsgSubType.REACTION_LIKE & 0xFFFF)) {
throw new IllegalArgumentException("Bad reaction subType: " + (this.subType & 0xFFFF));
}
// минимум: nameLen[1]+name[1]+global[4]+hash[32]
if (bodyBytes.length < 1 + 1 + 4 + 32) throw new IllegalArgumentException("ReactionBody too short");
ByteBuffer bb = ByteBuffer.wrap(bodyBytes).order(ByteOrder.BIG_ENDIAN);
int nameLen = Byte.toUnsignedInt(bb.get());
if (nameLen <= 0) throw new IllegalArgumentException("toBlockchainNameLen is 0");
if (bb.remaining() < nameLen + 4 + 32)
throw new IllegalArgumentException("ReactionBody payload too short");
if (bb.remaining() < nameLen + 4 + 32) throw new IllegalArgumentException("ReactionBody payload too short");
byte[] nameBytes = new byte[nameLen];
bb.get(nameBytes);
@@ -77,46 +70,28 @@ public final class ReactionBody implements BodyRecord, BodyHasTarget {
this.toBlockHash32 = new byte[32];
bb.get(this.toBlockHash32);
// запрет мусора в конце
if (bb.remaining() != 0) {
throw new IllegalArgumentException("Unexpected tail bytes, remaining=" + bb.remaining());
}
if (bb.remaining() != 0) throw new IllegalArgumentException("Unexpected tail bytes, remaining=" + bb.remaining());
}
/** Создание “вручную”. */
public ReactionBody(short subType,
String toBlockchainName,
int toBlockGlobalNumber,
byte[] toBlockHash32) {
public ReactionBody(String toBlockchainName, int toBlockGlobalNumber, byte[] toBlockHash32) {
Objects.requireNonNull(toBlockchainName, "toBlockchainName == null");
Objects.requireNonNull(toBlockHash32, "toBlockHash32 == null");
if (subType != SUB_LIKE)
throw new IllegalArgumentException("Unknown reaction subType: " + (subType & 0xFFFF));
this.subType = MsgSubType.REACTION_LIKE;
this.version = VER;
if (toBlockchainName.isBlank()) throw new IllegalArgumentException("toBlockchainName is blank");
if (toBlockGlobalNumber < 0) throw new IllegalArgumentException("toBlockGlobalNumber < 0");
if (toBlockHash32.length != 32) throw new IllegalArgumentException("toBlockHash32 != 32");
this.subType = subType;
this.toBlockchainName = toBlockchainName;
this.toBlockGlobalNumber = toBlockGlobalNumber;
this.toBlockHash32 = Arrays.copyOf(toBlockHash32, 32);
}
@Override public short type() { return TYPE; }
@Override public short version() { return VER; }
@Override public short subType() { return subType; }
@Override
public short expectedLineIndex() {
return LineIndex.REACTION;
}
@Override
public ReactionBody check() {
if (subType != SUB_LIKE)
if ((subType & 0xFFFF) != (MsgSubType.REACTION_LIKE & 0xFFFF))
throw new IllegalArgumentException("Bad reaction subType: " + (subType & 0xFFFF));
if (toBlockchainName == null || toBlockchainName.isBlank())
@@ -135,16 +110,9 @@ public final class ReactionBody implements BodyRecord, BodyHasTarget {
if (nameBytes.length == 0 || nameBytes.length > 255)
throw new IllegalArgumentException("toBlockchainName utf8 len must be 1..255");
// type[2]+ver[2]+subType[2] + nameLen[1]+name[N] + global[4] + hash[32]
int cap = 2 + 2 + 2 + 1 + nameBytes.length + 4 + 32;
int cap = 1 + nameBytes.length + 4 + 32;
ByteBuffer bb = ByteBuffer.allocate(cap).order(ByteOrder.BIG_ENDIAN);
bb.putShort(TYPE);
bb.putShort(VER);
bb.putShort(subType);
bb.put((byte) nameBytes.length);
bb.put(nameBytes);
bb.putInt(toBlockGlobalNumber);
@@ -153,43 +121,8 @@ public final class ReactionBody implements BodyRecord, BodyHasTarget {
return bb.array();
}
@Override
public String toString() {
String st = (subType == SUB_LIKE) ? "LIKE (1)" : "UNKNOWN";
/* ====================== BodyHasTarget ====================== */
return """
ReactionBody {
тип записи : REACTION (type=2, ver=1)
ожидаемая линия : 2
subType : %s
целевой блокчейн : "%s"
globalNumber цели : %d
hash цели (hex) : %s
}
""".formatted(
st,
toBlockchainName,
toBlockGlobalNumber,
toBlockHashHex()
);
}
public String toBlockHashHex() {
char[] HEX = "0123456789abcdef".toCharArray();
char[] out = new char[64];
for (int i = 0; i < 32; i++) {
int v = toBlockHash32[i] & 0xFF;
out[i * 2] = HEX[v >>> 4];
out[i * 2 + 1] = HEX[v & 0x0F];
}
return new String(out);
}
/* ===================================================================== */
/* ====================== BodyHasTarget контракт ========================= */
/* ===================================================================== */
/** В самом формате ReactionBody login цели не хранится => null. */
@Override public String toLogin() { return null; }
@Override public String toBchName() { return toBlockchainName; }