Solana账号模型
00 min
2023-11-23
2023-11-24
type
status
date
slug
summary
tags
category
icon
password

账号模型概括

与以太坊类似,Solana 也是基于账户模型的区块链。通过将任意状态存储于链上账户并同步复制给集群中的所有节点,可以创建复杂而强大的去中心化应用程序。
Solana 提供了一套不同于以太坊的账户模型,账户定义的字段如表 1-1 所示。Solana 的账户可以分为可执行账户和不可执行账户。
  • 可执行账户:存储不可变的数据,主要用于存储程序的 BPF 字节码。
  • 不可执行账户:存储可变的数据,主要用于存储程序的状态。
表 1-1 账户定义字段
字段
描述
lamports
账户余额
owner
账户所有者
executable
是否为可执行账户
data
账户存储的数据
rent_epoch
Solana链上程序的部署是按其账户大小进行定期收费的,如果账户无法支付租金,系统将清除该账号
我们知道以太坊上每个智能合约的代码和状态都存储在同一个账户中,而 Solana 链上程序是只读或无状态的,即程序的账户(可执行账户)只存储 BPF 字节码,不存储任何状态,程序会把状态存储在其他独立的账户(不可执行账户)中。为了区分某个账户是用作哪个程序的状态存储,每个账户都指定了一个程序作为其所有者。程序可以读取其不作为所有者的账户中的状态,但只有作为所有者的程序才能修改账户中的状态,任何其他程序所做的修改都会被还原并导致交易失败。
 

与以太坊比较账号模型

Solana运行时中帐户模型的高级概述,称为Sealevel
在以太坊和Solana等基于账户的链上,可以存储任意状态来创建复杂而强大的去中心化应用程序。然而,EVM和Sealevel之间存在一个主要区别,即如何存储状态。在以太坊上,只有智能合约有存储空间,它们对存储空间具有完全控制权。在Solana上,任何账户都可以存储状态,但智能合约的存储空间仅用于存储不可变的字节码。在Solana上,智能合约的状态实际上完全存储在其他账户中。为了确保合约无法修改另一个合约的状态,每个账户都会分配一个拥有状态更改独占控制权的拥有者合约。
为了直观地展示这种差异,这里是以两种平台为例的代币合约的存储情况。
在以太坊上,代币合约通常有一个映射,用于定义每个所有者地址的余额:
Token Contract(合约地址)
Storage
Owner Address(所有者)
Tokens
0xa0b869… (USDC)
mapping in 0xa0b869…
USDC
0xdac17f… (USDT)
mapping in 0xdac17f…
USDT
0xdac17f… (USDT)
mapping in 0xdac17f…
0xabd99e…
USDT
 
在Solana上,代币余额通常存储在唯一的账户中,存储账户地址由所有者账户地址派生得到。
Token Contract
Storage Address
Owner Address
Balance
EPjFWdd5… (USDC)
~ 1B USDC
EPjFWdd5… (USDC)
~ 23M USDC
Es9vMFrz… (USDT)
~ 17M USDT
在以太坊的EVM中,有两种类型的账户。基本账户只存储以太币余额,代码账户是链上智能合约的基础。每个代码账户除了存储EVM代码外,都有一个关联的存储映射,可以读取和写入任意数据。EVM为每个合约提供了读取和写入其自身存储的指令,但无法读取其他合约的存储。
 
在Solana的Sealevel中,也有两种类型的账户:可执行账户和不可执行账户。与EVM不同的是,这两种账户都可以存储数据。在Sealevel中,可执行账户是不可变的,可以存储自己的可执行字节码或存储可变可执行字节码的账户的代理地址。由于可执行账户是不可变的,因此其应用程序状态必须存储在不可执行账户中。在EVM中,合约只能读取和写入自己的存储。在Sealevel中,任何合约都可以读取或写入任何账户的数据。然而,运行时强制执行只有账户的“所有者”才能修改账户的规定。任何其他程序的更改将被重置,并导致交易失败。
 
