Tx.origin、msg.sender 和 onlyOwner 在 Solana 中:识别调用者

  • RareSkills
  • 发布于 2024-02-23 20:22
  • 阅读 228

本文详细比较了Solidity和Solana智能合约中的msg.sendertx.origin概念,并提供了在Solana中如何实现类似功能的代码示例。文章还介绍了如何在Solana中处理多个签名者以及如何实现onlyOwner功能。

tx.origin msg.sender onlyOwner在Solana中

在Solidity中,msg.sender 是一个全局变量,表示调用或发起智能合约函数调用的地址。全局变量 tx.origin 是签署交易的钱包。

在Solana中,没有与 msg.sender 相当的概念。

tx.origin 有类似的概念,但你需要注意的是,Solana交易可以有多个签名者,因此我们可以将其视为具有“多个 tx.origins”。

要在Solana中获取 “tx.origin” 地址,你需要通过将签名者账户添加到函数上下文中并在调用函数时传递调用者的账户来进行设置。

让我们看看如何在Solana中访问交易签名者的地址的示例:

use anchor_lang::prelude::*;

declare_id!("Hf96fZsgq9R6Y1AHfyGbhi9EAmaQw2oks8NqakS6XVt1");

#[program]
pub mod day14 {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let the_signer1: &mut Signer = &mut ctx.accounts.signer1;

        // 函数逻辑....

        msg!("签名者1: {:?}", *the_signer1.key);

        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    #[account(mut)]
    pub signer1: Signer<'info>,
}

在上面的代码片段中,Signer<'info> 用于验证 Initialize<'info> 账户结构中的 signer1 账户已签署该交易。

initialize 函数中,signer1 账户从上下文中可变引用并被赋值给 the_signer1 变量。

然后最后,我们使用 msg! 宏记录 signer1 的公钥(地址),并传入 *the_signer1.key,这会解引用并访问通过 the_signer1 指向的实际值的 key 字段或方法。

接下来是为上述程序编写测试:

describe("Day14", () => {
  // 将客户端配置为使用本地集群。
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.Day14 as Program<Day14>;

  it("由单个签名者签名", async () => {
    // 在这里添加你的测试。
    const tx = await program.methods.initialize().accounts({
      signer1: program.provider.publicKey
    }).rpc();

    console.log("签名者1: ", program.provider.publicKey.toBase58());
  });
});

在测试中,我们将我们的钱包账户作为签名者传递给 signer1 账户,然后调用初始化函数。之后,我们将钱包账户记录到控制台,以验证其与程序中的一致性。

练习: 你注意到了在运行测试后 shell_1(命令终端)和 shell_3(日志终端)中的输出有什么吗?

多个签名者

在Solana中,我们也可以有多个签名者签署一个交易,你可以把它看作是将一堆签名捆绑在一起并发送到一个交易中。一个用例是在一个交易中进行多签交易。

要做到这一点,我们只需在程序的账户结构中添加更多的 Signer 结构,然后确保在调用函数时传递必要的账户:

use anchor_lang::prelude::*;

declare_id!("Hf96fZsgq9R6Y1AHfyGbhi9EAmaQw2oks8NqakS6XVt1");

#[program]
pub mod day14 {
    use super::*;

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        let the_signer1: &mut Signer = &mut ctx.accounts.signer1;
        let the_signer2: &mut Signer = &mut ctx.accounts.signer2;

        msg!("签名者1: {:?}", *the_signer1.key);
        msg!("签名者2: {:?}", *the_signer2.key);

        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize<'info> {
    pub signer1: Signer<'info>,
    pub signer2: Signer<'info>,
}

上面的示例与单个签名者示例基本相同,有一个显著的区别。在这种情况下,我们向 Initialize 结构添加了另一个签名者账户(signer2),并在 initialize 函数中记录了两个签名者的公钥。

调用 initialize 函数与多个签名者是不同的,下面的测试显示如何调用多个签名者的函数:


describe("Day14", () => {
  // 将客户端配置为使用本地集群。
  anchor.setProvider(anchor.AnchorProvider.env());

  const program = anchor.workspace.Day14 as Program<Day14>;

  // 生成一个签名者来调用我们的函数
  let myKeypair = anchor.web3.Keypair.generate();

  it("由多个签名者签名", async () => {...

剩余50%的内容订阅专栏后可查看

点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
RareSkills
RareSkills
https://www.rareskills.io/