SHA256
Новый протокол Solana PDA 1.2
This commit is contained in:
@@ -52,113 +52,84 @@ function readStrU8(bytes, cursorRef) {
|
||||
|
||||
function parseServerFieldsFromUserPda(dataBytes) {
|
||||
const bytes = dataBytes instanceof Uint8Array ? dataBytes : new Uint8Array(dataBytes || []);
|
||||
if (bytes.length < 5) throw new Error('Некорректный формат PDA');
|
||||
if (bytes.length < 9) throw new Error('Некорректный формат PDA');
|
||||
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
||||
const cursorRef = { value: 0 };
|
||||
const readU16 = () => { if (cursorRef.value + 2 > bytes.length) throw new Error('Повреждённый формат PDA'); const v = view.getUint16(cursorRef.value, true); cursorRef.value += 2; return v; };
|
||||
const readU32 = () => { if (cursorRef.value + 4 > bytes.length) throw new Error('Повреждённый формат PDA'); const v = view.getUint32(cursorRef.value, true); cursorRef.value += 4; return v; };
|
||||
const readU64 = () => { if (cursorRef.value + 8 > bytes.length) throw new Error('Повреждённый формат PDA'); const v = view.getBigUint64(cursorRef.value, true); cursorRef.value += 8; return v; };
|
||||
const magic = new TextDecoder().decode(readBytes(bytes, cursorRef, 5));
|
||||
if (magic !== 'SHiNE') throw new Error('Некорректный формат PDA');
|
||||
cursorRef.value += 1; // format_major
|
||||
cursorRef.value += 1; // format_minor
|
||||
cursorRef.value += 2; // record_len
|
||||
const formatMajor = readU8(bytes, cursorRef);
|
||||
const formatMinor = readU8(bytes, cursorRef);
|
||||
if (formatMajor !== 1 || formatMinor !== 2) throw new Error(`Неподдерживаемый формат PDA ${formatMajor}.${formatMinor}`);
|
||||
const recordLen = readU16();
|
||||
if (recordLen < 73 || recordLen > bytes.length) throw new Error('Некорректный record_len PDA');
|
||||
cursorRef.value += 8; // created_at_ms
|
||||
cursorRef.value += 8; // updated_at_ms
|
||||
cursorRef.value += 4; // record_number
|
||||
cursorRef.value += 32; // prev_record_hash
|
||||
readStrU8(bytes, cursorRef); // login
|
||||
const login = readStrU8(bytes, cursorRef);
|
||||
const blocksCount = readU8(bytes, cursorRef);
|
||||
|
||||
let isServer = false;
|
||||
let serverAddress = '';
|
||||
let accessServers = [];
|
||||
let recoveryKey32 = null;
|
||||
let rootKey32 = null;
|
||||
let clientKey32 = null;
|
||||
let blockchainKey32 = null;
|
||||
let blockchainName = '';
|
||||
let homeserverSessions = [];
|
||||
let forkCount = 0;
|
||||
|
||||
for (let i = 0; i < blocksCount; i += 1) {
|
||||
const blockType = readU8(bytes, cursorRef);
|
||||
cursorRef.value += 1; // block_version
|
||||
|
||||
if (blockType === 0 || blockType === 1 || blockType === 2) {
|
||||
const blockVersion = readU8(bytes, cursorRef);
|
||||
if (blockVersion !== 0) throw new Error('Неподдерживаемая версия блока PDA');
|
||||
if (blockType === 1 || blockType === 2) {
|
||||
const key32 = readBytes(bytes, cursorRef, 32);
|
||||
if (blockType === 0) recoveryKey32 = key32;
|
||||
if (blockType === 1) rootKey32 = key32;
|
||||
if (blockType === 2) clientKey32 = key32;
|
||||
else clientKey32 = key32;
|
||||
continue;
|
||||
}
|
||||
|
||||
const payloadLen = readU16();
|
||||
const payloadEnd = cursorRef.value + payloadLen;
|
||||
if (payloadEnd > recordLen - 64) throw new Error('Повреждённый variable block PDA');
|
||||
|
||||
if (blockType === 3) {
|
||||
const count = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < count; j += 1) {
|
||||
cursorRef.value += 1;
|
||||
const currentBlockchainName = readStrU8(bytes, cursorRef);
|
||||
const currentBlockchainKey32 = readBytes(bytes, cursorRef, 32);
|
||||
if (!blockchainKey32) {
|
||||
blockchainKey32 = currentBlockchainKey32;
|
||||
blockchainName = currentBlockchainName;
|
||||
}
|
||||
cursorRef.value += 8 + 8 + 4 + 32 + 64;
|
||||
const arPresent = readU8(bytes, cursorRef);
|
||||
if (arPresent === 1) readStrU8(bytes, cursorRef);
|
||||
forkCount = readU16();
|
||||
if (forkCount < 1) throw new Error('BlockchainRegistry пуст');
|
||||
for (let j = 0; j < forkCount; j += 1) {
|
||||
blockchainKey32 = readBytes(bytes, cursorRef, 32);
|
||||
readU64(); // created_at_ms fork
|
||||
readU32(); // paid_limit_bytes
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 30) {
|
||||
isServer = readU8(bytes, cursorRef) === 1;
|
||||
if (isServer) {
|
||||
cursorRef.value += 1; // address_format_type
|
||||
cursorRef.value += 1; // address_format_version
|
||||
serverAddress = readStrU8(bytes, cursorRef);
|
||||
const syncCount = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < syncCount; j += 1) readStrU8(bytes, cursorRef);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 40) {
|
||||
} else if (blockType === 30) {
|
||||
const addressCount = readU8(bytes, cursorRef);
|
||||
if (addressCount !== 1) throw new Error('PDA 1.2 допускает ровно один адрес сервера');
|
||||
readU8(bytes, cursorRef); // address_format_type
|
||||
readU8(bytes, cursorRef); // address_format_version
|
||||
serverAddress = readStrU8(bytes, cursorRef);
|
||||
} else if (blockType === 40) {
|
||||
const accessCount = readU8(bytes, cursorRef);
|
||||
if (accessCount > 1) throw new Error('PDA 1.2 допускает максимум один access server');
|
||||
accessServers = [];
|
||||
for (let j = 0; j < accessCount; j += 1) accessServers.push(readStrU8(bytes, cursorRef));
|
||||
continue;
|
||||
}
|
||||
if (blockType === 50) {
|
||||
cursorRef.value += 1;
|
||||
const sessionsCount = readU8(bytes, cursorRef);
|
||||
for (let j = 0; j < sessionsCount; j += 1) {
|
||||
const sessionType = readU8(bytes, cursorRef);
|
||||
const sessionVersion = readU8(bytes, cursorRef);
|
||||
const sessionName = readStrU8(bytes, cursorRef);
|
||||
const sessionPubKey32 = readBytes(bytes, cursorRef, 32);
|
||||
if (sessionType === 100) {
|
||||
homeserverSessions.push({
|
||||
sessionType,
|
||||
sessionVersion,
|
||||
sessionName,
|
||||
sessionPubKeyBase58: new PublicKey(sessionPubKey32).toBase58(),
|
||||
sessionPubKeyB64: `ed25519/${btoa(String.fromCharCode(...sessionPubKey32))}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (blockType === 70) {
|
||||
cursorRef.value += 1;
|
||||
continue;
|
||||
}
|
||||
throw new Error(`Неизвестный блок PDA: ${blockType}`);
|
||||
cursorRef.value = payloadEnd;
|
||||
}
|
||||
|
||||
if (!rootKey32 || !clientKey32 || !blockchainKey32) throw new Error('PDA 1.2 не содержит обязательные ключи');
|
||||
return {
|
||||
isServer,
|
||||
isServer: Boolean(serverAddress),
|
||||
serverAddress: normalizeHostLike(serverAddress),
|
||||
accessServers: accessServers.map((value) => normalizeServerLogin(value)).filter(Boolean),
|
||||
publicKeys: {
|
||||
recoveryKeyBase58: recoveryKey32 ? new PublicKey(recoveryKey32).toBase58() : '',
|
||||
rootKeyBase58: rootKey32 ? new PublicKey(rootKey32).toBase58() : '',
|
||||
clientKeyBase58: clientKey32 ? new PublicKey(clientKey32).toBase58() : '',
|
||||
blockchainKeyBase58: blockchainKey32 ? new PublicKey(blockchainKey32).toBase58() : '',
|
||||
blockchainName,
|
||||
recoveryKeyBase58: '',
|
||||
rootKeyBase58: new PublicKey(rootKey32).toBase58(),
|
||||
clientKeyBase58: new PublicKey(clientKey32).toBase58(),
|
||||
blockchainKeyBase58: new PublicKey(blockchainKey32).toBase58(),
|
||||
blockchainName: `${normalizeServerLogin(login)}-${String(forkCount).padStart(3, '0')}`,
|
||||
},
|
||||
homeserverSessions,
|
||||
homeserverSessions: [],
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user