Solana程序是可升级的,并且没有构造函数

本文详细讲解了如何在Solana上使用Anchor框架部署程序,并解释了Solana程序的可变性和与Ethereum的差异。通过代码示例和命令行操作,展示了程序的部署、升级和测试过程。

显示 Anchor 部署的英雄图片

在本教程中,我们将从幕后看看 Anchor 如何部署 Solana 程序。

让我们看看 Anchor 在我们运行 anchor init deploy_tutorial 时为我们创建的测试文件:

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

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

  it("Is initialized!", async () => {
    // 在此添加你的测试。
    const tx = await program.methods.initialize().rpc();
    console.log("你的交易签名", tx);
  });
});

它生成的启动程序应该很熟悉:

use anchor_lang::prelude::*;

declare_id!("Fg6PaFpoGXkYsidMpWTK6W2BeZ7FEfcYkg476zPFsLnS");

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

    pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
        Ok(())
    }
}

#[derive(Accounts)]
pub struct Initialize {}

上述程序在哪里以及何时被部署?

合同唯一合理的部署地点是在测试文件中的这一行:

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

但这并没有意义,因为我们本该期望那是一个异步函数。

Anchor 在后台默默地部署程序。

Solana 程序默认没有构造函数

对于来自其他面向对象语言的人来说,这可能看起来不寻常。Rust 没有对象或类。

在以太坊智能合约中,构造函数可以配置存储或设置字节码和不可变变量。

那么“部署步骤”到底在哪里?

(如果你仍在运行 Solana 验证器和 Solana 日志,建议你重启并清除这两个终端)

让我们进行常规设置。创建一个名为 program-deploy 的新 Anchor 项目,并确保在其他终端中运行验证器和日志。

与其运行 anchor test,不如在终端中运行以下命令:

anchor deploy

Anchor 测试的截图

在上面的日志截图中,我们可以看到程序被部署的时刻。

现在,令人感兴趣的部分是。再次运行 anchor deploy

Anchor 测试升级后的截图

程序被部署到同一地址,但这次是 升级,而非重新部署。

程序 ID 没有改变,程序被覆盖了

Solana 程序默认是可变的

这可能对以太坊开发者来说是一个震惊,因为在以太坊中假设是不可变的。

如果作者可以随意更改程序,那么这个程序有什么意义?确实可以使 Solana 程序不可变。假设作者会先部署可变版本,随着时间的推移且未发现任何错误后,再将其重新部署为不可变。

在功能上,这与管理员控制的代理没有任何区别,后期所有者将所有权放弃给零地址。但可以说,Solana 模型更干净,因为以太坊代理可能会出问题。

另一个含义是:Solana 不需要 delegatecall,因为它不需要

在 Solidity 合约中,delegatecall 的主要用途是通过发出 delegatecalls 到新实现合约来升级代理合约的功能能力。然而,由于 Solana 中程序的字节码可以升级,因此无需调用实现合约。

另一个推论是:Solana 没有 Solidity 那种不可变变量(只能在构造函数中设置的变量)

在不重新部署程序的情况下运行测试

由于 Anchor 默认重新部署程序,让我们演示如何在不重新部署的情况下运行测试。

将测试更改为以下内容:

import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";

import fs from 'fs'
let idl = JSON.parse(fs.readFileSync('target/idl/deployed.json', 'utf-8'))

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

  // 更改为你的 programID
  const programID = "6p29sM4hEK8ZFT5AvsGJQG5nKUtHBKs13iVL6juo5Uqj";
  const program = new Program(idl, programID, anchor.getProvider());

  it("Is initialized!", async () => {
    // 在此添加你的测试。
    const tx = await program.methods.initialize().rpc();
    console.log("你的交易签名", tx);
  });
});

在你运行测试之前,建议清除 Solana 日志的终端并重启 solana-test-validator。

现在,使用以下测试命令:

anchor test --skip-local-validator --skip-deploy

现在查看日志终端:

跳过部署的 Anchor 测试截图

我们看到初始化指令已被执行,但程序既没有被部署也没有被升级,因为我们在 anchor test 中使用了 --skip-deploy 参数。

练习:要查看程序的字节码实际上发生了变化,部署两个打印不同 msg! 值的合约。具体步骤如下:

  1. lib.rs 中更新 initialize 函数,包括一个 msg! 语句,将字符串写入日志。
  2. anchor deploy
  3. anchor test --skip-local-validator --skip-deploy
  4. 检查日志以查看记录的消息
  5. 重复步骤 1 – 4,但更改 msg! 中的字符串
  6. 验证程序 ID 没有改变

你应该观察到消息字符串的变化,但程序 ID 保持不变。

总结

  • Solana 没有构造函数,程序“只是部署”。
  • Solana 没有不可变变量。
  • Solana 没有 delegatecall,程序可以“被更新”。

了解更多

本教程是 Solana 课程 的一部分。

最初发布于 2024 年 2 月 12 日

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

0 条评论

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