在Solana上使用Elusiv和Light的隐私保护

  • Helius
  • 发布于 2023-09-15 10:58
  • 阅读 21

本文探讨了区块链隐私的重要性以及在Solana上通过Elusiv和Light协议进行私密交易的方法。文章详细介绍了两种隐私协议的原理和用法,并提供了完整的代码示例,帮助开发者理解如何应用这些技术,确保交易的隐私性.

16分钟阅读

2023年9月13日

前提条件

  • 安装Node.js(用于安装所需依赖、运行脚本以及私下发送 $SOL)
  • 对TypeScript有基本的理解
  • 对@solana/web3.js有基本的理解

本文的内容是什么?

隐私是现代区块链技术的核心。公共账本系统、交易、账户余额以及程序的源代码往往对任何人开放。虽然这种前所未有的透明度被誉为区块链技术的标志,但对于许多应用或日常使用而言,隐私可能是一个至关重要的需求。

Elusiv和Light Protocol通过利用zk-SNARKS为Solana上的私人交易、私有程序和去中心化合规解决方案提供了最前沿的隐私解决方案。本文将深入探讨隐私的重要性,探讨为什么诸如Solana这样的区块链需要隐私功能,并提供逐步指南,讲解如何使用Elusiv的TypeScript SDK和Light的zk.js模块私下发送SOL。

隐私很重要

隐私是基本人权,是自主权和人类尊严的必要条件。隐私对于一个自由和开放的社会是必不可少的,因为它支撑着其他权利的行使,例如言论自由和结社自由。这使得人们能够在与政治生活相分开的空间内思考、言语、表达思想或行为,而不必时刻受到第三方的监视。最近的一些公共争议,如爱德华·斯诺登的公开披露、维基解密曝光的信息和Facebook-剑桥分析数据丑闻,引发了关于隐私重要性和边界的激烈辩论。

每个人都应该关心隐私

乍一看,主张建立在倡导透明度的去中心化技术基础上的公共账本应该支持隐私功能,似乎是不合常理的。无论你是坚定的网络朋克还是普通用户,每个人都应该关心隐私。你可能认为,既然你在网上没有做任何有害或非法的事情,那么又有谁在关注我在区块链上的行为呢?想象一下这个思想实验:

我们有两个角色:Alice(Alice),一个重视隐私并希望在没有审查的情况下进行购物的普通用户;而“恶意行为者”(MA)可以是一个压迫性的政府、一位控制人的雇主,或者一个网络犯罪分子。例如,假设Alice生活在一个政府对每位公民进行持续监控的国家,或者她在一家公司工作,该公司对某些合法的购买(如政治捐款或购买特定主题的书籍)有严格的政策限制。一天,Alice使用一种传统区块链(如Solana)进行购物,认为这是一个安全和私密的支付方式。事件的顺序如下:

  • Alice的交易被记录在_公共_账本上。她的交易包括她的公钥、接收者的公钥和发送的金额。
  • MA希望监控或控制Alice的行为,开始利用她的公钥追踪她的交易。
  • MA发现了Alice的购买行为,并根据他们的规则认为是“难以接受的”。
  • MA对Alice采取行动,具体措施可能根据情况不同,包括法律诉讼、监禁、对工作场所的制裁、社会羞辱或勒索。

这个思想实验展示了区块链上缺乏隐私如何影响普通用户的严峻现实。随着链上侦探和恶意数据收集技术的兴起,追踪普通用户的交易变得相当简单,这可能导致重大的现实后果。在一个日益数字化的世界中,如果没有隐私,我们将面临个人主权的侵蚀,所有决定都将被监控,生命的每个方面都将商品化。如果不实施隐私使能技术,区块链将只会加速这一进程。这就是Elusiv和Light Protocol的重要性所在。

什么是Elusiv?

