SHA256
Переделать remote AddBlock на сборку блока на homeserver
This commit is contained in:
+177
-15
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user