Архивация в Arweave

This commit is contained in:
AidarKC
2026-09-12 10:17:23 +03:00
parent ef068eca81
commit c3253dceac
48 changed files with 6681 additions and 1227 deletions
-16
View File
@@ -3157,22 +3157,6 @@ export class AuthService {
return response.payload || {};
}
async getTestFreeAvatarQuota() {
const response = await this.ws.request('TestGetFreeAvatarQuota', {});
if (response.status !== 200) throw opError('TestGetFreeAvatarQuota', response);
return response.payload || {};
}
async uploadTestFreeAvatar({ contentType, fileBytesBase64, sha256Hex }) {
const response = await this.ws.request('TestUploadFreeAvatar', {
contentType,
fileBytesBase64,
sha256Hex,
}, 60000);
if (response.status !== 200) throw opError('TestUploadFreeAvatar', response);
return response.payload || {};
}
async setUserRelation({ login, toLogin, kind, enabled, storagePwd }) {
const cleanKind = String(kind || '').trim().toLowerCase();
const kinds = CONNECTION_SUBTYPES[cleanKind];
+33 -2
View File
@@ -1,4 +1,4 @@
import { base58ToBytes, base64ToBytes, bytesToBase58, importPkcs8Ed25519, sha256Bytes, signBytes } from './crypto-utils.js';
import { base58ToBytes, base64ToBytes, base64UrlToBytes, bytesToBase58, bytesToBase64Url, importPkcs8Ed25519, sha256Bytes, signBytes } from './crypto-utils.js';
import { extractSeed32FromPkcs8B64 } from './client-key-utils.js';
import {
SHINE_LOGIN_GUARD_PROGRAM_ID,
@@ -27,6 +27,7 @@ const BLOCK_TYPE_SERVER_PROFILE = 30;
const BLOCK_TYPE_ACCESS_SERVERS = 40;
const BLOCK_TYPE_SESSIONS = 50;
const BLOCK_TYPE_TRUSTED_STATE = 70;
const BLOCK_TYPE_ARCHIVE_HEAD = 100;
const SESSIONS_MODE_MIXED = 1;
const SESSION_TYPE_USER = 1;
const SESSION_TYPE_HOMESERVER = 100;
@@ -326,6 +327,8 @@ function createPdaState({
sessionsMode,
sessions,
trustedCount,
archiveHeadTxId = '',
archiveHeadHash = null,
}) {
const serverProfile = isServer ? {
addressFormatType: Number(addressFormatType || 0),
@@ -360,6 +363,8 @@ function createPdaState({
sessionPubKey32: x?.sessionPubKey32 instanceof Uint8Array ? x.sessionPubKey32 : new Uint8Array(x?.sessionPubKey32 || 32),
})) : [],
trustedCount: Number(trustedCount || 0) & 0xff,
archiveHeadTxId: String(archiveHeadTxId || '').trim(),
archiveHeadHash: archiveHeadHash instanceof Uint8Array ? archiveHeadHash : new Uint8Array(archiveHeadHash || 32),
};
}
@@ -445,6 +450,8 @@ export function parseShineUserPda(dataBytes) {
let sessionsMode = SESSIONS_MODE_MIXED;
let sessions = [];
let trustedCount = 0;
let archiveHeadTxId = '';
let archiveHeadHash = new Uint8Array(32);
for (let i = 0; i < blocksCount; i += 1) {
const blockType = reader.readU8();
@@ -528,6 +535,11 @@ export function parseShineUserPda(dataBytes) {
trustedCount = reader.readU8();
continue;
}
if (blockType === BLOCK_TYPE_ARCHIVE_HEAD) {
archiveHeadTxId = bytesToBase64Url(reader.readBytes(32));
archiveHeadHash = reader.readBytes(32);
continue;
}
throw new Error(`Неизвестный блок PDA: ${blockType}`);
}
@@ -556,6 +568,8 @@ export function parseShineUserPda(dataBytes) {
sessionsMode,
sessions,
trustedCount,
archiveHeadTxId,
archiveHeadHash,
});
return {
@@ -586,6 +600,8 @@ export function serializeUnsignedRecordFromState(stateLike) {
sessionsMode: stateLike.sessionsMode,
sessions: stateLike.sessions,
trustedCount: stateLike.trustedCount,
archiveHeadTxId: stateLike.archiveHeadTxId,
archiveHeadHash: stateLike.archiveHeadHash,
});
const buf = [0x53, 0x48, 0x69, 0x4e, 0x45, 1, 0, 0, 0];
@@ -594,7 +610,8 @@ export function serializeUnsignedRecordFromState(stateLike) {
pushU32LE(buf, state.recordNumber);
for (const x of state.prevRecordHash) buf.push(x);
pushStrU8(buf, state.login);
buf.push(state.isServer ? 8 : 7);
const hasArchiveHead = Boolean(state.archiveHeadTxId);
buf.push((state.isServer ? 8 : 7) + (hasArchiveHead ? 1 : 0));
buf.push(BLOCK_TYPE_RECOVERY_KEY, 0);
for (const x of state.recoveryKey) buf.push(x);
@@ -636,6 +653,16 @@ export function serializeUnsignedRecordFromState(stateLike) {
buf.push(BLOCK_TYPE_TRUSTED_STATE, 0, state.trustedCount & 0xff);
if (hasArchiveHead) {
const txId32 = base64UrlToBytes(state.archiveHeadTxId);
if (txId32.length !== 32 || state.archiveHeadHash.length !== 32) {
throw new Error('Archive head должен содержать TX ID/hash по 32 байта');
}
buf.push(BLOCK_TYPE_ARCHIVE_HEAD, 0);
for (const x of txId32) buf.push(x);
for (const x of state.archiveHeadHash) buf.push(x);
}
const recordLen = buf.length + 64;
buf[7] = recordLen & 0xff;
buf[8] = (recordLen >>> 8) & 0xff;
@@ -1037,6 +1064,8 @@ export async function updateShineUserPdaOnSolana({
serverProfile,
accessServers,
trustedCount,
archiveHeadTxId = '',
archiveHeadHash = null,
}) {
const current = await readShineUserPda({ login, solanaEndpoint });
const cleanLogin = current.login;
@@ -1134,6 +1163,8 @@ export async function updateShineUserPdaOnSolana({
sessionsMode: current.sessionsMode,
sessions: current.sessions,
trustedCount: trustedCount == null ? current.trustedCount : trustedCount,
archiveHeadTxId: current.archiveHeadTxId,
archiveHeadHash: current.archiveHeadHash,
});
const unsignedNext = serializeUnsignedRecordFromState(nextState);