[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分钟未更新的区块,然后将它删除。