SHA256
GPT: сгенерено и не проверено (смена ключей пользователя)
This commit is contained in:
@@ -557,7 +557,12 @@ async function createShineUserPdaOnSolana({
|
||||
const clientKey32 = base64ToBytes(keyBundle.clientPair.publicKeyB64);
|
||||
const rootPriv = await importPkcs8Ed25519(keyBundle.rootPair.privatePkcs8B64);
|
||||
const bchPriv = await importPkcs8Ed25519(keyBundle.blockchainPair.privatePkcs8B64);
|
||||
const ctx = await buildCommonContext({ login: cleanLogin, clientPrivatePkcs8B64: keyBundle.clientPair.privatePkcs8B64, solanaEndpoint });
|
||||
const ctx = await buildCommonContext({
|
||||
login: cleanLogin,
|
||||
clientPrivatePkcs8B64: keyBundle.clientPair.privatePkcs8B64,
|
||||
payerPrivatePkcs8B64: keyBundle.blockchainPair.privatePkcs8B64,
|
||||
solanaEndpoint,
|
||||
});
|
||||
const ecoInfo = await ctx.connection.getAccountInfo(ctx.economyConfigPda, 'confirmed');
|
||||
if (!ecoInfo?.data) throw new Error('Economy config не инициализирован.');
|
||||
const startBonusLimit = parseUsersEconomyConfig(new Uint8Array(ecoInfo.data)).startBonusLimit;
|
||||
@@ -603,7 +608,7 @@ async function createShineUserPdaOnSolana({
|
||||
const createIx = new ctx.solana.TransactionInstruction({
|
||||
programId: ctx.usersProgram,
|
||||
keys: [
|
||||
{ pubkey: ctx.clientKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: ctx.payerKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: ctx.userPda, isSigner: false, isWritable: true },
|
||||
{ pubkey: ctx.solana.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
{ pubkey: ctx.inflowVault, isSigner: false, isWritable: true },
|
||||
@@ -618,7 +623,7 @@ async function createShineUserPdaOnSolana({
|
||||
const tx = new ctx.solana.Transaction();
|
||||
if (promoEdIx) tx.add(promoEdIx);
|
||||
tx.add(rootIx, recordIx, createIx);
|
||||
const signature = await ctx.solana.sendAndConfirmTransaction(ctx.connection, tx, [ctx.clientKeypair], { commitment: 'confirmed' });
|
||||
const signature = await ctx.solana.sendAndConfirmTransaction(ctx.connection, tx, [ctx.payerKeypair], { commitment: 'confirmed' });
|
||||
return { signature, userPda: ctx.userPda.toBase58(), pdaAddress: ctx.userPda.toBase58(), blockchainName: `${cleanLogin}-001` };
|
||||
} catch (error) { throw await attachSolanaLogs(error, ctx.connection); }
|
||||
}
|
||||
@@ -678,15 +683,27 @@ export async function updateShineUserPdaOnSolana({
|
||||
accessServers,
|
||||
}) {
|
||||
const current = await readShineUserPda({ login, solanaEndpoint });
|
||||
const ctx = await buildCommonContext({ login: current.login, clientPrivatePkcs8B64, payerPrivatePkcs8B64, solanaEndpoint });
|
||||
const ctx = await buildCommonContext({
|
||||
login: current.login,
|
||||
clientPrivatePkcs8B64,
|
||||
payerPrivatePkcs8B64: String(payerPrivatePkcs8B64 || blockchainPrivatePkcs8B64 || ''),
|
||||
solanaEndpoint,
|
||||
});
|
||||
const addLimit = BigInt(additionalLimitBytes || 0);
|
||||
if (addLimit < 0n || addLimit % LIMIT_STEP !== 0n) throw new Error(`Лимит можно увеличивать только шагом ${LIMIT_STEP}`);
|
||||
const authMode = authorityMode === 'blockchain' || authorityMode === AUTH_MODE_BLOCKCHAIN ? AUTH_MODE_BLOCKCHAIN : AUTH_MODE_ROOT;
|
||||
const rootKey = newRootPublicKey32 ? toUint8Array(newRootPublicKey32, 32) : current.rootKey;
|
||||
const clientKey = nextClientPublicKey32 ? toUint8Array(nextClientPublicKey32, 32) : current.clientKey;
|
||||
const rootChanged = bytesToBase58(rootKey) !== bytesToBase58(current.rootKey);
|
||||
if (authMode === AUTH_MODE_BLOCKCHAIN && rootChanged) throw new Error('Blockchain authority не может менять root key');
|
||||
if (rootChanged && newBlockchainPublicKey32) throw new Error('Root rotation и blockchain fork нужно делать разными транзакциями');
|
||||
const clientChanged = bytesToBase58(clientKey) !== bytesToBase58(current.clientKey);
|
||||
const fullKeyRotation = rootChanged && clientChanged && Boolean(newBlockchainPublicKey32);
|
||||
if ((rootChanged || clientChanged) && !fullKeyRotation) {
|
||||
throw new Error('Root/client keys меняются только как полная ротация: новый root + новый client + новый blockchain fork');
|
||||
}
|
||||
const authMode = fullKeyRotation ? AUTH_MODE_ROOT : AUTH_MODE_BLOCKCHAIN;
|
||||
if (authorityMode != null) {
|
||||
const requestedMode = authorityMode === 'root' || authorityMode === AUTH_MODE_ROOT ? AUTH_MODE_ROOT : AUTH_MODE_BLOCKCHAIN;
|
||||
if (requestedMode !== authMode) throw new Error(fullKeyRotation ? 'Полная ротация требует root authority' : 'Обычное обновление PDA выполняется blockchain authority');
|
||||
}
|
||||
|
||||
const updatedAtMs = BigInt(Date.now());
|
||||
const forks = current.forks.map((fork) => ({ blockchainKey: fork.blockchainKey, createdAtMs: BigInt(fork.createdAtMs), paidLimitBytes: BigInt(fork.paidLimitBytes) }));
|
||||
@@ -695,7 +712,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
appendedKey = toUint8Array(newBlockchainPublicKey32, 32);
|
||||
if (forks.some((f) => bytesToBase58(f.blockchainKey) === bytesToBase58(appendedKey))) throw new Error('Этот blockchain key уже был в истории fork');
|
||||
const lastForkAt = BigInt(forks.at(-1).createdAtMs);
|
||||
if (authMode === AUTH_MODE_BLOCKCHAIN && updatedAtMs - lastForkAt < FORK_COOLDOWN_MS) throw new Error('Новый fork обычным blockchain authority можно создавать не чаще одного раза в 72 часа');
|
||||
if (!fullKeyRotation && updatedAtMs - lastForkAt < FORK_COOLDOWN_MS) throw new Error('Новый fork обычным blockchain authority можно создавать не чаще одного раза в 72 часа');
|
||||
const newLimit = forks.at(-1).paidLimitBytes + addLimit;
|
||||
if (newLimit > 0xffffffffn) throw new Error('paid_limit_bytes превышает u32');
|
||||
forks.push({ blockchainKey: appendedKey, createdAtMs: updatedAtMs, paidLimitBytes: newLimit });
|
||||
@@ -728,23 +745,17 @@ export async function updateShineUserPdaOnSolana({
|
||||
const unsigned = serializeUnsignedRecordFromState(next);
|
||||
const hash = await sha256Bytes(unsigned);
|
||||
|
||||
const oldAuthorityPub = authMode === AUTH_MODE_ROOT ? current.rootKey : current.forks.at(-1).blockchainKey;
|
||||
const oldAuthorityPrivB64 = authMode === AUTH_MODE_ROOT ? rootPrivatePkcs8B64 : blockchainPrivatePkcs8B64;
|
||||
if (!oldAuthorityPrivB64) throw new Error(authMode === AUTH_MODE_ROOT ? 'Нужен root private key' : 'Нужен blockchain private key');
|
||||
const oldAuthorityPub = fullKeyRotation ? current.rootKey : current.forks.at(-1).blockchainKey;
|
||||
const oldAuthorityPrivB64 = fullKeyRotation ? rootPrivatePkcs8B64 : blockchainPrivatePkcs8B64;
|
||||
if (!oldAuthorityPrivB64) throw new Error(fullKeyRotation ? 'Для полной ротации нужен старый root private key' : 'Для обновления PDA нужен blockchain private key');
|
||||
const oldAuthorityPriv = await importPkcs8Ed25519(oldAuthorityPrivB64);
|
||||
const authSig = await signBytes(oldAuthorityPriv, hash);
|
||||
|
||||
let recordSignerPub;
|
||||
let recordSignerPrivB64;
|
||||
if (rootChanged) {
|
||||
recordSignerPub = rootKey;
|
||||
recordSignerPrivB64 = newRootPrivatePkcs8B64;
|
||||
} else if (appendedKey) {
|
||||
if (appendedKey) {
|
||||
recordSignerPub = appendedKey;
|
||||
recordSignerPrivB64 = newBlockchainPrivatePkcs8B64;
|
||||
} else if (authMode === AUTH_MODE_ROOT) {
|
||||
recordSignerPub = rootKey;
|
||||
recordSignerPrivB64 = rootPrivatePkcs8B64;
|
||||
} else {
|
||||
recordSignerPub = current.forks.at(-1).blockchainKey;
|
||||
recordSignerPrivB64 = blockchainPrivatePkcs8B64;
|
||||
@@ -758,7 +769,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
const updateIx = new ctx.solana.TransactionInstruction({
|
||||
programId: ctx.usersProgram,
|
||||
keys: [
|
||||
{ pubkey: ctx.clientKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: ctx.payerKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: ctx.userPda, isSigner: false, isWritable: true },
|
||||
{ pubkey: ctx.solana.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
{ pubkey: ctx.inflowVault, isSigner: false, isWritable: true },
|
||||
@@ -768,7 +779,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
data: serializeUpdateUserPdaArgs({ login: current.login, rootKey32: rootKey, updatedAtMs, additionalLimitBytes: addLimit, clientKey32: clientKey, authMode, newBlockchainKey32: appendedKey, serverAddresses: addresses, accessServers: nextAccess, recordSignature64: recordSig }),
|
||||
});
|
||||
try {
|
||||
const signature = await ctx.solana.sendAndConfirmTransaction(ctx.connection, new ctx.solana.Transaction().add(authIx, recordIx, updateIx), [ctx.clientKeypair], { commitment: 'confirmed' });
|
||||
const signature = await ctx.solana.sendAndConfirmTransaction(ctx.connection, new ctx.solana.Transaction().add(authIx, recordIx, updateIx), [ctx.payerKeypair], { commitment: 'confirmed' });
|
||||
return { signature, userPda: ctx.userPda.toBase58(), pdaAddress: ctx.userPda.toBase58(), paidLimitBytes: forks.at(-1).paidLimitBytes, forkCount: forks.length };
|
||||
} catch (error) { throw await attachSolanaLogs(error, ctx.connection); }
|
||||
}
|
||||
@@ -778,8 +789,9 @@ export async function updateServerOnSolana({ login, keyBundle, serverAddress, se
|
||||
login,
|
||||
solanaEndpoint,
|
||||
rootPrivatePkcs8B64: keyBundle.rootPair.privatePkcs8B64,
|
||||
blockchainPrivatePkcs8B64: keyBundle.blockchainPair.privatePkcs8B64,
|
||||
clientPrivatePkcs8B64: keyBundle.clientPair.privatePkcs8B64,
|
||||
authorityMode: 'root',
|
||||
authorityMode: 'blockchain',
|
||||
serverAddresses: serverAddresses || [{ addressFormatType, addressFormatVersion, address: serverAddress }],
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user