当前位置:   article > 正文

小试牛刀-区块链Solana多签账户

小试牛刀-区块链Solana多签账户

目录

1.什么是多签账户

2.多签账户的特点

2.1 多个签名者

2.2 最小签名要求

2.3 常见应用场景

3.多签账户实现

3.1 账户的创建

3.1.1 创建新账户

3.1.2 获取创建和初始账户事务

3.1.3 账户的签名

3.2 代币转移操作


Welcome to Code Block's blog

本篇文章主要介绍了

[小试牛刀-Solana多签账户]
❤博主广交技术好友,喜欢文章的可以关注一下❤

1.什么是多签账户

        在 Solana 区块链中,多签账户(Multisig Account)是一种智能合约账户,允许多个签名者共同管理和控制账户上的资产或操作。这种机制增强了账户的安全性和灵活性,特别适用于需要多个权限共同批准的操作场景,如资产管理、资金转移、或项目治理。                               

2.多签账户的特点

2.1 多个签名者

        多签账户通常指定多个签名者(即一组公钥),这些签名者可以是个人账户、智能合约账户或其他实体。

        每个签名者都有权批准账户的操作,但只有在达到预定义的签名数量时,操作才会生效。

2.2 最小签名要求

        多签账户设置了最小签名数(M)的要求,即在 N 个签名者中,至少需要 M 个签名才能执行账户的任何操作。

        这种机制通常被称为 M-of-N 签名方案,例如 2-of-3 签名意味着需要三个人中的两个人签署才能批准操作。

2.3 常见应用场景

        项目治理:在去中心化自治组织(DAO)中,使用多签账户来管理资金或项目决策。

        资产托管:增加资金管理的安全性,避免单一账户被攻击或失控。

        合作项目:需要多个合作方共同控制账户,确保所有操作都有多个利益相关方的同意。

3.多签账户实现

3.1 账户的创建

3.1.1 创建新账户

        首先创建一个新的账户,作为要创建多签账户,同时需要其公钥和私钥参与签名,实现代码如下:

  1. function generateSolanaWalletNacl(): { publicKey: string, privateKey: string } {
  2. const keyPair = nacl.sign.keyPair();
  3. const publicKeyBase58 = bs58.encode(Buffer.from(keyPair.publicKey)).toString();
  4. const privateKeyBase58 = bs58.encode(Buffer.from(keyPair.secretKey)).toString();
  5. return { publicKey: publicKeyBase58, privateKey: privateKeyBase58 };
  6. }

3.1.2 获取创建和初始账户事务

        这里multisigPubKey为上面创建的新账户的公钥,payer我们使用另一个账户(已有或创建)的公钥,保证payer中存在可以创建账户的费用,然后获取创建和初始化事务,代码如下:

  1. export async function createMultisigAccount(payer:string,multisigPubKey:PublicKey): Promise<Transaction> {
  2. const fromPubkey =new PublicKey(payer);
  3. //签名者列表
  4. const signers = [fromPubkey,multisigPubKey];
  5. // 获取多签账户所需的最小余额以免租金
  6. const lamports = await getMinimumBalanceForRentExemptMultisig(connection);
  7. // 创建多签账户的指令
  8. const createAccountInstruction = SystemProgram.createAccount({
  9. //这个账户的拥有者
  10. fromPubkey: fromPubkey,
  11. //多签账户公钥
  12. newAccountPubkey: multisigPubKey,
  13. //免租金最小金额
  14. lamports,
  15. space: 355, // 多签账户的空间大小
  16. //属于的程序地址,这里使用SPL程序
  17. programId: TOKEN_PROGRAM_ID,
  18. });
  19. // 初始化多签账户的指令
  20. const initializeMultisigInstruction = createInitializeMultisigInstruction(
  21. //多签账户
  22. multisigPubKey,
  23. //签名者列表
  24. signers,
  25. //最小签名数量,这里是2
  26. requiredSigners,
  27. //属于的程序地址,这里使用SPL程序
  28. TOKEN_PROGRAM_ID
  29. );
  30. //创建transaction并添加创建和初始化指令
  31. const transaction = new Transaction()
  32. .add(createAccountInstruction,initializeMultisigInstruction);
  33. //最近的区块hash
  34. transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  35. //付费者为拥有者
  36. transaction.feePayer=fromPubkey;
  37. console.log("签名者:"+signers)
  38. console.log("multisigPubKey:"+multisigPubKey)
  39. return transaction;
  40. }

3.1.3 账户的签名

        账户创建时需要签名者列表中的用户分别对transaction进行签名,(是对同一transaction进行签名),如果是拥有者公私钥在本地,可以将公私钥生成Keypair,可使用以下方式签名:

  1. const signer1=Keypair.generate()
  2. const signer2=Keypair.generate()
  3. //签名者1和签名者2 的keypair
  4. transaction.sign(signer1,signer2)

        但若是外部签名,如walletconnect连接的钱包用户作为签名者,则需要使用以下方式进行签名(重点):

