状态
需要先了解下 SMT 。
状态的实现
在 Starcoin 中,区块(Block)由一些交易(Transaction)组成,对一个区块的执行就是对区块内交易的执行。 交易执行的结果由状态表示。这里采用的全局状态,包含链上所有账户的最新状态和历史状态。 状态实际上就是账户地址(AccountAddress)到账户状态(AccountState)的映射。 Starcoin 中状态是一颗2级的 SMT 树,如下图所示。

状态是 AccountAddress 到 AccountState 的映射,随着新的 Block 的执行 , AccountState 会变化,由于需要保留历史状态相关的证明,这里使用了 SMT 这一数据结构。
为了方便,这里把 AccountAddress 到 AccountState 的状态称为 Account SMT。
如图中显示这里 (AccountAddress, AccountState) 存储在 Account SMT 的 Leaf 节点上。
在图中就是 Account SMT 的根节点是 Root_Hash,这里对应 BlockHeader 中的 state_root。
在 Starcoin 中 AccountAddress 不同于以太坊个人账户和合约账户是分开的,合约是部署在个人账户下。AccountState 分为两部分,分别是合约代码 ( Code ) 和 资源 ( Resource )。
Code 就是账号下合约代码, Resource 就是类似有哪些 Token (比如 STC )。
新 Block 执行, Code 状态和 Resource 状态都可能会改变。
这样 Code 状态用了一棵 SMT 记为 Code SMT, Resource 状态用了一棵 SMT 记为 Resource SMT。
AccountState 状态分为 Code SMT 和 Rescoure SMT,这样每次执行新 Block 后,先存储 Code SMT 和 Resource SMT, AccountState 只用存储 Code SMT 和 Resource SMT 的根节点。
在图中分别为 Code_Root_Hash1 和 Res_Root_Hash1。
当然这应该是一个事务的过程。
状态在 Starcoin 中对应代码
在 Starcoin 中 Account SMT 定义为 ChainStateDB
pub struct ChainStateDB {
store: Arc<dyn StateNodeStore>,
///global state tree.
state_tree: StateTree<AccountAddress>,
cache: Mutex<LruCache<AccountAddress, CacheItem>>,
updates: RwLock<HashSet<AccountAddress>>,
}
pub struct StateTree<K: RawKey> {
storage: Arc<dyn StateNodeStore>,
storage_root_hash: RwLock<HashValue>,
updates: RwLock<BTreeMap<K, Option<Blob>>>,
cache: Mutex<StateCache<K>>,
}
pub enum DataType {
CODE,
RESOURCE,
}
pub struct AccountState {
storage_roots: Vec<Option<HashValue>>,
}
struct AccountStateObject {
code_tree: Mutex<Option<StateTree<ModuleName>>>,
resource_tree: Mutex<StateTree<StructTag>>,
store: Arc<dyn StateNodeStore>,
}
这里 store 对应存储的 KvStore, ChainStateDB 的成员 state_tree 对应 AccountAddress -> AccountState 的 Account SMT 树。
StateTree 的成员 storage_root_hash 对应 SMT 的根节点。
AccountState 有2个 HashValue 元素,对应图中 Code_Root_Hash1 和 Res_Root_Hash1,也就是 AccountStateObject 中的 code_tree 和 resource_tree 的根节点。
AccountStateObject 的成员 code_tree 对应 ModuleName -> Vec<u8> 的 Code SMT 。。
AccountStateObject 的成员 resource_tree 对应 StructTag -> Vec<u8> 的 Resource SMT 。
ModuleName 类似于 Account1 (对应 starcoin-framework 里面的模块。
struct_tag 类似于 0x00000000000000000000000000000001::Account::Account。
可见 Starcoin 中状态是一个2级的 SMT 结构,ChainStateDB 对应一级 SMT , AccountStateObject 对应两个二级 SMT 。
StateTree API 说明
new
pub fn new(state_storage: Arc<dyn StateNodeStore>, state_root_hash: Option<HashValue>) -> Self;
创建 StateTree, 这里 state_storage 对应 KvStore,state_root_hash 对应 SMT 的根节点, 后面的更新查找都是基于这个根节点的 SMT 。
put
pub fn put(&self, key: K, value: Vec<u8>);
pub struct StateTree<K: RawKey> {
storage: Arc<dyn StateNodeStore>,
storage_root_hash: RwLock<HashValue>,
updates: RwLock<BTreeMap<K, Option<Blob>>>,
cache: Mutex<StateCache<K>>,
}
将要更新的 (key, value) 缓存在 StateTree 的成员 updates ,这里并没有参与 SMT 的计算
remove
pub fn remove(&self, key: &K);
一种 value 为 NONE 的特殊的 put 操作。
commit
pub fn commit(&self) -> Result<HashValue>;
将缓存于 updates 的 (key, value) 取出,参与到 SMT 更新中,并计算新的根节点。
这里会将新产生的 SMT Node, 生成的 (Hash(Node), Encode(Node)) 缓存在 StateTree 的
成员 cache 中。
flush
pub fn flush(&self) -> Result<()>;
将 StateTree 成员 cache 中的 (Hash(Node), Encode(Node)) 写到 KvStore 中。
get
pub fn get(&self, key: &K) -> Result<Option<Vec<u8>>>;
查找 StateTree 中 key 对应 value。 先检查 StateTree 成员 update 缓存中是否有,有直接返回。
没有按照 SMT 中 get_with_proof 流程。
get_with_proof
pub fn get_with_proof(&self, key: &K) -> Result<(Option<Vec<u8>>, SparseMerkleProof)>;
获取 proofproof 待补充
ChainStateDB API 说明
new
pub fn new(store: Arc<dyn StateNodeStore>, root_hash: Option<HashValue>) -> Self;
类似 StateTree new 生成根节点为 root_hash 的 SMT。
get_account_state_object
fn get_account_state_object(
&self,
account_address: &AccountAddress,
create: bool,
) -> Result<Arc<AccountStateObject>>;
获取 account_address 对应的 Code SMT 和 Resource SMT。
先根据 Account SMT 获取 account_address 对应的 AccountState,
再根据 AccountState 的 Code_Root_Hash 和 Res_Root_Hash 生成对应的 Code SMT 和 Resource SMT。
get_with_proof
fn get_with_proof(&self, access_path: &AccessPath) -> Result<StateWithProof>;
获取 proof, proof 待补充
StateView 相关
pub enum DataPath {
Code(#[schemars(with = "String")] ModuleName),
Resource(#[schemars(with = "String")] StructTag),
}
pub struct AccessPath {
pub address: AccountAddress,
pub path: DataPath,
}
fn get(&self, access_path: &AccessPath) -> Result<Option<Vec<u8>>>;
给 vm 提供状态信息查询接口, AccountAddress 查找对应的 AccountStateObject,再生成
Code Tree 和 Resource Tree, AccessPath 成员 path 对应为 ModuleName 由 Code Tree 提供查找,
否则 Resource Tree 提供查找。
幂等性相关
StateTree 幂等性由 SMT 保证, ChainStateDB 幂等性由 StateTree 保证。