Michael.W谈hyperledger Fabric第26期-详细带读Fabric的源码11-世界状态在代码级别的行为实现

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神,热爱一切被梨花照过的姑娘。
以下是我个人的公众号,如果有技术问题可以关注我的公众号来跟我交流。
同时我也会在这个公众号上每周更新我的原创文章,喜欢的小伙伴或者老伙计可以支持一下!
如果需要转发,麻烦注明作者。十分感谢!
后现代泼痞浪漫主义奠基人
公众号名称:后现代泼痞浪漫主义奠基人

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
相比其他语言的频繁更新,C++语言标准已经有十多年没有真正更新过了。而上一次 标准制定,正是面向对象概念开始盛行的时候。较之基于过程的编程语言,基于面向对 象、泛型编程等概念的C++无疑是非常先进的,而C++98标准的制定以及各种符合标准的 编译器的出现,又在客观上推动了编程方法的革命。因此在接下来的很多年中,似乎人人 都在学习并使用C++。商业公司在邀请C++专家为程序员讲课,学校里老师在为学生绘声 绘色地讲解面向对象编程,C++的书籍市场也是百花齐放,论坛、BBS的C++板块则充斥 了大量各种关于C++的讨论。随之而来的,招聘启事写着“要求熟悉C++编程”,派生与继 承成为了面试官审视毕业生基础知识的重点。凡此种种,不一而足。于是C++语言“病毒 性”地蔓延到各种编程环境,成为了使用最为广泛的编程语言之一。 十来年的时光转瞬飞逝,各种编程语言也在快马加鞭地向前发展。如今流行的编程语 言几乎无一不支持面向对象的概念。即使是古老的语言,也通过了制定新标准,开始支持 面向对象编程。随着Web开发、移动开发逐渐盛行,一些新流行起来的编程语言,由于在 应用的快速开发、调试、部署上有着独特的优势,逐渐成为了这些新领域中的主流。不过 这并不意味着C++正在失去其阵地。身为C的“后裔”,C++继承了C能够进行底层操作的特 性,因此,使用C/C++编写的程序往往具有更佳的运行时性能。在构建包括操作系统的各 种软件层,以及构建一些对性能要求较高的应用程序时,C/C++往往是最佳选择。更一般 地讲,即使是由其他语言编写的程序,往往也离不开由C/C++编写的编译器、运行库、操 作系统,或者虚拟机等提供支持。因此,C++已然成为了编程技术中的中流砥柱。如果用 个比喻来形容C++,那么可以说这十来年C++正是由“锋芒毕露”的青年时走向“成熟稳 重”的中年时。 不过十来年对于编程语言来说也是个很长的时间,长时间的沉寂甚至会让有的人认 为,C++就是这样一种语言:特性稳定,性能出色,易于学习而难于精通。长时间使用 C++的程序员也都熟悉了C++毛孔里每一个特性,甚至是现实上的一些细微的区别,比如 各种编译器对C++扩展的区别,也都熟稔于心。于是这个时候,C++11标准的横空出世, 以及C++之父Bjarne Stroustrup的一句“看起来像一门新语言”的说法,无疑让很多C++程序 员有些诚惶诚恐:C++11是否又带来了编程思维的革命?C++11是否保持了对C++98及C 的兼容?旧有的C++程序到了C++11是否需要被推倒重来? 事实上这些担心都是多余的。相比于C++98带来的面向对象的革命性,C++11带来的 却并非“翻天覆地”式的改变。很多时候,程序员保持着“C++98式”的观点来看待C++11代 码也同样是合理的。因为在编程思想上,C++11依然遵从了一贯的面向对象的思想,并深 入加强了泛型编程的支持。从我们的观察来看,C++11更多的是对步入“成熟稳重”的中年 时的C++的一种改造。比如,像auto类型推导这样的新特性,展现出的是语言的亲和 力;而右值引用、移动语义的特性,则着重于改变一些使用C++程序库时容易发生的性能 不佳的状况。当然,C++11中也有局部的创新,比如lambda函数的引入,以及原子类型的 设计等,都体现了语言与时俱进的活力。语言的诸多方面都在C++11中再次被锤炼,从而 变得更加合理、更加条理清晰、更加易用。C++11对C++语言改进的每一点,都呈现出了 经过长时间技术沉淀的编程语言的特色与风采。所以从这个角度上看,学习C++11与 C++98在思想上是一脉相承的,程序员可以用较小的代价对C++的知识进行更新换代。而 在现实中,只要修改少量已有代码(甚至不修改),就可以使用C++11编译器对旧有代码 进行升级编译而获得新标准带来的好处,这也非常具有实用性。因此,从很多方面来看, C++程序员都应该乐于升级换代已有的知识,而学习及使用C++11也正是大势所趋。 在本书开始编写的时候,C++11标准刚刚发布一年,而本书出版的时候,C++11也只 不过才诞生了两年。这一两年,各个编译器厂商或者组织都将支持C++11新特性作为了一 项重要工作。不过由于C++11的语言特性非常的多,因此本书在接近完成时,依然没有一 款编译器支持C++11所有的新特性。但从从业者的角度看,C++11迟早会普及,也迟早会 成为C++程序员的首选,因此即使现阶段编译器对C++新特性的支持还不充分,但还是有 必要在这个时机推出一本全面介绍C++11新特性的中文图书。希望通过这样的图书,使得 更多的中国程序员能够最快地了解C++11新语言标准的方方面面,并且使用最新的C++11 编译器来从各方面提升自己编写的C++程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值