首先,获取本地公私钥的签名,注意这里并没有对transaction进行签名,只是获取签名:

  1. export async function getLocalSign(transaction:Transaction,signer:Keypair){
  2. const signature = nacl.sign.detached(transaction.serializeMessage(), signer.secretKey);
  3. return signature;
  4. }

然后,walletconnet用户连接钱包对创建和初始化账户事务签名后,会获取到一个signature,将这个signature和本地公私钥获取的signature添加到transaction中,代码如下:

  1. //钱包签名
  2. const result =await sendWcTransaction(signClient,session,transaction);
  3. //本地签名
  4. const signature = nacl.sign.detached(transaction.serializeMessage(), bs58.decode(privateKey));
  5. transaction.addSignature(
  6. transaction.feePayer,
  7. //这里返回的是字符串,所以需要转换
  8. Buffer.from(bs58.decode(result.signature))
  9. )
  10. //<>!
  11. transaction.addSignature(
  12. multisigPubKey,
  13. //本地获取的是uint8array,无需转换
  14. Buffer.from(signature)
  15. )

为保证签名的有效性,需要先验证签名,验证方式如下:

  1. //验证签名
  2. const valid = transaction.verifySignatures();
  3. if (!valid){
  4. //验证未通过则不进行操作,或打印
  5. return ;
  6. }

验证通过后,就可以通过以下代码将要进行的创建事务提交到链上:

  1. export async function sendTransaction(transaction:Transaction){
  2. try{
  3. const txId =await connection.sendRawTransaction(transaction.serialize())
  4. return txId;
  5. }catch(error){
  6. console.error('Exception occurred:', error);
  7. return 'error';
  8. }
  9. }

链上截图:

可以看到这里创建并初始化了一个Multisig账户,即多签账户,并且Signers有两个地址,这样就完成了多签账户的创建.              

3.2 代币转移操作

        多签账户操作的转移和创建类似,同样需要两个或多个账户对transaction进行签名,获取transaction返回的[transaction,senderKeypair]分别是事务和本地的keyPair,方便后续签名使用,代码如下:

  1. export async function getMultisigTransferTransaction(
  2. ownerPublicKey:string,
  3. senderPublicKey: string,
  4. privateKey: string,
  5. drawPublicKey: string,
  6. tokenAmount: number,
  7. drawData: string
  8. ): Promise<[Transaction,Keypair]> {
  9. const ownerPubKey = new PublicKey(ownerPublicKey);
  10. const senderPubkey = new PublicKey(senderPublicKey);
  11. const drawPubkey = new PublicKey(drawPublicKey);
  12. const tokenMintAddress = BOGGY_TOKEN_MINT;
  13. const senderKeypair = Keypair.fromSecretKey(Uint8Array.from(bs58.decode(privateKey)));
  14. try {
  15. const sourceTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,senderPubkey);
  16. const destTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,drawPubkey);
  17. const transferInstruction = createTransferInstruction(
  18. sourceTokenAccount,
  19. destTokenAccount,
  20. senderPubkey,
  21. tokenAmount * 1e9,
  22. [senderPubkey,ownerPubKey],
  23. TOKEN_PROGRAM_ID
  24. );
  25. // 构造 Memo 指令
  26. const memoInstruction = new TransactionInstruction({
  27. keys: [
  28. { pubkey: ownerPubKey, isSigner: true , isWritable:false },
  29. { pubkey: senderPubkey, isSigner: true, isWritable: false }
  30. ],
  31. programId: MEMO_PROGRAM_ID,
  32. data: Buffer.from(drawData,'utf-8')
  33. });
  34. // 创建 compute unit price 指令,提高交易速度
  35. const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({
  36. microLamports: 7500,
  37. });
  38. const computeUnitLimitInstruction=ComputeBudgetProgram.setComputeUnitLimit({
  39. units:200000
  40. })
  41. const transaction = new Transaction().add(computeUnitPriceInstruction,computeUnitLimitInstruction,transferInstruction,memoInstruction);
  42. transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
  43. transaction.feePayer = ownerPubKey;
  44. return [transaction,senderKeypair];
  45. } catch (error) {
  46. console.error('Exception occurred:', error);
  47. }
  48. }

签名和发送的步骤和创建时签名是一样的,这里就不重复写了.

区块链内容感兴趣可以查看我的专栏:小试牛刀-区块链

感谢您的关注和收藏!!!!!!

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop】
推荐阅读
相关标签
  

闽ICP备14008679号