Дорабатываю добавление блоков. Ура добавилось. Осталось порядок навести и добавление файлов сделать и откат.
This commit is contained in:
AidarKC 2025-12-24 14:55:30 +03:00
parent 80ea016687
commit 834cf98ef9
4 changed files with 117 additions and 94 deletions

View File

@ -99,7 +99,6 @@ public final class BlockchainStateDAO {
?,?, ?,?,
?,?, ?,?,
?,?, ?,?,
?,?,
?,? ?,?
) )
ON CONFLICT(blockchainName) ON CONFLICT(blockchainName)

View File

@ -0,0 +1,81 @@
package server.logic.ws_protocol.JSON.handlers.blockchain;
import shine.db.dao.BlockchainStateDAO;
import shine.db.dao.BlocksDAO;
import shine.db.entities.BlockEntry;
import shine.db.entities.BlockchainStateEntry;
import java.sql.Connection;
import java.sql.SQLException;
public final class BlockchainDbWriter {
private final BlocksDAO blocksDAO;
private final BlockchainStateDAO stateDAO;
public BlockchainDbWriter(BlocksDAO blocksDAO, BlockchainStateDAO stateDAO) {
this.blocksDAO = blocksDAO;
this.stateDAO = stateDAO;
}
public void appendBlockAndState(
Connection c,
String login,
String blockchainName,
int globalNumber,
String prevGlobalHashHex,
byte[] blockBytes,
BlockchainStateEntry stOrNull,
String newHashHex
) throws SQLException {
insertBlockRow(c, login, blockchainName, globalNumber, prevGlobalHashHex, blockBytes);
BlockchainStateEntry st = stOrNull;
if (st == null) {
st = new BlockchainStateEntry();
st.setBlockchainName(blockchainName);
}
st.setLastGlobalNumber(globalNumber);
st.setLastGlobalHash(newHashHex);
st.setLastLineNumber(0, globalNumber);
st.setLastLineHash(0, newHashHex);
st.setUpdatedAtMs(System.currentTimeMillis());
stateDAO.upsert(c, st);
}
private void insertBlockRow(
Connection c,
String login,
String blockchainName,
int globalNumber,
String prevGlobalHashHex,
byte[] blockBytes
) throws SQLException {
BlockEntry e = new BlockEntry();
e.setLogin(login);
e.setBchName(blockchainName);
e.setBlockGlobalNumber(globalNumber);
e.setBlockGlobalPreHashe(prevGlobalHashHex);
e.setBlockLineIndex(0);
e.setBlockLineNumber(globalNumber);
e.setBlockLinePreHashe(prevGlobalHashHex);
e.setMsgType(0);
e.setBlockByte(blockBytes);
e.setToLogin(null);
e.setToBchName(null);
e.setToBlockGlobalNumber(null);
e.setToBlockHashe(null);
blocksDAO.upsert(c, e);
}
}

View File

