Hyperledger Fabric从源码分析账本

参考资料:

什么是账本?

Fabric 中的账本存储了有关业务对象的重要事实信息,既包括了对象属性当前的值,也包括产生这些当前值的交易的历史。这两个概念可以用下面的术语来概括,即账本包括以下两个主要内容:

  • 世界状态(代表着账本状态的当前值)
  • 区块链(代表着账本的交易记录)

一个通道持有一本账本,但是该通道上的所有 peer 节点都有该账本的副本,即逻辑上账本只有一份,但是物理上账本存储在每个 peer 节点的本地数据库中,且每个 peer 节点的账本保持一致(通过排序共识)。

下面来讲一下关于世界状态与区块链的相关概念

世界状态

世界状态将账本的当前值保存为唯一的账本状态。程序通常会需要账本对象当前的值,如果遍历整个区块链来计算对象的当前值会很麻烦——那么我们就可以从世界状态中获取当前值。

当我们调用链码发起查询操作的时候,查的就是世界状态当中的值,因为查询操作不会对账本的当前状态造成任何改变。

而当我们调用链码发起写操作(更改,删除,增加)时,应用程序提交一个交易提案到背书节点,背书节点背书之后返回,应用程序再将交易发送给排序节点排序。之后排序节点将交易打包成区块,将区块分发给各个组织的 peer 节点。此时,如果该交易是一笔无效交易(比如验证签名失败,背书失败等等原因),那么该笔交易只会被存储到本地的交易记录中,但是不会更改账本的世界状态——即无效交易不会导致账本世界状态的改变。

另外,世界状态中的每个状态都有一个版本号,版本号供 Fabric 内部使用,并且每次状态更改时版本号都会发生递增。举个例子,其当前状态版本号都是0,当发生了一次对账本对象的写操作的时候,账本的状态就发生了变化,这样的新的世界状态就产生了,其中的状态的版本号就是1。

每当更新状态时,都会检查该状态的版本,以确保当前状态与背书时的版本相匹配。这就确保了世界状态是按照预期进行更新的,没有发生并发更新,是并发安全的。

首次创建账本时,世界状态是空的。因为区块链上记录了所有代表有效世界状态更新的交易,所以任何时候都可以从区块链中重新生成世界状态。这样一来就变得非常方便,例如,创建节点时会自动生成世界状态。此外,如果某个节点发生异常,重启该节点时能够在接受交易之前重新生成世界状态。

区块链

区块链上存储的是账本的交易历史记录,就好比支付宝上的本月账单可以查看本月的每笔交易,不管是收入还是支出。

区块链的结构是一群相互链接的区块的序列化日志,其中每个区块都包含一系列交易,各项交易代表了一个对世界状态进行的操作。区块链中的这些区块是以后指向链接起来的,即后面的区块会去指向前面的区块,以此形成的一个后指向的链状结构。

区块链总是被作为一个文件来实现,而与之相反的是,世界状态被作为一个数据库来实现。这是一个明智的设计,因为区块链数据结构高度偏向于非常小的一组简单操作。

通俗地来说,一个通道拥有一个账本,一个账本有一条后指向的区块链,那么可以这么理解,一个通道有一条区块链,为了方便理解它们的概念,可以暂时简单地这么认为——一条区块链就是一个通道。

区块链的第一个区块被称作创世区块,它包含着整个通道的配置交易信息,不包含任何用户交易。

关于区块的相关概念可以看我之前写的文章——Hyperledger Fabric从源码分析区块,我这里就不过多阐述了。

命名空间

前面我们讨论账本时,似乎它只包括一个世界状态和一条区块链,但这显然过于简单化了。实际上,每个链码都有自己的世界状态,并且与所有其他链码的世界状态分离。世界状态位于一个命名空间中,因此只有位于同一链码中的智能合约才能访问一个给定的命名空间。

区块链没有命名空间。它包含来自许多不同链码命名空间的交易。

上面都是我结合自己已有的知识与官方文档,总结出来的一些概念性的东西,这有助于我们先从本质上理解账本,世界状态及区块链的相关概念,下面我会从源码角度做一些简单的分析。

源码分析