让我们仔细看看在每个平台上,帐户中到底存储了什么。
在EVM中,一个“基本”帐户非常简单。它包含一个nonce,每次该帐户发送交易时都会增加该nonce,还有一个balance字段,用于跟踪帐户剩余的wei。nonce字段有一个非常重要的目的。它防止任何交易在EVM中被处理两次。这是因为每个交易都会指定发送者的nonce,并且必须与存储中的发送者的nonce匹配才能被执行。由于nonce在每次交易后都会增加,因此无法运行相同的交易两次。如果没有这个nonce概念,交易可以被多次处理,这通常对用户来说是非常不想要的结果。
Field
Description
nonce
从此帐户发送的交易数量。
balance
账号拥有的WEI数量(余额)
在EVM中,“代码帐户”是所有活动发生的地方。由于代码帐户不能用于发送交易,nonce字段表示该帐户已创建的合约数量。类似于基本帐户,代码帐户可以持有wei,并使用EVM指令将wei发送到其他帐户。代码帐户还存储与EVM字节码相关的不可变哈希,以及跟踪存储中所有数据更改的哈希。实际的EVM字节码和存储数据与帐户存储分离,但经常本地缓存以供快速访问。
Field
Description 描述
nonce
此帐户创建的合约数量
balance
账号拥有的WEI数量(余额)
codeHash
此帐户的不可变 EVM 代码的哈希值
storageRoot
以编码此帐户的存储内容为根节点的Merkle树的256位哈希。
 
在Sealevel中,与EVM的主要相似之处是lamports字段,该字段跟踪每个帐户的余额。存在与EVM的nonce字段相似的地方。这是因为Solana上处理的nonce方式不同TODO。在Sealevel帐户中需要注意的关键字段是owner字段。该字段存储链上程序的地址,并表示允许对帐户的数据进行写入并从中减去其lamport余额的链上程序。程序拥有的帐户的概念大致对应于EVM中的帐户特定存储映射。但是,它具有额外的灵活性,允许任何链上程序读取不拥有权的数据。
Field
Description
lamports
该帐户拥有的lamports数量
owner
该帐户的程序所有者
executable
该帐户是否可以处理指令
data
该帐户存储的原始数据字节数组
rent_epoch
该帐户将欠租的下一个epoch
 

帐户存储

在EVM中,只有“代码帐户”具有存储空间。该存储空间实现为一个具有256位键空间的映射,其中每个键映射到256位值。对于非代码帐户,storageRoot被设置为特殊的“空”哈希,表示该帐户没有存储空间。 在Solana Sealevel VM中,所有帐户都可以存储数据。但是,可执行帐户数据仅用于处理事务的不可变字节码。那么,智能合约开发人员可以存储数据的地方呢?他们可以将数据存储在非可执行帐户中,这些帐户由可执行帐户所有。开发人员可以创建新的帐户,并将分配的拥有者设置为可执行帐户的地址,以存储数据。
 

帐户签名权限

问题:实际上谁有权限创建用于存储程序状态的帐户?
Solana帐户只有在帐户的签名权限批准更改后才能分配给程序。通常,签名权限仅表示相应的私钥必须对交易进行签名。
 
问题:当程序希望创建帐户时会发生什么?
由于程序执行状态对每个验证器都是公开的并且每个验证器都知道,所以无法秘密地发送消息以创建帐户。为了允许程序创建帐口令,Sealevel运行时提供了一个系统调用,允许程序从自己的地址派生一个地址,程序可以自由声明该地址进行签名。
 
问题:如果每个帐户都需要一个签名,如何在一次交易中创建多个帐户?
答案:Solana交易指定一个签名列表,并包含一个1232字节的条目,其中包含尽可能多的签名。这些签名必须经过验证,否则交易将被拒绝。每个签名也会增加费用。
 
问题:签名用于做什么?
答案:系统帐户必须对大多数系统指令进行签名。这包括将帐户分配给新程序,分配存储和转账。
 
问题:在EVM中有什么等效方法?
Ethereum交易有一个字段,其中包含一个签名,必须与发送消息的地址进行验证。任何额外的签名都必须作为交易二进制数据传递,并使用secpk2561加密函数进行验证,该函数使用EVM预编译程序以本机方式执行。
 
