Переделать remote AddBlock на сборку блока на homeserver

This commit is contained in:
AidarKC
2026-06-28 14:45:29 +04:00
parent 3068c3e2b8
commit ed83b1f906
4 changed files with 319 additions and 69 deletions
@@ -510,11 +510,13 @@ static void saveShineSessionPrefs();
static String normalizeLoginValue(const String &value);
static bool base58ToFixed32(const String &value, uint8_t out[32]);
static bool base64DecodeStd(const String &value, std::vector<uint8_t> &out);
static bool hex64ToBytes(const String &value, uint8_t out[32]);
static String bytesToBase64String(const uint8_t *data, size_t len);
static String jsonEscape(const String &value);
static bool jsonStringField(const String &json, const String &field, String &valueOut);
static bool jsonBoolField(const String &json, const String &field, bool &valueOut);
static bool jsonInt64Field(const String &json, const String &field, uint64_t &valueOut);
static bool jsonSignedInt64Field(const String &json, const String &field, int64_t &valueOut);
static bool parseUrlHostPortPath(const String &url, String &hostOut, uint16_t &portOut, String &pathOut, bool &secureOut);
static String formatPairingShortCode(const String &value);
static bool pairingMenuVisible();
@@ -938,6 +940,30 @@ static String normalizeLoginValue(const String &value) {
return out;
}
static int hexNibbleValue(char c) {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + (c - 'a');
if (c >= 'A' && c <= 'F') return 10 + (c - 'A');
return -1;
}
static bool hex64ToBytes(const String &value, uint8_t out[32]) {
String clean = value;
clean.trim();
if (clean.length() != 64) {
return false;
}
for (size_t i = 0; i < 32; ++i) {
int hi = hexNibbleValue(clean.charAt((int)(i * 2)));
int lo = hexNibbleValue(clean.charAt((int)(i * 2 + 1)));
if (hi < 0 || lo < 0) {
return false;
}
out[i] = (uint8_t)((hi << 4) | lo);
}
return true;
}
static bool isValidShineServerLoginValue(const String &value) {
if (value.isEmpty() || value.length() > 20) {
return false;
@@ -1625,6 +1651,34 @@ static bool jsonInt64Field(const String &json, const String &field, uint64_t &va
return true;
}
static bool jsonSignedInt64Field(const String &json, const String &field, int64_t &valueOut) {
String needle = "\"" + field + "\"";
int keyPos = json.indexOf(needle);
if (keyPos < 0) {
return false;
}
int colon = json.indexOf(':', keyPos + needle.length());
if (colon < 0) {
return false;
}
int pos = colon + 1;
while (pos < (int)json.length() && (json[pos] == ' ' || json[pos] == '\n' || json[pos] == '\r' || json[pos] == '\t')) {
pos++;
}
int start = pos;
if (pos < (int)json.length() && json[pos] == '-') {
pos++;
}
while (pos < (int)json.length() && isDigit((unsigned char)json[pos])) {
pos++;
}
if (pos == start || (pos == start + 1 && json[start] == '-')) {
return false;
}
valueOut = strtoll(json.substring(start, pos).c_str(), nullptr, 10);
return true;
}
static String formatSolValue(uint64_t lamports) {
uint64_t whole = lamports / 1000000000ULL;
uint64_t frac = (lamports % 1000000000ULL) / 1000000ULL;
@@ -2853,6 +2907,75 @@ static bool sendSignalResponse(const String &toLogin,
return shineWsRequest(gShineWs, "SendSignal", req, response, SHINE_RPC_TIMEOUT_MS);
}
static void appendUint16BE(std::vector<uint8_t> &out, uint16_t value) {
out.push_back((uint8_t)((value >> 8) & 0xFF));
out.push_back((uint8_t)(value & 0xFF));
}
static void appendInt32BE(std::vector<uint8_t> &out, int32_t value) {
uint32_t v = (uint32_t)value;
out.push_back((uint8_t)((v >> 24) & 0xFF));
out.push_back((uint8_t)((v >> 16) & 0xFF));
out.push_back((uint8_t)((v >> 8) & 0xFF));
out.push_back((uint8_t)(v & 0xFF));
}
static void appendInt64BE(std::vector<uint8_t> &out, int64_t value) {
uint64_t v = (uint64_t)value;
out.push_back((uint8_t)((v >> 56) & 0xFF));
out.push_back((uint8_t)((v >> 48) & 0xFF));
out.push_back((uint8_t)((v >> 40) & 0xFF));
out.push_back((uint8_t)((v >> 32) & 0xFF));
out.push_back((uint8_t)((v >> 24) & 0xFF));
out.push_back((uint8_t)((v >> 16) & 0xFF));
out.push_back((uint8_t)((v >> 8) & 0xFF));
out.push_back((uint8_t)(v & 0xFF));
}
static uint16_t readUint16BE(const uint8_t *data) {
return (uint16_t)(((uint16_t)data[0] << 8) | (uint16_t)data[1]);
}
static bool fetchRemoteAddBlockCursor(const String &login,
String &blockchainNameOut,
int32_t &lastBlockNumberOut,
String &lastBlockHashOut,
String &errorMessageOut,
String &errorCodeOut) {
const char *kRemoteZeroHash64 = "0000000000000000000000000000000000000000000000000000000000000000";
blockchainNameOut = "";
lastBlockNumberOut = -1;
lastBlockHashOut = String(kRemoteZeroHash64);
errorMessageOut = "";
errorCodeOut = "";
String getUserReq = String("{\"login\":\"") + jsonEscape(login) + "\"}";
String getUserResp;
bool ok = shineWsRequest(gShineWs, "GetUser", getUserReq, getUserResp, SHINE_RPC_TIMEOUT_MS);
uint64_t statusCode = 0;
jsonInt64Field(getUserResp, "status", statusCode);
if (!ok || statusCode != 200) {
jsonStringField(getUserResp, "message", errorMessageOut);
jsonStringField(getUserResp, "code", errorCodeOut);
if (errorCodeOut.isEmpty()) errorCodeOut = ok ? "getuser_rejected" : "getuser_request_failed";
if (errorMessageOut.isEmpty()) errorMessageOut = ok ? "GetUser rejected by server" : "GetUser request failed";
return false;
}
int64_t lastBlockNumberI64 = -1;
jsonStringField(getUserResp, "blockchainName", blockchainNameOut);
jsonStringField(getUserResp, "serverLastGlobalHash", lastBlockHashOut);
if (!jsonSignedInt64Field(getUserResp, "serverLastGlobalNumber", lastBlockNumberI64)) {
lastBlockNumberI64 = -1;
}
lastBlockNumberOut = (int32_t)lastBlockNumberI64;
if (lastBlockHashOut.isEmpty()) {
lastBlockHashOut = String(kRemoteZeroHash64);
}
lastBlockHashOut.toLowerCase();
return true;
}
static void queueWalletSignRequest(const PendingWalletRpcRequest &item,
const String &requestId,
const String &publicKeyBase58,
@@ -4393,11 +4516,8 @@ static void processPendingRemoteAddBlockRequests() {
gPendingRemoteAddBlockRequests.erase(gPendingRemoteAddBlockRequests.begin());
String responseData;
String requestLogin;
String blockchainName;
String prevBlockHash;
String blockPreimageB64;
uint64_t blockNumberU64 = 0;
String blockBodyB64;
if (item.fromLogin != gLoginValue) {
responseData = String("{\"ok\":false,\"error\":\"forbidden_login\",\"errorMessage\":\"Signal login mismatch\",\"requestId\":\"")
@@ -4412,26 +4532,68 @@ static void processPendingRemoteAddBlockRequests() {
continue;
}
jsonStringField(item.data, "login", requestLogin);
jsonStringField(item.data, "blockchainName", blockchainName);
jsonStringField(item.data, "prevBlockHash", prevBlockHash);
jsonStringField(item.data, "blockPreimageB64", blockPreimageB64);
jsonInt64Field(item.data, "blockNumber", blockNumberU64);
if (requestLogin != gLoginValue || blockchainName.isEmpty() || blockPreimageB64.isEmpty() || prevBlockHash.isEmpty()) {
jsonStringField(item.data, "blockBodyB64", blockBodyB64);
if (blockchainName.isEmpty() || blockBodyB64.isEmpty()) {
responseData = String("{\"ok\":false,\"error\":\"bad_request\",\"errorMessage\":\"Missing required AddBlock fields\",\"requestId\":\"")
+ jsonEscape(item.signalRequestId) + "\"}";
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
continue;
}
std::vector<uint8_t> preimage;
if (!base64DecodeStd(blockPreimageB64, preimage) || preimage.empty()) {
responseData = String("{\"ok\":false,\"error\":\"bad_preimage_base64\",\"errorMessage\":\"Invalid AddBlock preimage base64\",\"requestId\":\"")
std::vector<uint8_t> remoteBody;
if (!base64DecodeStd(blockBodyB64, remoteBody) || remoteBody.size() < 6) {
responseData = String("{\"ok\":false,\"error\":\"bad_block_body_base64\",\"errorMessage\":\"Invalid remote block body base64\",\"requestId\":\"")
+ jsonEscape(item.signalRequestId) + "\"}";
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
continue;
}
uint16_t msgType = readUint16BE(remoteBody.data());
uint16_t msgSubType = readUint16BE(remoteBody.data() + 2);
uint16_t msgVersion = readUint16BE(remoteBody.data() + 4);
std::vector<uint8_t> bodyBytes(remoteBody.begin() + 6, remoteBody.end());
String resolvedBlockchainName;
int32_t lastBlockNumber = -1;
String prevBlockHash;
String cursorError;
String cursorErrorCode;
if (!fetchRemoteAddBlockCursor(gLoginValue, resolvedBlockchainName, lastBlockNumber, prevBlockHash, cursorError, cursorErrorCode)) {
responseData = String("{\"ok\":false,\"error\":\"") + jsonEscape(cursorErrorCode)
+ "\",\"errorMessage\":\"" + jsonEscape(cursorError)
+ "\",\"requestId\":\"" + jsonEscape(item.signalRequestId) + "\"}";
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
continue;
}
if (resolvedBlockchainName.isEmpty()) {
resolvedBlockchainName = blockchainName;
}
int32_t nextBlockNumber = lastBlockNumber + 1;
String cleanPrevHash = prevBlockHash.isEmpty()
? String("0000000000000000000000000000000000000000000000000000000000000000")
: prevBlockHash;
uint8_t prevHash32[32] = {};
if (!hex64ToBytes(cleanPrevHash, prevHash32)) {
responseData = String("{\"ok\":false,\"error\":\"bad_prev_hash\",\"errorMessage\":\"Invalid previous block hash from GetUser\",\"requestId\":\"")
+ jsonEscape(item.signalRequestId) + "\"}";
sendSignalResponse(item.fromLogin, item.fromSessionId, "remote_addblock_result", item.signalRequestId, responseData);
continue;
}
std::vector<uint8_t> preimage;
preimage.reserve(2 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + bodyBytes.size());
appendUint16BE(preimage, 0);
preimage.insert(preimage.end(), prevHash32, prevHash32 + 32);
int32_t blockSize = (int32_t)(2 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + bodyBytes.size());
appendInt32BE(preimage, blockSize);
appendInt32BE(preimage, nextBlockNumber);
appendInt64BE(preimage, (int64_t)(shineNowMs() / 1000ULL));
appendUint16BE(preimage, msgType);
appendUint16BE(preimage, msgSubType);
appendUint16BE(preimage, msgVersion);
preimage.insert(preimage.end(), bodyBytes.begin(), bodyBytes.end());
uint8_t blockchainSeed[32] = {};
uint8_t blockchainPub[32] = {};
uint8_t blockchainSec[64] = {};
@@ -4456,9 +4618,9 @@ static void processPendingRemoteAddBlockRequests() {
fullBlock.push_back(0x01);
fullBlock.push_back(0x00);
fullBlock.insert(fullBlock.end(), signature, signature + 64);
String addBlockReq = String("{\"blockchainName\":\"") + jsonEscape(blockchainName)
+ "\",\"blockNumber\":" + String((unsigned long long)blockNumberU64)
+ ",\"prevBlockHash\":\"" + jsonEscape(prevBlockHash)
String addBlockReq = String("{\"blockchainName\":\"") + jsonEscape(resolvedBlockchainName)
+ "\",\"blockNumber\":" + String((long long)nextBlockNumber)
+ ",\"prevBlockHash\":\"" + jsonEscape(cleanPrevHash)
+ "\",\"blockBytesB64\":\"" + jsonEscape(bytesToBase64String(fullBlock.data(), fullBlock.size())) + "\"}";
String addBlockResp;
bool addBlockOk = shineWsRequest(gShineWs, "AddBlock", addBlockReq, addBlockResp, SHINE_RPC_TIMEOUT_MS);