Переделать remote AddBlock на сборку блока на homeserver
This commit is contained in:
parent
3068c3e2b8
commit
ed83b1f906
@ -300,11 +300,11 @@
|
||||
|
||||
То есть телефон без локального `blockchain.key` может:
|
||||
|
||||
- подготовить unsigned preimage блока;
|
||||
- подготовить только сырой payload операции без текущей вершины цепочки;
|
||||
- подписать сам `SendSignal` своим `session key`;
|
||||
- дополнительно подписать его `client key`, чтобы homeserver/ESP32 точно видел, что запрос пришёл от доверенного клиента этого же логина;
|
||||
- отправить запрос в выбранную `homeserver`-сессию;
|
||||
- получить от неё ответ после настоящего `AddBlock`.
|
||||
- получить от неё ответ после настоящего `AddBlock`, который homeserver соберёт и подпишет уже сама.
|
||||
|
||||
### Режимы доставки
|
||||
|
||||
@ -348,7 +348,7 @@
|
||||
"targetSessionId": "sess-hs-001",
|
||||
"signalType": "remote_addblock_request",
|
||||
"signalRequestId": "remote-addblock-001",
|
||||
"data": "{\"operation\":\"remote_addblock_request\",\"login\":\"alice\",\"blockchainName\":\"alice_main\",\"blockNumber\":152,\"prevBlockHash\":\"abc...\",\"blockPreimageB64\":\"...\"}",
|
||||
"data": "{\"operation\":\"remote_addblock_request\",\"signalRequestId\":\"remote-addblock-001\",\"blockchainName\":\"alice_main\",\"blockBodyB64\":\"...\"}",
|
||||
"timeMs": 1774700000123,
|
||||
"sessionSignatureB64": "BASE64_64",
|
||||
"clientSignatureB64": "BASE64_64"
|
||||
@ -385,7 +385,7 @@
|
||||
"targetSessionId": "sess-hs-001",
|
||||
"signalType": "remote_addblock_request",
|
||||
"signalRequestId": "remote-addblock-001",
|
||||
"data": "{\"operation\":\"remote_addblock_request\",\"login\":\"alice\",\"blockchainName\":\"alice_main\",\"blockNumber\":152,\"prevBlockHash\":\"abc...\",\"blockPreimageB64\":\"...\"}",
|
||||
"data": "{\"operation\":\"remote_addblock_request\",\"signalRequestId\":\"remote-addblock-001\",\"blockchainName\":\"alice_main\",\"blockBodyB64\":\"...\"}",
|
||||
"timeMs": 1774700000123,
|
||||
"sessionSignatureB64": "BASE64_64",
|
||||
"clientSignatureB64": "BASE64_64",
|
||||
@ -394,6 +394,30 @@
|
||||
}
|
||||
```
|
||||
|
||||
### Специфика `remote AddBlock`
|
||||
|
||||
Для `remote_addblock_request` поле `data` теперь содержит:
|
||||
|
||||
- `blockchainName`
|
||||
- `blockBodyB64`
|
||||
|
||||
Где `blockBodyB64` — это не финальный блок и не почти готовый preimage, а компактный бинарный контейнер:
|
||||
|
||||
- `msgType` (`u16`)
|
||||
- `msgSubType` (`u16`)
|
||||
- `msgVersion` (`u16`)
|
||||
- `bodyBytes`
|
||||
|
||||
После этого homeserver сама:
|
||||
|
||||
- вызывает `GetUser(login)` и получает `serverLastGlobalNumber/serverLastGlobalHash`;
|
||||
- вычисляет новый `blockNumber = last + 1`;
|
||||
- подставляет актуальный `prevBlockHash`;
|
||||
- ставит текущее время;
|
||||
- досчитывает полный preimage;
|
||||
- подписывает его своим `blockchain key`;
|
||||
- и только потом делает настоящий `AddBlock`.
|
||||
|
||||
### Специфические коды ошибок `SendSignal`
|
||||
|
||||
- `422 / NOT_AUTHENTICATED` — требуется авторизация.
|
||||
|
||||
@ -10,7 +10,9 @@
|
||||
- клиент без локального `blockchain.key` выбирает `homeserver`-сессию (`sessionType = 100`);
|
||||
- клиент отправляет в неё `remote_addblock_request` через `SendSignal`;
|
||||
- запрос подписывается `session key` и `client key`;
|
||||
- ESP32/homeserver автоматически подписывает настоящий `AddBlock` своим `blockchain key` и сам отправляет его на сервер;
|
||||
- UI больше не передаёт `blockNumber` и `prevBlockHash`;
|
||||
- UI передаёт только `blockchainName + blockBodyB64`;
|
||||
- ESP32/homeserver сама делает `GetUser(login)`, получает актуальную вершину цепочки, собирает финальный блок, подписывает настоящий `AddBlock` своим `blockchain key` и сам отправляет его на сервер;
|
||||
- результат возвращается назад сигналом `remote_addblock_result`.
|
||||
|
||||
## Что проверить вручную
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -751,6 +751,16 @@ function buildBlockPreimage({ prevBlockHashHex, blockNumber, msgType, msgSubType
|
||||
);
|
||||
}
|
||||
|
||||
function buildRemoteBlockBodyBytes({ msgType, msgSubType, msgVersion = 1, bodyBytes }) {
|
||||
const body = bodyBytes || new Uint8Array(0);
|
||||
return concatBytes(
|
||||
int16Bytes(msgType),
|
||||
int16Bytes(msgSubType),
|
||||
int16Bytes(msgVersion),
|
||||
body,
|
||||
);
|
||||
}
|
||||
|
||||
export class AuthService {
|
||||
constructor(serverUrl) {
|
||||
this.serverUrl = normalizeServerUrl(serverUrl);
|
||||
@ -1437,6 +1447,65 @@ export class AuthService {
|
||||
};
|
||||
}
|
||||
|
||||
async submitRemoteAddBlockBody({ login, storagePwd, blockchainName, blockBodyBytes }) {
|
||||
const cleanLogin = String(login || '').trim();
|
||||
const cleanBlockchainName = String(blockchainName || '').trim();
|
||||
if (!cleanLogin || !cleanBlockchainName) throw new Error('submitRemoteAddBlockBody: missing login/blockchainName');
|
||||
if (!(blockBodyBytes instanceof Uint8Array) || blockBodyBytes.length < 6) {
|
||||
throw new Error('submitRemoteAddBlockBody: bad blockBodyBytes');
|
||||
}
|
||||
|
||||
const remoteSessionId = String(this.remoteAddBlockSessionId || '').trim();
|
||||
if (!remoteSessionId) {
|
||||
throw new Error('На устройстве нет blockchain key и не выбрана homeserver-сессия для remote AddBlock');
|
||||
}
|
||||
|
||||
const signalRequestId = createSignalRequestId('remote-addblock');
|
||||
const responseWait = this.waitForSignal({
|
||||
signalType: SIGNAL_TYPE_REMOTE_ADDBLOCK_RESULT,
|
||||
signalRequestId,
|
||||
timeoutMs: 20000,
|
||||
});
|
||||
|
||||
const signalData = {
|
||||
operation: SIGNAL_TYPE_REMOTE_ADDBLOCK_REQUEST,
|
||||
signalRequestId,
|
||||
blockchainName: cleanBlockchainName,
|
||||
blockBodyB64: bytesToBase64(blockBodyBytes),
|
||||
};
|
||||
|
||||
await this.sendSignal({
|
||||
toLogin: cleanLogin,
|
||||
targetMode: SIGNAL_TARGET_SINGLE,
|
||||
targetSessionId: remoteSessionId,
|
||||
signalType: SIGNAL_TYPE_REMOTE_ADDBLOCK_REQUEST,
|
||||
signalRequestId,
|
||||
data: JSON.stringify(signalData),
|
||||
storagePwd,
|
||||
includeClientSignature: true,
|
||||
});
|
||||
|
||||
const signalPayload = await responseWait;
|
||||
let result = {};
|
||||
try {
|
||||
result = JSON.parse(String(signalPayload?.data || '{}'));
|
||||
} catch {
|
||||
throw new Error('Некорректный ответ remote AddBlock от homeserver');
|
||||
}
|
||||
if (!result?.ok) {
|
||||
throw new Error(String(result?.errorMessage || result?.error || 'remote_addblock_failed'));
|
||||
}
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
payload: {
|
||||
serverLastGlobalNumber: Number(result?.serverLastGlobalNumber ?? -1),
|
||||
serverLastGlobalHash: String(result?.serverLastGlobalHash || ZERO_HASH_HEX),
|
||||
remote: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
async runAddBlockWithRetry({ login, storagePwd, resolveFreshState, buildPreimage }) {
|
||||
let freshState = await resolveFreshState();
|
||||
let blockchainName = String(freshState?.blockchainName || '').trim();
|
||||
@ -1484,6 +1553,21 @@ export class AuthService {
|
||||
if (!cleanLogin) throw new Error('Missing login for AddBlock');
|
||||
if (!storagePwd) throw new Error('Missing storagePwd for AddBlock signing');
|
||||
|
||||
const keyBundle = await loadEncryptedUserSecrets(cleanLogin, storagePwd);
|
||||
const blockchainPrivatePkcs8 = String(keyBundle?.blockchainKey || '').trim();
|
||||
if (!blockchainPrivatePkcs8) {
|
||||
const user = await this.getUser(cleanLogin);
|
||||
const blockchainName = String(user?.blockchainName || `${cleanLogin}-${BCH_SUFFIX}`).trim();
|
||||
if (!blockchainName) throw new Error('Не удалось определить blockchainName для remote AddBlock');
|
||||
const response = await this.submitRemoteAddBlockBody({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
blockchainName,
|
||||
blockBodyBytes: buildRemoteBlockBodyBytes({ msgType, msgSubType, msgVersion, bodyBytes }),
|
||||
});
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
const { response, blockchainName } = await this.runAddBlockWithRetry({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
@ -2458,11 +2542,6 @@ export class AuthService {
|
||||
if (!cleanLogin || !cleanParam) throw new Error('Не переданы login/param.');
|
||||
if (!cleanValue) throw new Error('Значение параметра не может быть пустым.');
|
||||
if (!storagePwd) throw new Error('Не передан storagePwd для подписи AddBlock.');
|
||||
const { response } = await this.runAddBlockWithRetry({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
resolveFreshState: () => this.resolveFreshBlockchainCursor(cleanLogin),
|
||||
buildPreimage: async ({ blockNumber, prevBlockHash }) => {
|
||||
const bodyBytes = makeUserParamBodyBytes({
|
||||
lineCode: 0,
|
||||
prevLineNumber: -1,
|
||||
@ -2471,20 +2550,14 @@ export class AuthService {
|
||||
key: cleanParam,
|
||||
value: cleanValue,
|
||||
});
|
||||
return concatBytes(
|
||||
int16Bytes(0),
|
||||
hexToBytes(prevBlockHash),
|
||||
int32Bytes(2 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + bodyBytes.length),
|
||||
int32Bytes(blockNumber),
|
||||
int64Bytes(Math.floor(Date.now() / 1000)),
|
||||
int16Bytes(4),
|
||||
int16Bytes(1),
|
||||
int16Bytes(1),
|
||||
return this.addBlockSigned({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
msgType: 4,
|
||||
msgSubType: 1,
|
||||
msgVersion: 1,
|
||||
bodyBytes,
|
||||
);
|
||||
},
|
||||
});
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
async addBlockConnection({ login, toLogin, subType, storagePwd }) {
|
||||
@ -2504,11 +2577,6 @@ export class AuthService {
|
||||
const targetUser = await this.getUser(cleanToLogin);
|
||||
if (!targetUser?.exists) throw new Error('Пользователь цели не найден.');
|
||||
const toBlockchainName = String(targetUser?.blockchainName || `${cleanToLogin}-${BCH_SUFFIX}`).trim();
|
||||
const { response } = await this.runAddBlockWithRetry({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
resolveFreshState: () => this.resolveFreshBlockchainCursor(cleanLogin),
|
||||
buildPreimage: async ({ blockNumber, prevBlockHash }) => {
|
||||
const bodyBytes = makeConnectionBodyBytes({
|
||||
lineCode: 0,
|
||||
prevLineNumber: -1,
|
||||
@ -2518,20 +2586,14 @@ export class AuthService {
|
||||
toBlockNumber: 0,
|
||||
toBlockHashHex: ZERO_HASH_HEX,
|
||||
});
|
||||
return concatBytes(
|
||||
int16Bytes(0),
|
||||
hexToBytes(prevBlockHash),
|
||||
int32Bytes(2 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + bodyBytes.length),
|
||||
int32Bytes(blockNumber),
|
||||
int64Bytes(Math.floor(Date.now() / 1000)),
|
||||
int16Bytes(3),
|
||||
int16Bytes(cleanSubType),
|
||||
int16Bytes(1),
|
||||
return this.addBlockSigned({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
msgType: 3,
|
||||
msgSubType: cleanSubType,
|
||||
msgVersion: 1,
|
||||
bodyBytes,
|
||||
);
|
||||
},
|
||||
});
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user