文章详细介绍了 Solidity 中的块变量在 Solana 中的类比实现,包括 block.timestamp、block.number、block.coinbase 等,并提供了具体的代码示例和说明。
今天我们将覆盖 Solidity 中所有区块变量的类比。并不是所有的变量都有一一对应的类比。在 Solidity 中,我们有以下常用的区块变量:
以及一些较少使用的变量:
我们假设你已经知道它们的作用,但如果你需要复习,可以查看 Solidity 全局变量文档。
通过利用 Clock sysvar 中的 unix_timestamp
字段,我们可以访问 Solana 的区块时间戳。
首先,我们初始化一个新的 Anchor 项目:
anchor init sysvar
将初始化函数替换为以下内容:
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
let clock: Clock = Clock::get()?;
msg!(
"区块时间戳: {}",
// 获取 block.timestamp
clock.unix_timestamp,
);
Ok(())
}
Anchor 的预导入模块包含 Clock 结构,默认情况下会自动导入:
use anchor_lang::prelude::*;
有些令人困惑的是,unix_timestamp
返回的类型是 i64
,而不是 u64
,这意味着它支持负数,尽管时间不能为负数。然而,时间间隔可以是负数。
现在让我们创建一个程序,使用 Clock sysvar
中的 unix_timestamp
告诉我们当前是星期几。
chrono crate 提供了 Rust 中的日期和时间操作功能。
在程序目录 ./sysvar/Cargo.toml
中将 chrono
crate 添加为依赖项:
[dependencies]
chrono = "0.4.31"
在 sysvar
模块中导入 chrono
crate:
// ...其他代码
#[program]
pub mod sysvar {
use super::*;
use chrono::*; // 新增这一行
// ...
}
现在,我们在程序下方添加这个函数:
pub fn get_day_of_the_week(
_ctx: Context<Initialize>) -> Result<()> {
let clock = Clock::get()?;
let time_stamp = clock.unix_timestamp; // 当前时间戳
let date_time = chrono::NaiveDateTime::from_timestamp_opt(time_stamp, 0).unwrap();
let day_of_the_week = date_time.weekday();
msg!("星期几是: {}", day_of_the_week);
Ok(())
}
我们将从 Clock sysvar
获得的当前 unix 时间戳作为参数传递给 from_timestamp_opt
函数,返回一个包含日期和时间的 NaiveDateTime
结构。然后,我们调用星期几方法以根据传递的时间戳获取当前星期几。
并更新我们的测试:
it("获取星期几", async () => {
const tx = await program.methods.getDayOfTheWeek().rpc();
console.log("你的交易签名", tx);
});
再次运行测试并获取以下日志:
在插槽 36 中执行交易:
签名: 5HVAjmo85Yi3yeQX5t6fNorU1da4H1zvgcJN7BaiPGnRwQhjbKd5YHsVE8bppU9Bg2toF4iVBvhbwkAtMo4NJm7V
状态: Ok
日志信息:
程序 H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy 调用 [1]
程序日志: 指令: GetDayOfTheWeek
程序日志: 星期几是: Wed
程序 H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy 消耗了 1597 个计算单位(总计 200000 个计算单位)
注意日志中的“星期几是: Wed
”。
Solana 有一个“插槽编号”的概念,团队相关于“区块编号”,但并不相同。关于这二者的区别将在以下教程中讨论,因此我们暂时推迟关于如何获取“区块编号”的完整讨论。
在以太坊中,“块 coinbase”代表已成功挖掘一个区块的矿工的地址,而 Solana 使用一种基于领导者的共识机制,它结合了历史证明(PoH)和权益证明(PoS),取消了挖矿的概念。相反,一名 区块或插槽领导者 被任命来验证交易并在某些时间间隔内提出区块,依据一种被称为 领导者计划 的系统。该计划确定了在特定时间内的区块生产者。
然而,目前没有特定的方法可以在 Solana 程序中访问区块领导者的地址。
我们包括这一节以保持完整性,但这将很快被弃用。
对于不感兴趣的读者,可以跳过这一部分而没有后果。
Solana 有一个 RecentBlockhashes sysvar,它保存活动的最近区块哈希及其相关的费用计算器。然而,这个 sysvar
已被 弃用,并将在未来的 Solana 版本中不再支持。RecentBlockhashes sysvar
没有像 Clock sysvar
那样提供获取方法。然而,缺少此方法的 sysvar
可以使用 sysvar_name::from_account_info
访问。
我们还将介绍一些新语法,这将在稍后说明。现在,请将其视为模板:
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK: 只读
pub recent_blockhashes: AccountInfo<'info>,
}
以下是我们如何在 Solana 获取最新的区块哈希:
use anchor_lang::{prelude::*, solana_program::sysvar::recent_blockhashes::RecentBlockhashes};
// 替换程序 ID
declare_id!("H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy");
#[program]
pub mod sysvar {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
// 最近区块哈希
let arr = [ctx.accounts.recent_blockhashes.clone()];
let accounts_iter = &mut arr.iter();
let sh_sysvar_info = next_account_info(accounts_iter)?;
let recent_blockhashes = RecentBlockhashes::from_account_info(sh_sysvar_info)?;
let data = recent_blockhashes.last().unwrap();
msg!("最近区块哈希是: {:?}", data.blockhash);
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
/// CHECK: 只读
pub recent_blockhashes: AccountInfo<'info>,
}
测试文件:
import * as anchor from "@coral-xyz/anchor";
import { Program } from "@coral-xyz/anchor";
import { Sysvar } from "../target/types/sysvar";
describe("sysvar", () => {
// 配置客户端以使用本地集群。
anchor.setProvider(anchor.AnchorProvider.env());
const program = anchor.workspace.Sysvar as Program<Sysvar>;
it("已初始化!", async () => {
// 在这里添加你的测试。
const tx = await program.methods
.initialize()
.accounts({
recentBlockhashes: anchor.web3.SYSVAR_RECENT_BLOCKHASHES_PUBKEY,
})
.rpc();
console.log("交易哈希:", tx);
});
});
我们运行测试并获得以下日志:
日志信息:
程序 H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy 调用 [1]
程序日志: 指令: Initialize
程序日志: 最近区块哈希是: erVQHJdJ11oL4igkQwDnv7oPZoxt88j7u8DCwHkVFnC
程序 H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy 消耗了 46181 个计算单位(总计 200000 个计算单位)
程序 H52ppiSyiZyYVn1Yr9DgeUKeChktUiPwDfuuo932Uqxy 成功
我们可以看到最新的区块哈希。请注意,由于我们正在部署到本地节点,因此我们获得的区块哈希是本地节点的,而不是 Solana 主网络的。
在时间结构方面,Solana 在固定时间线中运作,划分为插槽,每个插槽是分配给领导者提出区块的时间部分。这些插槽进一步组织为纪元,在这些预定义期间内,领导者计划保持不变。
Solana 每区块计算单位 限制为 48000000。每个交易默认限制为 200,000 计算单位,尽管可以提高到 1.4 million 计算单位(我们将在后续教程中讨论这一点,但你可以 在这里查看示例)。
从 Rust 程序中无法访问此限制。
在以太坊中,basefee 是动态的,根据 EIP-1559 的规定;它是前一个区块利用率的函数。而在 Solana 中,交易的基本价格是静态的,因此不需要这样的变量。
区块难度是与工作量证明(PoW)区块链相关的概念。而 Solana 基于历史证明(PoH)与权益证明(PoS)共识机制运作,这不涉及区块难度的概念。
Solana 没有链 ID,因为它不是 EVM 兼容的区块链。block.chainid 是 Solidity 智能合约知道它们在测试网、L2、主网或其他 EVM 兼容链上的方式。
Solana 为 Devnet、Testnet 和 Mainnet 运行独立集群,但程序没有机制来知道它们所处的哪个集群。但你可以在部署时使用 Rust cfg 功能根据各个集群调整程序代码。以下是 根据集群更改程序 ID 的示例。
本教程是 Solana 课程 的一部分。
最初发布于 2024年2月18日
- 原文链接: rareskills.io/post/solan...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!