
Конфиденциальность в Solana с Elusiv и Light: всё, что нужно знать о конфиденциальности SOL
Содержание
- Предварительные требования
- О чём эта статья?
- Конфиденциальность важна
- Конфиденциальность касается каждого
- Что такое Elusiv?
- Как работает Elusiv?
- Что такое Light Protocol?
- Как работает Light Protocol?
- Конфиденциальная отправка SOL с помощью Elusiv
- Полный код
- Разбор кода Elusiv
- Конфиденциальная отправка SOL с помощью Light Protocol
- Полный код
- Разбор кода
- Заключение
- Дополнительные материалы
Предварительные требования
- Установленный Node.js (для установки необходимых зависимостей, запуска скриптов и конфиденциальной отправки $SOL)
- Базовое понимание TypeScript
- Базовое понимание @solana/web3.js
О чём эта статья?
Конфиденциальность лежит в основе современных блокчейн-технологий. Системы публичных реестров, транзакции, балансы аккаунтов и зачастую исходный код программ открыты для всех. Хотя этот беспрецедентный уровень прозрачности считается отличительной чертой блокчейна, для многих приложений и повседневных сценариев конфиденциальность жизненно необходима.
Elusiv и Light Protocol предлагают передовые решения для обеспечения конфиденциальности в Solana. Они используют zk-SNARKS для конфиденциальных транзакций, приватных программ и децентрализованных решений по соблюдению нормативных требований. В этой статье мы рассмотрим важность конфиденциальности, разберём, зачем таким блокчейнам, как Solana, нужны функции конфиденциальности, и пошагово покажем, как конфиденциально отправлять SOL с помощью TypeScript SDK от Elusiv и модуля zk.js от Light.
Конфиденциальность важна
Конфиденциальность — фундаментальное право человека, необходимое для автономии и сохранения человеческого достоинства. Она нужна свободному и открытому обществу, поскольку лежит в основе реализации других прав, включая свободу слова и объединений. Благодаря ей существует пространство вне политической жизни, где каждый может думать, говорить, выражать идеи и действовать без постоянного наблюдения со стороны третьих лиц. Недавние громкие события, такие как публичные разоблачения Эдварда Сноудена, публикации WikiLeaks и скандал с данными Facebook и Cambridge Analytica, вызвали острые споры о важности и границах конфиденциальности.
Конфиденциальность касается каждого
На первый взгляд идея добавить функции конфиденциальности в публичные реестры, построенные на децентрализованных технологиях, где прозрачность считается одним из основных принципов, может показаться противоречивой. Но независимо от того, убеждённый ли вы шифропанк или обычный пользователь, конфиденциальность должна волновать каждого. Можно подумать: если вы не делаете в интернете ничего вредного или незаконного, какая разница, кто видит ваши действия в блокчейне? Рассмотрим мысленный эксперимент:
У нас есть два персонажа: Алиса — обычный пользователь, которая ценит свою конфиденциальность и хочет совершать покупки без постороннего контроля, и злоумышленник, которого мы сократим до ЗЛ. Злоумышленником может быть, например, репрессивное правительство, контролирующий работодатель или киберпреступник. Допустим, Алиса живёт в стране, где государство постоянно следит за каждым гражданином, или работает в компании со строгими запретами на определённые законные покупки — например, политические пожертвования или книги на конкретные темы. Однажды Алиса совершает покупку через традиционный блокчейн, такой как Solana, считая его безопасным и конфиденциальным способом оплаты. Далее происходит следующее:
- Транзакция Алисы записывается в публичный реестр. Она содержит публичный ключ Алисы, публичный ключ получателя и отправленную сумму
- ЗЛ хочет отслеживать или контролировать поведение Алисы и начинает отслеживать её транзакции по публичному ключу
- ЗЛ обнаруживает покупку Алисы, которую считает «неприемлемой» по своим правилам
- ЗЛ принимает меры против Алисы. В зависимости от обстоятельств это может быть судебное преследование, лишение свободы, санкции на работе, публичная травля или шантаж
Этот мысленный эксперимент наглядно показывает, как отсутствие конфиденциальности в блокчейне может повлиять на обычного пользователя. Из-за распространения ончейн-расследований и вредоносных технологий сбора данных отследить транзакции обычного пользователя становится довольно просто. Это может привести к серьёзным последствиям в реальном мире. В условиях растущей цифровизации отсутствие конфиденциальности угрожает личной автономии: все наши решения могут отслеживаться, а каждый аспект существования — превращаться в товар. Без внедрения технологий конфиденциальности блокчейны лишь ускорят этот процесс. Именно здесь на помощь приходят Elusiv и Light Protocol.
Что такое Elusiv?
Elusiv описывает себя как «основанный на блокчейне протокол конфиденциальности с нулевым разглашением», который «предоставляет пользователям и приложениям доступ к универсальному шифрованию». Elusiv стремится обеспечить конфиденциальность без ущерба для защищённости, безопасности и децентрализации, применяя криптографию с нулевым разглашением и многосторонние вычисления на уровне пользователей и сети. Благодаря универсальному шифрованию Elusiv предоставляет инструменты, которые позволяют пользователям контролировать свои данные. Теперь пользователи могут выбирать, какие данные публиковать в блокчейне, а какие — нет.
Как работает Elusiv?
Пользователи Elusiv отправляют средства в общий пул под управлением программы Elusiv, пополняя свои приватные балансы. Из пула можно отправлять и получать токены, включая SOL, а также выводить средства. Важно понимать, что при модели общего пула окружающие по-прежнему видят депозиты в Elusiv и платежи из него. Однако по мере роста числа пользователей и транзакций со временем становится всё сложнее связать депозиты и платежи с конкретными аккаунтами. Гораздо проще идентифицировать единственного пользователя, который внёс 10 SOL и вывел 10 SOL, чем одного из миллионов пользователей, постепенно вносящих и выводящих разные суммы. Анонимность любит компанию.
Средства в пуле не содержат информации о вкладчиках. Однако с помощью доказательств с нулевым разглашением средства пользователя связываются с секретными значениями, что позволяет подтверждать право на неизрасходованные средства. Доказательства с нулевым разглашением, которые использует Elusiv, называются zk-SNARKS (краткие неинтерактивные аргументы знания с нулевым разглашением). Не углубляясь в сложности доказательств zk-SNARK, представьте их как математически доказанный способ показать другу, что вы знаете секретный пароль, не раскрывая сам пароль. Друг сможет быстро проверить, что вы говорите правду, не задавая дополнительных вопросов. Таким образом, Elusiv может учитывать ваши неизрасходованные средства, не сообщая всей Solana, сколько у вас в пуле. Если вы хотите больше узнать о доказательствах с нулевым разглашением, рекомендую посмотреть курс Портера о доказательствах с нулевым разглашением.
Что такое Light Protocol?
Light Protocol описывает себя как «zkLayer нового поколения в Solana», который позволяет выполнять приватные программы непосредственно в Solana. Конфиденциальность в Solana с помощью Light основана на трёх ключевых концепциях:
- Приватное состояние
- Модель UTXO
- Ончейн-проверка переходов приватного состояния
Как работает Light Protocol?
Light Protocol позволяет шифровать ончейн-состояние. Пользователи «владеют» этим состоянием и имеют исключительное право на его расшифровку. В отличие от подхода Elusiv с общим пулом средств, Light Protocol добавляет в Solana модель UTXO (неизрасходованных выходов транзакций). UTXO связан с защищённой парой ключей и может хранить два баланса: один в SOL, поскольку это токен для оплаты комиссий в Light Protocol, и один в другом SPL-токене. При таком подходе каждая транзакция создаёт новый UTXO, который добавляется в линейный список. Это позволяет избежать обновлений на месте: состояние остаётся скрытым и не раскрывает информацию о том, какое состояние обновляется и кем.
Логика программ в Solana почти всегда представляет собой эскроу с определёнными уникальными ограничениями. Light Protocol развивает эту модель, кодируя логику программы в схемах с нулевым разглашением (zk-SNARKS) на стороне клиента, где выполняются вычисления и создаётся доказательство. Оно передаётся в блокчейн, где специализированная программа-верификатор, созданная специально для этой схемы, проверяет его действительность. После успешной проверки пользовательская программа-верификатор вызывает другой встроенный верификатор. Он выполняет различные проверки безопасности и добавляет новые обязательства в программу дерева Меркла от Light.
Хотя это выходит за рамки статьи, Light позволяет с нуля создавать собственные приватные программы Solana (PSP). Прежде чем создавать такую программу, рекомендуем дочитать статью и ознакомиться с документацией. В документации есть руководство по созданию собственной PSP, доступное здесь.
Всё это здорово, но как применить это непосредственно при разработке в Solana?
Конфиденциальная отправка SOL с помощью Elusiv
Полный код
import * as ed from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));
import {
Connection,
PublicKey,
Keypair,
ConfirmedSignatureInfo,
Cluster,
LAMPORTS_PER_SOL,
clusterApiUrl,
} from "@solana/web3.js";
import { Elusiv, TokenType, SEED_MESSAGE } from "@elusiv/sdk";
const CLUSTER: Cluster = "devnet";
const RPC = "https://rpc-devnet.helius.xyz";
const main = async () => {
const connection: Connection = new Connection(RPC);
const keyPair: Keypair = generateKeypair();
const recipientPublicKey: PublicKey = new PublicKey(generateKeypair().publicKey);
await airdropSol(keyPair);
const seed: Uint8Array = ed.sign(Buffer.from(SEED_MESSAGE, "utf-8"), keyPair.secretKey.slice(0, 32));
const elusiv: Elusiv = await Elusiv.getElusivInstance(seed, keyPair.publicKey, connection, CLUSTER);
const balance: bigint = await elusiv.getLatestPrivateBalance("LAMPORTS");
try {
if (balance > BigInt(0)) {
const signature = await send(elusiv, recipientPublicKey, 1 * LAMPORTS_PER_SOL, "LAMPORTS");
console.log(`Sent with signature ${signature.signature}`);
} else {
console.log("Private balance is empty. Topping up...");
const amount = 1 * LAMPORTS_PER_SOL;
const tokenType = "LAMPORTS";
const topUpTxData = await elusiv.buildTopUpTx(amount, tokenType);
topUpTxData.tx.partialSign(keyPair);
const topUpSig: ConfirmedSignatureInfo = await elusiv.sendElusivTx(topUpTxData);
console.log(`Top-up complete with signature ${topUpSig.signature}`);
const signature: ConfirmedSignatureInfo = await send(elusiv, recipientPublicKey, 1 * LAMPORTS_PER_SOL, "LAMPORTS");
console.log(`Sent with signature ${signature.signature}`);
}
} catch (e) {
console.error(`Error sending SOL via Elusiv: ${e}`);
} finally {
console.log("Exiting program...");
process.exit(0);
}
};
const send = async (
elusiv: Elusiv,
recipient: PublicKey,
amount: number,
tokenType: TokenType
): Promise => {
const txt = await elusiv.buildSendTx(amount, recipient, tokenType);
return elusiv.sendElusivTx(txt);
};
const generateKeypair = (): Keypair => {
let keyPair = Keypair.generate();
console.log(`Public key: ${keyPair.publicKey.toBase58()}`);
console.log(`Private key: ${keyPair.secretKey}`);
return keyPair;
};
const airdropSol = async (wallet: Keypair) => {
try {
const connection = new Connection(clusterApiUrl("devnet"));
const airdropSignature = await connection.requestAirdrop(new PublicKey(wallet.publicKey), 1 * LAMPORTS_PER_SOL);
const latestBlockHash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
signature: airdropSignature,
});
console.log(`Airdropped 1 SOL to ${wallet.publicKey.toBase58()}`);
} catch (e) {
console.error(`Error sending SOL via Elusiv: ${e}`);
}
};
main();Разбор кода Elusiv
import * as ed from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));Сначала мы импортируем библиотеку noble-ed25519, поскольку подключение кошелька у нас не настроено. В рабочей среде для подписания транзакций вместо неё следует использовать Solana Wallet Adapter. Здесь нам нужна только функция sign. Однако при использовании import { sign } from "@noble/ed25519"; иногда возникает ошибка о том, что etc.sha512Sync не задан. Чтобы избежать этой ошибки, мы задаём его самостоятельно. Не стоит придавать этому большого значения.
import {
Connection,
PublicKey,
Keypair,
ConfirmedSignatureInfo,
Cluster,
LAMPORTS_PER_SOL,
clusterApiUrl,
} from "@solana/web3.js";
import { Elusiv, TokenType, SEED_MESSAGE } from "@elusiv/sdk";Затем мы импортируем web3.js от Solana и TypeScript SDK от Elusiv с необходимыми модулями.
const CLUSTER: Cluster = "devnet";
const RPC = "https://rpc-devnet.helius.xyz";Здесь мы задаём CLUSTER как "devnet" и указываем в качестве RPC Helius devnet RPC. Чтобы использовать этот скрипт в mainnet, нужно задать CLUSTER как "mainnet" и получить ключ Helius API на одном из следующих тарифов 😎
const main = async () => {
const connection: Connection = new Connection(RPC);
const keyPair: Keypair = generateKeypair();
const recipientPublicKey: PublicKey = new PublicKey(generateKeypair().publicKey);
// Rest of the code
}Мы объявляем функцию main асинхронной, поскольку внутри её блока await несколько функций. Затем мы устанавливаем подключение к devnet с помощью RPC и создаём объект Keypair через вспомогательную функцию generateKeypair. С помощью той же функции generateKeypair мы также создаём recipientPublicKey, чтобы получать защищённые SOL на случайный адрес. Для справки функция generateKeypair определяется так:
const generateKeypair = (): Keypair => {
let keyPair = Keypair.generate();
console.log(`Public key: ${keyPair.publicKey.toBase58()}`);
console.log(`Private key: ${keyPair.secretKey}`);
return keyPair;
};Эта функция создаёт тестовую пару ключей Solana, которую мы используем в скрипте. Для использования в mainnet её нужно заменить парой ключей собственного кошелька. Помните: никогда не храните приватный ключ в виде обычного текста. Импортируйте его правильно, указав корректный путь к файлу.
await airdropSol(keyPair);Поскольку мы используем новые тестовые пары ключей, нам понадобится немного SOL в devnet. Вспомогательная функция aidropSol отправляет SOL через airdrop на нашу новую пару ключей. Она определяется так:
const airdropSol = async (wallet: Keypair) => {
try {
const connection = new Connection(clusterApiUrl("devnet"));
const airdropSignature = await connection.requestAirdrop(new PublicKey(wallet.publicKey), 1 * LAMPORTS_PER_SOL);
const latestBlockHash = await connection.getLatestBlockhash();
await connection.confirmTransaction({
blockhash: latestBlockHash.blockhash,
lastValidBlockHeight: latestBlockHash.lastValidBlockHeight,
signature: airdropSignature,
});
console.log(`Airdropped 1 SOL to ${wallet.publicKey.toBase58()}`);
} catch (e) {
console.error(`Error sending SOL via Elusiv: ${e}`);
}
};Здесь мы отправляем себе 1 SOL (1 * LAMPORTS_PER_SOL) через airdrop в devnet. Мы получаем latestBlockHash, чтобы передать хеш блока и высоту последнего допустимого блока в connection.confirmTransaction(). Так мы подтверждаем, что 1 SOL был отправлен через airdrop на правильный адрес.
const seed: Uint8Array = ed.sign(Buffer.from(SEED_MESSAGE, "utf-8"), keyPair.secretKey.slice(0, 32));Elusiv требует входное начальное значение, чтобы обеспечить детерминированность некоторых операций. Поэтому мы импортируем и используем SEED_MESSAGE. SEED_MESSAGE — обязательное сообщение для подписи, которое используется для генерации начального значения Elusiv. Оно позволяет приложению расшифровывать ваши приватные активы и связанные с ними секретные значения, чтобы вы могли учитывать свои средства. С помощью Buffer.from() мы создаём новый буфер — необработанную двоичную структуру данных — из SEED_MESSAGE с кодировкой utf-8. Нам нужно использовать slice, поскольку в типе пары ключей Solana первые 32 байта содержат приватный ключ, а последние 32 байта — публичный. Таким образом, мы подписываем необходимое сообщение начального значения своим приватным ключом.
const elusiv: Elusiv = await Elusiv.getElusivInstance(seed, keyPair.publicKey, connection, CLUSTER);Здесь мы определяем elusiv — экземпляр Elusiv — с помощью seed, keyPair.publicKey, connection и CLUSTER. Таким образом, мы создаём экземпляр Elusiv в devnet, используя devnet RPC, свой публичный ключ и только что подписанное длинное сообщение.
const balance: bigint = await elusiv.getLatestPrivateBalance("LAMPORTS");Затем мы получаем приватный баланс — количество лампортов в пуле Elusiv. Значение возвращается как BigInt. Остальная часть функции main заключена в try-catch-finally block, который определяется так:
try {
if (balance > BigInt(0)) {
const signature = await send(elusiv, recipientPublicKey, 1 * LAMPORTS_PER_SOL, "LAMPORTS");
console.log(`Sent with signature ${signature.signature}`);
} else {
// Rest of code
}
}Если у нас есть приватный баланс, balance будет больше 0 и выполнится первый блок if-else. В условии if мы приводим 0 к BigInt, поскольку функция getLatestPrivateBalance возвращает тип BigInt. Затем мы создаём signature — переменную со значением, возвращённым send(). Функция send() определяется так:
const send = async (
elusiv: Elusiv,
recipient: PublicKey,
amount: number,
tokenType: TokenType
): Promise => {
const txt = await elusiv.buildSendTx(amount, recipient, tokenType);
return elusiv.sendElusivTx(txt);
};Эта функция отправляет нашу транзакцию через Elusiv и возвращает Promise типа ConfirmedSignatureInfo — подтверждённую подпись с её статусом. В качестве аргументов она принимает экземпляр Elusiv, публичный ключ получателя, отправляемую сумму и тип токена. Это могут быть не только лампорты, но и другие токены, например USDC, USDT, mSOL, BONK и SAMO. Затем мы задаём txt с помощью elusiv.buildSendTx(). buildSendTx() — метод экземпляра Elusiv, который создаёт транзакцию для отправки токенов указанному получателю с приватного баланса. После этого мы отправляем и возвращаем транзакцию с помощью инструкции return elusiv.sendElusivTx(txt), передавая в неё созданную транзакцию txt.
Если приватного баланса нет, выполняется блок else:
console.log("Private balance is empty. Topping up...");
const amount = 1 * LAMPORTS_PER_SOL;
const tokenType = "LAMPORTS";
const topUpTxData = await elusiv.buildTopUpTx(amount, tokenType);
topUpTxData.tx.partialSign(keyPair);
const topUpSig: ConfirmedSignatureInfo = await elusiv.sendElusivTx(topUpTxData);
console.log(`Top-up complete with signature ${topUpSig.signature}`);
const signature: ConfirmedSignatureInfo = await send(elusiv, recipientPublicKey, 1 * LAMPORTS_PER_SOL, "LAMPORTS");
console.log(`Sent with signature ${signature.signature}`);Мы сообщаем пользователю, что у него нет приватного баланса и мы собираемся его пополнить. Затем создаём транзакцию пополнения с помощью метода .buildTopUpTx() экземпляра Elusiv. Мы передаём сумму 1 SOL и указываем тип токена Lamports, поскольку отправляем SOL. Затем подписываем пополнение через .partialSign() в паре ключей транзакции, поскольку пополняем баланс со своего публичного ключа. После этого отправляем созданную транзакцию через elusiv.sendElusivTx(topUpTxData) и выводим подпись транзакции в журнал.
Оставшаяся часть блока try-catch-finally выглядит так:
} catch (e) {
console.error(`Error sending SOL via Elusiv: ${e}`);
} finally {
console.log("Exiting program...");
process.exit(0);
}Если при отправке SOL через Elusiv возникают ошибки, мы выводим их в консоль с помощью console.error(). Затем завершаем работу программы через process.exit(0).
Чтобы запустить весь процесс, мы вызываем функцию main() в конце файла.
Конфиденциальная отправка SOL с помощью Light Protocol
Полный код
import { BN } from "@coral-xyz/anchor";
import { PublicKey, Keypair, Connection } from "@solana/web3.js";
import { User, Provider as LightProvider, TestRelayer, confirmConfig, airdropSol } from "@lightprotocol/zk.js";
const initializeSolanaWallet = async (): Promise => {
const wallet = Keypair.generate();
console.log("Wallet initialized");
return wallet;
};
const requestAirdrop = async (connection: Connection, publicKey: PublicKey): Promise => {
await airdropSol({
connection,
lamports: 2e9,
recipientPublicKey: publicKey,
});
console.log("Airdrop requested...");
};
const setupTestRelayer = async (solanaWallet: Keypair): Promise => {
const testRelayer = new TestRelayer({
relayerPubkey: solanaWallet.publicKey,
relayerRecipientSol: solanaWallet.publicKey,
relayerFee: new BN(100_000),
payer: solanaWallet,
});
console.log("Test relayer initialized");
return testRelayer;
};
const initializeLightProvider = async (solanaWallet: Keypair, testRelayer: TestRelayer): Promise => {
const lightProvider = await LightProvider.init({
wallet: solanaWallet,
relayer: testRelayer,
confirmConfig,
});
console.log("Light provider initialized");
return lightProvider;
};
const initializeLightUser = async (lightProvider: LightProvider): Promise => {
const user = await User.init({ provider: lightProvider });
console.log("Light user initialized");
return user;
};
const performShieldOperation = async (user: User) => {
await user.shield({
publicAmountSol: "1.1",
token: "SOL",
});
console.log("Performed shield operation");
};
const executePrivateTransfer = async (user: User, testRecipientPublicKey: string) => {
const response = await user.transfer({
amountSol: "1",
token: "SOL",
recipient: testRecipientPublicKey,
});
console.log(`Executed private transfer! Txt hash: ${response.txHash}`);
};
const main = async () => {
try {
const solanaWallet = await initializeSolanaWallet();
const connection = new Connection("http://127.0.0.1:8899");
await requestAirdrop(connection, solanaWallet.publicKey);
const testRelayer = await setupTestRelayer(solanaWallet);
const lightProvider = await initializeLightProvider(solanaWallet, testRelayer);
const user = await initializeLightUser(lightProvider);
await performShieldOperation(user);
const testRecipientKeypair = Keypair.generate();
await requestAirdrop(connection, testRecipientKeypair.publicKey);
const lightProviderRecipient = await initializeLightProvider(testRecipientKeypair, testRelayer);
const testRecipient = await initializeLightUser(lightProviderRecipient);
await executePrivateTransfer(user, testRecipient.account.getPublicKey());
console.log(`Successfully sent 1 $SOL to ${testRecipient.account.getPublicKey()} privately!`);
} catch (e) {
console.error(`Error sending SOL via Light: ${e}`);
} finally {
console.log("Exiting program...");
process.exit(0);
}
};
main();Разбор кода
import { BN } from "@coral-xyz/anchor";
import { PublicKey, Keypair, Connection } from "@solana/web3.js";
import { User, Provider as LightProvider, TestRelayer, confirmConfig, airdropSol } from "@lightprotocol/zk.js";Сначала мы импортируем необходимые модули из Anchor, Solana/web3.js и Light, чтобы взаимодействовать с Solana через Light Protocol. Затем, прежде чем перейти к функции main, мы определяем несколько вспомогательных функций. По сравнению с нашей реализацией Elusiv процесс инициализации Light сложнее.
const initializeSolanaWallet = async (): Promise => {
const wallet = Keypair.generate();
console.log("Wallet initialized");
return wallet;
};Здесь мы создаём функцию initializeSolanaWallet, которая генерирует новый кошелёк Solana с помощью метода Keypair.generate().
const requestAirdrop = async (connection: Connection, publicKey: PublicKey): Promise => {
await airdropSol({
connection,
lamports: 2e9,
recipientPublicKey: publicKey,
});
console.log("Airdrop requested...");
};В отличие от Elusiv, библиотека Light предоставляет функцию airdropSol для отправки лампортов через airdrop на указанный публичный ключ. Мы создаём вспомогательную функцию requestAirdrop, чтобы отправить SOL на переданный publicKey.
const setupTestRelayer = async (solanaWallet: Keypair): Promise => {
const testRelayer = new TestRelayer({
relayerPubkey: solanaWallet.publicKey,
relayerRecipientSol: solanaWallet.publicKey,
relayerFee: new BN(100_000),
payer: solanaWallet,
});
console.log("Test relayer initialized");
return testRelayer;
};Чтобы отправлять защищённые SOL через Light, нужно настроить тестовый ретранслятор с помощью модуля TestRelayer. Ретрансляторы играют важную роль в экосистеме Light. Это веб-серверы, которые передают ZK-доказательства переходов состояния от клиента соответствующей ончейн-программе. Они также оплачивают комиссии за транзакции и получают вознаграждение после успешной проверки доказательства. Комиссия составляет 100 тыс. лампортов, и мы задаём её через new BN(100_000).
const initializeLightProvider = async (solanaWallet: Keypair, testRelayer: TestRelayer): Promise => {
const lightProvider = await LightProvider.init({
wallet: solanaWallet,
relayer: testRelayer,
confirmConfig,
});
console.log("Light provider initialized");
return lightProvider;
};Затем мы инициализируем Light Provider с помощью функции с подходящим названием initializeLightProvider(). Эта функция настраивает провайдера и связывает его с нашим кошельком Solana и тестовым ретранслятором через предоставленную Light конфигурацию confirmConfig. Она имеет тип ConfirmOptions и отвечает за этап проверки транзакции, уровни подтверждения и другие параметры.
const initializeLightUser = async (lightProvider: LightProvider): Promise => {
const user = await User.init({ provider: lightProvider });
console.log("Light user initialized");
return user;
};Назначение функции initializeLightUser понятно из названия: она инициализирует пользователя Light Protocol с помощью переданного lightProvider.
const performShieldOperation = async (user: User) => {
await user.shield({
publicAmountSol: "1.1",
token: "SOL",
});
console.log("Performed shield operation");
};Функция performShieldOperation позволяет пользователю защитить SOL, сделав их приватными. Здесь мы защищаем чуть больше 1 SOL, хотя отправляем только 1 защищённый SOL, чтобы учесть комиссии.
const executePrivateTransfer = async (user: User, testRecipientPublicKey: string) => {
const response = await user.transfer({
amountSol: "1",
token: "SOL",
recipient: testRecipientPublicKey,
});
console.log(`Executed private transfer! Txt hash: ${response.txHash}`);
};Это, пожалуй, самая важная вспомогательная функция, поскольку именно она выполняет приватный перевод. Здесь мы конфиденциально переводим 1 SOL из user указанному получателю testRecipientPublicKey.
const main = async () => {
try {
const solanaWallet = await initializeSolanaWallet();
const connection = new Connection("http://127.0.0.1:8899");
await requestAirdrop(connection, solanaWallet.publicKey);
const testRelayer = await setupTestRelayer(solanaWallet);
const lightProvider = await initializeLightProvider(solanaWallet, testRelayer);
const user = await initializeLightUser(lightProvider);
await performShieldOperation(user);
const testRecipientKeypair = Keypair.generate();
await requestAirdrop(connection, testRecipientKeypair.publicKey);
const lightProviderRecipient = await initializeLightProvider(testRecipientKeypair, testRelayer);
const testRecipient = await initializeLightUser(lightProviderRecipient);
await executePrivateTransfer(user, testRecipient.account.getPublicKey());
console.log(`Successfully sent 1 $SOL to ${testRecipient.account.getPublicKey()} privately!`);
} catch (e) {
console.error(`Error sending SOL via Light: ${e}`);
} finally {
console.log("Exiting program...");
process.exit(0);
}
};Функция main служит точкой входа нашего скрипта. Мы объявляем её асинхронной, поскольку внутри неё вызывается несколько асинхронных функций. Она последовательно выполняет все этапы: от инициализации кошелька до приватного перевода. Сначала мы настраиваем кошелёк Solana и подключение. Затем отправляем SOL на кошелёк через airdrop и инициализируем ретранслятор и Light Provider. После этого инициализируем кошелёк как пользователя Light и защищаем наши SOL. Затем создаём тестового получателя, отправляем ему немного SOL через airdrop, инициализируем его как пользователя Light и конфиденциально отправляем ему 1 SOL. Как и в коде Elusiv, мы помещаем логику в блок try-catch-finally, чтобы перехватывать ошибки и автоматически завершать программу после выполнения.
Можно заметить, что в отличие от Elusiv, где мы использовали devnet, здесь подключение настроено на "http://127.0.0.1:8899". На момент написания статьи Light Protocol не развёрнут в devnet/mainnet, поэтому ключи Helius пока не работают (👀). Для localhost потребуется запустить тестовый валидатор с предварительно загруженными аккаунтами и программами, необходимыми для выполнения кода. Для этого установите Light Protocol CLI и выполните следующую команду:
light test-validatorКоманда запустит валидатор в фоновом режиме. Сейчас также нужно вручную добавить chai (библиотеку утверждений, которая упрощает тестирование), поскольку внутри класса UTXO есть несколько модульных тестов. Однако эта проблема должна быть устранена в следующем выпуске. Ваш файл package.json для этого скрипта должен выглядеть примерно так:
{
"name": "helius-tutorial",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"devDependencies": {
"@lightprotocol/cli": "^0.1.1-alpha.21",
"@types/node": "^20.6.0",
"chai": "^4.3.8",
"typescript": "^5.2.2"
},
"dependencies": {
"@coral-xyz/anchor": "^0.28.1-beta.2",
"@lightprotocol/zk.js": "^0.3.2-alpha.14",
"@solana/web3.js": "^1.78.4"
}
}Заключение
Поздравляем! В этом руководстве мы подробно рассмотрели конфиденциальность, её важность и способы конфиденциальной отправки SOL с помощью Elusiv и Light Protocol. На примере Elusiv мы узнали, как получать приватные балансы, конфиденциально отправлять SOL и даже автоматически пополнять баланс, если он исчерпан или ещё не существует. На примере Light Protocol мы разобрали сложный, но очень модульный процесс инициализации, защитили SOL и выполнили приватный перевод.
Какой вариант выбрать? Всё зависит от ваших задач. В примере с Elusiv код был сосредоточен на пополнении приватного баланса из общего пула и конфиденциальной отправке транзакций. Для этого скрипт взаимодействует с заранее определённым смарт-контрактом, который управляет пулом Elusiv. В Light Protocol мы настроили более гибкий, хотя и сложный, процесс инициализации. Он создаёт рабочий процесс для нескольких пользователей, взаимодействующих с программами Solana, которые сохраняют конфиденциальность. При необходимости Light также позволяет создавать собственные приватные программы Solana (PSP). У обоих решений есть компромиссы, связанные, например, с документацией и особенностями реализации, но оба протокола нацелены на конфиденциальность и безопасные транзакции. Если приложению нужен более точный контроль функций конфиденциальности и требуется шифровать отдельные части состояния, рассмотрите Light Protocol. Если вы создаёте более простое решение и хотите добавить уровень конфиденциальности для переводов токенов в devnet/mainnet, лучше подойдёт Elusiv.
Описанные в статье методы, код и выводы дают надёжную отправную точку для создания ориентированных на конфиденциальность приложений в Solana. Значимость конфиденциальности для блокчейн-технологий трудно переоценить. Чем выше спрос на децентрализованные системы, тем острее потребность в функциях конфиденциальности, которые расширяют возможности пользователей, защищают их права и воплощают первоначальные цели блокчейн-технологий. Конфиденциальность важна, а Elusiv и Light Protocol помогут сделать Solana безопаснее!
Если вы, анон, дочитали до этого места — спасибо!
Дополнительные материалы
- Манифест шифропанка
- Elusiv
- Документация Elusiv
- SDK Elusiv
- Примеры Elusiv
- Light Protocol
- zk.js
- Примеры реализаций приватных программ Solana
Похожие статьи
Подпишитесь на Helius
Следите за новостями разработки на Solana и получайте уведомления о наших публикациях


