23 12 25
Дорабатываю добавление блоков! Вроде всё.Осталось ещё размер уточнить что без хэш и пподписи 2
This commit is contained in:
parent
9633e3528d
commit
26afcb892a
@ -6,151 +6,146 @@ import java.util.Arrays;
|
|||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ============================================================================
|
* BchBlockEntry_new — универсальный блок нового формата.
|
||||||
* BchBlockEntry — запись блокчейна SHiNE (.bch)
|
|
||||||
* ============================================================================
|
|
||||||
*
|
*
|
||||||
* RAW (BigEndian):
|
* RAW (BigEndian):
|
||||||
* [4] recordSize = RAW_HEADER_SIZE + body.length
|
* [4] recordSize (int) = RAW + signature + hash
|
||||||
* [4] recordNumber
|
* [4] recordNumber (int) глобальный номер блока
|
||||||
* [8] timestamp UNIX time (секунды)
|
* [8] timestamp (long) unix seconds
|
||||||
* [M] bodyBytes (внутри body первые 4 байта = [type][ver])
|
* [2] line (short)
|
||||||
|
* [4] lineNumber (int)
|
||||||
|
* [N] bodyBytes (body, начинается с [type][version])
|
||||||
*
|
*
|
||||||
* FULL:
|
* TAIL:
|
||||||
* RAW + signature64 + hash32
|
* [64] signature64 (Ed25519)
|
||||||
*
|
* [32] hash32 (SHA-256)
|
||||||
* ============================================================================
|
|
||||||
*/
|
*/
|
||||||
public class BchBlockEntry {
|
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;
|
||||||
|
|
||||||
/** RAW заголовок теперь: size + number + timestamp */
|
/** Размер фиксированного RAW-заголовка без body */
|
||||||
public static final int RAW_HEADER_SIZE = 4 + 4 + 8; // 16
|
public static final int RAW_HEADER_SIZE = 4 + 4 + 8 + 2 + 4;
|
||||||
|
|
||||||
// ---- Поля RAW-заголовка ----
|
// --- RAW ---
|
||||||
public final int recordSize; // [4] M + 16
|
public final int recordSize;
|
||||||
public final int recordNumber; // [4]
|
public final int recordNumber;
|
||||||
public final long timestamp; // [8]
|
public final long timestamp;
|
||||||
public final byte[] body; // [M] тело записи (начинается с type/ver)
|
public final short line;
|
||||||
|
public final int lineNumber;
|
||||||
|
public final byte[] bodyBytes;
|
||||||
|
|
||||||
// ---- Поля подписи и хэша ----
|
// --- TAIL ---
|
||||||
private byte[] signature64; // [64]
|
private final byte[] signature64;
|
||||||
private byte[] hash32; // [32]
|
private final byte[] hash32;
|
||||||
|
|
||||||
// ---- Кэшированные представления ----
|
// --- cached ---
|
||||||
public final byte[] rawBytes; // RAW без подписи/хэша
|
private final byte[] fullBytes;
|
||||||
private byte[] rawBytesWithSignatureAndHash; // FULL (может быть null)
|
|
||||||
|
|
||||||
// ========================================================================
|
/* ===================================================================== */
|
||||||
// КОНСТРУКТОР №1 — из полей (RAW only)
|
/* ====================== Конструктор из байт ========================== */
|
||||||
// ========================================================================
|
/* ===================================================================== */
|
||||||
public BchBlockEntry(int recordNumber,
|
|
||||||
long timestamp,
|
|
||||||
byte[] body) {
|
|
||||||
Objects.requireNonNull(body, "body == null");
|
|
||||||
|
|
||||||
this.recordNumber = recordNumber;
|
public BchBlockEntry(byte[] fullBytes) {
|
||||||
this.timestamp = timestamp;
|
Objects.requireNonNull(fullBytes, "fullBytes == null");
|
||||||
this.body = Arrays.copyOf(body, body.length);
|
if (fullBytes.length < RAW_HEADER_SIZE + SIGNATURE_LEN + HASH_LEN)
|
||||||
this.recordSize = body.length + RAW_HEADER_SIZE;
|
throw new IllegalArgumentException("Block too short");
|
||||||
|
|
||||||
ByteBuffer buf = ByteBuffer
|
ByteBuffer bb = ByteBuffer.wrap(fullBytes).order(ByteOrder.BIG_ENDIAN);
|
||||||
.allocate(RAW_HEADER_SIZE + body.length)
|
|
||||||
.order(ByteOrder.BIG_ENDIAN);
|
|
||||||
|
|
||||||
buf.putInt(recordSize);
|
this.recordSize = bb.getInt();
|
||||||
buf.putInt(recordNumber);
|
if (recordSize != fullBytes.length)
|
||||||
buf.putLong(timestamp);
|
throw new IllegalArgumentException("recordSize mismatch");
|
||||||
buf.put(body);
|
|
||||||
|
|
||||||
this.rawBytes = buf.array();
|
this.recordNumber = bb.getInt();
|
||||||
}
|
this.timestamp = bb.getLong();
|
||||||
|
this.line = bb.getShort();
|
||||||
|
this.lineNumber = bb.getInt();
|
||||||
|
|
||||||
// ========================================================================
|
int bodyLen = recordSize - RAW_HEADER_SIZE - SIGNATURE_LEN - HASH_LEN;
|
||||||
// КОНСТРУКТОР №2 — из полного массива (RAW + SIG + HASH)
|
if (bodyLen <= 0)
|
||||||
// ========================================================================
|
throw new IllegalArgumentException("Invalid body length");
|
||||||
public BchBlockEntry(byte[] rawWithSigAndHash) {
|
|
||||||
Objects.requireNonNull(rawWithSigAndHash, "rawWithSigAndHash == null");
|
|
||||||
if (rawWithSigAndHash.length < RAW_HEADER_SIZE + SIGNATURE_LEN + HASH_LEN)
|
|
||||||
throw new IllegalArgumentException("Слишком мало данных для полного блока");
|
|
||||||
|
|
||||||
ByteBuffer probe = ByteBuffer.wrap(rawWithSigAndHash).order(ByteOrder.BIG_ENDIAN);
|
this.bodyBytes = new byte[bodyLen];
|
||||||
int rs = probe.getInt(); // recordSize
|
bb.get(this.bodyBytes);
|
||||||
if (rs < RAW_HEADER_SIZE)
|
|
||||||
throw new IllegalArgumentException("Некорректный recordSize: " + rs);
|
|
||||||
if (rawWithSigAndHash.length < rs + SIGNATURE_LEN + HASH_LEN)
|
|
||||||
throw new IllegalArgumentException("Данные короче, чем raw+sig+hash");
|
|
||||||
|
|
||||||
this.rawBytes = Arrays.copyOfRange(rawWithSigAndHash, 0, rs);
|
|
||||||
|
|
||||||
ByteBuffer buf = ByteBuffer.wrap(this.rawBytes).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
this.recordSize = buf.getInt();
|
|
||||||
this.recordNumber = buf.getInt();
|
|
||||||
this.timestamp = buf.getLong();
|
|
||||||
|
|
||||||
int bodyLen = this.recordSize - RAW_HEADER_SIZE;
|
|
||||||
if (bodyLen < 0 || bodyLen != this.rawBytes.length - RAW_HEADER_SIZE)
|
|
||||||
throw new IllegalArgumentException("Неконсистентная длина тела блока");
|
|
||||||
|
|
||||||
this.body = new byte[bodyLen];
|
|
||||||
buf.get(this.body);
|
|
||||||
|
|
||||||
// подпись + хэш
|
|
||||||
ByteBuffer tail = ByteBuffer
|
|
||||||
.wrap(rawWithSigAndHash, rs, SIGNATURE_LEN + HASH_LEN)
|
|
||||||
.order(ByteOrder.BIG_ENDIAN);
|
|
||||||
|
|
||||||
this.signature64 = new byte[SIGNATURE_LEN];
|
this.signature64 = new byte[SIGNATURE_LEN];
|
||||||
tail.get(this.signature64);
|
bb.get(this.signature64);
|
||||||
this.hash32 = new byte[HASH_LEN];
|
|
||||||
tail.get(this.hash32);
|
|
||||||
|
|
||||||
this.rawBytesWithSignatureAndHash =
|
this.hash32 = new byte[HASH_LEN];
|
||||||
Arrays.copyOf(rawWithSigAndHash, rs + SIGNATURE_LEN + HASH_LEN);
|
bb.get(this.hash32);
|
||||||
|
|
||||||
|
this.fullBytes = Arrays.copyOf(fullBytes, fullBytes.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
/* ===================================================================== */
|
||||||
// Добавить подпись и хэш
|
/* ====================== Конструктор сборки ============================ */
|
||||||
// ========================================================================
|
/* ===================================================================== */
|
||||||
public BchBlockEntry addSignatureAndHash(byte[] signature64, byte[] hash32) {
|
|
||||||
|
public BchBlockEntry(int recordNumber,
|
||||||
|
long timestamp,
|
||||||
|
short line,
|
||||||
|
int lineNumber,
|
||||||
|
byte[] bodyBytes,
|
||||||
|
byte[] signature64,
|
||||||
|
byte[] hash32) {
|
||||||
|
|
||||||
|
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
|
||||||
Objects.requireNonNull(signature64, "signature64 == null");
|
Objects.requireNonNull(signature64, "signature64 == null");
|
||||||
Objects.requireNonNull(hash32, "hash32 == null");
|
Objects.requireNonNull(hash32, "hash32 == null");
|
||||||
if (signature64.length != SIGNATURE_LEN) throw new IllegalArgumentException("signature64 != 64");
|
|
||||||
if (hash32.length != HASH_LEN) throw new IllegalArgumentException("hash32 != 32");
|
|
||||||
|
|
||||||
|
if (signature64.length != SIGNATURE_LEN)
|
||||||
|
throw new IllegalArgumentException("signature64 != 64");
|
||||||
|
if (hash32.length != HASH_LEN)
|
||||||
|
throw new IllegalArgumentException("hash32 != 32");
|
||||||
|
|
||||||
|
this.recordNumber = recordNumber;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.line = line;
|
||||||
|
this.lineNumber = lineNumber;
|
||||||
|
this.bodyBytes = Arrays.copyOf(bodyBytes, bodyBytes.length);
|
||||||
this.signature64 = Arrays.copyOf(signature64, SIGNATURE_LEN);
|
this.signature64 = Arrays.copyOf(signature64, SIGNATURE_LEN);
|
||||||
this.hash32 = Arrays.copyOf(hash32, HASH_LEN);
|
this.hash32 = Arrays.copyOf(hash32, HASH_LEN);
|
||||||
|
|
||||||
byte[] full = new byte[this.rawBytes.length + SIGNATURE_LEN + HASH_LEN];
|
this.recordSize =
|
||||||
System.arraycopy(this.rawBytes, 0, full, 0, this.rawBytes.length);
|
RAW_HEADER_SIZE +
|
||||||
System.arraycopy(this.signature64, 0, full, this.rawBytes.length, SIGNATURE_LEN);
|
bodyBytes.length +
|
||||||
System.arraycopy(this.hash32, 0, full, this.rawBytes.length + SIGNATURE_LEN, HASH_LEN);
|
SIGNATURE_LEN +
|
||||||
this.rawBytesWithSignatureAndHash = full;
|
HASH_LEN;
|
||||||
return this;
|
|
||||||
|
ByteBuffer bb = ByteBuffer.allocate(recordSize).order(ByteOrder.BIG_ENDIAN);
|
||||||
|
bb.putInt(recordSize);
|
||||||
|
bb.putInt(recordNumber);
|
||||||
|
bb.putLong(timestamp);
|
||||||
|
bb.putShort(line);
|
||||||
|
bb.putInt(lineNumber);
|
||||||
|
bb.put(bodyBytes);
|
||||||
|
bb.put(this.signature64);
|
||||||
|
bb.put(this.hash32);
|
||||||
|
|
||||||
|
this.fullBytes = bb.array();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
|
||||||
// Геттеры
|
public byte[] getRawBytes() {
|
||||||
// ========================================================================
|
int rawLen = recordSize - SIGNATURE_LEN - HASH_LEN;
|
||||||
public byte[] getSignature64() { return signature64 == null ? null : Arrays.copyOf(signature64, SIGNATURE_LEN); }
|
byte[] raw = new byte[rawLen];
|
||||||
public byte[] getHash32() { return hash32 == null ? null : Arrays.copyOf(hash32, HASH_LEN); }
|
System.arraycopy(fullBytes, 0, raw, 0, rawLen);
|
||||||
public byte[] getRawBytesWithSignatureAndHash() {
|
return raw;
|
||||||
return rawBytesWithSignatureAndHash == null ? null : Arrays.copyOf(rawBytesWithSignatureAndHash, rawBytesWithSignatureAndHash.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ========================================================================
|
/* ===================================================================== */
|
||||||
// Отладка
|
|
||||||
// ========================================================================
|
public byte[] getSignature64() {
|
||||||
@Override
|
return Arrays.copyOf(signature64, SIGNATURE_LEN);
|
||||||
public String toString() {
|
}
|
||||||
return String.format(
|
|
||||||
"BchBlock[num=%d, time=%d, raw=%d, full=%s]",
|
public byte[] getHash32() {
|
||||||
recordNumber, timestamp,
|
return Arrays.copyOf(hash32, HASH_LEN);
|
||||||
rawBytes.length,
|
}
|
||||||
rawBytesWithSignatureAndHash == null ? "null" : String.valueOf(rawBytesWithSignatureAndHash.length)
|
|
||||||
);
|
public byte[] toBytes() {
|
||||||
|
return Arrays.copyOf(fullBytes, fullBytes.length);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,110 +0,0 @@
|
|||||||
package blockchain;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
import utils.blockchain.BchInfoEntry;
|
|
||||||
import utils.crypto.BchCryptoVerifier;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BchBlockValidator — проверяет корректность блока:
|
|
||||||
* 1) последовательность номеров блоков в цепочке;
|
|
||||||
* 2) криптографическую целостность (подпись и хэш).
|
|
||||||
*.
|
|
||||||
* Не проверяет:
|
|
||||||
* - структуру и содержимое body;
|
|
||||||
* - поля HEADER и логин (этим занимаются другие проверки).
|
|
||||||
*/
|
|
||||||
public final class BchBlockValidator {
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BchBlockValidator.class);
|
|
||||||
|
|
||||||
private BchBlockValidator() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Проверяет, что блок может быть корректно добавлен к цепочке.
|
|
||||||
*
|
|
||||||
* @param block блок (распарсенный из байт)
|
|
||||||
* @param chain информация о цепочке (BchInfoEntry)
|
|
||||||
* @param chainId идентификатор цепочки
|
|
||||||
* @return true если порядок и криптография корректны, иначе false
|
|
||||||
*/
|
|
||||||
public static boolean validate(BchBlockEntry block, BchInfoEntry chain, long chainId) {
|
|
||||||
if (block == null || chain == null) {
|
|
||||||
log.warn("❌ Ошибка: блок или данные о цепочке не переданы");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1️⃣ Проверка последовательности номера
|
|
||||||
int expectedNumber = chain.lastBlockNumber + 1;
|
|
||||||
if (block.recordNumber < expectedNumber) {
|
|
||||||
log.warn("❌ Блок с номером {} уже существует (ожидался {})", block.recordNumber, expectedNumber);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (block.recordNumber > expectedNumber) {
|
|
||||||
log.warn("❌ Нарушена последовательность: получен блок {}, ожидался {}", block.recordNumber, expectedNumber);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// // 1.5️⃣ Проверим, что body хотя бы содержит type/ver (первые 4 байта)
|
|
||||||
// try {
|
|
||||||
// short bodyType = block.getBodyType();
|
|
||||||
// short bodyVer = block.getBodyVer();
|
|
||||||
// // тут специально не валидируем смысл (это делает парсер/логика выше),
|
|
||||||
// // но оставим для диагностики
|
|
||||||
// log.debug("Body type/ver from bodyBytes: type={} ver={} (blockNum={}, chainId={})",
|
|
||||||
// bodyType, bodyVer, block.recordNumber, chainId);
|
|
||||||
// } catch (Exception e) {
|
|
||||||
// log.warn("❌ Некорректное тело блока: {}", e.getMessage());
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 2️⃣ Проверка публичного ключа
|
|
||||||
byte[] publicKey = chain.getPublicKey32();
|
|
||||||
if (publicKey == null || publicKey.length != 32) {
|
|
||||||
log.warn("❌ В цепочке отсутствует корректный публичный ключ (chainId={})", chainId);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3️⃣ Получаем предыдущий хэш
|
|
||||||
byte[] prevHash32 = hexToBytes(chain.lastBlockHash);
|
|
||||||
|
|
||||||
// 4️⃣ Проверка подписи и хэша
|
|
||||||
try {
|
|
||||||
boolean ok = BchCryptoVerifier.verifyAll(
|
|
||||||
chain.userLogin,
|
|
||||||
chainId,
|
|
||||||
prevHash32,
|
|
||||||
block.rawBytes,
|
|
||||||
block.getSignature64(),
|
|
||||||
block.getHash32(),
|
|
||||||
publicKey
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!ok) {
|
|
||||||
log.warn("❌ Криптографическая проверка не пройдена: хэш или подпись не совпадают (chainId={}, blockNum={})",
|
|
||||||
chainId, block.recordNumber);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("✅ Блок {} успешно прошёл проверку подписи и хэша (chainId={})",
|
|
||||||
block.recordNumber, chainId);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("❌ Исключение при проверке блока (chainId={}): {}", chainId, e.getMessage());
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -------------------- HEX → байты --------------------
|
|
||||||
|
|
||||||
private static byte[] hexToBytes(String hex) {
|
|
||||||
if (hex == null || hex.isEmpty()) return new byte[32]; // пустой хэш = 32 нуля
|
|
||||||
int len = hex.length();
|
|
||||||
byte[] out = new byte[len / 2];
|
|
||||||
for (int i = 0; i < len; i += 2) {
|
|
||||||
out[i / 2] = (byte) Integer.parseInt(hex.substring(i, i + 2), 16);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package blockchain_new;
|
package blockchain;
|
||||||
|
|
||||||
import utils.crypto.Ed25519Util;
|
import utils.crypto.Ed25519Util;
|
||||||
|
|
||||||
@ -8,11 +8,11 @@ import java.nio.charset.StandardCharsets;
|
|||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
||||||
public final class BchCryptoVerifier_new {
|
public final class BchCryptoVerifier {
|
||||||
|
|
||||||
private static final byte[] DOMAIN = "SHiNE".getBytes(StandardCharsets.US_ASCII);
|
private static final byte[] DOMAIN = "SHiNE".getBytes(StandardCharsets.US_ASCII);
|
||||||
|
|
||||||
private BchCryptoVerifier_new() {}
|
private BchCryptoVerifier() {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* preimage =
|
* preimage =
|
||||||
@ -1,106 +0,0 @@
|
|||||||
//package blockchain;
|
|
||||||
//
|
|
||||||
//import blockchain.body.BodyRecord;
|
|
||||||
//import blockchain.body.HeaderBody;
|
|
||||||
//import blockchain.body.TextBody;
|
|
||||||
//import org.slf4j.Logger;
|
|
||||||
//import org.slf4j.LoggerFactory;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * ============================================================================
|
|
||||||
// * BodyRecordParser — универсальный парсер тел (body) блоков .bch
|
|
||||||
// * ============================================================================
|
|
||||||
// *.
|
|
||||||
// * 🧩 Назначение:
|
|
||||||
// * Преобразует пару (recordType, recordTypeVersion, bodyBytes)
|
|
||||||
// * в конкретный объект, реализующий интерфейс {@link BodyRecord}.
|
|
||||||
// *.
|
|
||||||
// * 🔹 Особенность:
|
|
||||||
// * Используется объединённый 4-байтовый код:
|
|
||||||
// *.
|
|
||||||
// * fullCode = (recordType << 16) | (recordTypeVersion & 0xFFFF)
|
|
||||||
// *.
|
|
||||||
// * Это позволяет различать версии одного типа блока,
|
|
||||||
// * например: TextBody v1, TextBody v2 и т.д.
|
|
||||||
// *.
|
|
||||||
// * 🔹 Пример:
|
|
||||||
// * BodyRecord body = BodyRecordParser.parse(block.recordType, block.recordTypeVersion, block.body);
|
|
||||||
// *.
|
|
||||||
// * ============================================================================
|
|
||||||
// */
|
|
||||||
//public final class BodyRecordParser {
|
|
||||||
//
|
|
||||||
// private static final Logger log = LoggerFactory.getLogger(BodyRecordParser.class);
|
|
||||||
//
|
|
||||||
// private BodyRecordParser() {}
|
|
||||||
//
|
|
||||||
// /**
|
|
||||||
// * Распарсить тело блока по типу и версии записи.
|
|
||||||
// *
|
|
||||||
// * @param recordType код типа записи (0 = Header, 1 = Text, ...)
|
|
||||||
// * @param recordTypeVersion версия формата записи
|
|
||||||
// * @param body массив байт тела записи
|
|
||||||
// * @return объект, реализующий BodyRecord
|
|
||||||
// */
|
|
||||||
// public static BodyRecord parse(short recordType, short recordTypeVersion, byte[] body) {
|
|
||||||
// if (body == null)
|
|
||||||
// throw new IllegalArgumentException("body == null");
|
|
||||||
//
|
|
||||||
// // Объединяем тип и версию в 4-байтовый ключ
|
|
||||||
// int fullCode = ((recordType & 0xFFFF) << 16) | (recordTypeVersion & 0xFFFF);
|
|
||||||
//
|
|
||||||
// switch (fullCode) {
|
|
||||||
//
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // TYPE 0, VERSION 1 — HeaderBody v1
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // Заголовок цепочки пользователя (первый блок).
|
|
||||||
// //
|
|
||||||
// // Формат body (без общих 20 байт заголовка блока):
|
|
||||||
// // [8] ASCII tag = "SHiNE001"
|
|
||||||
// // [8] blockchainId (long, BE)
|
|
||||||
// // [4] blockchainType (int, BE)
|
|
||||||
// // [4] blockchainNumber (int, BE)
|
|
||||||
// // [1] userLoginLength = N (unsigned byte)
|
|
||||||
// // [N] userLogin (UTF-8)
|
|
||||||
// // [2] versionUserBch (short, BE)
|
|
||||||
// // [8] prevUserBchId (long, BE)
|
|
||||||
// // [32] publicKey32
|
|
||||||
// //
|
|
||||||
// // Назначение:
|
|
||||||
// // Создаёт новую пользовательскую цепочку (блок №0).
|
|
||||||
// case (0x0000_0001):
|
|
||||||
// return new HeaderBody(body);
|
|
||||||
//
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // TYPE 1, VERSION 1 — TextBody v1
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // Простое текстовое сообщение UTF-8.
|
|
||||||
// //
|
|
||||||
// // Формат body (без общих 20 байт заголовка блока):
|
|
||||||
// // [N] message (UTF-8)
|
|
||||||
// //
|
|
||||||
// // Назначение:
|
|
||||||
// // Текстовые и системные сообщения, описания, комментарии.
|
|
||||||
// case (0x0001_0001):
|
|
||||||
// return new TextBody(body);
|
|
||||||
//
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // РЕЗЕРВ — будущие типы и версии
|
|
||||||
// // ---------------------------------------------------------
|
|
||||||
// // Пример: (0x0001_0002) → TextBody v2 (например, JSON-структура)
|
|
||||||
// // (0x0002_0001) → FileBody v1
|
|
||||||
// //
|
|
||||||
// // case (0x0001_0002):
|
|
||||||
// // return new TextBodyV2(body);
|
|
||||||
// //
|
|
||||||
// // case (0x0002_0001):
|
|
||||||
// // return new FileBody(body);
|
|
||||||
//
|
|
||||||
// default:
|
|
||||||
// throw new IllegalArgumentException(String.format(
|
|
||||||
// "Неизвестный тип блока: type=%d version=%d (fullCode=0x%08X)",
|
|
||||||
// recordType, recordTypeVersion, fullCode));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
@ -1,151 +0,0 @@
|
|||||||
package blockchain_new;
|
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
|
||||||
import java.nio.ByteOrder;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* BchBlockEntry_new — универсальный блок нового формата.
|
|
||||||
*
|
|
||||||
* RAW (BigEndian):
|
|
||||||
* [4] recordSize (int) = RAW + signature + hash
|
|
||||||
* [4] recordNumber (int) глобальный номер блока
|
|
||||||
* [8] timestamp (long) unix seconds
|
|
||||||
* [2] line (short)
|
|
||||||
* [4] lineNumber (int)
|
|
||||||
* [N] bodyBytes (body, начинается с [type][version])
|
|
||||||
*
|
|
||||||
* TAIL:
|
|
||||||
* [64] signature64 (Ed25519)
|
|
||||||
* [32] hash32 (SHA-256)
|
|
||||||
*/
|
|
||||||
public final class BchBlockEntry_new {
|
|
||||||
|
|
||||||
public static final int SIGNATURE_LEN = 64;
|
|
||||||
public static final int HASH_LEN = 32;
|
|
||||||
|
|
||||||
/** Размер фиксированного RAW-заголовка без body */
|
|
||||||
public static final int RAW_HEADER_SIZE = 4 + 4 + 8 + 2 + 4;
|
|
||||||
|
|
||||||
// --- RAW ---
|
|
||||||
public final int recordSize;
|
|
||||||
public final int recordNumber;
|
|
||||||
public final long timestamp;
|
|
||||||
public final short line;
|
|
||||||
public final int lineNumber;
|
|
||||||
public final byte[] bodyBytes;
|
|
||||||
|
|
||||||
// --- TAIL ---
|
|
||||||
private final byte[] signature64;
|
|
||||||
private final byte[] hash32;
|
|
||||||
|
|
||||||
// --- cached ---
|
|
||||||
private final byte[] fullBytes;
|
|
||||||
|
|
||||||
/* ===================================================================== */
|
|
||||||
/* ====================== Конструктор из байт ========================== */
|
|
||||||
/* ===================================================================== */
|
|
||||||
|
|
||||||
public BchBlockEntry_new(byte[] fullBytes) {
|
|
||||||
Objects.requireNonNull(fullBytes, "fullBytes == null");
|
|
||||||
if (fullBytes.length < RAW_HEADER_SIZE + SIGNATURE_LEN + HASH_LEN)
|
|
||||||
throw new IllegalArgumentException("Block too short");
|
|
||||||
|
|
||||||
ByteBuffer bb = ByteBuffer.wrap(fullBytes).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
|
|
||||||
this.recordSize = bb.getInt();
|
|
||||||
if (recordSize != fullBytes.length)
|
|
||||||
throw new IllegalArgumentException("recordSize mismatch");
|
|
||||||
|
|
||||||
this.recordNumber = bb.getInt();
|
|
||||||
this.timestamp = bb.getLong();
|
|
||||||
this.line = bb.getShort();
|
|
||||||
this.lineNumber = bb.getInt();
|
|
||||||
|
|
||||||
int bodyLen = recordSize - RAW_HEADER_SIZE - SIGNATURE_LEN - HASH_LEN;
|
|
||||||
if (bodyLen <= 0)
|
|
||||||
throw new IllegalArgumentException("Invalid body length");
|
|
||||||
|
|
||||||
this.bodyBytes = new byte[bodyLen];
|
|
||||||
bb.get(this.bodyBytes);
|
|
||||||
|
|
||||||
this.signature64 = new byte[SIGNATURE_LEN];
|
|
||||||
bb.get(this.signature64);
|
|
||||||
|
|
||||||
this.hash32 = new byte[HASH_LEN];
|
|
||||||
bb.get(this.hash32);
|
|
||||||
|
|
||||||
this.fullBytes = Arrays.copyOf(fullBytes, fullBytes.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================================================================== */
|
|
||||||
/* ====================== Конструктор сборки ============================ */
|
|
||||||
/* ===================================================================== */
|
|
||||||
|
|
||||||
public BchBlockEntry_new(int recordNumber,
|
|
||||||
long timestamp,
|
|
||||||
short line,
|
|
||||||
int lineNumber,
|
|
||||||
byte[] bodyBytes,
|
|
||||||
byte[] signature64,
|
|
||||||
byte[] hash32) {
|
|
||||||
|
|
||||||
Objects.requireNonNull(bodyBytes, "bodyBytes == null");
|
|
||||||
Objects.requireNonNull(signature64, "signature64 == null");
|
|
||||||
Objects.requireNonNull(hash32, "hash32 == null");
|
|
||||||
|
|
||||||
if (signature64.length != SIGNATURE_LEN)
|
|
||||||
throw new IllegalArgumentException("signature64 != 64");
|
|
||||||
if (hash32.length != HASH_LEN)
|
|
||||||
throw new IllegalArgumentException("hash32 != 32");
|
|
||||||
|
|
||||||
this.recordNumber = recordNumber;
|
|
||||||
this.timestamp = timestamp;
|
|
||||||
this.line = line;
|
|
||||||
this.lineNumber = lineNumber;
|
|
||||||
this.bodyBytes = Arrays.copyOf(bodyBytes, bodyBytes.length);
|
|
||||||
this.signature64 = Arrays.copyOf(signature64, SIGNATURE_LEN);
|
|
||||||
this.hash32 = Arrays.copyOf(hash32, HASH_LEN);
|
|
||||||
|
|
||||||
this.recordSize =
|
|
||||||
RAW_HEADER_SIZE +
|
|
||||||
bodyBytes.length +
|
|
||||||
SIGNATURE_LEN +
|
|
||||||
HASH_LEN;
|
|
||||||
|
|
||||||
ByteBuffer bb = ByteBuffer.allocate(recordSize).order(ByteOrder.BIG_ENDIAN);
|
|
||||||
bb.putInt(recordSize);
|
|
||||||
bb.putInt(recordNumber);
|
|
||||||
bb.putLong(timestamp);
|
|
||||||
bb.putShort(line);
|
|
||||||
bb.putInt(lineNumber);
|
|
||||||
bb.put(bodyBytes);
|
|
||||||
bb.put(this.signature64);
|
|
||||||
bb.put(this.hash32);
|
|
||||||
|
|
||||||
this.fullBytes = bb.array();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] getRawBytes() {
|
|
||||||
int rawLen = recordSize - SIGNATURE_LEN - HASH_LEN;
|
|
||||||
byte[] raw = new byte[rawLen];
|
|
||||||
System.arraycopy(fullBytes, 0, raw, 0, rawLen);
|
|
||||||
return raw;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ===================================================================== */
|
|
||||||
|
|
||||||
public byte[] getSignature64() {
|
|
||||||
return Arrays.copyOf(signature64, SIGNATURE_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getHash32() {
|
|
||||||
return Arrays.copyOf(hash32, HASH_LEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] toBytes() {
|
|
||||||
return Arrays.copyOf(fullBytes, fullBytes.length);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,7 +1,7 @@
|
|||||||
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
package server.logic.ws_protocol.JSON.handlers.blockchain;
|
||||||
|
|
||||||
import blockchain_new.BchBlockEntry_new;
|
import blockchain.BchBlockEntry;
|
||||||
import blockchain_new.BchCryptoVerifier_new;
|
import blockchain.BchCryptoVerifier;
|
||||||
import server.logic.ws_protocol.WireCodes;
|
import server.logic.ws_protocol.WireCodes;
|
||||||
import shine.db.SqliteDbController;
|
import shine.db.SqliteDbController;
|
||||||
import shine.db.dao.BlockchainStateDAO;
|
import shine.db.dao.BlockchainStateDAO;
|
||||||
@ -102,9 +102,9 @@ public final class BlockchainStateService_new {
|
|||||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, "");
|
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
final BchBlockEntry_new block;
|
final BchBlockEntry block;
|
||||||
try {
|
try {
|
||||||
block = new BchBlockEntry_new(blockBytes);
|
block = new BchBlockEntry(blockBytes);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_format", 0, "");
|
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_format", 0, "");
|
||||||
}
|
}
|
||||||
@ -158,7 +158,7 @@ public final class BlockchainStateService_new {
|
|||||||
byte[] prevLineHash32 = prevGlobalHash32; // пока линии не используем
|
byte[] prevLineHash32 = prevGlobalHash32; // пока линии не используем
|
||||||
|
|
||||||
// 5) крипто-проверка
|
// 5) крипто-проверка
|
||||||
boolean ok = BchCryptoVerifier_new.verifyAll(
|
boolean ok = BchCryptoVerifier.verifyAll(
|
||||||
login,
|
login,
|
||||||
prevGlobalHash32,
|
prevGlobalHash32,
|
||||||
prevLineHash32,
|
prevLineHash32,
|
||||||
|
|||||||
@ -35,9 +35,7 @@ public final class Net_AddBlock_new_Handler implements JsonMessageHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Даже при ошибке (например bad_global_sequence) можно вернуть “что сервер считает последним”
|
// Даже при ошибке (например bad_global_sequence) можно вернуть “что сервер считает последним”
|
||||||
if (r.serverLastGlobalNumber != null) {
|
resp.setServerLastGlobalNumber(r.serverLastGlobalNumber);
|
||||||
resp.setServerLastGlobalNumber(r.serverLastGlobalNumber);
|
|
||||||
}
|
|
||||||
if (r.serverLastGlobalHash != null) {
|
if (r.serverLastGlobalHash != null) {
|
||||||
resp.setServerLastGlobalHash(r.serverLastGlobalHash);
|
resp.setServerLastGlobalHash(r.serverLastGlobalHash);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user