@ -8,7 +8,6 @@ import shine.db.SqliteDbController;
import shine.db.dao.BlockchainStateDAO; import shine.db.dao.BlockchainStateDAO;
import shine.db.dao.BlocksDAO; import shine.db.dao.BlocksDAO;
import shine.db.dao.SolanaUsersDAO; import shine.db.dao.SolanaUsersDAO;
import shine.db.entities.BlockEntry;
import shine.db.entities.BlockchainStateEntry; import shine.db.entities.BlockchainStateEntry;
import shine.db.entities.SolanaUserEntry; import shine.db.entities.SolanaUserEntry;
import utils.blockchain.BlockchainNameUtil; import utils.blockchain.BlockchainNameUtil;
@ -56,6 +55,7 @@ public final class BlockchainStateService {
private final BlocksDAO blocksDAO = BlocksDAO.getInstance(); private final BlocksDAO blocksDAO = BlocksDAO.getInstance();
private final BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance(); private final BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();
private final SolanaUsersDAO solanaUsersDAO = SolanaUsersDAO.getInstance(); private final SolanaUsersDAO solanaUsersDAO = SolanaUsersDAO.getInstance();
private final BlockchainDbWriter dbWriter = new BlockchainDbWriter(blocksDAO, stateDAO);
private BlockchainStateService() {} private BlockchainStateService() {}
@ -75,18 +75,13 @@ public final class BlockchainStateService {
String blockBytesB64 String blockBytesB64
) { ) {
// Базовая валидация
if (blockchainName == null || blockchainName.isBlank()) if (blockchainName == null || blockchainName.isBlank())
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "empty_blockchain_name", 0, ""); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "empty_blockchain_name", 0, "");
// Можно быстро проверить, что login согласован с blockchainName (если хочешь строгость) String login = BlockchainNameUtil.loginFromBlockchainName(blockchainName);
String loginFromBlockchainName = BlockchainNameUtil.loginFromBlockchainName(blockchainName); if (login == null || login.isBlank())
if (loginFromBlockchainName == null || loginFromBlockchainName.isBlank())
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_blockchain_name", 0, ""); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_blockchain_name", 0, "");
// todo действительно давай прото брать логин из имени блокчена и не передавать его отдельно в запросе!
String login = loginFromBlockchainName;
byte[] blockBytes; byte[] blockBytes;
try { try {
blockBytes = decodeBase64(blockBytesB64); blockBytes = decodeBase64(blockBytesB64);
@ -94,19 +89,19 @@ public final class BlockchainStateService {
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, ""); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, "");
} }
// todo ну и ещё тут нужно проверить что не только сам формат блока верный, но и запись в этом блоке верная - тоесть что её можно распарсить!
final BchBlockEntry block; final BchBlockEntry block;
try { try {
block = new BchBlockEntry(blockBytes); block = new BchBlockEntry(blockBytes);
// проверяем, что body распарсится и валидируется
BodyRecordParser.parse(block.bodyBytes).check();
} 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, "");
} }
// Проверка, что глобальный номер совпадает try {
BodyRecordParser.parse(block.bodyBytes).check();
} catch (Exception e) {
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_body", 0, "");
}
if (block.recordNumber != globalNumber) { if (block.recordNumber != globalNumber) {
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "global_number_mismatch", 0, ""); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "global_number_mismatch", 0, "");
} }
@ -115,7 +110,6 @@ public final class BlockchainStateService {
boolean oldAutoCommit = c.getAutoCommit(); boolean oldAutoCommit = c.getAutoCommit();
c.setAutoCommit(false); c.setAutoCommit(false);
try { try {
// 1) пользователь (ключ подписи берём из loginKey)
SolanaUserEntry u = solanaUsersDAO.getByLogin(c, login); SolanaUserEntry u = solanaUsersDAO.getByLogin(c, login);
if (u == null) { if (u == null) {
c.rollback(); c.rollback();
@ -128,49 +122,39 @@ public final class BlockchainStateService {
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_user_login_key", 0, ""); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_user_login_key", 0, "");
} }
// 2) состояние блокчейна
BlockchainStateEntry st = stateDAO.getByBlockchainName(c, blockchainName); BlockchainStateEntry st = stateDAO.getByBlockchainName(c, blockchainName);
//todo тут надо учесть тот случай что если это 0 блок тоесть начало блокчейна то логично что ещё нет самого файла блокчейна и по этому нет и BlockchainStateEntry int serverLastNum;
boolean isGenesis = (globalNumber == 0); String serverLastHash;
if (st == null) { if (st == null) {
if (!isGenesis) { if (globalNumber != 0) {
c.rollback(); c.rollback();
return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, ""); return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, "");
} }
st = new BlockchainStateEntry(); serverLastNum = -1;
st.setBlockchainName(blockchainName); serverLastHash = "";
st.setLogin(login); } else {
st.setLastGlobalNumber(-1); serverLastNum = st.getLastGlobalNumber();
st.setLastGlobalHash(""); serverLastHash = nn(st.getLastGlobalHash());
st.setLastLineNumber(0, -1);
st.setLastLineHash(0, "");
} }
// 3) проверяем последовательность глобального номера int expected = serverLastNum + 1;
int expected = st.getLastGlobalNumber() + 1;
if (globalNumber != expected) { if (globalNumber != expected) {
c.rollback(); c.rollback();
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_global_number", st.getLastGlobalNumber(), nn(st.getLastGlobalHash())); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_global_number", serverLastNum, serverLastHash);
} }
// 4) prev hashes (пока line == global)
byte[] prevGlobalHash32 = hexTo32(nn(prevGlobalHashHex)); byte[] prevGlobalHash32 = hexTo32(nn(prevGlobalHashHex));
byte[] serverPrevGlobal32 = hexTo32(nn(st.getLastGlobalHash())); byte[] serverPrevGlobal32 = (st == null) ? new byte[32] : hexTo32(nn(st.getLastGlobalHash()));
// чтобы не принимали «левый prev»:
if (!bytesEq(prevGlobalHash32, serverPrevGlobal32)) { if (!bytesEq(prevGlobalHash32, serverPrevGlobal32)) {
c.rollback(); c.rollback();
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_prev_global_hash", st.getLastGlobalNumber(), nn(st.getLastGlobalHash())); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_prev_global_hash", serverLastNum, serverLastHash);
} }
byte[] prevLineHash32 = prevGlobalHash32; // пока линии не используем byte[] prevLineHash32 = prevGlobalHash32;
//todo точно так же как и глобальный проверяем преведущий хэш по линии. он пока не используется, в том плане что у нас везде линия 0 и приведущий хэш по линии по сути равен приведущему глобальному хэшу, но его тоже надо проверять.
// по сути можно сказать он используется просто пока везде только одна линия с индексом 0
// 5) крипто-проверка
boolean ok = BchCryptoVerifier.verifyAll( boolean ok = BchCryptoVerifier.verifyAll(
login, login,
prevGlobalHash32, prevGlobalHash32,
@ -183,28 +167,24 @@ public final class BlockchainStateService {
if (!ok) { if (!ok) {
c.rollback(); c.rollback();
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_signature_or_hash", st.getLastGlobalNumber(), nn(st.getLastGlobalHash())); return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_signature_or_hash", serverLastNum, serverLastHash);
} }
//todo сам код добавление блока вынести в отдельный класс - тк там надо потом усложнить действие
// 6) вставка блока
insertBlockRow(c, login, blockchainName, globalNumber, prevGlobalHashHex, blockBytes);
// 7) обновление состояния hash теперь = hash32 нового блока (HEX)
String newHashHex = toHex(block.getHash32()); String newHashHex = toHex(block.getHash32());
st.setLastGlobalNumber(globalNumber);
st.setLastGlobalHash(newHashHex);
// линии пока равны глобалу dbWriter.appendBlockAndState(
st.setLastLineNumber(0, globalNumber); c,
st.setLastLineHash(0, newHashHex); login,
blockchainName,
st.setUpdatedAtMs(System.currentTimeMillis()); globalNumber,
stateDAO.upsert(c, st); nn(prevGlobalHashHex),
blockBytes,
st,
newHashHex
);
c.commit(); c.commit();
return new AddBlockResult(WireCodes.Status.OK, null, st.getLastGlobalNumber(), nn(st.getLastGlobalHash())); return new AddBlockResult(WireCodes.Status.OK, null, globalNumber, newHashHex);
} catch (Exception e) { } catch (Exception e) {
try { c.rollback(); } catch (SQLException ignore) {} try { c.rollback(); } catch (SQLException ignore) {}
@ -217,40 +197,6 @@ public final class BlockchainStateService {
} }
} }
private void insertBlockRow(
Connection c,
String login,
String blockchainName,
int globalNumber,
String prevGlobalHashHex,
byte[] blockBytes
) throws SQLException {
BlockEntry e = new BlockEntry();
e.setLogin(login);
e.setBchName(blockchainName);
e.setBlockGlobalNumber(globalNumber);
e.setBlockGlobalPreHashe(nn(prevGlobalHashHex));
// линии пока не используем (равны глобалу)
e.setBlockLineIndex(0);
e.setBlockLineNumber(globalNumber);
e.setBlockLinePreHashe(nn(prevGlobalHashHex));
e.setMsgType(0);
e.setBlockByte(blockBytes);
// nullable ссылки (как ты просил ранее)
e.setToLogin(null);
e.setToBchName(null);
e.setToBlockGlobalNumber(null);
e.setToBlockHashe(null);
blocksDAO.upsert(c, e);
}
// -------------------- utils -------------------- // -------------------- utils --------------------
private static String nn(String s) { return s == null ? "" : s; } private static String nn(String s) { return s == null ? "" : s; }

