
Privacidad en Solana con Elusiv y Light: todo lo que necesitas saber sobre la privacidad en SOL
Tabla de contenido
- Requisitos previos
- ¿De qué trata este artículo?
- La privacidad es importante
- A todos debería importarles la privacidad
- ¿Qué es Elusiv?
- ¿Cómo funciona Elusiv?
- ¿Qué es Light Protocol?
- ¿Cómo funciona Light Protocol?
- Enviar SOL de forma privada con Elusiv
- El código completo
- Análisis del código de Elusiv
- Enviar SOL de forma privada con Light Protocol
- El código completo
- Análisis del código
- Conclusión
- Recursos adicionales / Lecturas complementarias
Requisitos previos
- Node.js instalado (para instalar las dependencias necesarias, ejecutar los scripts y enviar $SOL de forma privada)
- Conocimientos básicos de TypeScript
- Conocimientos básicos de @solana/web3.js
¿De qué trata este artículo?
La privacidad es un elemento central de la tecnología blockchain moderna. En los sistemas de registro público, las transacciones, los saldos de las cuentas y, a menudo, el código fuente de un programa están disponibles para que cualquiera los consulte. Aunque este nivel de transparencia sin precedentes se considera una característica distintiva de la tecnología blockchain, la privacidad puede ser un requisito esencial para muchas aplicaciones o para el uso cotidiano.
Elusiv y Light Protocol ofrecen soluciones de vanguardia para la privacidad en Solana mediante zk-SNARKS para transacciones privadas, programas privados y soluciones descentralizadas de cumplimiento normativo. En este artículo, analizamos la importancia de la privacidad, explicamos por qué las blockchains como Solana necesitan funciones de privacidad y ofrecemos una guía paso a paso para enviar SOL de forma privada con el SDK de TypeScript de Elusiv y el módulo zk.js de Light.
La privacidad es importante
La privacidad es un derecho humano fundamental, esencial para la autonomía y la preservación de la dignidad humana. Es necesaria para una sociedad libre y abierta porque sustenta el ejercicio de otros derechos, como la libertad de expresión y de asociación. Esto permite crear un espacio separado de la vida política donde cualquiera pueda pensar, hablar, expresar ideas o comportarse sin la vigilancia constante de un tercero. Controversias públicas recientes, como las revelaciones de Edward Snowden, la información expuesta por WikiLeaks y el escándalo de datos de Facebook-Cambridge Analytica, han provocado intensos debates sobre la importancia y los límites de la privacidad.
A todos debería importarles la privacidad
A primera vista, puede parecer contradictorio defender que los registros públicos, construidos sobre tecnologías descentralizadas que promueven la transparencia como uno de sus principios fundamentales, deban incluir funciones de privacidad. Seas un cypherpunk convencido o un usuario promedio, la privacidad debería importarle a todo el mundo. Quizá pienses que, como no haces nada dañino ni ilegal en línea, no importa que otros vean lo que haces en la blockchain. Considera este experimento mental:
Tenemos dos personajes: Alice, una usuaria promedio que valora su privacidad y quiere hacer compras sin estar bajo escrutinio; y un actor malicioso, al que abreviaré como MA. Un actor malicioso podría ser, por ejemplo, un gobierno opresivo, un empleador controlador o un ciberdelincuente. Supongamos que Alice vive en un país donde el gobierno vigila constantemente a cada ciudadano, o que trabaja para una empresa con políticas estrictas contra ciertas compras legales (por ejemplo, donaciones políticas o libros sobre determinados temas). Un día, Alice hace una compra mediante una blockchain tradicional, como Solana, creyendo que es un método de pago seguro y privado. La secuencia de acontecimientos es la siguiente:
- La transacción de Alice se registra en el libro mayor público. Su transacción incluye su clave pública, la clave pública del destinatario y la cantidad enviada
- MA, que quiere vigilar o controlar el comportamiento de Alice, comienza a rastrear sus transacciones mediante su clave pública
- MA descubre la compra de Alice, que considera "objetable" según sus reglas
- MA toma medidas contra Alice que, según las circunstancias, podrían incluir acciones legales, encarcelamiento, sanciones laborales, escarnio público o chantaje
Este experimento mental ilustra la dura realidad de cómo la falta de privacidad en la blockchain puede afectar al usuario promedio. Con el auge de los investigadores on-chain y las tecnologías maliciosas de recolección de datos, rastrear las transacciones de un usuario promedio se está volviendo muy sencillo, lo que puede tener consecuencias importantes en el mundo real. En un mundo cada vez más digital, sin privacidad corremos el riesgo de perder la soberanía individual, de que todas nuestras decisiones sean vigiladas y de que cada aspecto de nuestra existencia se convierta en mercancía. Las blockchains solo acelerarán este proceso si no implementamos tecnologías que protejan la privacidad. Aquí es donde entran Elusiv y Light Protocol.
¿Qué es Elusiv?
Elusiv se describe como "un protocolo de privacidad basado en blockchain y de conocimiento cero" que "permite a usuarios y aplicaciones acceder al cifrado universal". Elusiv busca ofrecer privacidad sin sacrificar la protección, la seguridad ni la descentralización mediante la aplicación de criptografía de conocimiento cero y computación multipartita al usuario y a la red. Este objetivo de cifrado universal permite que Elusiv proporcione herramientas con las que los usuarios pueden controlar sus datos. Ahora pueden elegir qué se comparte en la blockchain y qué no.
¿Cómo funciona Elusiv?
Elusiv funciona haciendo que los usuarios envíen fondos a un fondo común administrado por el programa Elusiv, que recarga sus saldos privados. Desde este fondo, los usuarios pueden enviar y recibir tokens como SOL, además de retirar fondos. Es importante señalar que, con este modelo de fondo común, las personas aún pueden observar los depósitos y pagos de Elusiv. Sin embargo, a medida que aumentan los usuarios y las transacciones, se vuelve cada vez más difícil vincular los depósitos y pagos con cuentas específicas a lo largo del tiempo: es más fácil vincular a un único usuario que deposita 10 SOL y retira 10 SOL que identificar a un usuario entre millones que hace depósitos y pagos incrementales. El anonimato funciona mejor en compañía.
Los fondos del fondo común no contienen información sobre quienes los depositaron, pero, mediante pruebas de conocimiento cero, se asocian valores secretos con los fondos de un usuario para poder generar pruebas de autoridad sobre los fondos no gastados. Las pruebas de conocimiento cero utilizadas en Elusiv se conocen como zk-SNARKS (Zero-Knowledge Succinct Non-Interactive Argument of Knowledge). Sin profundizar en la complejidad de las pruebas zk-SNARK, puedes verlas como una forma demostrada matemáticamente de mostrarle a un amigo que conoces una contraseña secreta sin revelársela. Tu amigo puede comprobar rápidamente que dices la verdad sin tener que preguntarte nada más. De esta forma, Elusiv puede llevar un registro de tus fondos no gastados sin decirle a toda Solana cuánto tienes en el fondo común. Si quieres aprender más sobre las pruebas de conocimiento cero, te recomiendo consultar la clase de Porter sobre pruebas de conocimiento cero.
¿Qué es Light Protocol?
Light Protocol se describe como una "zkLayer de nueva generación en Solana" que permite ejecutar programas privados directamente en Solana. Light se basa en tres conceptos fundamentales que permiten la privacidad en Solana:
- Estado privado
- Modelo UTXO
- Verificación on-chain de las transiciones de estado privado
¿Cómo funciona Light Protocol?
Light Protocol permite cifrar el estado on-chain. Los usuarios son "propietarios" de este estado y tienen derechos exclusivos para descifrarlo. A diferencia del enfoque de fondo común de Elusiv, Light Protocol incorpora a Solana un modelo UTXO (Unspent Transaction Outputs). Un UTXO está vinculado a un par de claves protegido y puede contener dos saldos: uno en SOL (porque es el token usado para pagar comisiones en Light Protocol) y otro en un token SPL distinto. Con este enfoque, cada transacción genera un nuevo UTXO que se agrega a una lista lineal, lo cual evita las actualizaciones in situ. El estado permanece oculto en vez de revelar información sobre qué estado se actualiza y quién lo hace.
La lógica de los programas en Solana casi siempre consiste en un depósito en garantía con restricciones particulares. Light Protocol innova sobre este diseño al codificar la lógica del programa en circuitos de conocimiento cero (zk-SNARKS) del lado del cliente, donde se generan un cálculo y una prueba. Esta prueba se comparte on-chain, donde un programa verificador dedicado y diseñado específicamente para el circuito comprueba su validez. Tras una verificación correcta, el programa verificador personalizado llama a otro verificador nativo que realiza varias comprobaciones de seguridad y agrega los nuevos compromisos al programa de árbol de Merkle de Light.
Aunque queda fuera del alcance de este artículo, Light te permite crear desde cero tu propio Private Solana Program (PSP) personalizado. Te recomendamos leer el resto de este artículo y consultar su documentación antes de intentar crear uno. Su documentación incluye un tutorial para crear tu propio PSP personalizado, que puedes consultar aquí.
Todo esto está muy bien, pero ¿cómo puedo aplicarlo directamente al desarrollo en Solana?
Enviar SOL de forma privada con Elusiv
El código completo
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();Análisis del código de Elusiv
import * as ed from "@noble/ed25519";
import { sha512 } from "@noble/hashes/sha512";
ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m));Primero importamos la biblioteca noble-ed25519, ya que no configuramos ninguna conexión con una wallet. En un entorno de producción, usarías Solana Wallet Adapter para firmar transacciones. Aquí solo necesitamos la funcionalidad sign. Sin embargo, al usar import { sign } from "@noble/ed25519";, a veces puede aparecer un error que indica que etc.sha512Sync no está configurado. Para evitarlo, lo configuramos nosotros mismos. No te preocupes demasiado por esto.
import {
Connection,
PublicKey,
Keypair,
ConfirmedSignatureInfo,
Cluster,
LAMPORTS_PER_SOL,
clusterApiUrl,
} from "@solana/web3.js";
import { Elusiv, TokenType, SEED_MESSAGE } from "@elusiv/sdk";Después importamos web3.js de Solana y el SDK de TypeScript de Elusiv con los módulos necesarios.
const CLUSTER: Cluster = "devnet";
const RPC = "https://rpc-devnet.helius.xyz";Aquí definimos CLUSTER como "devnet" y configuramos como RPC el RPC de Helius para devnet. Si quisieras usar este script en mainnet, asignarías "mainnet" a CLUSTER y obtendrías una clave de API de Helius con uno de los siguientes planes 😎
const main = async () => {
const connection: Connection = new Connection(RPC);
const keyPair: Keypair = generateKeypair();
const recipientPublicKey: PublicKey = new PublicKey(generateKeypair().publicKey);
// Rest of the code
}Marcamos nuestra función main como asíncrona porque aplicamos await a varias funciones dentro de su bloque. Luego establecemos nuestra conexión con devnet mediante RPC y creamos un objeto Keypair con la función de utilidad generateKeypair. También creamos un recipientPublicKey con la función generateKeypair antes mencionada para poder recibir el SOL protegido en una dirección aleatoria. Como referencia, la función generateKeypair se define así:
const generateKeypair = (): Keypair => {
let keyPair = Keypair.generate();
console.log(`Public key: ${keyPair.publicKey.toBase58()}`);
console.log(`Private key: ${keyPair.secretKey}`);
return keyPair;
};Esta función crea un par de claves de prueba de Solana que usaremos en este script. Si quisieras usarlo en mainnet, reemplazarías el par de claves por el de tu propia wallet. Recuerda nunca guardar tu clave privada como texto sin formato e importarla correctamente con la ruta de archivo adecuada.
await airdropSol(keyPair);Necesitamos algo de SOL de devnet porque estamos usando pares de claves de prueba recién generados. Usamos la función de utilidad aidropSol para enviar SOL por airdrop a nuestro nuevo par de claves. Se define así:
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}`);
}
};Aquí nos enviamos 1 SOL (1 * LAMPORTS_PER_SOL) por airdrop en devnet. Obtenemos el latestBlockHash para pasar el hash del bloque y la última altura de bloque válida a connection.confirmTransaction(). Así confirmamos que enviamos 1 SOL por airdrop a la dirección correcta.
const seed: Uint8Array = ed.sign(Buffer.from(SEED_MESSAGE, "utf-8"), keyPair.secretKey.slice(0, 32));Elusiv necesita una semilla de entrada para garantizar que ciertas operaciones sean deterministas. Por eso importamos y usamos SEED_MESSAGE. SEED_MESSAGE es un mensaje obligatorio que debe firmarse, ya que se usa para generar la semilla de Elusiv. Permite que la aplicación descifre tus activos privados con sus valores secretos asociados para que puedas llevar un registro de tus fondos. Usamos Buffer.from() para crear un nuevo búfer (una estructura de datos binarios sin procesar) a partir de SEED_MESSAGE con codificación utf-8. Debemos usar slice porque el tipo de par de claves de Solana almacena la clave privada en los primeros 32 bytes y la clave pública en los últimos 32 bytes. Por lo tanto, firmamos el mensaje de semilla obligatorio con nuestra clave privada.
const elusiv: Elusiv = await Elusiv.getElusivInstance(seed, keyPair.publicKey, connection, CLUSTER);Aquí definimos elusiv, una instancia de Elusiv, mediante seed, keyPair.publicKey, connection y CLUSTER. De esta manera, creamos una instancia de Elusiv en devnet con el RPC de devnet, nuestra clave pública y el mensaje largo que acabamos de firmar.
const balance: bigint = await elusiv.getLatestPrivateBalance("LAMPORTS");Después obtenemos nuestro saldo privado, es decir, la cantidad de Lamports que tenemos en el fondo común de Elusiv, que se devuelve como un BigInt. El resto de la función main está encapsulado en un try-catch-finally block, que se define así:
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
}
}Si tenemos saldo privado, balance será mayor que 0 y se ejecutará el primer bloque de if-else. Convertimos 0 a BigInt en la condición if porque el tipo de retorno de getLatestPrivateBalance es BigInt. Después creamos signature, una variable cuyo valor es el resultado de send(). La función send() se define así:
const send = async (
elusiv: Elusiv,
recipient: PublicKey,
amount: number,
tokenType: TokenType
): Promise => {
const txt = await elusiv.buildSendTx(amount, recipient, tokenType);
return elusiv.sendElusivTx(txt);
};Esta función envía nuestra transacción mediante Elusiv y devuelve una Promise de tipo ConfirmedSignatureInfo (una firma confirmada con su estado). Como argumentos, recibe una instancia de Elusiv, la clave pública de un destinatario, una cantidad que se enviará y el tipo de token (que no tiene que ser Lamports y puede ser uno de varios tokens, como USDC, USDT, mSOL, BONK y SAMO). Luego establecemos txt mediante elusiv.buildSendTx(). buildSendTx() es un método de la instancia de Elusiv que crea una transacción para enviar tokens a un destinatario específico usando el saldo privado. Luego enviamos y devolvemos la transacción mediante nuestra instrucción return elusiv.sendElusivTx(txt), a la que pasamos la transacción creada txt.
Si no tenemos saldo privado, se ejecuta el bloque 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}`);Informamos al usuario en el registro que no tiene saldo privado y que vamos a recargarlo. Luego creamos la transacción de recarga mediante el método .buildTopUpTx() de la instancia de Elusiv. Pasamos un valor de 1 SOL y Lamports como tipo de token porque enviamos SOL. Después firmamos la recarga con un .partialSign() en el par de claves de la transacción, ya que realizamos la recarga desde nuestra clave pública. Por último, enviamos la transacción creada mediante elusiv.sendElusivTx(topUpTxData) y registramos la firma de la transacción.
El resto del bloque try-catch-finally es el siguiente:
} catch (e) {
console.error(`Error sending SOL via Elusiv: ${e}`);
} finally {
console.log("Exiting program...");
process.exit(0);
}Si se produce algún error al enviar SOL mediante Elusiv, lo registramos en la consola con console.error(). Después salimos del programa mediante process.exit(0).
Para iniciar todo este proceso, llamamos a la función main() al final del archivo.
Enviar SOL de forma privada con Light Protocol
El código completo
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();Análisis del código
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";Primero importamos los módulos necesarios de Anchor, Solana/web3.js y Light para interactuar con Solana mediante Light Protocol. Después definimos varias funciones de utilidad antes de llegar a nuestra función main. Light tiene un proceso de inicialización más complejo que nuestra implementación de Elusiv.
const initializeSolanaWallet = async (): Promise => {
const wallet = Keypair.generate();
console.log("Wallet initialized");
return wallet;
};Aquí creamos la función initializeSolanaWallet, que genera una nueva wallet de Solana mediante el método Keypair.generate().
const requestAirdrop = async (connection: Connection, publicKey: PublicKey): Promise => {
await airdropSol({
connection,
lamports: 2e9,
recipientPublicKey: publicKey,
});
console.log("Airdrop requested...");
};A diferencia de Elusiv, Light incluye en su biblioteca una función airdropSol para enviar Lamports por airdrop a una clave pública específica. Creamos la función de utilidad requestAirdrop para enviar SOL al publicKey proporcionado.
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;
};Para enviar SOL protegido mediante Light, debemos configurar un relayer de prueba con el módulo TestRelayer. Los relayers desempeñan una función importante en el ecosistema de Light: son servidores web que reenvían desde el cliente al programa on-chain correspondiente las pruebas ZK que demuestran las transiciones de estado. También pagan las comisiones de la transacción a cambio de una comisión cuando la prueba se verifica correctamente. Las comisiones son de 100 000 Lamports, valor que configuramos con 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;
};Después inicializamos Light Provider con nuestra función initializeLightProvider(), cuyo nombre describe claramente su propósito. Esta función configura un proveedor y lo vincula con nuestra wallet de Solana y el relayer de prueba mediante la configuración proporcionada por Light, confirmConfig. Esta configuración es de tipo ConfirmOptions y se ocupa del paso de verificación de la transacción, los niveles de compromiso, etc.
const initializeLightUser = async (lightProvider: LightProvider): Promise => {
const user = await User.init({ provider: lightProvider });
console.log("Light user initialized");
return user;
};La función initializeLightUser se explica por sí sola: inicializa un usuario de Light Protocol con el lightProvider proporcionado.
const performShieldOperation = async (user: User) => {
await user.shield({
publicAmountSol: "1.1",
token: "SOL",
});
console.log("Performed shield operation");
};La función performShieldOperation permite al usuario proteger SOL y hacerlo privado. Aquí protegemos más de 1 SOL, aunque solo enviamos 1 SOL protegido, para cubrir las comisiones.
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}`);
};Esta es posiblemente la función de utilidad más importante, ya que ejecuta la transferencia privada. Aquí transferimos de forma privada 1 SOL desde user al destinatario especificado 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);
}
};La función main sirve como punto de entrada de nuestro script. La marcamos como asíncrona porque dentro de su ámbito se llaman varias funciones asíncronas. Ejecuta todos los pasos en orden, desde la inicialización de la wallet hasta la transferencia privada. Primero configuramos nuestra wallet y conexión de Solana. Después enviamos SOL por airdrop a la wallet e inicializamos el relayer y Light Provider. Luego inicializamos nuestra wallet como usuario de Light y protegemos nuestro SOL. A continuación, creamos un destinatario de prueba, le enviamos algo de SOL por airdrop, lo inicializamos como usuario de Light y le enviamos 1 SOL de forma privada. Al igual que en nuestro código de Elusiv, colocamos la lógica en un bloque try-catch-finally para detectar errores y salir automáticamente del programa cuando termine.
Quizá observes que, a diferencia de Elusiv, donde usábamos devnet, aquí configuramos la conexión como "http://127.0.0.1:8899". En el momento de escribir este artículo, es importante señalar que Light Protocol no está implementado en devnet/mainnet, por lo que las claves de Helius no funcionarán (todavía 👀). Para localhost, tendrás que ejecutar un validador de prueba con las cuentas y programas necesarios precargados para poder ejecutar tu código. Para ello, puedes instalar la CLI de Light Protocol y ejecutar el siguiente comando:
light test-validatorEsto iniciará un validador en segundo plano. Actualmente también tendrás que agregar chai (una biblioteca de aserciones que facilita las pruebas) de forma manual, ya que hay algunas pruebas unitarias dentro de la clase UTXO. Sin embargo, esto debería resolverse en la próxima versión. Tu package.json para este script debería verse más o menos así:
{
"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"
}
}Conclusión
¡Felicitaciones! En este tutorial analizamos los pormenores de la privacidad, su importancia y cómo realizar transacciones de SOL de forma privada mediante Elusiv y Light Protocol. Con Elusiv, vimos cómo obtener saldos privados, enviar SOL de forma privada e incluso facilitar recargas automáticas cuando el saldo privado se agota o no existe. Con Light Protocol, vimos cómo configurar su complejo y altamente modular proceso de inicialización, proteger nuestro SOL y ejecutar una transferencia privada.
Entonces, quizá te preguntes cuál deberías usar. ¡Depende! Con Elusiv, nuestro código se centró en recargar un saldo privado desde un fondo común y enviar transacciones de forma privada. Para lograrlo, el script interactúa con un contrato inteligente predefinido que administra el fondo común de Elusiv. Con Light Protocol, establecimos un proceso de inicialización más flexible, aunque complejo, para crear un flujo de trabajo que administra varios usuarios que interactúan con programas de Solana que preservan la privacidad. Con Light también podemos crear nuestros propios Private Solana Programs (PSP) personalizados si es necesario. Ambos presentan ventajas y desventajas en aspectos como la documentación y la implementación específica, pero los dos protocolos buscan ofrecer privacidad y transacciones seguras. Si tu aplicación necesita un control más granular de las funciones de privacidad y quieres cifrar partes específicas del estado de la aplicación, considera usar Light Protocol. Si estás desarrollando algo más sencillo y quieres agregar una capa de privacidad a las transferencias de tokens en devnet/mainnet, Elusiv podría ser la mejor opción.
Las técnicas, el código y los conocimientos incluidos en este artículo ofrecen un punto de partida sólido para desarrollar aplicaciones centradas en la privacidad en Solana. La importancia de la privacidad en la tecnología blockchain es incuestionable. A medida que crece la demanda de sistemas descentralizados, también aumenta la necesidad de funciones de privacidad que empoderen a los usuarios, protejan sus derechos y hagan realidad los verdaderos objetivos de la tecnología blockchain inicial. La privacidad importa. ¡Ahora, con Elusiv y Light Protocol, puedes hacer de Solana una blockchain más segura!
Si llegaste hasta aquí, anon, ¡gracias!
Recursos adicionales / Lecturas complementarias
- Manifiesto de un cypherpunk
- Elusiv
- Documentación de Elusiv
- SDK de Elusiv
- Ejemplos de Elusiv
- Light Protocol
- zk.js
- Ejemplos de implementación de Private Solana Programs
Artículos relacionados
Suscríbete a Helius
Mantente al día con las novedades del desarrollo en Solana y recibe actualizaciones cuando publiquemos


