概述
Michael.W谈hyperledger Fabric第26期-详细带读Fabric的源码11-世界状态在代码级别的行为实现
- 1 世界状态的底层依赖
- 2 上层智能合约的键值对与底层存储的键值对是如何相关联的?
- 3 如何持久化区块的状态信息
- 4 如何记录最新存储的区块状态
1 世界状态的底层依赖
关于世界状态的定义可以去我之前写的一篇帖子《Michael.W谈hyperledger Fabric第3期-关于Fabric你所需要知道的基本知识二》中看一下。
世界状态存储在状态数据库中,我在这里只谈默认的levelDB做状态数据库的情况,其源代码文件文职:core/ledger/kvledger/txmgmt/statedb/stateleveldb/stateleveldb.go
2 上层智能合约的键值对与底层存储的键值对是如何相关联的?
这涉及到数据隔离问题。
levelDB没有多数据库特性,所有的KV键值对都是存储在同样的地方。这就为不同通道的数据存储带来了问题,比如两个通道中都有kv对:michael=10,那到底要如何进行数据隔离呢?
看一下源代码:
// 组合件的分割符[1]
var compositeKeySep = []byte{0x00}
var lastKeyIndicator = byte(0x01)
var savePointKey = []byte{0x00}
- [1] Fabric将智能合约键、智能合约所在的通道名和智能合约的名字拼接在一起,形成一个组合键。然后用这个组合键作为底层数据库存储的唯一键。看一下组合键的过程:
这样就保证底层数据库中的键值唯一性。再来看看反解组合键的过程:func constructCompositeKey(ns string, key string) []byte { // 参数ns是前面讲过的namespace,由账本ID(对应通道)和链码名组合而成。 // 将namespace和key值拼接在一起,中间插入分隔符compositeKeySep return append(append([]byte(ns), compositeKeySep...), []byte(key)...) }
func splitCompositeKey(compositeKey []byte) (string, string) { // 以compositeKeySep为基准将组合件分成两个部分 split := bytes.SplitN(compositeKey, compositeKeySep, 2) return string(split[0]), string(split[1]) }
3 如何持久化区块的状态信息
不多bb,直接看源码:
// 该方法实现了VersionedDB接口
func (vdb *versionedDB) ApplyUpdates(batch *statedb.UpdateBatch, height *version.Height) error {
// 关于参数:
// batch:前面在交易验证中返回的待更新的状态集
// height:当前的区块高度
// 实例化了一个levelDB数据库的事务,可以理解为一个新的状态容器
dbBatch := leveldbhelper.NewUpdateBatch()
// 获取到待更新的状态集中包含的所有namespace
namespaces := batch.GetUpdatedNamespaces()
// 遍历这些待更新的namespace
for _, ns := range namespaces {
// 根据namespace得到这个namespace对应的需要更新的具体状态
// updates类型为:map[string]*VersionedValue
updates := batch.GetUpdates(ns)
for k, vv := range updates {
// 形成组合组合键值。可见key值就包含在namespace对应的需要更新的具体状态中
compositeKey := constructCompositeKey(ns, k)
logger.Debugf("Channel [%s]: Applying key=[%#v]", vdb.dbName, compositeKey)
// 如果对应的更新状态为空,表示这是一个删除操作。
if vv.Value == nil {
dbBatch.Delete(compositeKey)
} else {
// 将这个组合键和对应的状态更新到新的状态容器dbBatch中
dbBatch.Put(compositeKey, statedb.EncodeValue(vv.Value, vv.Version))
}
}
}
// 当整个更新状态集都被处理完成之后,将0x00作为键值,当前区块高度作为值,也添加到状态容器dbBatch中。用来标识这个区块结束
dbBatch.Put(savePointKey, height.ToBytes())
// 对以上事务进行提交
if err := vdb.db.WriteBatch(dbBatch, true); err != nil {
return err
}
return nil
}
4 如何记录最新存储的区块状态
func (vdb *versionedDB) GetLatestSavePoint() (*version.Height, error) {
// 返回数据库中savePointKey对应的值(即区块高度),这个是被序列化过的
versionBytes, err := vdb.db.Get(savePointKey)
if err != nil {
return nil, err
}
if versionBytes == nil {
return nil, nil
}
// 反序列化的区块高度
version, _ := version.NewHeightFromBytes(versionBytes)
// 返回区块高度对象
return version, nil
}
参数中的区块高度类的定义如下:
type Height struct {
// 区块号
BlockNum uint64
// 区块中的交易编号
TxNum uint64
}
剩下还有一些辅助功能的方法:
// 处理富文本查询的方法。由于levelDB无法做这类查询,所以直接返回错误。如果想进行富文本查询,请换couchDB来做数据库
func (vdb *versionedDB) ExecuteQuery(namespace, query string) (statedb.ResultsIterator, error) {
return nil, errors.New("ExecuteQuery not supported for leveldb")
}
// 查询单个键对应的值
func (vdb *versionedDB) GetState(namespace string, key string) (*statedb.VersionedValue, error) {
logger.Debugf("GetState(). ns=%s, key=%s", namespace, key)
compositeKey := constructCompositeKey(namespace, key)
dbVal, err := vdb.db.Get(compositeKey)
if err != nil {
return nil, err
}
if dbVal == nil {
return nil, nil
}
val, ver := statedb.DecodeValue(dbVal)
return &statedb.VersionedValue{Value: val, Version: ver}, nil
}
// 查询多个键对应值。逻辑很简单
func (vdb *versionedDB) GetStateMultipleKeys(namespace string, keys []string) ([]*statedb.VersionedValue, error) {
vals := make([]*statedb.VersionedValue, len(keys))
for i, key := range keys {
val, err := vdb.GetState(namespace, key)
if err != nil {
return nil, err
}
vals[i] = val
}
return vals, nil
}
关于操作levelDB的源代码逻辑不是很难,基本仔细看一遍就能明白!
ps:
本人热爱图灵,热爱中本聪,热爱V神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
公众号名称:后现代泼痞浪漫主义奠基人
最后
以上就是会撒娇月光为你收集整理的Michael.W谈hyperledger Fabric第26期-详细带读Fabric的源码11-世界状态在代码级别的行为实现的全部内容,希望文章能够帮你解决Michael.W谈hyperledger Fabric第26期-详细带读Fabric的源码11-世界状态在代码级别的行为实现所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复