SHA256
Миграция PDA на client.key
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { base64ToBytes, importPkcs8Ed25519, sha256Bytes, signBytes } from './crypto-utils.js';
|
||||
import { extractSeed32FromPkcs8B64 } from './device-key-utils.js';
|
||||
import { extractSeed32FromPkcs8B64 } from './client-key-utils.js';
|
||||
import {
|
||||
SHINE_LOGIN_GUARD_PROGRAM_ID,
|
||||
SHINE_PAYMENTS_PROGRAM_ID,
|
||||
@@ -16,8 +16,9 @@ const BLOCKCHAIN_TYPE_MAIN_USER = 1;
|
||||
const ED25519_PROGRAM_ID = 'Ed25519SigVerify111111111111111111111111111';
|
||||
const SYSVAR_INSTRUCTIONS_ID = 'Sysvar1nstructions1111111111111111111111111';
|
||||
|
||||
const BLOCK_TYPE_RECOVERY_KEY = 0;
|
||||
const BLOCK_TYPE_ROOT_KEY = 1;
|
||||
const BLOCK_TYPE_DEVICE_KEY = 2;
|
||||
const BLOCK_TYPE_CLIENT_KEY = 2;
|
||||
const BLOCK_TYPE_BLOCKCHAIN_REGISTRY = 3;
|
||||
const BLOCK_TYPE_SERVER_PROFILE = 30;
|
||||
const BLOCK_TYPE_ACCESS_SERVERS = 40;
|
||||
@@ -176,10 +177,11 @@ function serializeCreateUserPdaArgs(args) {
|
||||
const buf = [];
|
||||
buf.push(3);
|
||||
pushStrU8(buf, args.login);
|
||||
for (const x of args.recoveryKey32) buf.push(x);
|
||||
for (const x of args.rootKey32) buf.push(x);
|
||||
pushU64LE(buf, args.createdAtMs);
|
||||
pushU64LE(buf, BigInt(args.additionalLimitBytes || 0n));
|
||||
for (const x of args.deviceKey32) buf.push(x);
|
||||
for (const x of args.clientKey32) buf.push(x);
|
||||
for (const x of args.blockchainPublicKey32) buf.push(x);
|
||||
pushStrU8(buf, args.blockchainName);
|
||||
pushU64LE(buf, args.usedBytes);
|
||||
@@ -218,13 +220,14 @@ function serializeUpdateUserPdaArgs(args) {
|
||||
const buf = [];
|
||||
buf.push(4);
|
||||
pushStrU8(buf, args.login);
|
||||
for (const x of args.recoveryKey32) buf.push(x);
|
||||
for (const x of args.rootKey32) buf.push(x);
|
||||
pushU64LE(buf, args.createdAtMs);
|
||||
pushU64LE(buf, args.updatedAtMs);
|
||||
pushU32LE(buf, args.version);
|
||||
for (const x of args.prevHash32) buf.push(x);
|
||||
pushU64LE(buf, args.additionalLimitBytes);
|
||||
for (const x of args.deviceKey32) buf.push(x);
|
||||
for (const x of args.clientKey32) buf.push(x);
|
||||
for (const x of args.blockchainPublicKey32) buf.push(x);
|
||||
pushStrU8(buf, args.blockchainName);
|
||||
pushU64LE(buf, args.usedBytes);
|
||||
@@ -288,8 +291,9 @@ function createPdaState({
|
||||
updatedAtMs,
|
||||
recordNumber,
|
||||
prevRecordHash,
|
||||
recoveryKey,
|
||||
rootKey,
|
||||
deviceKey,
|
||||
clientKey,
|
||||
blockchain,
|
||||
isServer,
|
||||
addressFormatType,
|
||||
@@ -313,8 +317,10 @@ function createPdaState({
|
||||
recordNumber,
|
||||
prevRecordHash,
|
||||
login,
|
||||
recoveryKey,
|
||||
rootKey,
|
||||
deviceKey,
|
||||
clientKey,
|
||||
clientKey: clientKey,
|
||||
blockchain,
|
||||
isServer: Boolean(isServer),
|
||||
serverProfile,
|
||||
@@ -373,8 +379,9 @@ export function parseShineUserPda(dataBytes) {
|
||||
const login = reader.readStrU8();
|
||||
const blocksCount = reader.readU8();
|
||||
|
||||
let recoveryKey = null;
|
||||
let rootKey = null;
|
||||
let deviceKey = null;
|
||||
let clientKey = null;
|
||||
let blockchain = null;
|
||||
let isServer = false;
|
||||
let addressFormatType = 0;
|
||||
@@ -390,12 +397,16 @@ export function parseShineUserPda(dataBytes) {
|
||||
const blockType = reader.readU8();
|
||||
reader.readU8();
|
||||
|
||||
if (blockType === BLOCK_TYPE_RECOVERY_KEY) {
|
||||
recoveryKey = reader.readBytes(32);
|
||||
continue;
|
||||
}
|
||||
if (blockType === BLOCK_TYPE_ROOT_KEY) {
|
||||
rootKey = reader.readBytes(32);
|
||||
continue;
|
||||
}
|
||||
if (blockType === BLOCK_TYPE_DEVICE_KEY) {
|
||||
deviceKey = reader.readBytes(32);
|
||||
if (blockType === BLOCK_TYPE_CLIENT_KEY) {
|
||||
clientKey = reader.readBytes(32);
|
||||
continue;
|
||||
}
|
||||
if (blockType === BLOCK_TYPE_BLOCKCHAIN_REGISTRY) {
|
||||
@@ -466,7 +477,7 @@ export function parseShineUserPda(dataBytes) {
|
||||
throw new Error(`Неизвестный блок PDA: ${blockType}`);
|
||||
}
|
||||
|
||||
if (!rootKey || !deviceKey || !blockchain) {
|
||||
if (!recoveryKey || !rootKey || !clientKey || !blockchain) {
|
||||
throw new Error('В PDA отсутствуют обязательные блоки');
|
||||
}
|
||||
|
||||
@@ -478,8 +489,9 @@ export function parseShineUserPda(dataBytes) {
|
||||
updatedAtMs,
|
||||
recordNumber,
|
||||
prevRecordHash,
|
||||
recoveryKey,
|
||||
rootKey,
|
||||
deviceKey,
|
||||
clientKey,
|
||||
blockchain,
|
||||
isServer,
|
||||
addressFormatType,
|
||||
@@ -507,8 +519,9 @@ export function serializeUnsignedRecordFromState(stateLike) {
|
||||
updatedAtMs: stateLike.updatedAtMs,
|
||||
recordNumber: stateLike.recordNumber,
|
||||
prevRecordHash: stateLike.prevRecordHash,
|
||||
recoveryKey: stateLike.recoveryKey,
|
||||
rootKey: stateLike.rootKey,
|
||||
deviceKey: stateLike.deviceKey,
|
||||
clientKey: stateLike.clientKey ?? stateLike.clientKey,
|
||||
blockchain: stateLike.blockchain,
|
||||
isServer: stateLike.isServer,
|
||||
addressFormatType: stateLike.addressFormatType ?? stateLike.serverProfile?.addressFormatType,
|
||||
@@ -527,13 +540,16 @@ 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 ? 7 : 6);
|
||||
buf.push(state.isServer ? 8 : 7);
|
||||
|
||||
buf.push(BLOCK_TYPE_RECOVERY_KEY, 0);
|
||||
for (const x of state.recoveryKey) buf.push(x);
|
||||
|
||||
buf.push(BLOCK_TYPE_ROOT_KEY, 0);
|
||||
for (const x of state.rootKey) buf.push(x);
|
||||
|
||||
buf.push(BLOCK_TYPE_DEVICE_KEY, 0);
|
||||
for (const x of state.deviceKey) buf.push(x);
|
||||
buf.push(BLOCK_TYPE_CLIENT_KEY, 0);
|
||||
for (const x of state.clientKey) buf.push(x);
|
||||
|
||||
buf.push(BLOCK_TYPE_BLOCKCHAIN_REGISTRY, 0, 1, state.blockchain.blockchainType);
|
||||
pushStrU8(buf, state.blockchain.blockchainName);
|
||||
@@ -656,14 +672,15 @@ async function buildCreateContext({ login, keyBundle, solanaEndpoint }) {
|
||||
const [economyConfigPda] = solana.PublicKey.findProgramAddressSync([enc.encode(SHINE_USERS_ECONOMY_CONFIG_SEED)], usersProgram);
|
||||
const [inflowVault] = solana.PublicKey.findProgramAddressSync([enc.encode(SHINE_PAYMENTS_INFLOW_VAULT_SEED)], paymentsProgram);
|
||||
|
||||
const recoveryKey32 = base64ToBytes(keyBundle.recoveryPair.publicKeyB64);
|
||||
const rootKey32 = base64ToBytes(keyBundle.rootPair.publicKeyB64);
|
||||
const blockchainKey32 = base64ToBytes(keyBundle.blockchainPair.publicKeyB64);
|
||||
const deviceKey32 = base64ToBytes(keyBundle.devicePair.publicKeyB64);
|
||||
const clientKey32 = base64ToBytes(keyBundle.clientPair.publicKeyB64);
|
||||
|
||||
const rootPrivKey = await importPkcs8Ed25519(keyBundle.rootPair.privatePkcs8B64);
|
||||
const bchPrivKey = await importPkcs8Ed25519(keyBundle.blockchainPair.privatePkcs8B64);
|
||||
const deviceSeed32 = extractSeed32FromPkcs8B64(keyBundle.devicePair.privatePkcs8B64);
|
||||
const deviceKeypair = solana.Keypair.fromSeed(deviceSeed32);
|
||||
const clientSeed32 = extractSeed32FromPkcs8B64(keyBundle.clientPair.privatePkcs8B64);
|
||||
const clientKeypair = solana.Keypair.fromSeed(clientSeed32);
|
||||
|
||||
return {
|
||||
cleanLogin,
|
||||
@@ -678,12 +695,13 @@ async function buildCreateContext({ login, keyBundle, solanaEndpoint }) {
|
||||
userPda,
|
||||
economyConfigPda,
|
||||
inflowVault,
|
||||
recoveryKey32,
|
||||
rootKey32,
|
||||
blockchainKey32,
|
||||
deviceKey32,
|
||||
clientKey32,
|
||||
rootPrivKey,
|
||||
bchPrivKey,
|
||||
deviceKeypair,
|
||||
clientKeypair,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -720,8 +738,9 @@ async function createShineUserPdaOnSolana({
|
||||
updatedAtMs: createdAtMs,
|
||||
recordNumber: 0,
|
||||
prevRecordHash: zeroHash32,
|
||||
recoveryKey: ctx.recoveryKey32,
|
||||
rootKey: ctx.rootKey32,
|
||||
deviceKey: ctx.deviceKey32,
|
||||
clientKey: ctx.clientKey32,
|
||||
blockchain: createBlockchainState({
|
||||
blockchainName,
|
||||
blockchainPublicKey: ctx.blockchainKey32,
|
||||
@@ -749,10 +768,11 @@ async function createShineUserPdaOnSolana({
|
||||
|
||||
const ixData = serializeCreateUserPdaArgs({
|
||||
login: cleanLogin,
|
||||
recoveryKey32: ctx.recoveryKey32,
|
||||
rootKey32: ctx.rootKey32,
|
||||
createdAtMs,
|
||||
additionalLimitBytes: 0n,
|
||||
deviceKey32: ctx.deviceKey32,
|
||||
clientKey32: ctx.clientKey32,
|
||||
blockchainPublicKey32: ctx.blockchainKey32,
|
||||
blockchainName,
|
||||
usedBytes: 0n,
|
||||
@@ -785,7 +805,7 @@ async function createShineUserPdaOnSolana({
|
||||
const createIx = new ctx.solana.TransactionInstruction({
|
||||
programId: ctx.usersProgram,
|
||||
keys: [
|
||||
{ pubkey: ctx.deviceKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: ctx.clientKeypair.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 },
|
||||
@@ -799,7 +819,7 @@ async function createShineUserPdaOnSolana({
|
||||
const signature = await ctx.solana.sendAndConfirmTransaction(
|
||||
ctx.connection,
|
||||
new ctx.solana.Transaction().add(ed25519RootIx, ed25519BchIx, createIx),
|
||||
[ctx.deviceKeypair],
|
||||
[ctx.clientKeypair],
|
||||
{ commitment: 'confirmed' },
|
||||
);
|
||||
|
||||
@@ -848,7 +868,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
login,
|
||||
solanaEndpoint,
|
||||
rootPrivatePkcs8B64,
|
||||
devicePrivatePkcs8B64,
|
||||
clientPrivatePkcs8B64,
|
||||
blockchainPrivatePkcs8B64,
|
||||
additionalLimitBytes = 0n,
|
||||
nextUsedBytes,
|
||||
@@ -882,8 +902,8 @@ export async function updateShineUserPdaOnSolana({
|
||||
const [userPda] = solana.PublicKey.findProgramAddressSync([enc.encode(SHINE_USERS_USER_PDA_SEED_PREFIX), enc.encode(cleanLogin)], usersProgram);
|
||||
const [economyPda] = solana.PublicKey.findProgramAddressSync([enc.encode(SHINE_USERS_ECONOMY_CONFIG_SEED)], usersProgram);
|
||||
const [inflowVault] = solana.PublicKey.findProgramAddressSync([enc.encode(SHINE_PAYMENTS_INFLOW_VAULT_SEED)], paymentsProgram);
|
||||
const deviceSeed32 = extractSeed32FromPkcs8B64(devicePrivatePkcs8B64);
|
||||
const deviceKeypair = solana.Keypair.fromSeed(deviceSeed32);
|
||||
const clientSeed32 = extractSeed32FromPkcs8B64(clientPrivatePkcs8B64);
|
||||
const clientKeypair = solana.Keypair.fromSeed(clientSeed32);
|
||||
|
||||
const lastBlockStateBytes = buildLastBlockStateBytes(
|
||||
cleanLogin,
|
||||
@@ -928,8 +948,9 @@ export async function updateShineUserPdaOnSolana({
|
||||
updatedAtMs,
|
||||
recordNumber: newRecordNumber,
|
||||
prevRecordHash: prevHash,
|
||||
recoveryKey: current.recoveryKey,
|
||||
rootKey: current.rootKey,
|
||||
deviceKey: current.deviceKey,
|
||||
clientKey: current.clientKey ?? current.clientKey,
|
||||
blockchain: createBlockchainState({
|
||||
blockchainName: currentBch.blockchainName,
|
||||
blockchainPublicKey: currentBch.blockchainPublicKey,
|
||||
@@ -957,13 +978,14 @@ export async function updateShineUserPdaOnSolana({
|
||||
|
||||
const ixData = serializeUpdateUserPdaArgs({
|
||||
login: cleanLogin,
|
||||
recoveryKey32: current.recoveryKey,
|
||||
rootKey32: current.rootKey,
|
||||
createdAtMs: current.createdAtMs,
|
||||
updatedAtMs,
|
||||
version: newRecordNumber,
|
||||
prevHash32: prevHash,
|
||||
additionalLimitBytes: addLimit,
|
||||
deviceKey32: current.deviceKey,
|
||||
clientKey32: current.clientKey,
|
||||
blockchainPublicKey32: currentBch.blockchainPublicKey,
|
||||
blockchainName: currentBch.blockchainName,
|
||||
usedBytes: effectiveUsed,
|
||||
@@ -996,7 +1018,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
const updateIx = new solana.TransactionInstruction({
|
||||
programId: usersProgram,
|
||||
keys: [
|
||||
{ pubkey: deviceKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: clientKeypair.publicKey, isSigner: true, isWritable: true },
|
||||
{ pubkey: userPda, isSigner: false, isWritable: true },
|
||||
{ pubkey: solana.SystemProgram.programId, isSigner: false, isWritable: false },
|
||||
{ pubkey: inflowVault, isSigner: false, isWritable: true },
|
||||
@@ -1011,7 +1033,7 @@ export async function updateShineUserPdaOnSolana({
|
||||
const signature = await solana.sendAndConfirmTransaction(
|
||||
connection,
|
||||
new solana.Transaction().add(computeIx, heapIx, edIxRoot, edIxBch, updateIx),
|
||||
[deviceKeypair],
|
||||
[clientKeypair],
|
||||
{ commitment: 'confirmed' },
|
||||
);
|
||||
|
||||
@@ -1040,7 +1062,7 @@ export async function updateServerOnSolana({
|
||||
login,
|
||||
solanaEndpoint,
|
||||
rootPrivatePkcs8B64: keyBundle.rootPair.privatePkcs8B64,
|
||||
devicePrivatePkcs8B64: keyBundle.devicePair.privatePkcs8B64,
|
||||
clientPrivatePkcs8B64: keyBundle.clientPair.privatePkcs8B64,
|
||||
serverProfile: {
|
||||
addressFormatType,
|
||||
addressFormatVersion,
|
||||
|
||||
Reference in New Issue
Block a user