24 12 25
Дорабатываю добавление блоков. Ура добавилось. Осталось порядок навести и добавление файлов сделать и откат.
This commit is contained in:
parent
80ea016687
commit
834cf98ef9
@ -99,7 +99,6 @@ public final class BlockchainStateDAO {
|
||||
?,?,
|
||||
?,?,
|
||||
?,?,
|
||||
?,?,
|
||||
?,?
|
||||
)
|
||||
ON CONFLICT(blockchainName)
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -8,7 +8,6 @@ import shine.db.SqliteDbController;
|
||||
import shine.db.dao.BlockchainStateDAO;
|
||||
import shine.db.dao.BlocksDAO;
|
||||
import shine.db.dao.SolanaUsersDAO;
|
||||
import shine.db.entities.BlockEntry;
|
||||
import shine.db.entities.BlockchainStateEntry;
|
||||
import shine.db.entities.SolanaUserEntry;
|
||||
import utils.blockchain.BlockchainNameUtil;
|
||||
@ -56,6 +55,7 @@ public final class BlockchainStateService {
|
||||
private final BlocksDAO blocksDAO = BlocksDAO.getInstance();
|
||||
private final BlockchainStateDAO stateDAO = BlockchainStateDAO.getInstance();
|
||||
private final SolanaUsersDAO solanaUsersDAO = SolanaUsersDAO.getInstance();
|
||||
private final BlockchainDbWriter dbWriter = new BlockchainDbWriter(blocksDAO, stateDAO);
|
||||
|
||||
private BlockchainStateService() {}
|
||||
|
||||
@ -75,18 +75,13 @@ public final class BlockchainStateService {
|
||||
String blockBytesB64
|
||||
) {
|
||||
|
||||
// Базовая валидация
|
||||
if (blockchainName == null || blockchainName.isBlank())
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "empty_blockchain_name", 0, "");
|
||||
|
||||
// Можно быстро проверить, что login согласован с blockchainName (если хочешь строгость)
|
||||
String loginFromBlockchainName = BlockchainNameUtil.loginFromBlockchainName(blockchainName);
|
||||
if (loginFromBlockchainName == null || loginFromBlockchainName.isBlank())
|
||||
String login = BlockchainNameUtil.loginFromBlockchainName(blockchainName);
|
||||
if (login == null || login.isBlank())
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_blockchain_name", 0, "");
|
||||
|
||||
// todo действительно давай прото брать логин из имени блокчена и не передавать его отдельно в запросе!
|
||||
String login = loginFromBlockchainName;
|
||||
|
||||
byte[] blockBytes;
|
||||
try {
|
||||
blockBytes = decodeBase64(blockBytesB64);
|
||||
@ -94,19 +89,19 @@ public final class BlockchainStateService {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_block_base64", 0, "");
|
||||
}
|
||||
|
||||
// todo ну и ещё тут нужно проверить что не только сам формат блока верный, но и запись в этом блоке верная - тоесть что её можно распарсить!
|
||||
final BchBlockEntry block;
|
||||
try {
|
||||
block = new BchBlockEntry(blockBytes);
|
||||
|
||||
// проверяем, что body распарсится и валидируется
|
||||
BodyRecordParser.parse(block.bodyBytes).check();
|
||||
|
||||
} catch (Exception e) {
|
||||
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) {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "global_number_mismatch", 0, "");
|
||||
}
|
||||
@ -115,7 +110,6 @@ public final class BlockchainStateService {
|
||||
boolean oldAutoCommit = c.getAutoCommit();
|
||||
c.setAutoCommit(false);
|
||||
try {
|
||||
// 1) пользователь (ключ подписи берём из loginKey)
|
||||
SolanaUserEntry u = solanaUsersDAO.getByLogin(c, login);
|
||||
if (u == null) {
|
||||
c.rollback();
|
||||
@ -128,49 +122,39 @@ public final class BlockchainStateService {
|
||||
return new AddBlockResult(WireCodes.Status.BAD_REQUEST, "bad_user_login_key", 0, "");
|
||||
}
|
||||
|
||||
// 2) состояние блокчейна
|
||||
BlockchainStateEntry st = stateDAO.getByBlockchainName(c, blockchainName);
|
||||
|
||||
//todo тут надо учесть тот случай что если это 0 блок тоесть начало блокчейна то логично что ещё нет самого файла блокчейна и по этому нет и BlockchainStateEntry
|
||||
boolean isGenesis = (globalNumber == 0);
|
||||
int serverLastNum;
|
||||
String serverLastHash;
|
||||
|
||||
if (st == null) {
|
||||
if (!isGenesis) {
|
||||
if (globalNumber != 0) {
|
||||
c.rollback();
|
||||
return new AddBlockResult(WireCodes.Status.NOT_FOUND, "blockchain_state_not_found", 0, "");
|
||||
}
|
||||
st = new BlockchainStateEntry();
|
||||
st.setBlockchainName(blockchainName);
|
||||
st.setLogin(login);
|
||||
st.setLastGlobalNumber(-1);
|
||||
st.setLastGlobalHash("");
|
||||
st.setLastLineNumber(0, -1);
|
||||
st.setLastLineHash(0, "");
|
||||
serverLastNum = -1;
|
||||
serverLastHash = "";
|
||||
} else {
|
||||
serverLastNum = st.getLastGlobalNumber();
|
||||
serverLastHash = nn(st.getLastGlobalHash());
|
||||
}
|
||||
|
||||
// 3) проверяем последовательность глобального номера
|
||||
int expected = st.getLastGlobalNumber() + 1;
|
||||
int expected = serverLastNum + 1;
|
||||
if (globalNumber != expected) {
|
||||
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[] serverPrevGlobal32 = hexTo32(nn(st.getLastGlobalHash()));
|
||||
byte[] serverPrevGlobal32 = (st == null) ? new byte[32] : hexTo32(nn(st.getLastGlobalHash()));
|
||||
|
||||
// чтобы не принимали «левый prev»:
|
||||
if (!bytesEq(prevGlobalHash32, serverPrevGlobal32)) {
|
||||
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; // пока линии не используем
|
||||
//todo точно так же как и глобальный проверяем преведущий хэш по линии. он пока не используется, в том плане что у нас везде линия 0 и приведущий хэш по линии по сути равен приведущему глобальному хэшу, но его тоже надо проверять.
|
||||
// по сути можно сказать он используется просто пока везде только одна линия с индексом 0
|
||||
byte[] prevLineHash32 = prevGlobalHash32;
|
||||
|
||||
|
||||
// 5) крипто-проверка
|
||||
boolean ok = BchCryptoVerifier.verifyAll(
|
||||
login,
|
||||
prevGlobalHash32,
|
||||
@ -183,28 +167,24 @@ public final class BlockchainStateService {
|
||||
|
||||
if (!ok) {
|
||||
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());
|
||||
st.setLastGlobalNumber(globalNumber);
|
||||
st.setLastGlobalHash(newHashHex);
|
||||
|
||||
// линии пока равны глобалу
|
||||
st.setLastLineNumber(0, globalNumber);
|
||||
st.setLastLineHash(0, newHashHex);
|
||||
|
||||
st.setUpdatedAtMs(System.currentTimeMillis());
|
||||
stateDAO.upsert(c, st);
|
||||
dbWriter.appendBlockAndState(
|
||||
c,
|
||||
login,
|
||||
blockchainName,
|
||||
globalNumber,
|
||||
nn(prevGlobalHashHex),
|
||||
blockBytes,
|
||||
st,
|
||||
newHashHex
|
||||
);
|
||||
|
||||
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) {
|
||||
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 --------------------
|
||||
|
||||
private static String nn(String s) { return s == null ? "" : s; }
|
||||
|
||||
@ -17,12 +17,10 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
|
||||
Net_AddBlock_Request req = (Net_AddBlock_Request) baseReq;
|
||||
|
||||
// todo если пришёл запрос на добавление то надо блочить работу с этим блокчейном по req.getBlockchainName(),
|
||||
// с помощью класса BlockchainLocks и разлочивать работу только в конце завершения работы этого хэндлера, что бы не случилось паралельной работы двух потоков с одним и тем же блокчейном!
|
||||
ReentrantLock lock = BlockchainLocks.lockFor(req.getBlockchainName());
|
||||
String bchName = req.getBlockchainName();
|
||||
ReentrantLock lock = BlockchainLocks.lockFor(bchName);
|
||||
lock.lock();
|
||||
try {
|
||||
|
||||
var r = BlockchainStateService.getInstance().addBlockAtomically(
|
||||
req.getBlockchainName(),
|
||||
req.getGlobalNumber(),
|
||||
@ -42,7 +40,6 @@ public final class Net_AddBlock_Handler implements JsonMessageHandler {
|
||||
resp.setReasonCode(r.reasonCode);
|
||||
}
|
||||
|
||||
// Даже при ошибке (например bad_global_sequence) можно вернуть “что сервер считает последним”
|
||||
resp.setServerLastGlobalNumber(r.serverLastGlobalNumber);
|
||||
if (r.serverLastGlobalHash != null) {
|
||||
resp.setServerLastGlobalHash(r.serverLastGlobalHash);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user