Добавил мелких доп проверок

(Все тесты тесты проходят)
This commit is contained in:
AidarKC 2026-01-15 15:24:25 +03:00
parent bbca821dcd
commit b69075cbac
2 changed files with 42 additions and 3 deletions

View File

@ -37,6 +37,18 @@ public final class BchBlockEntry {
public static final int SIGNATURE_LEN = 64; public static final int SIGNATURE_LEN = 64;
public static final int HASH_LEN = 32; public static final int HASH_LEN = 32;
/**
* Максимальный допустимый размер блока (preimage+signature), чтобы не уложить сервер по памяти/диску.
* 4 МБ нормальный потолок под тексты/метаданные, и при этом защищает от мусора/атаки.
*/
public static final int MAX_BLOCK_FULL_BYTES = 4 * 1024 * 1024;
/**
* Насколько блок может обгонять текущее время (защита от кривых часов/вбросов).
* Если timestamp больше now + 60 сек блок считаем неверным.
*/
public static final long MAX_FUTURE_SECONDS = 60;
/** Размер фиксированного RAW-заголовка без body */ /** Размер фиксированного RAW-заголовка без body */
public static final int RAW_HEADER_SIZE = public static final int RAW_HEADER_SIZE =
32 // prevHash32 32 // prevHash32
@ -80,6 +92,9 @@ public final class BchBlockEntry {
if (fullBytes.length < RAW_HEADER_SIZE + SIGNATURE_LEN) { if (fullBytes.length < RAW_HEADER_SIZE + SIGNATURE_LEN) {
throw new IllegalArgumentException("Block too short"); throw new IllegalArgumentException("Block too short");
} }
if (fullBytes.length > MAX_BLOCK_FULL_BYTES) {
throw new IllegalArgumentException("Block too large: " + fullBytes.length + " > " + MAX_BLOCK_FULL_BYTES);
}
ByteBuffer bb = ByteBuffer.wrap(fullBytes).order(ByteOrder.BIG_ENDIAN); ByteBuffer bb = ByteBuffer.wrap(fullBytes).order(ByteOrder.BIG_ENDIAN);
@ -93,10 +108,19 @@ public final class BchBlockEntry {
if (blockSize + SIGNATURE_LEN != fullBytes.length) { if (blockSize + SIGNATURE_LEN != fullBytes.length) {
throw new IllegalArgumentException("blockSize mismatch: blockSize=" + blockSize + " fullLen=" + fullBytes.length); throw new IllegalArgumentException("blockSize mismatch: blockSize=" + blockSize + " fullLen=" + fullBytes.length);
} }
if (blockSize + SIGNATURE_LEN > MAX_BLOCK_FULL_BYTES) {
throw new IllegalArgumentException("Block too large by blockSize: " + (blockSize + SIGNATURE_LEN) + " > " + MAX_BLOCK_FULL_BYTES);
}
this.blockNumber = bb.getInt(); this.blockNumber = bb.getInt();
this.timestamp = bb.getLong(); this.timestamp = bb.getLong();
// запрет в будущее больше чем на 1 минуту
long now = Instant.now().getEpochSecond();
if (this.timestamp > now + MAX_FUTURE_SECONDS) {
throw new IllegalArgumentException("timestamp is too far in future: ts=" + this.timestamp + " now=" + now + " maxFutureSec=" + MAX_FUTURE_SECONDS);
}
this.type = bb.getShort(); this.type = bb.getShort();
this.subType = bb.getShort(); this.subType = bb.getShort();
this.version = bb.getShort(); this.version = bb.getShort();
@ -116,7 +140,7 @@ public final class BchBlockEntry {
// hash32 = sha256(preimage) // hash32 = sha256(preimage)
this.hash32 = BchCryptoVerifier.sha256(preimage); this.hash32 = BchCryptoVerifier.sha256(preimage);
// parse body по header.type/subType/version // parse body по header.type/subType/version + ОБЯЗАТЕЛЬНЫЙ check()
this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes); this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes);
this.fullBytes = Arrays.copyOf(fullBytes, fullBytes.length); this.fullBytes = Arrays.copyOf(fullBytes, fullBytes.length);
@ -147,6 +171,12 @@ public final class BchBlockEntry {
if (prevHash32.length != 32) throw new IllegalArgumentException("prevHash32 != 32"); if (prevHash32.length != 32) throw new IllegalArgumentException("prevHash32 != 32");
if (signature64.length != SIGNATURE_LEN) throw new IllegalArgumentException("signature64 != 64"); if (signature64.length != SIGNATURE_LEN) throw new IllegalArgumentException("signature64 != 64");
// запрет в будущее больше чем на 1 минуту
long now = Instant.now().getEpochSecond();
if (timestamp > now + MAX_FUTURE_SECONDS) {
throw new IllegalArgumentException("timestamp is too far in future: ts=" + timestamp + " now=" + now + " maxFutureSec=" + MAX_FUTURE_SECONDS);
}
this.prevHash32 = Arrays.copyOf(prevHash32, 32); this.prevHash32 = Arrays.copyOf(prevHash32, 32);
this.blockNumber = blockNumber; this.blockNumber = blockNumber;
this.timestamp = timestamp; this.timestamp = timestamp;
@ -158,7 +188,12 @@ public final class BchBlockEntry {
this.blockSize = RAW_HEADER_SIZE + this.bodyBytes.length; this.blockSize = RAW_HEADER_SIZE + this.bodyBytes.length;
// parse body по header int fullLen = this.blockSize + SIGNATURE_LEN;
if (fullLen > MAX_BLOCK_FULL_BYTES) {
throw new IllegalArgumentException("Block too large: " + fullLen + " > " + MAX_BLOCK_FULL_BYTES);
}
// parse body по header + ОБЯЗАТЕЛЬНЫЙ check()
this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes); this.body = BodyRecordParser.parse(this.type, this.subType, this.version, this.bodyBytes);
// build preimage // build preimage

View File

@ -17,7 +17,7 @@ public final class BodyRecordParser {
// ключ = (type<<16)|version (как раньше по смыслу), но берём из HEADER // ключ = (type<<16)|version (как раньше по смыслу), но берём из HEADER
int key = (t << 16) | v; int key = (t << 16) | v;
return switch (key) { BodyRecord r = switch (key) {
case HeaderBody.KEY -> new HeaderBody(subType, version, bodyBytes); case HeaderBody.KEY -> new HeaderBody(subType, version, bodyBytes);
case TextBody.KEY -> new TextBody(subType, version, bodyBytes); case TextBody.KEY -> new TextBody(subType, version, bodyBytes);
case ReactionBody.KEY -> new ReactionBody(subType, version, bodyBytes); case ReactionBody.KEY -> new ReactionBody(subType, version, bodyBytes);
@ -28,5 +28,9 @@ public final class BodyRecordParser {
t, v, (subType & 0xFFFF) t, v, (subType & 0xFFFF)
)); ));
}; };
// 1) построили объект
// 2) ОБЯЗАТЕЛЬНО прогнали валидацию
return r.check();
} }
} }