ваше сообщение коммита

This commit is contained in:
2025-08-04 14:16:05 +03:00
parent e30d672c52
commit df37507bbe
22 changed files with 421 additions and 273 deletions

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../../../../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../../../../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

File diff suppressed because one or more lines are too long

View File

@@ -1,4 +1,4 @@
{
"_format": "hh-sol-dbg-1",
"buildInfo": "../../build-info/ca6cf114dd2b9a54ebfddbb4ba9a86a9.json"
"buildInfo": "../../build-info/5f658ec7c83a39083e0b58539865c835.json"
}

File diff suppressed because one or more lines are too long

View File

@@ -42,8 +42,8 @@
]
},
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/ERC20.sol": {
"lastModificationDate": 1751738715692,
"contentHash": "227a6eb2225701c12d9c959b758b6333",
"lastModificationDate": 1753876422727,
"contentHash": "59dfce11284f2636db261df9b6a18f81",
"sourceName": "@openzeppelin/contracts/token/ERC20/ERC20.sol",
"solcConfig": {
"version": "0.8.20",
@@ -158,8 +158,8 @@
]
},
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/interfaces/draft-IERC6093.sol": {
"lastModificationDate": 1751738715692,
"contentHash": "267d92fe4de67b1bdb3302c08f387dbf",
"lastModificationDate": 1753876422645,
"contentHash": "5041977bbe908de2e6ed0270447f79ad",
"sourceName": "@openzeppelin/contracts/interfaces/draft-IERC6093.sol",
"solcConfig": {
"version": "0.8.20",
@@ -188,7 +188,7 @@
},
"imports": [],
"versionPragmas": [
"^0.8.20"
">=0.8.4"
],
"artifacts": [
"IERC1155Errors",
@@ -197,8 +197,8 @@
]
},
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol": {
"lastModificationDate": 1751738715692,
"contentHash": "794db3115001aa372c79326fcfd44b1f",
"lastModificationDate": 1753876422727,
"contentHash": "513778b30d2750f5d2b9b19bbcf748a5",
"sourceName": "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol",
"solcConfig": {
"version": "0.8.20",
@@ -229,15 +229,15 @@
"../IERC20.sol"
],
"versionPragmas": [
"^0.8.20"
">=0.6.2"
],
"artifacts": [
"IERC20Metadata"
]
},
"/home/alex/Digital_Legal_Entity(DLE)/backend/node_modules/@openzeppelin/contracts/token/ERC20/IERC20.sol": {
"lastModificationDate": 1751738715692,
"contentHash": "8f19f64d2adadf448840908bbaf431c8",
"lastModificationDate": 1753876422727,
"contentHash": "9261adf6457863de3e9892f51317ec89",
"sourceName": "@openzeppelin/contracts/token/ERC20/IERC20.sol",
"solcConfig": {
"version": "0.8.20",
@@ -266,7 +266,7 @@
},
"imports": [],
"versionPragmas": [
"^0.8.20"
">=0.4.16"
],
"artifacts": [
"IERC20"

View File

@@ -17,6 +17,7 @@ const logger = require('../utils/logger');
const auth = require('../middleware/auth');
const path = require('path');
const fs = require('fs');
const ethers = require('ethers'); // Added ethers for private key validation
/**
* @route POST /api/dle-v2
@@ -28,8 +29,8 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
const dleParams = req.body;
logger.info('Получен запрос на создание DLE v2:', dleParams);
// Если параметр partners не был передан явно, используем адрес авторизованного пользователя
if (!dleParams.partners || dleParams.partners.length === 0) {
// Если параметр initialPartners не был передан явно, используем адрес авторизованного пользователя
if (!dleParams.initialPartners || dleParams.initialPartners.length === 0) {
// Проверяем, есть ли в сессии адрес кошелька пользователя
if (!req.user || !req.user.walletAddress) {
return res.status(400).json({
@@ -39,11 +40,11 @@ router.post('/', auth.requireAuth, auth.requireAdmin, async (req, res, next) =>
}
// Используем адрес авторизованного пользователя
dleParams.partners = [req.user.address || req.user.walletAddress];
dleParams.initialPartners = [req.user.address || req.user.walletAddress];
// Если суммы не указаны, используем значение по умолчанию (100% токенов)
if (!dleParams.amounts || dleParams.amounts.length === 0) {
dleParams.amounts = ['1000000'];
if (!dleParams.initialAmounts || dleParams.initialAmounts.length === 0) {
dleParams.initialAmounts = ['1000000000000000000000000']; // 1,000,000 токенов
}
}
@@ -91,23 +92,40 @@ router.get('/', auth.requireAuth, async (req, res, next) => {
});
/**
* @route GET /api/dle-v2/defaults
* @desc Получить настройки по умолчанию для DLE v2
* @access Private (только для авторизованных пользователей)
* @route GET /api/dle-v2/default-params
* @desc Получить параметры по умолчанию для создания DLE v2
* @access Private
*/
router.get('/defaults', auth.requireAuth, async (req, res, next) => {
// Возвращаем настройки по умолчанию, которые будут использоваться
// при заполнении формы на фронтенде
res.json({
success: true,
data: {
votingDelay: 1, // 1 блок задержки перед началом голосования
votingPeriod: 45818, // ~1 неделя в блоках (при 13 секундах на блок)
proposalThreshold: '100000', // 100,000 токенов
quorumPercentage: 4, // 4% от общего количества токенов
minTimelockDelay: 2 // 2 дня
}
});
router.get('/default-params', auth.requireAuth, async (req, res, next) => {
try {
const defaultParams = {
name: '',
symbol: '',
location: '',
coordinates: '',
jurisdiction: 1,
oktmo: 45000000000,
okvedCodes: [],
kpp: 770101001,
quorumPercentage: 51,
initialPartners: [],
initialAmounts: [],
supportedChainIds: [1, 137, 56, 42161],
currentChainId: 1
};
res.json({
success: true,
data: defaultParams
});
} catch (error) {
logger.error('Ошибка при получении параметров по умолчанию:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при получении параметров по умолчанию'
});
}
});
/**
@@ -170,4 +188,74 @@ router.delete('/:dleAddress', auth.requireAuth, auth.requireAdmin, async (req, r
}
});
/**
* @route POST /api/dle-v2/validate-private-key
* @desc Валидировать приватный ключ и получить адрес кошелька
* @access Private
*/
router.post('/validate-private-key', auth.requireAuth, async (req, res, next) => {
try {
const { privateKey } = req.body;
if (!privateKey) {
return res.status(400).json({
success: false,
message: 'Приватный ключ не передан'
});
}
// Логируем входящий ключ (только для отладки)
logger.info('Получен приватный ключ для валидации:', privateKey);
logger.info('Длина входящего ключа:', privateKey.length);
try {
// Очищаем ключ от префикса 0x если есть
const cleanKey = privateKey.startsWith('0x') ? privateKey.slice(2) : privateKey;
// Логируем очищенный ключ (только для отладки)
logger.info('Очищенный ключ:', cleanKey);
logger.info('Длина очищенного ключа:', cleanKey.length);
// Проверяем длину и формат (64 символа в hex)
if (cleanKey.length !== 64 || !/^[a-fA-F0-9]+$/.test(cleanKey)) {
logger.error('Некорректный формат ключа. Длина:', cleanKey.length, 'Формат:', /^[a-fA-F0-9]+$/.test(cleanKey));
return res.status(400).json({
success: false,
message: 'Некорректный формат приватного ключа'
});
}
// Генерируем адрес из приватного ключа
const wallet = new ethers.Wallet('0x' + cleanKey);
const address = wallet.address;
// Логируем сгенерированный адрес
logger.info('Сгенерированный адрес из приватного ключа:', address);
res.json({
success: true,
data: {
isValid: true,
address: address,
error: null
}
});
} catch (error) {
logger.error('Ошибка при генерации адреса из приватного ключа:', error);
res.status(400).json({
success: false,
message: 'Некорректный приватный ключ'
});
}
} catch (error) {
logger.error('Ошибка при валидации приватного ключа:', error);
res.status(500).json({
success: false,
message: error.message || 'Произошла ошибка при валидации приватного ключа'
});
}
});
module.exports = router;

View File

@@ -213,7 +213,7 @@ router.get('/', requireAuth, async (req, res, next) => {
// --- Формируем ответ ---
const contacts = users.map(u => ({
id: u.id,
name: [u.first_name, u.last_name].filter(Boolean).join(' ') || null,
name: null, // Имена теперь только в зашифрованных колонках
email: u.email || null,
telegram: u.telegram || null,
wallet: u.wallet || null,
@@ -418,22 +418,37 @@ router.get('/:id', async (req, res, next) => {
}
try {
// Получаем ключ шифрования
const fs = require('fs');
const path = require('path');
let encryptionKey = 'default-key';
try {
const keyPath = path.join(__dirname, '../ssl/keys/full_db_encryption.key');
if (fs.existsSync(keyPath)) {
encryptionKey = fs.readFileSync(keyPath, 'utf8').trim();
}
} catch (keyError) {
console.error('Error reading encryption key:', keyError);
}
const query = db.getQuery();
// Получаем пользователя
const userResult = await query('SELECT id, decrypt_text(first_name_encrypted, $2) as first_name, decrypt_text(last_name_encrypted, $2) as last_name, created_at, preferred_language, is_blocked FROM users WHERE id = $1', [userId, encryptionKey]);
const userResult = await query('SELECT id, created_at, preferred_language, is_blocked FROM users WHERE id = $1', [userId]);
if (userResult.rows.length === 0) {
return res.status(404).json({ error: 'User not found' });
}
const user = userResult.rows[0];
// Получаем идентификаторы
const identitiesResult = await query('SELECT decrypt_text(provider_encrypted, $2) as provider, decrypt_text(provider_id_encrypted, $2) as provider_id FROM user_identities WHERE user_id = $1', [userId, encryptionKey]);
const identitiesResult = await query('SELECT CASE WHEN provider_encrypted IS NULL OR provider_encrypted = \'\' THEN NULL ELSE decrypt_text(provider_encrypted, $2) END as provider, CASE WHEN provider_id_encrypted IS NULL OR provider_id_encrypted = \'\' THEN NULL ELSE decrypt_text(provider_id_encrypted, $2) END as provider_id FROM user_identities WHERE user_id = $1', [userId, encryptionKey]);
const identityMap = {};
for (const id of identitiesResult.rows) {
identityMap[id.provider] = id.provider_id;
}
res.json({
id: user.id,
name: [user.first_name, user.last_name].filter(Boolean).join(' ') || null,
name: null, // Пока не используем имена
email: identityMap.email || null,
telegram: identityMap.telegram || null,
wallet: identityMap.wallet || null,
@@ -545,9 +560,9 @@ router.post('/import', requireAuth, async (req, res) => {
].filter(Boolean);
for (const idn of identities) {
// Проверяем, есть ли уже такой идентификатор у пользователя
const exists = await dbq('SELECT 1 FROM user_identities WHERE user_id = $1 AND provider = $2 AND provider_id = $3', [userId, idn.provider, idn.provider_id]);
const exists = await dbq('SELECT 1 FROM user_identities WHERE user_id = $1 AND provider_encrypted = encrypt_text($2, $4) AND provider_id_encrypted = encrypt_text($3, $4)', [userId, idn.provider, idn.provider_id, encryptionKey]);
if (!exists.rows.length) {
await dbq('INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3) ON CONFLICT DO NOTHING', [userId, idn.provider, idn.provider_id]);
await dbq('INSERT INTO user_identities (user_id, provider_encrypted, provider_id_encrypted) VALUES ($1, encrypt_text($2, $4), encrypt_text($3, $4)) ON CONFLICT DO NOTHING', [userId, idn.provider, idn.provider_id, encryptionKey]);
}
}
} catch (e) {

View File

@@ -34,70 +34,59 @@ async function main() {
const DLE = await ethers.getContractFactory("DLE");
// Преобразуем параметры голосования
const votingDelay = deployParams.votingDelay || 1;
const votingPeriod = deployParams.votingPeriod || 45818; // ~1 неделя
const proposalThreshold = deployParams.proposalThreshold || ethers.parseEther("100000");
const quorumPercentage = deployParams.quorumPercentage || 4;
const minTimelockDelay = (deployParams.minTimelockDelay || 2) * 24 * 60 * 60; // дни в секунды
// Создаем структуру DLEConfig
const dleConfig = {
name: deployParams.name,
symbol: deployParams.symbol,
location: deployParams.location,
coordinates: deployParams.coordinates || "0,0",
jurisdiction: deployParams.jurisdiction || 1,
oktmo: deployParams.oktmo || 45000000000,
okvedCodes: deployParams.okvedCodes || [],
kpp: deployParams.kpp || 770101001,
quorumPercentage: deployParams.quorumPercentage || 51,
initialPartners: deployParams.initialPartners,
initialAmounts: deployParams.initialAmounts,
supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161] // Ethereum, Polygon, BSC, Arbitrum
};
const dle = await DLE.deploy(
deployParams.name,
deployParams.symbol,
deployParams.location,
deployParams.isicCodes || [],
votingDelay,
votingPeriod,
proposalThreshold,
quorumPercentage,
minTimelockDelay
);
const currentChainId = deployParams.currentChainId || 1; // По умолчанию Ethereum
const dle = await DLE.deploy(dleConfig, currentChainId);
await dle.waitForDeployment();
const dleAddress = await dle.getAddress();
console.log(`DLE v2 задеплоен по адресу: ${dleAddress}`);
// 2. Получаем адрес таймлока
const timelockAddress = await dle.getTimelockAddress();
console.log(`Таймлок создан по адресу: ${timelockAddress}`);
// 3. Распределяем начальные токены
console.log("\n3. Распределение начальных токенов...");
const distributeTx = await dle.distributeInitialTokens(
deployParams.partners,
deployParams.amounts
);
await distributeTx.wait();
console.log(`Токены распределены между партнерами`);
// 4. Получаем информацию о DLE
// 2. Получаем информацию о DLE
const dleInfo = await dle.getDLEInfo();
console.log("\n4. Информация о DLE:");
console.log("\n2. Информация о DLE:");
console.log(`Название: ${dleInfo.name}`);
console.log(`Символ: ${dleInfo.symbol}`);
console.log(`Местонахождение: ${dleInfo.location}`);
console.log(`Коды деятельности: ${dleInfo.isicCodes.join(', ')}`);
console.log(`Коды деятельности: ${dleInfo.okvedCodes.join(', ')}`);
console.log(`Дата создания: ${new Date(dleInfo.creationTimestamp * 1000).toISOString()}`);
// 5. Сохраняем информацию о созданном DLE
console.log("\n5. Сохранение информации о DLE v2...");
// 3. Сохраняем информацию о созданном DLE
console.log("\n3. Сохранение информации о DLE v2...");
const dleData = {
name: deployParams.name,
symbol: deployParams.symbol,
location: deployParams.location,
isicCodes: deployParams.isicCodes || [],
coordinates: deployParams.coordinates || "0,0",
jurisdiction: deployParams.jurisdiction || 1,
oktmo: deployParams.oktmo || 45000000000,
okvedCodes: deployParams.isicCodes || [],
kpp: deployParams.kpp || 770101001,
dleAddress: dleAddress,
timelockAddress: timelockAddress,
creationBlock: (await distributeTx.provider.getBlockNumber()),
creationTimestamp: (await distributeTx.provider.getBlock()).timestamp,
creationBlock: (await dle.provider.getBlockNumber()),
creationTimestamp: (await dle.provider.getBlock()).timestamp,
deployedManually: true,
version: "v2",
governanceSettings: {
votingDelay: votingDelay,
votingPeriod: votingPeriod,
proposalThreshold: proposalThreshold.toString(),
quorumPercentage: quorumPercentage,
minTimelockDelay: deployParams.minTimelockDelay || 2
quorumPercentage: deployParams.quorumPercentage || 51,
supportedChainIds: deployParams.supportedChainIds || [1, 137, 56, 42161],
currentChainId: currentChainId
}
};
@@ -105,13 +94,11 @@ async function main() {
console.log("\nDLE v2 успешно создан!");
console.log(`Адрес DLE: ${dleAddress}`);
console.log(`Адрес таймлока: ${timelockAddress}`);
console.log(`Версия: v2 (единый контракт)`);
return {
success: true,
dleAddress: dleAddress,
timelockAddress: timelockAddress,
data: dleData
};

View File

@@ -1,23 +0,0 @@
{
"name": "рога и копыта",
"symbol": "roga",
"location": "325000, 中国, 瓯海区",
"isicCodes": [
"9000"
],
"partners": [
"0xf45aa4917b3775ba37f48aeb3dc1a943561e9e0b",
"0x15a4ed4759e5762174b300a4cf51cc17ad967f4d"
],
"amounts": [
"1000000000000000000",
"10000000000000000000000"
],
"network": "sepolia",
"minTimelockDelay": 5,
"votingDelay": 46523,
"votingPeriod": 6646,
"proposalThreshold": "0",
"quorumPercentage": 51,
"privateKey": "7de38b2ada1d23581342f106c8587ce26068797b3bc06656e24b9dcd1810c7b1"
}

View File

@@ -103,10 +103,11 @@ class AuthService {
const userId = newUserResult.rows[0].id;
// Добавляем идентификатор кошелька (всегда в нижнем регистре)
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
[userId, 'wallet', normalizedAddress]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: 'wallet',
provider_id: normalizedAddress
});
// Проверяем, есть ли у пользователя роль админа
const isAdmin = await checkAdminRole(normalizedAddress);
@@ -400,11 +401,11 @@ class AuthService {
// Если в сессии нет авторизованного пользователя, проверяем существующие идентификаторы
// Проверяем, существует ли уже пользователь с таким Telegram ID
const existingUserResult = await db.getQuery()(
`SELECT u.*, ui.provider, ui.provider_id
`SELECT u.*, decrypt_text(ui.provider_encrypted, $2) as provider, decrypt_text(ui.provider_id_encrypted, $2) as provider_id
FROM users u
JOIN user_identities ui ON u.id = ui.user_id
WHERE ui.provider = 'telegram' AND ui.provider_id = $1`,
[telegramId]
WHERE ui.provider_encrypted = encrypt_text('telegram', $2) AND ui.provider_id_encrypted = encrypt_text($1, $2)`,
[telegramId, encryptionKey]
);
// Если пользователь существует с таким telegramId, используем его
@@ -423,10 +424,11 @@ class AuthService {
isNewUser = true;
// Добавляем Telegram идентификатор
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider, provider_id) VALUES ($1, $2, $3)',
[userId, 'telegram', telegramId]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: 'telegram',
provider_id: telegramId
});
logger.info(
`[verifyTelegramAuth] Created new user ${userId} for Telegram ID ${telegramId}`
@@ -582,10 +584,34 @@ class AuthService {
async getUserIdentities(userId) {
try {
const identities = await encryptedDb.getData('user_identities', { user_id: userId }, null, 'created_at DESC');
return identities;
// Данные уже расшифрованы encryptedDb, просто переименовываем поля
const formattedIdentities = identities.map(identity => ({
id: identity.id,
user_id: identity.user_id,
created_at: identity.created_at,
provider: identity.provider, // Уже расшифровано
provider_id: identity.provider_id // Уже расшифровано
}));
return formattedIdentities;
} catch (error) {
logger.error('[getUserIdentities] Error:', error);
throw error;
// Если произошла ошибка расшифровки, попробуем получить данные напрямую
try {
logger.info(`[AuthService] Trying to get unencrypted data for user ${userId}`);
const { rows } = await db.getQuery()(
'SELECT id, user_id, created_at, provider_encrypted as provider, provider_id_encrypted as provider_id FROM user_identities WHERE user_id = $1 ORDER BY created_at DESC',
[userId]
);
logger.info(`[AuthService] Found ${rows.length} unencrypted identities for user ${userId}`);
return rows;
} catch (fallbackError) {
logger.error(`[AuthService] Fallback error getting identities for user ${userId}:`, fallbackError);
throw fallbackError;
}
}
}
@@ -667,11 +693,11 @@ class AuthService {
}
// Добавляем новый идентификатор для пользователя
await db.getQuery()(
`INSERT INTO user_identities (user_id, provider, provider_id)
VALUES ($1, $2, $3)`,
[userId, provider, normalizedProviderId]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: provider,
provider_id: normalizedProviderId
});
// Проверяем и обновляем роль администратора, если это идентификатор кошелька
let isAdmin = false;

View File

@@ -15,7 +15,7 @@ const path = require('path');
const fs = require('fs');
const { ethers } = require('ethers');
const logger = require('../utils/logger');
const { getRpcUrlByNetworkId } = require('./rpcProviderService');
const { getRpcUrlByChainId } = require('./rpcProviderService');
/**
* Сервис для управления DLE v2 (Digital Legal Entity)
@@ -49,10 +49,15 @@ class DLEV2Service {
fs.copyFileSync(paramsFile, tempParamsFile);
logger.info(`Файл параметров скопирован успешно`);
// Получаем rpc_url из базы по выбранной сети
const rpcUrl = await getRpcUrlByNetworkId(deployParams.network);
// Определяем сеть для деплоя (берем первую из выбранных сетей)
const chainId = deployParams.supportedChainIds && deployParams.supportedChainIds.length > 0
? deployParams.supportedChainIds[0]
: 1; // По умолчанию Ethereum
// Получаем rpc_url из базы по chain_id
const rpcUrl = await getRpcUrlByChainId(chainId);
if (!rpcUrl) {
throw new Error(`RPC URL для сети ${deployParams.network} не найден в базе данных`);
throw new Error(`RPC URL для сети с chain_id ${chainId} не найден в базе данных`);
}
if (!dleParams.privateKey) {
throw new Error('Приватный ключ для деплоя не передан');
@@ -62,8 +67,8 @@ class DLEV2Service {
const result = await this.runDeployScript(paramsFile, {
rpcUrl,
privateKey: dleParams.privateKey,
networkId: deployParams.network,
envNetworkKey: deployParams.network.toUpperCase()
networkId: chainId.toString(),
envNetworkKey: chainId.toString().toUpperCase()
});
// Очищаем временные файлы
@@ -94,61 +99,73 @@ class DLEV2Service {
throw new Error('Местонахождение DLE обязательно');
}
if (!params.partners || !Array.isArray(params.partners)) {
if (!params.initialPartners || !Array.isArray(params.initialPartners)) {
throw new Error('Партнеры должны быть массивом');
}
if (!params.amounts || !Array.isArray(params.amounts)) {
if (!params.initialAmounts || !Array.isArray(params.initialAmounts)) {
throw new Error('Суммы должны быть массивом');
}
if (params.partners.length !== params.amounts.length) {
if (params.initialPartners.length !== params.initialAmounts.length) {
throw new Error('Количество партнеров должно соответствовать количеству сумм распределения');
}
if (params.partners.length === 0) {
if (params.initialPartners.length === 0) {
throw new Error('Должен быть указан хотя бы один партнер');
}
if (params.quorumPercentage > 100) {
throw new Error('Процент кворума не может превышать 100%');
if (params.quorumPercentage > 100 || params.quorumPercentage < 1) {
throw new Error('Процент кворума должен быть от 1% до 100%');
}
// Проверяем адреса партнеров
for (let i = 0; i < params.partners.length; i++) {
if (!ethers.isAddress(params.partners[i])) {
throw new Error(`Неверный адрес партнера ${i + 1}: ${params.partners[i]}`);
for (let i = 0; i < params.initialPartners.length; i++) {
if (!ethers.isAddress(params.initialPartners[i])) {
throw new Error(`Неверный адрес партнера ${i + 1}: ${params.initialPartners[i]}`);
}
}
// Проверяем, что выбраны сети
if (!params.supportedChainIds || !Array.isArray(params.supportedChainIds) || params.supportedChainIds.length === 0) {
throw new Error('Должна быть выбрана хотя бы одна сеть для деплоя');
}
}
/**
* Подготавливает параметры для деплоя
* @param {Object} params - Параметры DLE
* @returns {Object} - Подготовленные параметры
* @param {Object} params - Параметры DLE из формы
* @returns {Object} - Подготовленные параметры для скрипта деплоя
*/
prepareDeployParams(params) {
// Создаем копию объекта, чтобы не изменять исходный
const deployParams = { ...params };
// Преобразуем суммы из строк или чисел в BigNumber, если нужно
deployParams.amounts = params.amounts.map(amount => {
if (typeof amount === 'string' && !amount.startsWith('0x')) {
return ethers.parseEther(amount).toString();
}
return amount.toString();
});
if (deployParams.initialAmounts && Array.isArray(deployParams.initialAmounts)) {
deployParams.initialAmounts = deployParams.initialAmounts.map(amount => {
if (typeof amount === 'string' && !amount.startsWith('0x')) {
return ethers.parseEther(amount).toString();
}
return amount.toString();
});
}
// Преобразуем параметры голосования
deployParams.votingDelay = params.votingDelay || 1;
deployParams.votingPeriod = params.votingPeriod || 45818; // ~1 неделя
deployParams.proposalThreshold = params.proposalThreshold || ethers.parseEther("100000").toString();
deployParams.quorumPercentage = params.quorumPercentage || 4;
deployParams.minTimelockDelay = params.minTimelockDelay || 2;
// Убеждаемся, что okvedCodes - это массив
if (!Array.isArray(deployParams.okvedCodes)) {
deployParams.okvedCodes = [];
}
// Убеждаемся, что isicCodes - это массив
if (!Array.isArray(deployParams.isicCodes)) {
deployParams.isicCodes = [];
// Убеждаемся, что supportedChainIds - это массив
if (!Array.isArray(deployParams.supportedChainIds)) {
deployParams.supportedChainIds = [1]; // По умолчанию Ethereum
}
// Устанавливаем currentChainId как первую выбранную сеть
if (deployParams.supportedChainIds.length > 0) {
deployParams.currentChainId = deployParams.supportedChainIds[0];
} else {
deployParams.currentChainId = 1; // По умолчанию Ethereum
}
return deployParams;
@@ -244,20 +261,18 @@ class DLEV2Service {
extractDeployResult(stdout) {
// Ищем строки с адресами в выводе
const dleAddressMatch = stdout.match(/DLE v2 задеплоен по адресу: (0x[a-fA-F0-9]{40})/);
const timelockAddressMatch = stdout.match(/Таймлок создан по адресу: (0x[a-fA-F0-9]{40})/);
if (dleAddressMatch && timelockAddressMatch) {
if (dleAddressMatch) {
return {
success: true,
data: {
dleAddress: dleAddressMatch[1],
timelockAddress: timelockAddressMatch[1],
version: 'v2'
}
};
}
throw new Error('Не удалось извлечь адреса из вывода скрипта');
throw new Error('Не удалось извлечь адрес DLE из вывода скрипта');
}
/**

View File

@@ -77,9 +77,9 @@ class EncryptedDataService {
const originalName = col.column_name.replace('_encrypted', '');
// console.log(`🔓 Расшифровываем поле ${col.column_name} -> ${originalName}`);
if (col.data_type === 'jsonb') {
return `decrypt_json(${col.column_name}, $1) as "${originalName}"`;
return `CASE WHEN ${col.column_name} IS NULL OR ${col.column_name} = '' THEN NULL ELSE decrypt_json(${col.column_name}, $1) END as "${originalName}"`;
} else {
return `decrypt_text(${col.column_name}, $1) as "${originalName}"`;
return `CASE WHEN ${col.column_name} IS NULL OR ${col.column_name} = '' THEN NULL ELSE decrypt_text(${col.column_name}, $1) END as "${originalName}"`;
}
} else if (!col.column_name.includes('_encrypted')) {
// Проверяем, есть ли зашифрованная версия этой колонки
@@ -181,6 +181,13 @@ class EncryptedDataService {
return rows;
} catch (error) {
// console.error(`❌ Ошибка получения данных из ${tableName}:`, error);
// Если ошибка связана с расшифровкой, попробуем получить данные без расшифровки
if (error.message && error.message.includes('invalid base64')) {
console.log(`⚠️ Ошибка расшифровки в ${tableName}, пытаемся получить данные без расшифровки`);
return await this.executeUnencryptedQuery(tableName, conditions, limit, orderBy);
}
throw error;
}
}
@@ -230,17 +237,18 @@ class EncryptedDataService {
// Проверяем, что значение не пустое перед шифрованием
if (value === null || value === undefined || (typeof value === 'string' && value.trim() === '')) {
// Пропускаем пустые значения
// console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
console.log(`⚠️ Пропускаем пустое зашифрованное поле ${key}`);
continue;
}
const currentParamIndex = paramIndex++;
filteredData[key] = value; // Добавляем в отфильтрованные данные
// console.log(`✅ Добавили зашифрованное поле ${key} в filteredData`);
console.log(`✅ Добавили зашифрованное поле ${key} = "${value}" в filteredData`);
if (encryptedColumn.data_type === 'jsonb') {
encryptedData[`${key}_encrypted`] = `encrypt_json($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
} else {
encryptedData[`${key}_encrypted`] = `encrypt_text($${currentParamIndex}, ${hasEncryptedFields ? '$1::text' : 'NULL'})`;
}
console.log(`🔐 Будем шифровать ${key} -> ${key}_encrypted`);
} else if (unencryptedColumn) {
// Если есть незашифрованная колонка, сохраняем как есть
// Проверяем, что значение не пустое перед сохранением (кроме role и sender_type)
@@ -297,6 +305,10 @@ class EncryptedDataService {
const query = `INSERT INTO ${tableName} (${columns.join(', ')}) VALUES (${placeholders}) RETURNING *`;
const params = hasEncryptedFields ? [this.encryptionKey, ...Object.values(filteredData)] : [...Object.values(filteredData)];
console.log(`🔍 Выполняем INSERT запрос:`, query);
console.log(`🔍 Параметры:`, params);
console.log(`🔍 Ключ шифрования:`, this.encryptionKey ? 'установлен' : 'не установлен');
const { rows } = await db.getQuery()(query, params);
return rows[0];
}

View File

@@ -151,10 +151,34 @@ class IdentityService {
try {
const identities = await encryptedDb.getData('user_identities', { user_id: userId });
logger.info(`[IdentityService] Found ${identities.length} identities for user ${userId}`);
return identities;
// Данные уже расшифрованы encryptedDb, просто переименовываем поля
const formattedIdentities = identities.map(identity => ({
id: identity.id,
user_id: identity.user_id,
created_at: identity.created_at,
provider: identity.provider, // Уже расшифровано
provider_id: identity.provider_id // Уже расшифровано
}));
return formattedIdentities;
} catch (error) {
logger.error(`[IdentityService] Error getting identities for user ${userId}:`, error);
return [];
// Если произошла ошибка расшифровки, попробуем получить данные напрямую
try {
logger.info(`[IdentityService] Trying to get unencrypted data for user ${userId}`);
const { rows } = await db.getQuery()(
'SELECT id, user_id, created_at, provider_encrypted as provider, provider_id_encrypted as provider_id FROM user_identities WHERE user_id = $1',
[userId]
);
logger.info(`[IdentityService] Found ${rows.length} unencrypted identities for user ${userId}`);
return rows;
} catch (fallbackError) {
logger.error(`[IdentityService] Fallback error getting identities for user ${userId}:`, fallbackError);
return [];
}
}
}

View File

@@ -62,4 +62,9 @@ async function getRpcUrlByNetworkId(networkId) {
return providers[0]?.rpc_url || null;
}
module.exports = { getAllRpcProviders, saveAllRpcProviders, upsertRpcProvider, deleteRpcProvider, getRpcUrlByNetworkId };
async function getRpcUrlByChainId(chainId) {
const providers = await encryptedDb.getData('rpc_providers', { chain_id: chainId }, 1);
return providers[0]?.rpc_url || null;
}
module.exports = { getAllRpcProviders, saveAllRpcProviders, upsertRpcProvider, deleteRpcProvider, getRpcUrlByNetworkId, getRpcUrlByChainId };

View File

@@ -56,25 +56,14 @@ async function checkUserIdentity(userId, provider, providerId) {
// Добавление новой идентификации
async function addUserIdentity(userId, provider, providerId) {
// Получаем ключ шифрования
const fs = require('fs');
const path = require('path');
let encryptionKey = 'default-key';
const encryptedDb = require('../services/encryptedDatabaseService');
try {
const keyPath = path.join(__dirname, '../ssl/keys/full_db_encryption.key');
if (fs.existsSync(keyPath)) {
encryptionKey = fs.readFileSync(keyPath, 'utf8').trim();
}
} catch (keyError) {
console.error('Error reading encryption key:', keyError);
}
try {
await db.getQuery()(
'INSERT INTO user_identities (user_id, provider_encrypted, provider_id_encrypted) VALUES ($1, encrypt_text($2, $4), encrypt_text($3, $4))',
[userId, provider, providerId, encryptionKey]
);
await encryptedDb.saveData('user_identities', {
user_id: userId,
provider: provider,
provider_id: providerId
});
return true;
} catch (error) {
if (error.code === '23505') {

View File

@@ -10,76 +10,63 @@
* GitHub: https://github.com/HB3-ACCELERATOR
*/
import api from '@/api/axios';
// Сервис для работы с DLE v2
import axios from 'axios';
/**
* Сервис для работы с DLE v2 (Digital Legal Entity)
* Современный подход с единым контрактом
* Создает новое DLE v2
* @param {Object} dleParams - Параметры DLE
* @returns {Promise<Object>} - Результат создания
*/
class DLEV2Service {
/**
* Создает новое DLE v2
* @param {Object} dleParams - Параметры DLE
* @returns {Promise<Object>} - Результат создания
*/
async createDLE(dleParams) {
try {
const response = await api.post('/dle-v2', dleParams);
return response.data;
} catch (error) {
// console.error('Ошибка при создании DLE v2:', error);
throw error;
}
export const createDLE = async (dleParams) => {
try {
const response = await axios.post('/api/dle-v2', dleParams);
return response.data;
} catch (error) {
console.error('Ошибка при создании DLE:', error);
throw error;
}
};
/**
* Получает список всех DLE v2
* @returns {Promise<Array>} - Список DLE v2
*/
async getAllDLEs() {
try {
const response = await api.get('/dle-v2');
return response.data.data || [];
} catch (error) {
// console.error('Ошибка при получении списка DLE v2:', error);
return [];
}
/**
* Получает список всех DLE v2
* @returns {Promise<Object>} - Список DLE
*/
export const getAllDLEs = async () => {
try {
const response = await axios.get('/api/dle-v2');
return response.data;
} catch (error) {
console.error('Ошибка при получении списка DLE:', error);
throw error;
}
};
/**
* Получает настройки по умолчанию для DLE v2
* @returns {Promise<Object>} - Настройки по умолчанию
*/
async getDefaults() {
try {
const response = await api.get('/dle-v2/defaults');
return response.data.data;
} catch (error) {
// console.error('Ошибка при получении настроек по умолчанию DLE v2:', error);
return {
votingDelay: 1,
votingPeriod: 45818,
proposalThreshold: '100000',
quorumPercentage: 4,
minTimelockDelay: 2
};
}
/**
* Получает информацию о конкретном DLE v2
* @param {string} dleAddress - Адрес DLE
* @returns {Promise<Object>} - Информация о DLE
*/
export const getDLEInfo = async (dleAddress) => {
try {
const response = await axios.get(`/api/dle-v2/${dleAddress}`);
return response.data;
} catch (error) {
console.error('Ошибка при получении информации о DLE:', error);
throw error;
}
};
/**
* Удаляет DLE v2 по адресу
* @param {string} dleAddress - Адрес DLE
* @returns {Promise<Object>} - Результат удаления
*/
async deleteDLE(dleAddress) {
try {
const response = await api.delete(`/dle-v2/${dleAddress}`);
return response.data;
} catch (error) {
// console.error('Ошибка при удалении DLE v2:', error);
throw error;
}
/**
* Получает параметры по умолчанию для создания DLE v2
* @returns {Promise<Object>} - Параметры по умолчанию
*/
export const getDefaultParams = async () => {
try {
const response = await axios.get('/api/dle-v2/default-params');
return response.data;
} catch (error) {
console.error('Ошибка при получении параметров по умолчанию:', error);
throw error;
}
}
export default new DLEV2Service();
};

View File

@@ -569,6 +569,9 @@
v-model="unifiedPrivateKey"
class="form-control"
placeholder="Введите приватный ключ (0x... или без префикса)"
@input="() => { console.log('Input event triggered'); validatePrivateKey('unified'); }"
@focus="() => console.log('Input field focused')"
@blur="() => console.log('Input field blurred')"
>
<span class="input-icon" @click="showUnifiedKey = !showUnifiedKey">
<i :class="showUnifiedKey ? 'fas fa-eye-slash' : 'fas fa-eye'"></i>
@@ -1951,15 +1954,18 @@ const toggleKeyVisibility = (chainId) => {
};
// Валидация приватного ключа с дебаунсом
const validatePrivateKey = (chainId) => {
const validatePrivateKey = async (chainId) => {
console.log('Функция validatePrivateKey вызвана для chainId:', chainId);
// Очищаем предыдущий таймер
if (validatePrivateKey.timeout) {
clearTimeout(validatePrivateKey.timeout);
}
// Устанавливаем новый таймер для дебаунса
validatePrivateKey.timeout = setTimeout(() => {
validatePrivateKey.timeout = setTimeout(async () => {
const key = chainId === 'unified' ? unifiedPrivateKey.value : privateKeys[chainId];
console.log('Ключ для валидации:', key);
if (!key) {
keyValidation[chainId] = null;
@@ -1967,26 +1973,33 @@ const validatePrivateKey = (chainId) => {
}
try {
// Простая валидация длины и формата
const cleanKey = key.startsWith('0x') ? key.slice(2) : key;
// Логируем отправляемый ключ (только для отладки)
console.log('Отправляем приватный ключ для валидации:', key);
console.log('Длина ключа:', key.length);
console.log('Полный ключ:', key);
if (cleanKey.length === 64 && /^[a-fA-F0-9]+$/.test(cleanKey)) {
// Генерируем адрес из приватного ключа (упрощенная версия)
const address = '0x' + cleanKey.substring(0, 40);
// Отправляем запрос на бэкенд для валидации
const response = await axios.post('/api/dle-v2/validate-private-key', {
privateKey: key
});
keyValidation[chainId] = {
isValid: true,
address: address,
error: null
};
console.log('Ответ от сервера:', response.data);
if (response.data.success) {
keyValidation[chainId] = response.data.data;
} else {
throw new Error('Некорректный формат ключа');
keyValidation[chainId] = {
isValid: false,
address: null,
error: response.data.message
};
}
} catch (error) {
console.error('Ошибка валидации приватного ключа:', error);
keyValidation[chainId] = {
isValid: false,
address: null,
error: 'Некорректный приватный ключ'
error: error.response?.data?.message || 'Ошибка валидации приватного ключа'
};
}
}, 300); // Задержка 300мс
@@ -2107,6 +2120,9 @@ watch([() => dleSettings.name, () => dleSettings.tokenSymbol, selectedNetworks],
// Инициализация
onMounted(() => {
console.log('🚀 DleDeployFormView компонент загружен - ТЕСТ ОБНОВЛЕНИЯ');
alert('Компонент загружен - проверьте консоль');
// Загружаем список стран
loadCountries();
@@ -2209,7 +2225,10 @@ const deploySmartContracts = async () => {
supportedChainIds: dleSettings.selectedNetworks || [],
// Текущая цепочка (будет установлена при деплое)
currentChainId: dleSettings.selectedNetworks[0] || 1
currentChainId: dleSettings.selectedNetworks[0] || 1,
// Приватный ключ для деплоя
privateKey: unifiedPrivateKey.value
};
console.log('Данные для деплоя DLE:', deployData);
@@ -2247,6 +2266,9 @@ const isFormValid = computed(() => {
dleSettings.governanceQuorum > 0 &&
dleSettings.governanceQuorum <= 100 &&
dleSettings.selectedNetworks.length > 0 &&
// Проверка приватного ключа
unifiedPrivateKey.value &&
keyValidation.unified?.isValid &&
// Валидация координат
validateCoordinates(dleSettings.coordinates)
);