25 12 25
Добавил восстановление на случай застрявших темп файлов. Оно работает! И уведомление админа о критических ощибках, если файлы блокчейна поврежденны
This commit is contained in:
parent
6c2449f623
commit
25aa57dc5e
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@ -14,6 +14,7 @@
|
|||||||
<option value="$PROJECT_DIR$/shine-server-crypto" />
|
<option value="$PROJECT_DIR$/shine-server-crypto" />
|
||||||
<option value="$PROJECT_DIR$/shine-server-db" />
|
<option value="$PROJECT_DIR$/shine-server-db" />
|
||||||
<option value="$PROJECT_DIR$/shine-server-geo" />
|
<option value="$PROJECT_DIR$/shine-server-geo" />
|
||||||
|
<option value="$PROJECT_DIR$/shine-server-log" />
|
||||||
<option value="$PROJECT_DIR$/shine-server-net-protocol" />
|
<option value="$PROJECT_DIR$/shine-server-net-protocol" />
|
||||||
<option value="$PROJECT_DIR$/shine-server-net-server" />
|
<option value="$PROJECT_DIR$/shine-server-net-server" />
|
||||||
</set>
|
</set>
|
||||||
|
|||||||
@ -39,6 +39,7 @@ dependencies {
|
|||||||
|
|
||||||
|
|
||||||
implementation project(':shine-server-config') // модуль настроек из application.properties
|
implementation project(':shine-server-config') // модуль настроек из application.properties
|
||||||
|
implementation project(":shine-server-log") // модуль логирования и уведомления админов
|
||||||
|
|
||||||
implementation project(':shine-server-crypto') // модуль сервера для работы с криптографией
|
implementation project(':shine-server-crypto') // модуль сервера для работы с криптографией
|
||||||
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
rootProject.name = 'shine-server-server'
|
rootProject.name = 'shine-server-server'
|
||||||
|
|
||||||
|
include 'shine-server-log'
|
||||||
include 'shine-server-config'
|
include 'shine-server-config'
|
||||||
include 'shine-server-geo'
|
include 'shine-server-geo'
|
||||||
include 'shine-server-crypto'
|
include 'shine-server-crypto'
|
||||||
|
|||||||
@ -25,6 +25,8 @@ dependencies {
|
|||||||
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
||||||
|
|
||||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
|
testImplementation 'org.junit.jupiter:junit-jupiter:5.11.0'
|
||||||
|
|
||||||
|
implementation project(":shine-server-log") // модуль логирования и уведомления админов
|
||||||
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,59 +0,0 @@
|
|||||||
//package utils.files;
|
|
||||||
//
|
|
||||||
//import java.nio.charset.StandardCharsets;
|
|
||||||
//import java.util.Arrays;
|
|
||||||
//
|
|
||||||
///**
|
|
||||||
// * ===============================================================
|
|
||||||
// * FileStoreUtilSelfTest — запускаемый тест утилиты FileStoreUtil.
|
|
||||||
// * ---------------------------------------------------------------
|
|
||||||
// * Сценарий:
|
|
||||||
// * 1) Создаём «блокчейн-файл» для id=20251021 с начальными данными.
|
|
||||||
// * 2) Дозаписываем ещё порцию данных.
|
|
||||||
// * 3) Читаем целиком и печатаем длину + превью.
|
|
||||||
// *.
|
|
||||||
// * Ожидаемый итог:
|
|
||||||
// * • В папке "data" появится файл "20251021.bch"
|
|
||||||
// * • В консоли будет длина содержимого и небольшой превью-дамп.
|
|
||||||
// * ===============================================================
|
|
||||||
// */
|
|
||||||
//public class FileStoreUtilSelfTest {
|
|
||||||
//
|
|
||||||
// public static void main(String[] args) {
|
|
||||||
// System.out.println("=== FileStoreUtil self-test ===");
|
|
||||||
//
|
|
||||||
// FileStoreUtil fs = FileStoreUtil.getInstance();
|
|
||||||
//
|
|
||||||
// long blockchainId = 20251021L;
|
|
||||||
//
|
|
||||||
// byte[] part1 = "Hello ".getBytes(StandardCharsets.UTF_8);
|
|
||||||
// byte[] part2 = "Blockchain!".getBytes(StandardCharsets.UTF_8);
|
|
||||||
//
|
|
||||||
// // 1) создаём новый файл для «блокчейна»
|
|
||||||
// fs.newBlockchain(blockchainId, part1);
|
|
||||||
//
|
|
||||||
// // 2) дозаписываем данные
|
|
||||||
// fs.addDataToBlockchain(blockchainId, part2);
|
|
||||||
//
|
|
||||||
// // 3) читаем всё содержимое и показываем превью
|
|
||||||
// byte[] all = fs.readAllDataFromBlockchain(blockchainId);
|
|
||||||
// System.out.println("Total bytes read: " + all.length);
|
|
||||||
// System.out.println("Preview (UTF-8): " + new String(all, StandardCharsets.UTF_8));
|
|
||||||
//
|
|
||||||
// // небольшой hex-дамп первых 32 байт (для наглядности)
|
|
||||||
// System.out.println("Preview (HEX 32B): " + toHexPreview(all, 32));
|
|
||||||
//
|
|
||||||
// System.out.println("✅ Self-test passed (файл: data/" + blockchainId + FileStoreUtil.BLOCKCHAIN_FILE_EXTENSION + ")");
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// private static String toHexPreview(byte[] data, int max) {
|
|
||||||
// int n = Math.min(data.length, max);
|
|
||||||
// StringBuilder sb = new StringBuilder(n * 2);
|
|
||||||
// for (int i = 0; i < n; i++) {
|
|
||||||
// sb.append(String.format("%02X", data[i]));
|
|
||||||
// if (i + 1 < n) sb.append(' ');
|
|
||||||
// }
|
|
||||||
// if (data.length > n) sb.append(" ...");
|
|
||||||
// return sb.toString();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
27
shine-server-log/build.gradle
Normal file
27
shine-server-log/build.gradle
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
}
|
||||||
|
|
||||||
|
group = 'shine'
|
||||||
|
version = '1.0.0'
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(17)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
|
||||||
|
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
|
}
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package server.ws;
|
package shine.log;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -12,7 +12,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
* - пишет МАКСИМАЛЬНО ЗАМЕТНЫЙ лог
|
* - пишет МАКСИМАЛЬНО ЗАМЕТНЫЙ лог
|
||||||
*
|
*
|
||||||
* TODO:
|
* TODO:
|
||||||
* - отправка уведомления администратору:
|
* - реальная отправка уведомления администратору:
|
||||||
* * Telegram bot / email / SMS / webhook / Sentry / PagerDuty
|
* * Telegram bot / email / SMS / webhook / Sentry / PagerDuty
|
||||||
* * желательно с hostname, временем, именем блокчейна, размерами и stacktrace
|
* * желательно с hostname, временем, именем блокчейна, размерами и stacktrace
|
||||||
* ===============================================================
|
* ===============================================================
|
||||||
@ -23,17 +23,21 @@ public final class BlockchainAdminNotifier {
|
|||||||
|
|
||||||
private BlockchainAdminNotifier() {}
|
private BlockchainAdminNotifier() {}
|
||||||
|
|
||||||
|
public static void critical(String message) {
|
||||||
|
critical(message, null);
|
||||||
|
}
|
||||||
|
|
||||||
public static void critical(String message, Throwable t) {
|
public static void critical(String message, Throwable t) {
|
||||||
|
|
||||||
String bannerTop =
|
String bannerTop =
|
||||||
"\n" +
|
"\n" +
|
||||||
"=================================================================\n" +
|
"=================================================================\n" +
|
||||||
"==================== !!! CRITICAL ALERT !!! ===================\n" +
|
"==================== !!! КРИТИЧЕСКАЯ ОШИБКА !!! ===============\n" +
|
||||||
"=================================================================";
|
"=================================================================";
|
||||||
|
|
||||||
String bannerBottom =
|
String bannerBottom =
|
||||||
"=================================================================\n" +
|
"=================================================================\n" +
|
||||||
"==================== !!! ACTION REQUIRED !!! ===================\n" +
|
"==================== !!! НУЖНО ВМЕШАТЕЛЬСТВО !!! ===============\n" +
|
||||||
"=================================================================\n";
|
"=================================================================\n";
|
||||||
|
|
||||||
if (t == null) {
|
if (t == null) {
|
||||||
@ -51,6 +55,6 @@ public final class BlockchainAdminNotifier {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Реальная отправка уведомления администратору (telegram/email/webhook/sentry)
|
// TODO: Реальная отправка уведомления администратору (Telegram/email/webhook/Sentry)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -25,6 +25,8 @@ dependencies {
|
|||||||
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
implementation "org.slf4j:slf4j-api:2.0.16" // вызов логгера
|
||||||
|
|
||||||
implementation project(':shine-server-config') // модуль с настройками
|
implementation project(':shine-server-config') // модуль с настройками
|
||||||
|
implementation project(":shine-server-log") // модуль логирования и уведомления админов
|
||||||
|
|
||||||
implementation project(':shine-server-crypto') // модуль сервера для работы с криптографией
|
implementation project(':shine-server-crypto') // модуль сервера для работы с криптографией
|
||||||
implementation project(':shine-server-blockchain') // модуль для работы с блокчейном
|
implementation project(':shine-server-blockchain') // модуль для работы с блокчейном
|
||||||
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
implementation project(':shine-server-db') // модуль для работы с БД содержит и сущности из БД и саму работу с БД
|
||||||
|
|||||||
@ -9,6 +9,7 @@ import shine.db.dao.BlocksDAO;
|
|||||||
import shine.db.entities.BlockEntry;
|
import shine.db.entities.BlockEntry;
|
||||||
import shine.db.entities.BlockchainStateEntry;
|
import shine.db.entities.BlockchainStateEntry;
|
||||||
import utils.files.FileStoreUtil;
|
import utils.files.FileStoreUtil;
|
||||||
|
import shine.log.BlockchainAdminNotifier;
|
||||||
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
@ -26,7 +27,12 @@ import java.sql.SQLException;
|
|||||||
* Важно:
|
* Важно:
|
||||||
* - Шаг (2) — строго атомарный (SQL tx).
|
* - Шаг (2) — строго атомарный (SQL tx).
|
||||||
* - Шаг (3) — атомарный на уровне ФС, если поддерживается ATOMIC_MOVE.
|
* - Шаг (3) — атомарный на уровне ФС, если поддерживается ATOMIC_MOVE.
|
||||||
* - Если сервер упадёт между (2) и (3), останется tmp — твой recovery при старте починит.
|
*
|
||||||
|
* ДОПОЛНЕНИЕ (КРИТИЧНО):
|
||||||
|
* - Перед тем как дописывать блок, проверяем:
|
||||||
|
* реальный размер <name>.bch == st.fileSizeBytes.
|
||||||
|
* Если не совпадает — считаем это критической внешней порчей файлов,
|
||||||
|
* шлём уведомление админу и НЕ продолжаем запись.
|
||||||
*/
|
*/
|
||||||
public final class BlockchainWriter {
|
public final class BlockchainWriter {
|
||||||
|
|
||||||
@ -46,6 +52,7 @@ public final class BlockchainWriter {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Главный метод:
|
* Главный метод:
|
||||||
|
* - (0) проверяет соответствие размера файла и state (если это не genesis)
|
||||||
* - создаёт tmp-файл (старое+новое),
|
* - создаёт tmp-файл (старое+новое),
|
||||||
* - атомарно коммитит БД (block+state),
|
* - атомарно коммитит БД (block+state),
|
||||||
* - атомарно заменяет основной файл.
|
* - атомарно заменяет основной файл.
|
||||||
@ -59,6 +66,15 @@ public final class BlockchainWriter {
|
|||||||
String newHashHex
|
String newHashHex
|
||||||
) throws SQLException {
|
) throws SQLException {
|
||||||
|
|
||||||
|
// =====================================================================
|
||||||
|
// ШАГ 0. КРИТИЧЕСКАЯ ПРОВЕРКА КОНСИСТЕНТНОСТИ:
|
||||||
|
// - если state есть и ожидает ненулевой размер,
|
||||||
|
// то основной файл должен существовать и иметь точно этот размер.
|
||||||
|
// - если не так — это почти наверняка внешнее вмешательство/порча,
|
||||||
|
// и продолжать запись НЕЛЬЗЯ.
|
||||||
|
// =====================================================================
|
||||||
|
verifyMainFileSizeMatchesStateOrAlert(login, blockchainName, block, stOrNull);
|
||||||
|
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
// ШАГ 1. Готовим bytes нового блока (включая signature+hash)
|
// ШАГ 1. Готовим bytes нового блока (включая signature+hash)
|
||||||
// =====================================================================
|
// =====================================================================
|
||||||
@ -90,19 +106,22 @@ public final class BlockchainWriter {
|
|||||||
try {
|
try {
|
||||||
oldBytes = fs.readBlockchain(blockchainName);
|
oldBytes = fs.readBlockchain(blockchainName);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// ✅ Добавили подробный лог: это очень важная точка
|
|
||||||
log.error("Ошибка чтения старого файла блокчейна перед записью tmp (login={}, blockchainName={}, oldFileSize={}, blockNumber={})",
|
log.error("Ошибка чтения старого файла блокчейна перед записью tmp (login={}, blockchainName={}, oldFileSize={}, blockNumber={})",
|
||||||
login, blockchainName, oldFileSize, block.recordNumber, e);
|
login, blockchainName, oldFileSize, block.recordNumber, e);
|
||||||
|
|
||||||
// Здесь лучше падать: state говорит, что файл есть, а прочитать нельзя.
|
|
||||||
throw new SQLException("Cannot read old blockchain file for: " + blockchainName, e);
|
throw new SQLException("Cannot read old blockchain file for: " + blockchainName, e);
|
||||||
}
|
}
|
||||||
|
|
||||||
// (на будущее) можно проверять согласованность: oldBytes.length == oldFileSize
|
// (в идеале это всегда должно совпадать после verifyMainFileSizeMatchesStateOrAlert)
|
||||||
// но ты всё равно будешь делать recovery при старте — оставим как подсказку.
|
|
||||||
if (oldBytes.length != (int) oldFileSize) {
|
if (oldBytes.length != (int) oldFileSize) {
|
||||||
log.warn("Несовпадение размера файла блокчейна: state говорит oldFileSize={}, а реально прочитали oldBytes.length={} (login={}, blockchainName={}, blockNumber={})",
|
String msg =
|
||||||
oldFileSize, oldBytes.length, login, blockchainName, block.recordNumber);
|
"Несовпадение размера файла блокчейна при чтении: " +
|
||||||
|
"state ожидал oldFileSize=" + oldFileSize +
|
||||||
|
", а реально прочитали oldBytes.length=" + oldBytes.length +
|
||||||
|
" (login=" + login +
|
||||||
|
", blockchainName=" + blockchainName +
|
||||||
|
", blockNumber=" + block.recordNumber + ").";
|
||||||
|
BlockchainAdminNotifier.critical(msg, null);
|
||||||
|
throw new SQLException(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
tmpBytes = concat(oldBytes, newBlockFullBytes);
|
tmpBytes = concat(oldBytes, newBlockFullBytes);
|
||||||
@ -113,7 +132,6 @@ public final class BlockchainWriter {
|
|||||||
try {
|
try {
|
||||||
fs.writeBlockchainTmp(blockchainName, tmpBytes);
|
fs.writeBlockchainTmp(blockchainName, tmpBytes);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
// ✅ Добавили подробный лог: это тоже критично
|
|
||||||
log.error("Ошибка записи tmp файла блокчейна (login={}, blockchainName={}, tmpBytesLen={}, oldFileSize={}, newFileSize={}, blockNumber={})",
|
log.error("Ошибка записи tmp файла блокчейна (login={}, blockchainName={}, tmpBytesLen={}, oldFileSize={}, newFileSize={}, blockNumber={})",
|
||||||
login, blockchainName, tmpBytes.length, oldFileSize, newFileSize, block.recordNumber, e);
|
login, blockchainName, tmpBytes.length, oldFileSize, newFileSize, block.recordNumber, e);
|
||||||
throw new SQLException("Cannot write tmp blockchain file for: " + blockchainName, e);
|
throw new SQLException("Cannot write tmp blockchain file for: " + blockchainName, e);
|
||||||
@ -145,7 +163,6 @@ public final class BlockchainWriter {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
try { c.rollback(); } catch (SQLException ignore) {}
|
try { c.rollback(); } catch (SQLException ignore) {}
|
||||||
|
|
||||||
// ✅ ВАЖНО: логируем причину отката + контекст
|
|
||||||
log.error("Ошибка транзакции БД при добавлении блока (rollback выполнен) (login={}, blockchainName={}, blockNumber={}, prevHash={}, newHash={}, oldFileSize={}, newFileSize={})",
|
log.error("Ошибка транзакции БД при добавлении блока (rollback выполнен) (login={}, blockchainName={}, blockNumber={}, prevHash={}, newHash={}, oldFileSize={}, newFileSize={})",
|
||||||
login, blockchainName, block.recordNumber, prevGlobalHashHex, newHashHex, oldFileSize, newFileSize, e);
|
login, blockchainName, block.recordNumber, prevGlobalHashHex, newHashHex, oldFileSize, newFileSize, e);
|
||||||
|
|
||||||
@ -159,23 +176,14 @@ public final class BlockchainWriter {
|
|||||||
// =================================================================
|
// =================================================================
|
||||||
// ШАГ 5. После успешного коммита БД — атомарно заменяем файл:
|
// ШАГ 5. После успешного коммита БД — атомарно заменяем файл:
|
||||||
// <name>.tmp_bch -> <name>.bch
|
// <name>.tmp_bch -> <name>.bch
|
||||||
//
|
|
||||||
// Если тут упадём:
|
|
||||||
// - БД уже обновлена
|
|
||||||
// - tmp остаётся
|
|
||||||
// - recovery при старте восстановит консистентность
|
|
||||||
// =================================================================
|
// =================================================================
|
||||||
if (committed) {
|
if (committed) {
|
||||||
try {
|
try {
|
||||||
fs.atomicReplaceBlockchainFile(blockchainName);
|
fs.atomicReplaceBlockchainFile(blockchainName);
|
||||||
} catch (Exception moveError) {
|
} catch (Exception moveError) {
|
||||||
// ✅ Очень важная ситуация: БД уже committed, а файл не заменился
|
|
||||||
log.error("БД закоммичена, но атомарная замена файла блокчейна не удалась. tmp оставлен для recovery. (login={}, blockchainName={}, blockNumber={}, newHash={}, tmpBytesLen={})",
|
log.error("БД закоммичена, но атомарная замена файла блокчейна не удалась. tmp оставлен для recovery. (login={}, blockchainName={}, blockNumber={}, newHash={}, tmpBytesLen={})",
|
||||||
login, blockchainName, block.recordNumber, newHashHex, tmpBytes.length, moveError);
|
login, blockchainName, block.recordNumber, newHashHex, tmpBytes.length, moveError);
|
||||||
|
|
||||||
// Здесь ВАЖНО: мы уже не можем откатить БД.
|
|
||||||
// Оставляем tmp и даём наверх ошибку — клиент увидит internal_error,
|
|
||||||
// а ты при старте починишь файловую часть.
|
|
||||||
throw new SQLException(
|
throw new SQLException(
|
||||||
"DB committed but file replace failed; tmp kept for recovery. blockchainName=" + blockchainName,
|
"DB committed but file replace failed; tmp kept for recovery. blockchainName=" + blockchainName,
|
||||||
moveError
|
moveError
|
||||||
@ -185,6 +193,73 @@ public final class BlockchainWriter {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Проверка: реальный размер <name>.bch должен совпадать с st.fileSizeBytes.
|
||||||
|
* Если нет — это критическая внешняя порча/вмешательство, уведомляем админа и падаем.
|
||||||
|
*/
|
||||||
|
private void verifyMainFileSizeMatchesStateOrAlert(
|
||||||
|
String login,
|
||||||
|
String blockchainName,
|
||||||
|
BchBlockEntry block,
|
||||||
|
BlockchainStateEntry stOrNull
|
||||||
|
) throws SQLException {
|
||||||
|
|
||||||
|
if (stOrNull == null) {
|
||||||
|
// genesis — state ещё нет, проверять нечего
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
long expected = stOrNull.getFileSizeBytes();
|
||||||
|
if (expected <= 0) {
|
||||||
|
// state есть, но ожидаемый размер 0 — это либо пустая цепочка, либо старый формат.
|
||||||
|
// Здесь не трогаем (но можно усилить правила позже).
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
String mainFileName = fs.buildBlockchainFileName(blockchainName);
|
||||||
|
|
||||||
|
// Если файла нет — это уже очень подозрительно: state говорит “файл есть и размер > 0”
|
||||||
|
if (!fs.exists(mainFileName)) {
|
||||||
|
String msg =
|
||||||
|
"КРИТИЧЕСКАЯ ОШИБКА КОНСИСТЕНТНОСТИ: state ожидает основной файл, но его нет. " +
|
||||||
|
"login=" + login +
|
||||||
|
", blockchainName=" + blockchainName +
|
||||||
|
", expectedSizeFromState=" + expected +
|
||||||
|
", blockNumber=" + (block != null ? block.recordNumber : -1) + ".";
|
||||||
|
|
||||||
|
BlockchainAdminNotifier.critical(msg, null);
|
||||||
|
throw new SQLException(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
long real;
|
||||||
|
try {
|
||||||
|
real = fs.size(mainFileName);
|
||||||
|
} catch (Exception e) {
|
||||||
|
String msg =
|
||||||
|
"КРИТИЧЕСКАЯ ОШИБКА: не удалось получить размер основного файла блокчейна. " +
|
||||||
|
"login=" + login +
|
||||||
|
", blockchainName=" + blockchainName +
|
||||||
|
", expectedSizeFromState=" + expected +
|
||||||
|
", blockNumber=" + (block != null ? block.recordNumber : -1) + ".";
|
||||||
|
BlockchainAdminNotifier.critical(msg, e);
|
||||||
|
throw new SQLException(msg, e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (real != expected) {
|
||||||
|
String msg =
|
||||||
|
"КРИТИЧЕСКАЯ ОШИБКА КОНСИСТЕНТНОСТИ: размер файла блокчейна НЕ СОВПАДАЕТ с state. " +
|
||||||
|
"login=" + login +
|
||||||
|
", blockchainName=" + blockchainName +
|
||||||
|
", expectedSizeFromState=" + expected +
|
||||||
|
", realMainFileSize=" + real +
|
||||||
|
", blockNumber=" + (block != null ? block.recordNumber : -1) + ". " +
|
||||||
|
"Похоже на внешнее вмешательство/порчу файла. Запись нового блока остановлена.";
|
||||||
|
|
||||||
|
BlockchainAdminNotifier.critical(msg, null);
|
||||||
|
throw new SQLException(msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Обновление состояния blockchain_state (создаём если отсутствует).
|
* Обновление состояния blockchain_state (создаём если отсутствует).
|
||||||
* Пока линии не используются: lineIndex=0 и lineHash = globalHash.
|
* Пока линии не используются: lineIndex=0 и lineHash = globalHash.
|
||||||
@ -274,7 +349,6 @@ public final class BlockchainWriter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static long safeAdd(long x, long y) {
|
private static long safeAdd(long x, long y) {
|
||||||
// защита от переполнения long (маловероятно, но пусть будет)
|
|
||||||
long r = x + y;
|
long r = x + y;
|
||||||
if (((x ^ r) & (y ^ r)) < 0) {
|
if (((x ^ r) & (y ^ r)) < 0) {
|
||||||
throw new IllegalArgumentException("fileSizeBytes overflow: " + x + " + " + y);
|
throw new IllegalArgumentException("fileSizeBytes overflow: " + x + " + " + y);
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import org.slf4j.LoggerFactory;
|
|||||||
import shine.db.dao.BlockchainStateDAO;
|
import shine.db.dao.BlockchainStateDAO;
|
||||||
import shine.db.entities.BlockchainStateEntry;
|
import shine.db.entities.BlockchainStateEntry;
|
||||||
import utils.files.FileStoreUtil;
|
import utils.files.FileStoreUtil;
|
||||||
|
import shine.log.BlockchainAdminNotifier;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.file.*;
|
import java.nio.file.*;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user