问题:Solana VM能否验证Ethereum的secpk2561指令?
答案:是的,它已添加以支持Wormhole Ethereum / Solana桥接。
 

帐户所有者

Sealevel中的每个帐户都有指定的所有者。由于帐户可以通过接收lamports来创建,因此在创建帐户时必须分配默认所有者。Sealevel中的默认所有者称为“System Program”。系统程序主要负责帐户创建和lamport传输。
帐户类型
Name
Owner
Description
Sysvar
Sysvar
用于加载区块链状态(如最新区块和当前租金费用)的帐户
Native Program
Native Loader
用于指示Native程序(如System、Stake和Vote程序)的帐户,这些程序不使用BPF字节码
BPF Program
BPF Loader
用于处理BPF字节码的帐户

Sealevel Runtime 帐户规则

Immutability 不可变性

1.可执行帐户是完全不可变的。
以上规则优先于所有后续规则。这意味着程序不能向可执行帐户添加lamports,并且永远无法修改或删除其数据。
 

Data Allocation 数据分配

  1. 只有系统程序才可以更改帐户数据的大小。
  1. 新分配的帐户数据始终为零。
  1. 无法减小帐户数据的大小。
目前,程序无法增加其拥有的帐户的数据大小。如果需要更多数据,它们必须将数据从一个帐户复制到较大的帐户。因此,大多数程序不会在帐户数据中存储动态大小的映射和数组。相反,它们将这些数据存储在许多帐户中。例如,EVM映射的每个键值对可以存储在一个新帐户中。
 

Data 数据

  1. 只有帐户的所有者可以修改其数据。
  1. 只有在数据被清零的情况下才能将帐户分配给新所有者。
以上规则保证程序始终可以完全信任存储在其拥有的帐户中的数据。数据已被清零或以前由程序修改。这些保证共同作用以形成与EVM帐户存储机制相同的信任保证。
在Sealevel中,可执行字节码存储在帐户数据中,而在EVM中,代码存储在单独的数据存储中。
 

Balance 余额

  1. 只有帐户的所有者可以减去其lamports
  1. 任何程序帐户都可以向帐户添加lamports
这意味着一旦帐户被程序所有,私钥将无法用于与系统程序一起转账lamports,因为系统程序不再被允许从帐户中发送lamports
 

Ownership 所有权

  1. 只有帐户的所有者才能指定新所有者
由于系统程序默认拥有所有帐户,系统程序最常用于将帐户指定给其他程序。
 

Rent 租金

  1. 每epoch(~2天)收取租金,并根据帐户大小确定租金
  1. 拥有足够余额以支付2年租金的帐户可免收费用。
由于租金费用会慢慢耗尽帐户余额,因此程序必须考虑是否要求将其用于存储的帐户必须免租金。如果不要求帐户免租金,它们最终可能会耗尽 lamports 并被运行时删除。删除后,可以重新创建帐户。因此,依赖特定数据存储的帐户应在写入数据之前要求帐户免租金。
 

Zero Balance 零余额

  1. 余额为零的帐户将在处理事务结束时被删除。
  1. 在事务期间可能会创建余额为零的临时帐户。
关闭帐户的程序应考虑帐户数据在事务完全处理后才会被删除。仅将lamports减小到零不足以删除帐户。这意味着如果一个程序从另一个程序中调用,它可能会再次调用相同的尚未删除的帐户。
 

New Executable Accounts 新可执行帐户

  1. 只有指定的加载程序可以更改帐户的可执行状态
在Sealevel中,可执行帐户的创建方式与普通帐户相同,但其所有者必须设置为加载程序。加载程序处理事务将字节码写入帐户数据,只有在程序通过加载程序的验证过程后,才会将其标记为可执行。
由于可执行帐偶是不可变的,因此在帐户标记为可执行时,其中的任何lamports将被冻结。因此,lamports余额应不超过用于免租金存储的最低余额。
 
💡
欢迎您在底部评论区留言,一起交流~
 
 

Comments