文章详细介绍了Solana区块链中数据存储的机制,特别是如何通过账户和程序来管理和初始化存储数据。文章通过对比以太坊的存储方式,深入探讨了Solana的存储模型和使用Rust语言进行账户初始化的具体步骤。
迄今为止,我们的所有教程都没有使用“存储变量”或存储任何永久性内容。
在 Solidity 和 Ethereum 中,另一种更为特殊的设计模式是 SSTORE2 或 SSTORE3,其中数据存储在另一个智能合约的字节码中。
在 Solana 中,这并不是一种特殊的设计模式,而是一种常规做法!
回想一下,除非程序被标记为不可变,否则我们可以随意更新 Solana 程序的字节码(如果我们是原始部署者)。
Solana 使用相同的机制来存储数据。
以太坊中的存储槽实际上是一个巨大的键值存储:
{
key: [smart_contract_address, storage slot]
value: 32_byte_slot // (例如: 0x00)
}
Solana 的模型类似:它是一个巨大的键值存储,其中“键”是一个 base 58 编码的地址,而值是一个可以大到 10MB 的数据块(或可选择不存储任何内容)。它可以这样可视化:
{
// key 是一个 base58 编码的 32 字节序列
key: ETnqC8mvPRyUVXyXoph22EQ1GS5sTs1zndkn5eGMYWfs
value: {
data: 020000006ad1897139ac2bdb67a3c66a...
// 其他字段省略
}
}
在 Ethereum 中,一个智能合约的字节码和存储变量是分别存储的,即它们是不同索引的,必须使用不同的 API 加载。
以下图表展示了以太坊如何维护状态。每个帐户都是 Merkle 树中的一个叶子。请注意,“存储变量”被存储在智能合约的帐户(帐户 1)“内部”。 在 Solana 中,一切都是帐户,可以潜在地保存数据。有时我们将一个帐户称为“程序帐户”,另一个帐户称为“存储帐户”,但唯一的区别在于可执行标志是否设置为真,以及我们打算如何使用帐户的数据字段。
下面,我们可以看到 Solana 存储是一个从 Solana 地址到帐户的巨大键值存储:
想象一下,如果以太坊没有存储变量,智能合约默认是可变的。要存储数据,你必须创建其他“智能合约”,并将数据保存在它们的字节码中,然后在必要时修改它。这是 Solana 的一种心理模型。
另一种心理模型是 Unix 中的一切都是文件,有些文件是可执行的。可以将 Solana 帐户视为文件。它们保存内容,但它们也具有元数据,指示谁拥有该文件,它是否可执行,等等。
在 Ethereum 中,存储变量直接与智能合约耦合。除非智能合约通过公共变量、delegatecall 或某些设置方法授予读写访问权限,否则存储变量默认只能由单个合约写入或读取(尽管任何人都可以从链下读取存储变量)。在 Solana 中,所有“存储变量”可以被任何程序读取,但只有其所有者程序可以写入它。
存储与程序“绑定”的方式是通过所有者字段。
在下图中,我们看到帐户 B 是由程序帐户 A 所拥有。我们知道 A 是程序帐户,因为“可执行”设置为 true
。这表明 B 的数据字段将存储 A 的数据:
在 Ethereum 中,我们可以直接写入一个我们之前未使用的存储变量。然而,Solana 程序需要一个显式的初始化事务。也就是说,我们必须先创建帐户,然后才能向其中写入数据。
可以在一个事务中初始化并写入一个 Solana 帐户——然而这会引入安全问题,这将使讨论变得复杂。如果我们现在处理它,暂时只需说 Solana 帐户必须在使用之前进行初始化。
让我们将以下 Solidity 代码翻译为 Solana:
contract BasicStorage {
Struct MyStorage {
uint64 x;
}
MyStorage public myStorage;
function set(uint64 _x) external {
myStorage.x = _x;
}
}
将单个变量包裹在一个结构中可能看起来很奇怪。
但在 Solana 程序中,特别是在 Anchor 中,所有存储,或者说帐户数据,都被视为结构。原因在于帐户数据的灵活性。由于帐户是可以非常大的数据块(最大可达 10MB),我们需要某种“结构”来解读数据,否则它只是一个毫无意义的字节序列。
在后台,Anchor 在我们尝试读取或写入数据时会对帐户数据进行反序列化和序列化为结构。
如上所述,我们需要在使用 Solana 帐户之前对其进行初始化,因此在实现 set()
函数之前,我们需要编写 initialize()
函数。
让我们创建一个名为 basic_storage
的新 Anchor 项目。
以下是我们编写的最小代码,以初始化一个仅保存一个数字 x
的 MyStorage
结构。 (请参见底部代码中的结构 MyStorage
):
use anchor_lang::prelude::*;
use std::mem::size_of;
declare_id!("GLKUcCtHx6nkuDLTz5TNFrR4tt4wDNuk24Aid2GrDLC6");
#[program]
pub mod basic_storage {
use super::*;
pub fn initialize(ctx: Context<Initialize>) -> Result<()> {
Ok(())
}
}
#[derive(Accounts)]
pub struct Initialize<'info> {
#[account(init,
payer = signer,
space=size_of::<MyStorage>() + 8,
seeds = [],
bump)]
pub my_storage: Account<'info, MyStorage>,
#[account(mut)]
pub signer: Signer<'info>,
pub system_program: Program<'info, System>,
}
#[account]
pub struct MyStorage {
x:...
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!