首先,之前说到,每个 peer 节点都拥有一份账本的副本,当 peer 节点启动时,会去加载这个账本,这样我们就可以从 peer 节点的启动 serve 函数中找寻蛛丝马迹——peer/node/start.go

func serve(args []string) error {
   
   
  //initialize resource management exit
	ledgermgmt.Initialize(
		&ledgermgmt.Initializer{
   
   
			CustomTxProcessors:            peer.ConfigTxProcessors,
			PlatformRegistry:              pr,
			DeployedChaincodeInfoProvider: deployedCCInfoProvider,
			MembershipInfoProvider:        membershipInfoProvider,
			MetricsProvider:               metricsProvider,
			HealthCheckRegistry:           opsSystem,
		},
	)
}

主要是调用了账本管理包的初始化函数进行初始化,跟踪进行看下

var openedLedgers map[string]ledger.PeerLedger
var ledgerProvider ledger.PeerLedgerProvider
var initialized bool

func initialize(initializer *Initializer) {
   
   
	logger.Info("Initializing ledger mgmt")
	lock.Lock()
	defer lock.Unlock()
  
  // 初始化initialized 与 openedLedgers
	initialized = true
	openedLedgers = make(map[string]ledger.PeerLedger)
  
  // 执行customtx的初始化,主要是做了是初始化了一个 Processors对象,该对象存储着指定交易类型及它对应的处理方法
	customtx.Initialize(initializer.CustomTxProcessors)
  // 执行链码注册账本事件的初始化,包括回调函数等信息
	cceventmgmt.Initialize(&chaincodeInfoProviderImpl{
   
   
		initializer.PlatformRegistry,
		initializer.DeployedChaincodeInfoProvider,
	})
	finalStateListeners := addListenerForCCEventsHandler(initializer.DeployedChaincodeInfoProvider, []ledger.StateListener{
   
   })
  // 创建账本服务提供者
	provider, err := kvledger.NewProvider()
	if err != nil {
   
   
		panic(errors.WithMessage(err, "Error in instantiating ledger provider"))
	}
  // 账本服务提供者初始化
	err = provider.Initialize(&ledger.Initializer{
   
   
		StateListeners:                finalStateListeners,
		DeployedChaincodeInfoProvider: initializer.DeployedChaincodeInfoProvider,
		MembershipInfoProvider:        initializer.MembershipInfoProvider,
		MetricsProvider:               initializer.MetricsProvider,
		HealthCheckRegistry:           initializer.HealthCheckRegistry,
	})
	if err != nil {
   
   
		panic(errors.WithMessage(err, "Error initializing ledger provider"))
	}
  // 赋值给全局账本服务提供者
	ledgerProvider = provider
	logger.Info("ledger mgmt initialized")
}

在该函数中,主要初始化了三个全局变量—— openedLedgersledgerProviderinitialized

initialized = true
openedLedgers = make(map[string]ledger.PeerLedger)
provider, err := kvledger.NewProvider()
ledgerProvider = provider

initialized 表示是否初始化,openedLedgers 存放 peer 的账本映射,而 ledgerProvider 表示的是账本服务提供者 kvledger,跟踪进去看下它的原型

// NewProvider instantiates a new Provider.
// This is not thread-safe and assumed to be synchronized be the caller
func NewProvider() (ledger.PeerLedgerProvider, error) {
   
   
	logger.Info("Initializing ledger provider")
	// Initialize the ID store (inventory of chainIds/ledgerIds)
	idStore := openIDStore(ledgerconfig.GetLedgerProviderPath())
	// Initialize the history database (index for history of values by key)
	historydbProvider := historyleveldb.NewHistoryDBProvider()

	fileLock := leveldbhelper.NewFileLock(ledgerconfig.GetFileLockPath())
	if err := fileLock.Lock(); err != nil {
   
   
		return nil, errors.Wrap(err, "as another peer node command is executing,"+
			" wait for that command to complete its execution or terminate it before retrying")
	}

	logger.Info("ledger provider Initialized")
	provider := &Provider{
   
   idStore, nil,
		nil, historydbProvider, nil, nil, nil, nil, nil, nil, fileLock}
	return provider, nil
}

// PeerLedgerProvider provides handle to ledger instances
type PeerLedgerProvider interface {
   
   
	Initialize(ini
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值