24 12 25
Дорабатываю добавление блоков. Ура добавилось. Осталось порядок навести и добавление файлов сделать и откат.
This commit is contained in:
parent
80ea016687
commit
834cf98ef9
@ -99,7 +99,6 @@ public final class BlockchainStateDAO {
|
|||||||
?,?,
|
?,?,
|
||||||
?,?,
|
?,?,
|
||||||
?,?,
|
?,?,
|
||||||
?,?,
|
|
||||||
?,?
|
?,?
|
||||||
)
|
)
|
||||||
ON CONFLICT(blockchainName)
|
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.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; }
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user