Исправил что бы в интерфейсе BodyHasTarget не требывалось хранить в блоках  BodyHasTarget
и в блоках коннекстин не зранилась поле тоЛогин в байты блока.

(Все тесты тесты проходят)
This commit is contained in:
AidarKC 2026-01-15 15:09:45 +03:00
parent 5fe41c7656
commit d9fe1f02b8
3 changed files with 41 additions and 47 deletions

View File

@ -1,20 +1,25 @@
package blockchain.body; package blockchain.body;
import utils.blockchain.BlockchainNameUtil;
/** /**
* BodyHasTarget дополнительный интерфейс для body, которые "ссылаются" на цель (to-поля). * BodyHasTarget контракт для body, которые "ссылаются" на цель (to-поля).
* *
* Идея: * ВАЖНО (новое правило):
* - Не все body имеют "to". * - toLogin НЕ храним в байтах блока.
* - Но для индексации и удобства запросов в БД мы хотим единообразно доставать: * - toLogin всегда вычисляем из toBchName по стандарту:
* toLogin, toBchName, toBlockGlobalNumber, toBlockHashe * toBchName = login + "-NNN" => toLogin = login
* *
* Важно: * Все методы могут возвращать null (если target отсутствует).
* - Все методы могут возвращать null.
*/ */
public interface BodyHasTarget { public interface BodyHasTarget {
/** login цели (nullable). */ /** login цели (nullable). */
String toLogin(); default String toLogin() {
String bch = toBchName();
if (bch == null) return null;
return BlockchainNameUtil.loginFromBlockchainName(bch);
}
/** blockchainName цели (nullable). */ /** blockchainName цели (nullable). */
String toBchName(); String toBchName();

View File

@ -1,6 +1,7 @@
package blockchain.body; package blockchain.body;
import blockchain.MsgSubType; import blockchain.MsgSubType;
import utils.blockchain.BlockchainNameUtil;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
@ -16,18 +17,18 @@ import java.util.Objects;
* CONTACT=20, UNCONTACT=21 * CONTACT=20, UNCONTACT=21
* FOLLOW=30, UNFOLLOW=31 * FOLLOW=30, UNFOLLOW=31
* *
* bodyBytes (BigEndian), новый формат: * bodyBytes (BigEndian), новый формат (toLogin НЕ ХРАНИМ):
* [4] prevLineNumber * [4] prevLineNumber
* [32] prevLineHash32 * [32] prevLineHash32
* [4] thisLineNumber * [4] thisLineNumber
* *
* [1] toLoginLen (uint8)
* [N] toLogin UTF-8
*
* [1] toBlockchainNameLen (uint8) * [1] toBlockchainNameLen (uint8)
* [M] toBlockchainName UTF-8 * [N] toBlockchainName UTF-8
* [4] toBlockGlobalNumber (int32) * [4] toBlockGlobalNumber (int32)
* [32] toBlockHash32 (raw 32 bytes) * [32] toBlockHash32 (raw 32 bytes)
*
* toLogin вычисляется автоматически из toBlockchainName:
* toLogin = BlockchainNameUtil.loginFromBlockchainName(toBlockchainName)
*/ */
public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasLine { public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasLine {
@ -45,7 +46,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
public final int thisLineNumber; public final int thisLineNumber;
// payload // payload
public final String toLogin;
public final String toBlockchainName; public final String toBlockchainName;
public final int toBlockGlobalNumber; public final int toBlockGlobalNumber;
public final byte[] toBlockHash32; public final byte[] toBlockHash32;
@ -64,8 +64,8 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
} }
// минимум: // минимум:
// line(4+32+4) + toLoginLen[1]+toLogin[1] + toBchLen[1]+toBch[1] + global[4] + hash[32] // line(4+32+4) + toBchLen[1]+toBch[1] + global[4] + hash[32]
if (bodyBytes.length < (4 + 32 + 4) + 1 + 1 + 1 + 1 + 4 + 32) { if (bodyBytes.length < (4 + 32 + 4) + 1 + 1 + 4 + 32) {
throw new IllegalArgumentException("ConnectionBody too short"); throw new IllegalArgumentException("ConnectionBody too short");
} }
@ -78,14 +78,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
this.thisLineNumber = bb.getInt(); this.thisLineNumber = bb.getInt();
int toLoginLen = Byte.toUnsignedInt(bb.get());
if (toLoginLen <= 0) throw new IllegalArgumentException("toLoginLen is 0");
if (bb.remaining() < toLoginLen) throw new IllegalArgumentException("toLogin payload too short");
byte[] toLoginBytes = new byte[toLoginLen];
bb.get(toLoginBytes);
this.toLogin = new String(toLoginBytes, StandardCharsets.UTF_8);
int bchLen = Byte.toUnsignedInt(bb.get()); int bchLen = Byte.toUnsignedInt(bb.get());
if (bchLen <= 0) throw new IllegalArgumentException("toBlockchainNameLen is 0"); if (bchLen <= 0) throw new IllegalArgumentException("toBlockchainNameLen is 0");
if (bb.remaining() < bchLen + 4 + 32) throw new IllegalArgumentException("Connection payload too short"); if (bb.remaining() < bchLen + 4 + 32) throw new IllegalArgumentException("Connection payload too short");
@ -106,20 +98,21 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
byte[] prevLineHash32, byte[] prevLineHash32,
int thisLineNumber, int thisLineNumber,
short subType, short subType,
String toLogin,
String toBlockchainName, String toBlockchainName,
int toBlockGlobalNumber, int toBlockGlobalNumber,
byte[] toBlockHash32) { byte[] toBlockHash32) {
Objects.requireNonNull(toLogin, "toLogin == null");
Objects.requireNonNull(toBlockchainName, "toBlockchainName == null"); Objects.requireNonNull(toBlockchainName, "toBlockchainName == null");
Objects.requireNonNull(toBlockHash32, "toBlockHash32 == null"); Objects.requireNonNull(toBlockHash32, "toBlockHash32 == null");
if (!isValidSubType(subType)) throw new IllegalArgumentException("Bad connection subType: " + (subType & 0xFFFF)); if (!isValidSubType(subType)) throw new IllegalArgumentException("Bad connection subType: " + (subType & 0xFFFF));
if (toLogin.isBlank()) throw new IllegalArgumentException("toLogin is blank");
if (!toLogin.matches("^[A-Za-z0-9_]+$")) throw new IllegalArgumentException("toLogin must match ^[A-Za-z0-9_]+$");
if (toBlockchainName.isBlank()) throw new IllegalArgumentException("toBlockchainName is blank"); if (toBlockchainName.isBlank()) throw new IllegalArgumentException("toBlockchainName is blank");
// Железное правило формата: bchName -> login + "-NNN"
if (BlockchainNameUtil.loginFromBlockchainName(toBlockchainName) == null) {
throw new IllegalArgumentException("toBlockchainName must match login+\"-NNN\": " + toBlockchainName);
}
if (toBlockGlobalNumber < 0) throw new IllegalArgumentException("toBlockGlobalNumber < 0"); if (toBlockGlobalNumber < 0) throw new IllegalArgumentException("toBlockGlobalNumber < 0");
if (toBlockHash32.length != 32) throw new IllegalArgumentException("toBlockHash32 != 32"); if (toBlockHash32.length != 32) throw new IllegalArgumentException("toBlockHash32 != 32");
@ -130,7 +123,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
this.subType = subType; this.subType = subType;
this.version = VER; this.version = VER;
this.toLogin = toLogin;
this.toBlockchainName = toBlockchainName; this.toBlockchainName = toBlockchainName;
this.toBlockGlobalNumber = toBlockGlobalNumber; this.toBlockGlobalNumber = toBlockGlobalNumber;
this.toBlockHash32 = Arrays.copyOf(toBlockHash32, 32); this.toBlockHash32 = Arrays.copyOf(toBlockHash32, 32);
@ -158,10 +150,13 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
if (prevLineHash32 == null || prevLineHash32.length != 32) throw new IllegalArgumentException("prevLineHash32 invalid"); if (prevLineHash32 == null || prevLineHash32.length != 32) throw new IllegalArgumentException("prevLineHash32 invalid");
} }
if (toLogin == null || toLogin.isBlank()) throw new IllegalArgumentException("toLogin is blank"); if (toBlockchainName == null || toBlockchainName.isBlank())
if (!toLogin.matches("^[A-Za-z0-9_]+$")) throw new IllegalArgumentException("toLogin must match ^[A-Za-z0-9_]+$"); throw new IllegalArgumentException("toBlockchainName is blank");
// гарантируем вычислимый toLogin (иначе target битый по стандарту)
if (BlockchainNameUtil.loginFromBlockchainName(toBlockchainName) == null)
throw new IllegalArgumentException("toBlockchainName must match login+\"-NNN\": " + toBlockchainName);
if (toBlockchainName == null || toBlockchainName.isBlank()) throw new IllegalArgumentException("toBlockchainName is blank");
if (toBlockGlobalNumber < 0) throw new IllegalArgumentException("toBlockGlobalNumber < 0"); if (toBlockGlobalNumber < 0) throw new IllegalArgumentException("toBlockGlobalNumber < 0");
if (toBlockHash32 == null || toBlockHash32.length != 32) throw new IllegalArgumentException("toBlockHash32 invalid"); if (toBlockHash32 == null || toBlockHash32.length != 32) throw new IllegalArgumentException("toBlockHash32 invalid");
@ -170,10 +165,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
@Override @Override
public byte[] toBytes() { public byte[] toBytes() {
byte[] toLoginBytes = toLogin.getBytes(StandardCharsets.UTF_8);
if (toLoginBytes.length == 0 || toLoginBytes.length > 255)
throw new IllegalArgumentException("toLogin utf8 len must be 1..255");
byte[] bchBytes = toBlockchainName.getBytes(StandardCharsets.UTF_8); byte[] bchBytes = toBlockchainName.getBytes(StandardCharsets.UTF_8);
if (bchBytes.length == 0 || bchBytes.length > 255) if (bchBytes.length == 0 || bchBytes.length > 255)
throw new IllegalArgumentException("toBlockchainName utf8 len must be 1..255"); throw new IllegalArgumentException("toBlockchainName utf8 len must be 1..255");
@ -182,7 +173,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
throw new IllegalArgumentException("toBlockHash32 != 32"); throw new IllegalArgumentException("toBlockHash32 != 32");
int cap = (4 + 32 + 4) int cap = (4 + 32 + 4)
+ 1 + toLoginBytes.length
+ 1 + bchBytes.length + 1 + bchBytes.length
+ 4 + 32; + 4 + 32;
@ -192,9 +182,6 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
bb.put(prevLineHash32 == null ? new byte[32] : Arrays.copyOf(prevLineHash32, 32)); bb.put(prevLineHash32 == null ? new byte[32] : Arrays.copyOf(prevLineHash32, 32));
bb.putInt(thisLineNumber); bb.putInt(thisLineNumber);
bb.put((byte) toLoginBytes.length);
bb.put(toLoginBytes);
bb.put((byte) bchBytes.length); bb.put((byte) bchBytes.length);
bb.put(bchBytes); bb.put(bchBytes);
@ -216,7 +203,8 @@ public final class ConnectionBody implements BodyRecord, BodyHasTarget, BodyHasL
@Override public int thisLineNumber() { return thisLineNumber; } @Override public int thisLineNumber() { return thisLineNumber; }
/* ====================== BodyHasTarget ===================== */ /* ====================== BodyHasTarget ===================== */
@Override public String toLogin() { return toLogin; } // toLogin() теперь default в интерфейсе и вычисляется из toBchName()
@Override public String toBchName() { return toBlockchainName; } @Override public String toBchName() { return toBlockchainName; }
@Override public Integer toBlockGlobalNumber() { return toBlockGlobalNumber; } @Override public Integer toBlockGlobalNumber() { return toBlockGlobalNumber; }
@Override public byte[] toBlockHasheBytes() { return toBlockHash32; } @Override public byte[] toBlockHasheBytes() { return toBlockHash32; }

View File

@ -19,6 +19,7 @@ import static org.junit.jupiter.api.Assertions.*;
* ВАЖНО: * ВАЖНО:
* - НЕТ обращения к blockchain.LineIndex (можно удалить LineIndex.java). * - НЕТ обращения к blockchain.LineIndex (можно удалить LineIndex.java).
* - Линии берём через ChainState.nextLineByType(TYPE_...). * - Линии берём через ChainState.nextLineByType(TYPE_...).
* - ConnectionBody: toLogin в байтах НЕ хранится, вычисляется из toBlockchainName.
*/ */
public class IT_03_AddBlock_NoAuth { public class IT_03_AddBlock_NoAuth {
@ -177,7 +178,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION);
sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_FOLLOW, MsgSubType.CONNECTION_FOLLOW,
u2, bch2, 0, new byte[32] bch2, 0, new byte[32]
), t); ), t);
} }
@ -186,7 +187,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION);
sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_FOLLOW, MsgSubType.CONNECTION_FOLLOW,
u3, bch3, 0, new byte[32] bch3, 0, new byte[32]
), t); ), t);
} }
@ -195,7 +196,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION);
sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_FOLLOW, MsgSubType.CONNECTION_FOLLOW,
u1, bch1, 0, new byte[32] bch1, 0, new byte[32]
), t); ), t);
} }
@ -204,7 +205,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION);
sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_FRIEND, MsgSubType.CONNECTION_FRIEND,
u1, bch1, 0, new byte[32] bch1, 0, new byte[32]
), t); ), t);
} }
@ -219,7 +220,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st1.nextLineByType(ChainState.TYPE_CONNECTION);
sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender1.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_FRIEND, MsgSubType.CONNECTION_FRIEND,
u2, bch2, 0, new byte[32] bch2, 0, new byte[32]
), t); ), t);
} }
@ -227,7 +228,7 @@ public class IT_03_AddBlock_NoAuth {
var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION); var ln = st2.nextLineByType(ChainState.TYPE_CONNECTION);
sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber, sender2.send(new ConnectionBody(ln.prevLineNumber, ln.prevLineHash32, ln.thisLineNumber,
MsgSubType.CONNECTION_UNFRIEND, MsgSubType.CONNECTION_UNFRIEND,
u1, bch1, 0, new byte[32] bch1, 0, new byte[32]
), t); ), t);
} }