SHA256
Добавить диагностику server PDA и баланс device (не проверено)
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
deriveKeyBundleFromPassword,
|
||||
fillKeyFields,
|
||||
parseLoginList,
|
||||
refreshDeviceBalance,
|
||||
setGenMessage,
|
||||
setStatus,
|
||||
setupPasswordEye,
|
||||
@@ -40,6 +41,21 @@ $('btnTopupDevnet').addEventListener('click', () => {
|
||||
}
|
||||
});
|
||||
|
||||
$('btnRefreshBalance').addEventListener('click', async () => {
|
||||
try {
|
||||
$('btnRefreshBalance').disabled = true;
|
||||
await refreshDeviceBalance({
|
||||
endpoint: $('endpoint').value,
|
||||
deviceAddress: $('devPub').value,
|
||||
targetNode: $('deviceBalance'),
|
||||
});
|
||||
} catch (error) {
|
||||
setStatus($('status'), error?.message || String(error), 'error');
|
||||
} finally {
|
||||
$('btnRefreshBalance').disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
$('btnGen').addEventListener('click', async () => {
|
||||
clearGenMessage($('genMsg'));
|
||||
clearStatus($('status'));
|
||||
@@ -53,7 +69,8 @@ $('btnGen').addEventListener('click', async () => {
|
||||
});
|
||||
fillKeyFields(fieldMap, keyBundle, masterSecret32);
|
||||
updateSolAddress(fieldMap);
|
||||
setGenMessage($('genMsg'), 'Ключи и master secret сгенерированы из логина и пароля.', 'ok');
|
||||
$('deviceBalance').textContent = 'Теперь можно проверить баланс device-аккаунта.';
|
||||
setGenMessage($('genMsg'), 'Ключи и master secret сгенерированы из логина и пароля. Транзакцию оплатит device-ключ.', 'ok');
|
||||
} catch (error) {
|
||||
setGenMessage($('genMsg'), error?.message || String(error), 'err');
|
||||
} finally {
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
deriveMasterSecretFromPassword,
|
||||
publicKeyB64FromPkcs8Ed25519,
|
||||
} from '../../js/services/crypto-utils.js';
|
||||
import { formatSol, getBalanceSol } from '../../js/services/solana-wallet-service.js';
|
||||
|
||||
const LOGIN_RE = /^[a-z0-9_]{1,20}$/;
|
||||
const ED25519_PKCS8_PREFIX = new Uint8Array([
|
||||
@@ -189,12 +190,56 @@ export function updateSolAddress(fieldMap) {
|
||||
}
|
||||
}
|
||||
|
||||
export function setText(idOrNode, value) {
|
||||
const node = typeof idOrNode === 'string' ? $(idOrNode) : idOrNode;
|
||||
if (node) node.textContent = String(value || '');
|
||||
}
|
||||
|
||||
export function wireDeviceAddressPreview(fieldMap) {
|
||||
const update = () => updateSolAddress(fieldMap);
|
||||
$(fieldMap.devPub).addEventListener('input', update);
|
||||
update();
|
||||
}
|
||||
|
||||
export function publicKeyBytesToBase58(value) {
|
||||
return bytesToBase58(value instanceof Uint8Array ? value : new Uint8Array(value || []));
|
||||
}
|
||||
|
||||
export function compareExpectedPublicKeys(expected, actual) {
|
||||
const exp = String(expected || '').trim();
|
||||
const act = String(actual || '').trim();
|
||||
return {
|
||||
matches: Boolean(exp) && Boolean(act) && exp === act,
|
||||
expected: exp,
|
||||
actual: act,
|
||||
};
|
||||
}
|
||||
|
||||
export function summarizeKeyComparison(resultMap) {
|
||||
const labels = {
|
||||
root: 'root',
|
||||
blockchain: 'blockchain',
|
||||
device: 'device',
|
||||
};
|
||||
const mismatches = Object.entries(resultMap)
|
||||
.filter(([, result]) => !result.matches)
|
||||
.map(([key]) => labels[key] || key);
|
||||
return {
|
||||
allMatch: mismatches.length === 0,
|
||||
mismatches,
|
||||
};
|
||||
}
|
||||
|
||||
export async function refreshDeviceBalance({ endpoint, deviceAddress, targetNode }) {
|
||||
const cleanEndpoint = String(endpoint || '').trim();
|
||||
const cleanAddress = String(deviceAddress || '').trim();
|
||||
if (!cleanEndpoint) throw new Error('Укажите Solana endpoint');
|
||||
if (!cleanAddress) throw new Error('Сначала укажите device-адрес');
|
||||
const balance = await getBalanceSol({ endpoint: cleanEndpoint, address: cleanAddress });
|
||||
setText(targetNode, `Баланс device: ${formatSol(balance.sol, 6)} SOL (${balance.lamports} lamports)`);
|
||||
return balance;
|
||||
}
|
||||
|
||||
export function buildDevnetTopupUrl(walletAddress) {
|
||||
const cleanWallet = String(walletAddress || '').trim();
|
||||
const url = new URL('../devnet-topup-view', window.location.href);
|
||||
|
||||
@@ -4,15 +4,20 @@ import {
|
||||
buildKeyBundleFromForm,
|
||||
clearGenMessage,
|
||||
clearStatus,
|
||||
compareExpectedPublicKeys,
|
||||
deriveKeyBundleFromPassword,
|
||||
fillKeyFields,
|
||||
formatBigInt,
|
||||
formatTimestamp,
|
||||
openDevnetTopup,
|
||||
parseLoginList,
|
||||
publicKeyBytesToBase58,
|
||||
refreshDeviceBalance,
|
||||
setGenMessage,
|
||||
setStatus,
|
||||
setText,
|
||||
setupPasswordEye,
|
||||
summarizeKeyComparison,
|
||||
updateSolAddress,
|
||||
validateLoginOrThrow,
|
||||
wireDeviceAddressPreview,
|
||||
@@ -32,9 +37,62 @@ const fieldMap = {
|
||||
|
||||
let currentPda = null;
|
||||
|
||||
function resetExpectedKeysUi() {
|
||||
$('expectedKeysBox').style.display = 'none';
|
||||
setText('expectedRootPub', '');
|
||||
setText('expectedBchPub', '');
|
||||
setText('expectedDevPub', '');
|
||||
clearGenMessage($('expectedKeysStatus'));
|
||||
setText('deviceBalance', 'Баланс device ещё не запрашивался.');
|
||||
}
|
||||
|
||||
function renderExpectedKeys(parsed) {
|
||||
$('expectedKeysBox').style.display = 'block';
|
||||
setText('expectedRootPub', publicKeyBytesToBase58(parsed.rootKey));
|
||||
setText('expectedBchPub', publicKeyBytesToBase58(parsed.blockchain.blockchainPublicKey));
|
||||
setText('expectedDevPub', publicKeyBytesToBase58(parsed.deviceKey));
|
||||
setGenMessage($('expectedKeysStatus'), 'После генерации ключей этот блок покажет, совпадают ли они с уже записанной PDA.', 'warn');
|
||||
}
|
||||
|
||||
function compareCurrentFormKeysWithPda() {
|
||||
if (!currentPda) throw new Error('Сначала загрузите PDA');
|
||||
const blockchainActual = String($('bchPub').value || '').trim();
|
||||
return {
|
||||
resultMap: {
|
||||
root: compareExpectedPublicKeys(publicKeyBytesToBase58(currentPda.rootKey), $('rootPub').value),
|
||||
blockchain: blockchainActual
|
||||
? compareExpectedPublicKeys(publicKeyBytesToBase58(currentPda.blockchain.blockchainPublicKey), blockchainActual)
|
||||
: { matches: true, expected: publicKeyBytesToBase58(currentPda.blockchain.blockchainPublicKey), actual: '' },
|
||||
device: compareExpectedPublicKeys(publicKeyBytesToBase58(currentPda.deviceKey), $('devPub').value),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function renderComparisonStatus() {
|
||||
if (!currentPda) return false;
|
||||
try {
|
||||
const { resultMap } = compareCurrentFormKeysWithPda();
|
||||
const summary = summarizeKeyComparison(resultMap);
|
||||
if (summary.allMatch) {
|
||||
setGenMessage($('expectedKeysStatus'), 'Ключи совпадают с загруженной PDA. Похоже, пароль верный.', 'ok');
|
||||
return true;
|
||||
}
|
||||
setGenMessage(
|
||||
$('expectedKeysStatus'),
|
||||
`Не совпали ключи: ${summary.mismatches.join(', ')}. Похоже, пароль неверный или введены не те ключи.`,
|
||||
'err',
|
||||
);
|
||||
return false;
|
||||
} catch (error) {
|
||||
setGenMessage($('expectedKeysStatus'), error?.message || String(error), 'err');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
setupPasswordEye($('btnEye'), $('password'));
|
||||
wireDeviceAddressPreview(fieldMap);
|
||||
$('password').value = '';
|
||||
resetExpectedKeysUi();
|
||||
|
||||
$('btnTopupDevnet').addEventListener('click', () => {
|
||||
try {
|
||||
@@ -44,6 +102,21 @@ $('btnTopupDevnet').addEventListener('click', () => {
|
||||
}
|
||||
});
|
||||
|
||||
$('btnRefreshBalance').addEventListener('click', async () => {
|
||||
try {
|
||||
$('btnRefreshBalance').disabled = true;
|
||||
await refreshDeviceBalance({
|
||||
endpoint: $('endpoint').value,
|
||||
deviceAddress: $('devPub').value,
|
||||
targetNode: $('deviceBalance'),
|
||||
});
|
||||
} catch (error) {
|
||||
setStatus($('status'), error?.message || String(error), 'error');
|
||||
} finally {
|
||||
$('btnRefreshBalance').disabled = false;
|
||||
}
|
||||
});
|
||||
|
||||
$('btnGen').addEventListener('click', async () => {
|
||||
clearGenMessage($('genMsg'));
|
||||
clearStatus($('status'));
|
||||
@@ -54,7 +127,15 @@ $('btnGen').addEventListener('click', async () => {
|
||||
const { masterSecret32, keyBundle } = await deriveKeyBundleFromPassword({ login, password });
|
||||
fillKeyFields(fieldMap, keyBundle, masterSecret32);
|
||||
updateSolAddress(fieldMap);
|
||||
setGenMessage($('genMsg'), 'Ключи и master secret сгенерированы из логина и пароля.', 'ok');
|
||||
$('deviceBalance').textContent = 'Теперь можно проверить баланс device-аккаунта.';
|
||||
const keysOk = renderComparisonStatus();
|
||||
setGenMessage(
|
||||
$('genMsg'),
|
||||
keysOk
|
||||
? 'Ключи и master secret сгенерированы из логина и пароля. Device-ключ оплатит транзакцию.'
|
||||
: 'Ключи сгенерированы, но не совпали с уже загруженной PDA. Скорее всего, пароль неверный.',
|
||||
keysOk ? 'ok' : 'err',
|
||||
);
|
||||
} catch (error) {
|
||||
setGenMessage($('genMsg'), error?.message || String(error), 'err');
|
||||
} finally {
|
||||
@@ -69,6 +150,7 @@ $('btnLoad').addEventListener('click', async () => {
|
||||
currentPda = null;
|
||||
$('pdaInfo').style.display = 'none';
|
||||
$('updateForm').style.display = 'none';
|
||||
resetExpectedKeysUi();
|
||||
try {
|
||||
const login = validateLoginOrThrow($('login').value);
|
||||
const endpoint = String($('endpoint').value || '').trim();
|
||||
@@ -93,6 +175,7 @@ $('btnLoad').addEventListener('click', async () => {
|
||||
$('syncServers').value = parsed.syncServers.join('\n');
|
||||
$('pdaInfo').style.display = 'block';
|
||||
$('updateForm').style.display = 'block';
|
||||
renderExpectedKeys(parsed);
|
||||
setStatus($('status'), 'PDA загружена. Можно менять адрес или sync_servers.', 'success');
|
||||
} catch (error) {
|
||||
setStatus($('status'), error?.message || String(error), 'error');
|
||||
@@ -121,6 +204,9 @@ $('btnUpdate').addEventListener('click', async () => {
|
||||
$('devPub').value = normalized.devPubB58;
|
||||
$('devPriv').value = normalized.devPrivB58;
|
||||
updateSolAddress(fieldMap);
|
||||
if (!renderComparisonStatus()) {
|
||||
throw new Error('Ключи не совпадают с загруженной PDA. Проверьте пароль или введённые ключи.');
|
||||
}
|
||||
|
||||
setStatus($('status'), 'Отправка update_user_pda в Solana...', 'info');
|
||||
const result = await updateServerOnSolana({
|
||||
|
||||
Reference in New Issue
Block a user