View File

@ -17,12 +17,10 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
Net_AddBlock_Request req = (Net_AddBlock_Request) baseReq; Net_AddBlock_Request req = (Net_AddBlock_Request) baseReq;
// todo если пришёл запрос на добавление то надо блочить работу с этим блокчейном по req.getBlockchainName(), String bchName = req.getBlockchainName();
// с помощью класса BlockchainLocks и разлочивать работу только в конце завершения работы этого хэндлера, что бы не случилось паралельной работы двух потоков с одним и тем же блокчейном! ReentrantLock lock = BlockchainLocks.lockFor(bchName);
ReentrantLock lock = BlockchainLocks.lockFor(req.getBlockchainName());
lock.lock(); lock.lock();
try { try {
var r = BlockchainStateService.getInstance().addBlockAtomically( var r = BlockchainStateService.getInstance().addBlockAtomically(
req.getBlockchainName(), req.getBlockchainName(),
req.getGlobalNumber(), req.getGlobalNumber(),
@ -42,7 +40,6 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
resp.setReasonCode(r.reasonCode); resp.setReasonCode(r.reasonCode);
} }
// Даже при ошибке (например bad_global_sequence) можно вернуть что сервер считает последним
resp.setServerLastGlobalNumber(r.serverLastGlobalNumber); resp.setServerLastGlobalNumber(r.serverLastGlobalNumber);
if (r.serverLastGlobalHash != null) { if (r.serverLastGlobalHash != null) {
resp.setServerLastGlobalHash(r.serverLastGlobalHash); resp.setServerLastGlobalHash(r.serverLastGlobalHash);