17 12 25
Заработало блок добавляется в файл и в статус блокчена в БД!! Но ещё надо сделать таблицу с записями :) 2
This commit is contained in:
parent
e9c11d6b75
commit
4fb6b10a97
@ -20,7 +20,7 @@ import java.sql.Statement;
|
|||||||
* - active_sessions
|
* - active_sessions
|
||||||
* - users_params
|
* - users_params
|
||||||
* - ip_geo_cache
|
* - ip_geo_cache
|
||||||
* - blockchain_state (MVP: одна таблица, линии 0..7 внутри строки)
|
* - blockchain_state (MVP)
|
||||||
*/
|
*/
|
||||||
public class DatabaseInitializer {
|
public class DatabaseInitializer {
|
||||||
|
|
||||||
@ -160,8 +160,12 @@ public class DatabaseInitializer {
|
|||||||
blockchain_id INTEGER NOT NULL PRIMARY KEY,
|
blockchain_id INTEGER NOT NULL PRIMARY KEY,
|
||||||
user_login TEXT NOT NULL,
|
user_login TEXT NOT NULL,
|
||||||
public_key_base64 TEXT NOT NULL,
|
public_key_base64 TEXT NOT NULL,
|
||||||
|
|
||||||
size_limit INTEGER NOT NULL,
|
size_limit INTEGER NOT NULL,
|
||||||
size_bytes INTEGER NOT NULL,
|
size_bytes INTEGER NOT NULL,
|
||||||
|
|
||||||
|
file_size_bytes INTEGER NOT NULL,
|
||||||
|
|
||||||
last_global_number INTEGER NOT NULL,
|
last_global_number INTEGER NOT NULL,
|
||||||
last_global_hash TEXT NOT NULL,
|
last_global_hash TEXT NOT NULL,
|
||||||
updated_at_ms INTEGER NOT NULL,
|
updated_at_ms INTEGER NOT NULL,
|
||||||
|
|||||||
@ -21,7 +21,6 @@ public final class BlockchainStateDAO {
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
// старый метод оставим
|
|
||||||
public BlockchainStateEntry getByBlockchainId(long blockchainId) throws SQLException {
|
public BlockchainStateEntry getByBlockchainId(long blockchainId) throws SQLException {
|
||||||
try (Connection c = db.getConnection()) {
|
try (Connection c = db.getConnection()) {
|
||||||
return getByBlockchainId(c, blockchainId);
|
return getByBlockchainId(c, blockchainId);
|
||||||
@ -36,6 +35,7 @@ public final class BlockchainStateDAO {
|
|||||||
public_key_base64,
|
public_key_base64,
|
||||||
size_limit,
|
size_limit,
|
||||||
size_bytes,
|
size_bytes,
|
||||||
|
file_size_bytes,
|
||||||
last_global_number,
|
last_global_number,
|
||||||
last_global_hash,
|
last_global_hash,
|
||||||
line0_last_number, line0_last_hash,
|
line0_last_number, line0_last_hash,
|
||||||
@ -60,7 +60,6 @@ public final class BlockchainStateDAO {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// старый метод оставим
|
|
||||||
public void upsert(BlockchainStateEntry e) throws SQLException {
|
public void upsert(BlockchainStateEntry e) throws SQLException {
|
||||||
try (Connection c = db.getConnection()) {
|
try (Connection c = db.getConnection()) {
|
||||||
upsert(c, e);
|
upsert(c, e);
|
||||||
@ -75,6 +74,7 @@ public final class BlockchainStateDAO {
|
|||||||
public_key_base64,
|
public_key_base64,
|
||||||
size_limit,
|
size_limit,
|
||||||
size_bytes,
|
size_bytes,
|
||||||
|
file_size_bytes,
|
||||||
last_global_number,
|
last_global_number,
|
||||||
last_global_hash,
|
last_global_hash,
|
||||||
line0_last_number, line0_last_hash,
|
line0_last_number, line0_last_hash,
|
||||||
@ -104,6 +104,7 @@ public final class BlockchainStateDAO {
|
|||||||
public_key_base64 = excluded.public_key_base64,
|
public_key_base64 = excluded.public_key_base64,
|
||||||
size_limit = excluded.size_limit,
|
size_limit = excluded.size_limit,
|
||||||
size_bytes = excluded.size_bytes,
|
size_bytes = excluded.size_bytes,
|
||||||
|
file_size_bytes = excluded.file_size_bytes,
|
||||||
last_global_number = excluded.last_global_number,
|
last_global_number = excluded.last_global_number,
|
||||||
last_global_hash = excluded.last_global_hash,
|
last_global_hash = excluded.last_global_hash,
|
||||||
line0_last_number = excluded.line0_last_number,
|
line0_last_number = excluded.line0_last_number,
|
||||||
@ -132,6 +133,7 @@ public final class BlockchainStateDAO {
|
|||||||
ps.setString(i++, nn(e.getPublicKeyBase64()));
|
ps.setString(i++, nn(e.getPublicKeyBase64()));
|
||||||
ps.setInt(i++, e.getSizeLimit());
|
ps.setInt(i++, e.getSizeLimit());
|
||||||
ps.setInt(i++, e.getSizeBytes());
|
ps.setInt(i++, e.getSizeBytes());
|
||||||
|
ps.setLong(i++, e.getFileSizeBytes());
|
||||||
ps.setInt(i++, e.getLastGlobalNumber());
|
ps.setInt(i++, e.getLastGlobalNumber());
|
||||||
ps.setString(i++, nn(e.getLastGlobalHash()));
|
ps.setString(i++, nn(e.getLastGlobalHash()));
|
||||||
|
|
||||||
@ -153,6 +155,7 @@ public final class BlockchainStateDAO {
|
|||||||
|
|
||||||
e.setSizeLimit(rs.getInt("size_limit"));
|
e.setSizeLimit(rs.getInt("size_limit"));
|
||||||
e.setSizeBytes(rs.getInt("size_bytes"));
|
e.setSizeBytes(rs.getInt("size_bytes"));
|
||||||
|
e.setFileSizeBytes(rs.getLong("file_size_bytes"));
|
||||||
|
|
||||||
e.setLastGlobalNumber(rs.getInt("last_global_number"));
|
e.setLastGlobalNumber(rs.getInt("last_global_number"));
|
||||||
e.setLastGlobalHash(rs.getString("last_global_hash"));
|
e.setLastGlobalHash(rs.getString("last_global_hash"));
|
||||||
|
|||||||
@ -16,6 +16,9 @@ public final class BlockchainStateEntry {
|
|||||||
private int sizeLimit;
|
private int sizeLimit;
|
||||||
private int sizeBytes;
|
private int sizeBytes;
|
||||||
|
|
||||||
|
/** NEW: размер файла блокчейна в байтах (то, что будем сверять/чинить при старте). */
|
||||||
|
private long fileSizeBytes;
|
||||||
|
|
||||||
private int lastGlobalNumber;
|
private int lastGlobalNumber;
|
||||||
private String lastGlobalHash; // HEX(64) либо пустая строка для "нулевого"
|
private String lastGlobalHash; // HEX(64) либо пустая строка для "нулевого"
|
||||||
|
|
||||||
@ -32,12 +35,12 @@ public final class BlockchainStateEntry {
|
|||||||
this.lastGlobalHash = "";
|
this.lastGlobalHash = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- удобный конструктор (если хочешь) ---
|
|
||||||
public BlockchainStateEntry(long blockchainId,
|
public BlockchainStateEntry(long blockchainId,
|
||||||
String userLogin,
|
String userLogin,
|
||||||
String publicKeyBase64,
|
String publicKeyBase64,
|
||||||
int sizeLimit,
|
int sizeLimit,
|
||||||
int sizeBytes,
|
int sizeBytes,
|
||||||
|
long fileSizeBytes,
|
||||||
int lastGlobalNumber,
|
int lastGlobalNumber,
|
||||||
String lastGlobalHash,
|
String lastGlobalHash,
|
||||||
int[] lastLineNumbers,
|
int[] lastLineNumbers,
|
||||||
@ -48,6 +51,7 @@ public final class BlockchainStateEntry {
|
|||||||
this.publicKeyBase64 = publicKeyBase64;
|
this.publicKeyBase64 = publicKeyBase64;
|
||||||
this.sizeLimit = sizeLimit;
|
this.sizeLimit = sizeLimit;
|
||||||
this.sizeBytes = sizeBytes;
|
this.sizeBytes = sizeBytes;
|
||||||
|
this.fileSizeBytes = fileSizeBytes;
|
||||||
this.lastGlobalNumber = lastGlobalNumber;
|
this.lastGlobalNumber = lastGlobalNumber;
|
||||||
this.lastGlobalHash = lastGlobalHash == null ? "" : lastGlobalHash;
|
this.lastGlobalHash = lastGlobalHash == null ? "" : lastGlobalHash;
|
||||||
|
|
||||||
@ -65,8 +69,6 @@ public final class BlockchainStateEntry {
|
|||||||
this.updatedAtMs = updatedAtMs;
|
this.updatedAtMs = updatedAtMs;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- getters / setters ---
|
|
||||||
|
|
||||||
public long getBlockchainId() { return blockchainId; }
|
public long getBlockchainId() { return blockchainId; }
|
||||||
public void setBlockchainId(long blockchainId) { this.blockchainId = blockchainId; }
|
public void setBlockchainId(long blockchainId) { this.blockchainId = blockchainId; }
|
||||||
|
|
||||||
@ -82,6 +84,9 @@ public final class BlockchainStateEntry {
|
|||||||
public int getSizeBytes() { return sizeBytes; }
|
public int getSizeBytes() { return sizeBytes; }
|
||||||
public void setSizeBytes(int sizeBytes) { this.sizeBytes = sizeBytes; }
|
public void setSizeBytes(int sizeBytes) { this.sizeBytes = sizeBytes; }
|
||||||
|
|
||||||
|
public long getFileSizeBytes() { return fileSizeBytes; }
|
||||||
|
public void setFileSizeBytes(long fileSizeBytes) { this.fileSizeBytes = fileSizeBytes; }
|
||||||
|
|
||||||
public int getLastGlobalNumber() { return lastGlobalNumber; }
|
public int getLastGlobalNumber() { return lastGlobalNumber; }
|
||||||
public void setLastGlobalNumber(int lastGlobalNumber) { this.lastGlobalNumber = lastGlobalNumber; }
|
public void setLastGlobalNumber(int lastGlobalNumber) { this.lastGlobalNumber = lastGlobalNumber; }
|
||||||
|
|
||||||
|
|||||||
@ -29,6 +29,7 @@ public final class BlockchainStateService_new {
|
|||||||
this.stateAfter = stateAfter;
|
this.stateAfter = stateAfter;
|
||||||
this.lineIndex = lineIndex;
|
this.lineIndex = lineIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isOk() { return reasonCode == null && httpStatus == 200; }
|
public boolean isOk() { return reasonCode == null && httpStatus == 200; }
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,7 +41,6 @@ public final class BlockchainStateService_new {
|
|||||||
|
|
||||||
// Локи по blockchainId (MVP, один сервер)
|
// Локи по blockchainId (MVP, один сервер)
|
||||||
private final ConcurrentMap<Long, ReentrantLock> locks = new ConcurrentHashMap<>();
|
private final ConcurrentMap<Long, ReentrantLock> locks = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
private ReentrantLock lockFor(long blockchainId) {
|
private ReentrantLock lockFor(long blockchainId) {
|
||||||
return locks.computeIfAbsent(blockchainId, k -> new ReentrantLock());
|
return locks.computeIfAbsent(blockchainId, k -> new ReentrantLock());
|
||||||
}
|
}
|
||||||
@ -80,6 +80,8 @@ public final class BlockchainStateService_new {
|
|||||||
if (lineIndex < 0 || lineIndex > 7)
|
if (lineIndex < 0 || lineIndex > 7)
|
||||||
return new Result(400, "BAD_LINE_INDEX", null, lineIndex);
|
return new Result(400, "BAD_LINE_INDEX", null, lineIndex);
|
||||||
|
|
||||||
|
boolean isHeaderBlock = (globalNumber == 0 && lineIndex == 0 && block.lineNumber == 0);
|
||||||
|
|
||||||
ReentrantLock lock = lockFor(blockchainId);
|
ReentrantLock lock = lockFor(blockchainId);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try (Connection conn = SqliteDbController.getInstance().getConnection()) {
|
try (Connection conn = SqliteDbController.getInstance().getConnection()) {
|
||||||
@ -87,18 +89,14 @@ public final class BlockchainStateService_new {
|
|||||||
BlockchainStateDAO stateDao = BlockchainStateDAO.getInstance();
|
BlockchainStateDAO stateDao = BlockchainStateDAO.getInstance();
|
||||||
SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
SolanaUsersDAO usersDao = SolanaUsersDAO.getInstance();
|
||||||
|
|
||||||
// читаем state В ЭТОМ ЖЕ conn
|
|
||||||
BlockchainStateEntry state = stateDao.getByBlockchainId(conn, blockchainId);
|
BlockchainStateEntry state = stateDao.getByBlockchainId(conn, blockchainId);
|
||||||
|
|
||||||
boolean isHeaderBlock = (globalNumber == 0 && lineIndex == 0 && block.lineNumber == 0);
|
|
||||||
|
|
||||||
if (state == null) {
|
if (state == null) {
|
||||||
// state отсутствует — разрешаем ТОЛЬКО header-блок
|
// state отсутствует — разрешаем ТОЛЬКО header-блок
|
||||||
if (!isHeaderBlock) {
|
if (!isHeaderBlock) {
|
||||||
return new Result(404, "UNKNOWN_BLOCKCHAIN", null, lineIndex);
|
return new Result(404, "UNKNOWN_BLOCKCHAIN", null, lineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Проверяем пользователя и соответствие bchId
|
|
||||||
SolanaUserEntry u = usersDao.getByLogin(conn, login);
|
SolanaUserEntry u = usersDao.getByLogin(conn, login);
|
||||||
if (u == null) {
|
if (u == null) {
|
||||||
return new Result(404, "UNKNOWN_USER", null, lineIndex);
|
return new Result(404, "UNKNOWN_USER", null, lineIndex);
|
||||||
@ -112,25 +110,21 @@ public final class BlockchainStateService_new {
|
|||||||
return new Result(409, "GLOBAL_HASH_MISMATCH", null, lineIndex);
|
return new Result(409, "GLOBAL_HASH_MISMATCH", null, lineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Создаём “нулевой” state ДО записи header (last_global_number = -1)
|
// Создаём начальный state (ЕЩЁ НЕ ПИШЕМ В БД — запишем после успешного append)
|
||||||
state = createInitialState(blockchainId, login, u.getLoginKey(), safeLimit(u.getBchLimit()));
|
state = createInitialState(blockchainId, login, u.getLoginKey(), safeLimit(u.getBchLimit()));
|
||||||
// stateDao.upsert(conn, state); //TODO так здесь наверное его в БД сохранять не надо если всё верно то потом дополненный сохраниться
|
|
||||||
} else {
|
} else {
|
||||||
// state есть — обычная проверка login
|
|
||||||
if (!login.equals(state.getUserLogin())) {
|
if (!login.equals(state.getUserLogin())) {
|
||||||
return new Result(403, "LOGIN_MISMATCH", state, lineIndex);
|
return new Result(403, "LOGIN_MISMATCH", state, lineIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Перечитывать не надо, state актуален в переменной.
|
|
||||||
|
|
||||||
// expected global
|
// expected global
|
||||||
int expectedGlobal = state.getLastGlobalNumber() + 1;
|
int expectedGlobal = state.getLastGlobalNumber() + 1;
|
||||||
if (globalNumber != expectedGlobal) {
|
if (globalNumber != expectedGlobal) {
|
||||||
return new Result(409, "OUT_OF_SEQUENCE_GLOBAL", state, lineIndex);
|
return new Result(409, "OUT_OF_SEQUENCE_GLOBAL", state, lineIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
// prev global hash сверяем
|
// prev global hash
|
||||||
String dbPrevGlobalHash = nn(state.getLastGlobalHash());
|
String dbPrevGlobalHash = nn(state.getLastGlobalHash());
|
||||||
if (!eqHash(prevGlobalHashHex, dbPrevGlobalHash)) {
|
if (!eqHash(prevGlobalHashHex, dbPrevGlobalHash)) {
|
||||||
return new Result(409, "GLOBAL_HASH_MISMATCH", state, lineIndex);
|
return new Result(409, "GLOBAL_HASH_MISMATCH", state, lineIndex);
|
||||||
@ -144,10 +138,10 @@ public final class BlockchainStateService_new {
|
|||||||
|
|
||||||
// TODO: крипто-проверка (потом подключим)
|
// TODO: крипто-проверка (потом подключим)
|
||||||
|
|
||||||
// 1) запись блока в файл
|
// 1) запись блока в файл (append-only)
|
||||||
FileStoreUtil.getInstance().addDataToBlockchain(blockchainId, block.toBytes());
|
FileStoreUtil.getInstance().addDataToBlockchain(blockchainId, block.toBytes());
|
||||||
|
|
||||||
// 2) апдейт state
|
// 2) апдейт state + запись state в БД
|
||||||
String newHashHex = bytesToHex(block.getHash32());
|
String newHashHex = bytesToHex(block.getHash32());
|
||||||
|
|
||||||
state.setLastGlobalNumber(globalNumber);
|
state.setLastGlobalNumber(globalNumber);
|
||||||
@ -156,15 +150,18 @@ public final class BlockchainStateService_new {
|
|||||||
state.setLastLineNumber(lineIndex, block.lineNumber);
|
state.setLastLineNumber(lineIndex, block.lineNumber);
|
||||||
state.setLastLineHash(lineIndex, newHashHex);
|
state.setLastLineHash(lineIndex, newHashHex);
|
||||||
|
|
||||||
|
// Логический размер (как было)
|
||||||
state.setSizeBytes(state.getSizeBytes() + fullBytes.length);
|
state.setSizeBytes(state.getSizeBytes() + fullBytes.length);
|
||||||
|
|
||||||
|
// Новый: размер файла (ровно по твоему правилу)
|
||||||
|
state.setFileSizeBytes(state.getFileSizeBytes() + fullBytes.length);
|
||||||
|
|
||||||
state.setUpdatedAtMs(System.currentTimeMillis());
|
state.setUpdatedAtMs(System.currentTimeMillis());
|
||||||
|
|
||||||
stateDao.upsert(conn, state);
|
stateDao.upsert(conn, state);
|
||||||
|
|
||||||
return new Result(200, null, state, lineIndex);
|
return new Result(200, null, state, lineIndex);
|
||||||
|
|
||||||
} catch (SQLException e) {
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
@ -180,18 +177,19 @@ public final class BlockchainStateService_new {
|
|||||||
s.setPublicKeyBase64(nn(loginKeyBase64));
|
s.setPublicKeyBase64(nn(loginKeyBase64));
|
||||||
|
|
||||||
s.setSizeLimit(sizeLimit);
|
s.setSizeLimit(sizeLimit);
|
||||||
s.setSizeBytes(0);
|
|
||||||
|
|
||||||
// как ты хочешь:
|
s.setSizeBytes(0);
|
||||||
|
s.setFileSizeBytes(0);
|
||||||
|
|
||||||
s.setLastGlobalNumber(-1);
|
s.setLastGlobalNumber(-1);
|
||||||
s.setLastGlobalHash(ZERO64);
|
s.setLastGlobalHash(ZERO64);
|
||||||
|
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
if (i == 0) {
|
if (i == 0) {
|
||||||
// линия 0: заглавный блок имеет lineNumber=0
|
// линия 0: заглавный блок имеет lineNumber=0 -> значит "последний" до него = -1
|
||||||
s.setLastLineNumber(i, -1);
|
s.setLastLineNumber(i, -1);
|
||||||
} else {
|
} else {
|
||||||
// остальные линии: первый блок будет lineNumber=1
|
// остальные линии: первый блок будет lineNumber=1 -> значит "последний" до него = 0
|
||||||
s.setLastLineNumber(i, 0);
|
s.setLastLineNumber(i, 0);
|
||||||
}
|
}
|
||||||
s.setLastLineHash(i, ZERO64);
|
s.setLastLineHash(i, ZERO64);
|
||||||
@ -202,7 +200,7 @@ public final class BlockchainStateService_new {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static int safeLimit(Integer limit) {
|
private static int safeLimit(Integer limit) {
|
||||||
if (limit == null || limit <= 0) return 1_000_000; // fallback (test)
|
if (limit == null || limit <= 0) return 1_000_000; // TEST ONLY fallback
|
||||||
return limit;
|
return limit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user