Elusiv自称为“一个基于区块链的零知识隐私协议”,其“使用户和应用程序能够访问通用加密”。Elusiv旨在通过将零知识密码学和多方计算应用于用户和网络,实现隐私而不牺牲安全性、安全性和去中心化。这一通用加密的目标使Elusiv能够提供工具,使用户能够控制自己的数据。现在,用户可以选择在区块链上共享什么,以及不共享什么。

Elusiv是如何工作的?

Elusiv通过让用户将资金发送到由Elusiv程序管理的共享池来工作,从而补充他们的私人余额。在该池中,用户可以发送和接收代币如SOL,并提取资金。需要注意的是,使用这种共享池模型,人们仍然可以观察到向Elusiv的存款和付款。然而,随着用户和交易数量的增加,时间久了,链接存款和付款到特定账户变得越来越困难 - 链接一个存放10 SOL并提取10 SOL的孤立用户要比在数百万中链接一个进行增量存款和支付的用户容易得多。匿名喜欢热闹。

池中的资金不包含与存款人相关的任何信息,但使用零知识证明,秘密值与用户的资金相关联,以便在未花费的资金上作出权限证明。Elusiv中使用的零知识证明被称为zk-SNARKS(零知识简洁非交互式知识论证)。无须深入研究zk-SNARK证明的复杂性,可以将它们视为一种数学上证明的方法,以证明你知道一个秘密密码而不告诉对方是什么。当你的朋友能够迅速检查你所说的是否属实而无需再询问你任何其他问题时,这种证明得以实现。因此,Elusiv能够跟踪你的未花费资金,而不告诉Solana你在池中拥有多少资金。如果你有兴趣了解更多关于零知识证明的信息,我建议你查看Porter关于零知识证明的课程

什么是Light Protocol?

Light Protocol自称为一个“下一代zkLayer在Solana上”,使得在Solana上能够直接执行私有程序。Light Protocol的三个基本概念使其在Solana上实现隐私:

  • 私有状态
  • UTXO模型
  • 在链上验证私有状态转换

Light Protocol是如何工作的?

Light Protocol允许链上状态被加密。在这里,用户“拥有”这一状态,并享有独占的解密权利。与Elusiv的池资金模式不同,Light Protocol为Solana引入了UTXO(未花费交易输出)模型。UTXO与一个隐蔽的密钥对相关联,可以持有两个余额,一个是SOL(因为它是Light Protocol中的交易费用代币),另一个是其他SPL代币。在这种方法下,每笔交易都会生成一个新的UTXO并附加到一个线性列表中,从而避免就地更新;状态被隐蔽,而不是泄露有关哪个状态被更新以及由谁更新的信息。

Solana上的程序逻辑几乎总是一个具有某些独特约束的托管。Light Protocol通过将程序逻辑编码到客户端的零知识电路(zk-SNARKS)中来创新这个设计,在其中生成计算和证明。该证明被共享到链上,专门为电路而构建的验证程序检查其有效性。在成功验证后,自定义验证程序调用另一个本地验证器,完成各种安全检查,并将新的承诺添加到Light的Merkle树程序中。

尽管超出了本文的范围,Light允许从头创建你的自定义私人Solana程序(PSP)。建议你在尝试创建自己的程序之前阅读本文其余部分,并查看他们的文档。他们的文档包含有关如何创建自定义PSP的教程,你可以在这里找到。

这一切都很酷,但我如何将其直接应用于Solana编程?

使用Elusiv私下发送SOL

完整代码

代码

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";

然后,我们引入Solana的web3.js和Elusiv的TypeScript SDK及所需模块。

代码

const CLUSTER: Cluster = "devnet";
const RPC = "https://rpc-devnet.helius.xyz";

在这里,我们将CLUSTER定义为“devnet”,并将我们的RPC设置为Helius的devnet RPC。如果你想在主网上使用此脚本,你应将CLUSTER定义为“mainnet”,并从以下计划中的一项获得一个Helius API密钥这里 😎

代码

const main = async () => {
    const connection: Connection = new Connection(RPC);
    const keyPair: Keypair = generateKeypair();
    const recipientPublicKey: PublicKey = new PublicKey(generateKeypair().publicKey);

    // 代码的其余部分
}

