Edit me

[TOC]

store

store部分我们重点关注接口部分,存储的相关操作基本都是通过接口实现的。

common

store.go

定义一个状态的结构体:

type StateItem struct {
   Key   string
   Value states.StateValue    //state中的一个接口,实现序列化和解析操作
   State ItemState            //有None,Deleted,Changed三种状态
   Trie  bool
}

定义五种接口,接口的实现是在ledgerstore目录下,接口定义了存储的基本操作,如添加,删除,查找,更新等:

type StoreIterator interface
type PersistStore interface
type StateStore interface
type MemoryCacheStore interface
type EventStore interface

data_entry_prefix.go

定义事件的类型 —

leveldbstore

使用levelDB存储数据,并且实现了自身的迭代器:

type LevelDBStore struct {
   db    *leveldb.DB // LevelDB instance
   batch *leveldb.Batch
}

ledgerstore

block_cache.go

定义两个数据结构来进行transaction和block的缓存:

//Value of transaction cache
type TransactionCacheaValue struct {
   Tx     *types.Transaction
   Height uint32
}
//BlockCache with block cache and transaction hash
type BlockCache struct {
   blockCache       *lru.ARCCache
   transactionCache *lru.ARCCache
}

这里用到了golang的lru缓存库,它是用双向链表加list和map实现的,可以快速的插入和读取,提高性能: https://godoc.org/github.com/hashicorp/golang-lru

定义两个全局变量,约束transaction和block缓存数量:

BLOCK_CAHE_SIZE        = 10    //Block cache size
TRANSACTION_CACHE_SIZE = 10000 //Transaction cache size

block_cache.go中提供的方法:

func NewBlockCache() (*BlockCache, error) //根据上面两个尺寸使用lru.NewARC创建BlockCache
func (this *BlockCache) AddBlock(block *types.Block) //添加block到缓存,用block的哈希作索引
func (this *BlockCache) GetBlock(blockHash common.Uint256) *types.Block //根据哈希索引快速取出block缓存
func (this *BlockCache) AddTransaction(tx *types.Transaction, height uint32) //把交易打包成TransactionCacheaValue形式,并使用交易哈希作为索引存储交易到缓存中
func (this *BlockCache) GetTransaction(txHash common.Uint256) (*types.Transaction, uint32) //取回交易数据

block_store.go

定义存储区块的数据结构:

type BlockStore struct {
   enableCache bool                       //Is enable lru cache
   dbDir       string                     //The path of store file
   cache       *BlockCache                //The cache of block, if have.
   store       *leveldbstore.LevelDBStore //block store handler
}

定义了下面几个方法:

func NewBlockStore(dbDir string, enableCache bool) (*BlockStore, error) //创建BlockStore对象
func (this *BlockStore) NewBatch() //创建levelDB处理器
func (this *BlockStore) SaveBlock(block *types.Block) error //存储区块到levelDB,也就是header和transactions
func (this *BlockStore) SaveHeader(block *types.Block, sysFee common.Fixed64) error //将区块头存入levelDB,里面存储了区块的哈希,以及每个账单的哈希
func (this *BlockStore) putTransaction(tx *types.Transaction, height uint32) error //将账单存入levelDB
func (this *BlockStore) SaveCurrentBlock(height uint32, blockHash common.Uint256) error //存储区块的哈希和height到levelDB

其他的方法基本也是做了些存储读取的工作,就不再赘述了。 —

event_store.go

event_store.go似乎是把交易和区块同一个事件进行了绑定,但是绑定的意义是什么还需后面追查。 —

state_store.go

Merkle数是存在状态数据库中的:

type StateStore struct {
   dbDir           string                    //Store file path
   store           scom.PersistStore         //Store handler
   merklePath      string                    //Merkle tree store path
   merkleTree      *merkle.CompactMerkleTree //Merkle tree of block root
   merkleHashStore merkle.HashStore
}

NewStateStore创建了Merkle数,然后提供了一些方法对数据库进行操作。 —

tx_handler.go

主要提供了两个方法:

func (self *StateStore) HandleDeployTransaction(stateBatch *statestore.StateBatch, tx *types.Transaction) error

这个方法是部署智能合约的,账单也是通过智能合约来实现的。

func (self *StateStore) HandleInvokeTransaction(store store.LedgerStore, stateBatch *statestore.StateBatch, tx *types.Transaction, block *types.Block, eventStore scommon.EventStore) error

将合约通过事件的方式广播出去,里面包含合约地址,通过地址确定这个合约的作用是应用合约还是转账。 —

ledger_store.go

前面所有的准备工作都是为了最后这个账单的存储。 因此在账单的数据结构中就可以看到它包含了前面的数据结构:

type LedgerStoreImp struct {
   blockStore       *BlockStore                         //BlockStore for saving block & transaction data
   stateStore       *StateStore                         //StateStore for saving state data, like balance, smart contract execution result, and so on.
   eventStore       *EventStore                         //EventStore for saving log those gen after smart contract executed.
   storedIndexCount uint32                              //record the count of have saved block index
   currBlockHeight  uint32                              //Current block height
   currBlockHash    common.Uint256                      //Current block hash
   headerCache      map[common.Uint256]*ledgerCacheItem //Cache header to saving in sync block. BlockHash => Header
   blockCache       map[common.Uint256]*ledgerCacheItem //Cache block to saving in sync block. BlockHash => Block
   headerIndex      map[uint32]common.Uint256           //Header index, Mapping header height => block hash
   savingBlock      bool                                //is saving block now
   lock             sync.RWMutex
   exitCh           chan interface{}
}

NewLedgerStore方法基本就是初始化上面的结构体,然后启动一个协程:

case <-ticker.C: //10s触发一次
   go this.clearBlockCache()
case <-timeoutTicker.C: //60s触发一次
   go this.clearTimeoutBlock()

这个协程在我们梳理初始化流程时遇到过,clearBlockCache()方法保存区块到数据库,同时清楚缓存中对应的区块,然后将保存事件广播到网络中,在保存的过程中会调用相关的合约;clearTimeoutBlock()则是在缓存中查找15分钟未更新的区块,然后将它删除。

Tags: