跳到主要内容

状态

需要先了解下 SMT

状态的实现

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

two_level_state.png

状态是 AccountAddressAccountState 的映射,随着新的 Block 的执行 , AccountState 会变化,由于需要保留历史状态相关的证明,这里使用了 SMT 这一数据结构。 为了方便,这里把 AccountAddressAccountState 的状态称为 Account SMT。 如图中显示这里 (AccountAddress, AccountState) 存储在 Account SMTLeaf 节点上。 在图中就是 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 SMTAccountState 状态分为 Code SMTRescoure SMT,这样每次执行新 Block 后,先存储 Code SMTResource SMTAccountState 只用存储 Code SMTResource SMT 的根节点。 在图中分别为 Code_Root_Hash1Res_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 对应存储的 KvStoreChainStateDB 的成员 state_tree 对应 AccountAddress -> AccountStateAccount SMT 树。 StateTree 的成员 storage_root_hash 对应 SMT 的根节点。 AccountState 有2个 HashValue 元素,对应图中 Code_Root_Hash1Res_Root_Hash1,也就是 AccountStateObject 中的 code_treeresource_tree 的根节点。 AccountStateObject 的成员 code_tree 对应 ModuleName -> Vec<u8>Code SMT 。。 AccountStateObject 的成员 resource_tree 对应 StructTag -> Vec<u8>Resource SMTModuleName 类似于 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 对应 KvStorestate_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);

一种 valueNONE 的特殊的 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>>>;

查找 StateTreekey 对应 value。 先检查 StateTree 成员 update 缓存中是否有,有直接返回。 没有按照 SMTget_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_hashSMT

get_account_state_object

 fn get_account_state_object(
&self,
account_address: &AccountAddress,
create: bool,
) -> Result<Arc<AccountStateObject>>;

获取 account_address 对应的 Code SMTResource SMT。 先根据 Account SMT 获取 account_address 对应的 AccountState, 再根据 AccountStateCode_Root_HashRes_Root_Hash 生成对应的 Code SMTResource 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 TreeResource Tree, AccessPath 成员 path 对应为 ModuleNameCode Tree 提供查找, 否则 Resource Tree 提供查找。

幂等性相关

StateTree 幂等性由 SMT 保证, ChainStateDB 幂等性由 StateTree 保证。

资源

draw.io