我们将main函数标记为异步,因为我们在其块中await了几个功能。然后,我们使用RPC建立与devnet的连接,并通过实用函数generateKeypair创建一个Keypair对象。我们还使用前述的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密钥对,将用于此脚本。如你想在主网上使用此脚本,你应该用自己的钱包密钥对替换它。请记住,绝不要以明文存储你的私钥,并使用正确的文件路径正确导入它。

代码

await airdropSol(keyPair);

我们需要一些devnet SOL,因为我们使用了新生成的测试密钥对。我们使用aidropSol实用函数向我们的新密钥对空投SOL,函数定义如下:

代码

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}`);
  }
};

在这里,我们在devnet上向自己空投1 SOL(1 * LAMPORTS_PER_SOL)。我们检索latestBlockHash以便将区块哈希和最后有效区块高度传递给connection.confirmTransaction(),从而确认我们已将1 SOL空投到正确的地址。

代码

const seed: Uint8Array = ed.sign(Buffer.from(SEED_MESSAGE, "utf-8"), keyPair.secretKey.slice(0, 32));

Elusiv需要输入种子以确保某些操作是确定的,因此我们导入并使用SEED_MESSAGESEED_MESSAGE是一个必需的签名消息,因为它用于生成Elusiv种子;它使得应用程序能够使用其关联的秘密值解密你的私人资产,以便你可以跟踪你的资金。我们使用Buffer.from()以utf-8编码创建SEED_MESSAGE的新缓冲区(原始二进制数据结构)。我们需要使用slice,因为Solana的密钥对类型使得前32个字节为私钥,后32个字节为公钥。因此,我们用我们的私钥签名所需的种子消息。

代码

const elusiv: Elusiv = await Elusiv.getElusivInstance(seed, keyPair.publicKey, connection, CLUSTER);

在这里,我们定义了elusiv,其是Elusiv的一个实例,使用我们的seedkeyPair.publicKeyconnectionCLUSTER。因此,我们正在使用devnet RPC、我们的公钥和刚刚签署的长消息在devnet上创建一个Elusiv实例。

代码

const balance: bigint = await elusiv.getLatestPrivateBalance("LAMPORTS");

接着我们获取我们的私有余额 - 在Elusiv池中我们所持有的Lamports数量,这个值以BigInt的形式返回。main函数的其余部分被封装在一个try-catch-finally块中,其定义如下:

代码

try {
    if (balance > BigInt(0)) {
      const signature = await send(elusiv, recipientPublicKey, 1 * LAMPORTS_PER_SOL, "LAMPORTS");

      console.log(`Sent with signature ${signature.signature}`);
    } else {
      // 代码的其余部分
    }
}

如果我们有私有余额,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实例、接收者的公钥、发送的金额和代币类型(不必是Lamports,也可以是其他代币比如USDC、USDT、mSOL、BONK和SAMO)作为参数。我们首先使用elusiv.buildSendTx()的返回值设置txtbuildSendTx()是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}`);

我们向用户记录他们没有私有余额,并表示我们正在给他们充值。然后,我们利用Elusiv实例上的buildTopUpTx()方法构建充值交易。我们传入1 SOL的值和Lamports的代币类型,因为我们正在发送SOL。接着,我们用性对交易的秘钥对进行部分签名,因为我们是用公钥充值。然后,我们通过elusiv.sendElusivTx(topUpTxData)启动构建的交易,并记录交易签名。

try-catch-finally块的剩余部分如下:

代码

} catch (e) {
    console.error(`Error sending SOL via Elusiv: ${e}`);
} finally {
    console.log("Exiting program...");
    process.exit(0);
}

如果我们在通过Elusiv发送SOL的过程中遇到任何错误,我们通过console.error()记录错误。随后,通过process.exit(0)退出程序。

为了启动整个过程,我们在文件的底部调用main()函数。

使用Light Protocol私下发送SOL

完整代码

