首页 前端知识 小试牛刀-区块链Solana多签账户

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

2024-08-16 22:08:02 前端知识 前端哥 663 505 我要收藏

目录

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 创建新账户

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

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

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

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

export async function createMultisigAccount(payer:string,multisigPubKey:PublicKey): Promise<Transaction> {
    const fromPubkey =new PublicKey(payer);
    //签名者列表
    const signers = [fromPubkey,multisigPubKey];
    // 获取多签账户所需的最小余额以免租金
    const lamports = await getMinimumBalanceForRentExemptMultisig(connection);
    // 创建多签账户的指令
    const createAccountInstruction = SystemProgram.createAccount({
        //这个账户的拥有者
        fromPubkey: fromPubkey,
        //多签账户公钥
        newAccountPubkey: multisigPubKey,
        //免租金最小金额
        lamports,
        space: 355, // 多签账户的空间大小
        //属于的程序地址,这里使用SPL程序
        programId: TOKEN_PROGRAM_ID,

    });
    // 初始化多签账户的指令
    const initializeMultisigInstruction = createInitializeMultisigInstruction(
        //多签账户
        multisigPubKey,
        //签名者列表
        signers,
        //最小签名数量,这里是2
        requiredSigners,
        //属于的程序地址,这里使用SPL程序
        TOKEN_PROGRAM_ID
    );
    //创建transaction并添加创建和初始化指令
    const transaction = new Transaction()
        .add(createAccountInstruction,initializeMultisigInstruction);
    //最近的区块hash
    transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
    //付费者为拥有者
    transaction.feePayer=fromPubkey;
    console.log("签名者:"+signers)
    console.log("multisigPubKey:"+multisigPubKey)
    return transaction;
}

3.1.3 账户的签名

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

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

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

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

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

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

    //钱包签名
    const result =await sendWcTransaction(signClient,session,transaction);
    //本地签名
    const signature = nacl.sign.detached(transaction.serializeMessage(), bs58.decode(privateKey));
    transaction.addSignature(
        transaction.feePayer,
        //这里返回的是字符串,所以需要转换
        Buffer.from(bs58.decode(result.signature))
    )
    //<>!
    transaction.addSignature(
        multisigPubKey,
        //本地获取的是uint8array,无需转换
        Buffer.from(signature)
    )

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

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

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

export async function sendTransaction(transaction:Transaction){
    try{
        const txId =await connection.sendRawTransaction(transaction.serialize())
        return txId;
    }catch(error){
        console.error('Exception occurred:', error);
        return 'error';
    }
}

链上截图:

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

3.2 代币转移操作

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

export async function getMultisigTransferTransaction(
    ownerPublicKey:string,
    senderPublicKey: string,
    privateKey: string,
    drawPublicKey: string,
    tokenAmount: number,
    drawData: string
): Promise<[Transaction,Keypair]> {
    const ownerPubKey = new PublicKey(ownerPublicKey);
    const senderPubkey = new PublicKey(senderPublicKey);
    const drawPubkey = new PublicKey(drawPublicKey);
    const tokenMintAddress = BOGGY_TOKEN_MINT;

    
    const senderKeypair = Keypair.fromSecretKey(Uint8Array.from(bs58.decode(privateKey)));
    try {
        const sourceTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,senderPubkey);
        const destTokenAccount = await getAssociatedTokenAddress(tokenMintAddress,drawPubkey);
        const transferInstruction = createTransferInstruction(
            sourceTokenAccount,
            destTokenAccount,
            senderPubkey,
            tokenAmount * 1e9,
            [senderPubkey,ownerPubKey],
            TOKEN_PROGRAM_ID
        );
        // 构造 Memo 指令
        const memoInstruction = new TransactionInstruction({
            keys: [
            { pubkey: ownerPubKey,  isSigner: true , isWritable:false },
            { pubkey: senderPubkey, isSigner: true, isWritable: false }
            ],
            programId: MEMO_PROGRAM_ID,
            data: Buffer.from(drawData,'utf-8')
        });
        // 创建 compute unit price 指令,提高交易速度
        const computeUnitPriceInstruction = ComputeBudgetProgram.setComputeUnitPrice({
            microLamports: 7500,
        });
        const computeUnitLimitInstruction=ComputeBudgetProgram.setComputeUnitLimit({
            units:200000
        })
        const transaction = new Transaction().add(computeUnitPriceInstruction,computeUnitLimitInstruction,transferInstruction,memoInstruction);
        transaction.recentBlockhash = (await connection.getLatestBlockhash()).blockhash;
        transaction.feePayer = ownerPubKey;

        return [transaction,senderKeypair];
    } catch (error) {
        console.error('Exception occurred:', error);
    }
}

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

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

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

转载请注明出处或者链接地址:https://www.qianduange.cn//article/15795.html
评论
发布的文章

HTML5学习记录

2024-04-29 12:04:01

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!