代码

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函数 - Light的初始化过程比我们的Elusiv实现要复杂得多。

代码

const initializeSolanaWallet = async (): Promise => {
  const wallet = Keypair.generate();

  console.log("Wallet initialized");
  return wallet;
};

在这里,我们创建了initializeSolanaWallet函数,它使用Keypair.generate()方法生成新的Solana钱包。

代码

const requestAirdrop = async (connection: Connection, publicKey: PublicKey): Promise => {
  await airdropSol({
    connection,
    lamports: 2e9,
    recipientPublicKey: publicKey,
  });
  console.log("Airdrop requested...");
};

与Elusiv不同,Light在其库中提供了airdropSol函数,用于向特定公钥空投Lamports。我们创建了requestAirdrop这个实用函数以向传入的publicKey发送SOL。

代码

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;
};

为了通过Light发送屏蔽的SOL,我们需要使用TestRelayer模块设置测试中继器。中继器在Light生态系统中扮演了重要角色,因为它们是将ZK证明(证明状态转换)的网络服务器,从客户端转发到相应的链上程序。它们还会为交易费用支付费用,以换取成功证明验证后的费用。费用为100k Lamports,我们用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;
};

接下来,我们通过命名为initializeLightProvider()的函数初始化Light Provider。此函数设置一个提供程序,并将其与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函数的功能不言而喻 - 它使用传入的lightProvider为Light Protocol初始化用户。

代码

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}`);
};

这很可能是最重要的实用函数,因为它实际上执行了一次私有转账。在这里,我们从user向指定的接收者testRecipientPublicKey私下转账1 SOL。

代码

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,并初始化中继器和Light Provider。接着,我们将钱包初始化为Light用户并屏蔽我们的SOL。我们会创建一个测试接收者,向其空投一些SOL,将其初始化为Light用户,并私下向他们发送1 SOL。就像我们的Elusiv代码一样,我们将逻辑放入try-catch-finally块中,以捕获任何错误并在完成后自动退出程序。

你可能会注意到,与Elusiv不同,这里我们将连接设置为"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"
  }
}

结论

恭喜你!在本教程中,我们深入探讨了隐私的复杂性、其重要性,以及如何使用Elusiv和Light Protocol私下交易SOL。通过Elusiv,我们了解了如何检索私有余额、私下发送SOL,甚至在私有余额耗尽或不存在时进行自动补充。通过Light Protocol,我们了解了如何设置其复杂的、高度模块化的初始化过程,屏蔽我们的SOL并执行私有转账。

那么,你可能在想我应该使用哪个?这主要取决于你的需求!使用Elusiv时,我们的代码关注的是从共享池中补充私有余额并私下发送交易。该脚本与一个定义良好的智能合约进行交互,该合约控制Elusiv池以实现此目标。使用Light Protocol,我们建立了一个更灵活、但相对复杂的初始化流程,以创建一个工作流来管理多个用户与隐私保护的Solana程序之间的交互。使用Light,我们也可以在需要时创建自己的自定义私人Solana程序(PSP)。二者在文档和特定实现等领域都有利弊,不过都有隐私和安全交易的目标。如果你的应用程序需要更细粒度的隐私特性控制,并希望对应用程序状态的特定部分进行加密,请考虑使用Light Protocol。或者,如果你正在构建更简单的应用并希望在devnet/mainnet上为代币转账添加隐私层,Elusiv可能是更好的选择。

本文中所提供的技术、代码和见解为构建以隐私为中心的Solana应用程序提供了强有力的起点。区块链技术中隐私的重要性不容小觑。随着对去中心化系统的需求不断增长,隐私使能特性的需要逐步增强,以赋权用户、保护他们的权利,并实现早期区块链技术的真正目标。隐私很重要,而现在,借助Elusiv和Light Protocol,你可以让Solana成为更加安全的区块链!

如果你读到了这里,感谢你!

额外资源 / 深入阅读

  • 原文链接: helius.dev/blog/privacy-...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Helius
Helius
https://www.helius.dev/