以太坊原始碼分析(24)core-state原始碼分析
core/state 包主要為以太坊的state trie提供了一層快取層(cache)
- database主要提供了trie樹的抽象,提供trie樹的快取和合約程式碼長度的快取。
- journal主要提供了操作日誌,以及操作回滾的功能。
- state_object是account物件的抽象,提供了賬戶的一些功能。
- statedb主要是提供了state trie的部分功能。
## database.go
database.go 提供了一個資料庫的抽象。
資料結構
// Database wraps access to tries and contract code.
type Database interface {
// Accessing tries:
// OpenTrie opens the main account trie.
// OpenStorageTrie opens the storage trie of an account.
// OpenTrie 開啟了主賬號的trie樹
// OpenStorageTrie 開啟了一個賬號的storage trie
OpenTrie(root common.Hash) (Trie, error)
OpenStorageTrie(addrHash, root common.Hash) (Trie, error)
// Accessing contract code:
// 訪問合約程式碼
ContractCode(addrHash, codeHash common.Hash) ([]byte, error)
// 訪問合約的大小。 這個方法可能經常被呼叫。因為有快取。
ContractCodeSize(addrHash, codeHash common.Hash) (int, error)
// CopyTrie returns an independent copy of the given trie.
// CopyTrie 返回了一個指定trie的獨立的copy
CopyTrie(Trie) Trie
}
// NewDatabase creates a backing store for state. The returned database is safe for
// concurrent use and retains cached trie nodes in memory.
func NewDatabase(db ethdb.Database) Database {
csc, _ := lru.New(codeSizeCacheSize)
return &cachingDB{db: db, codeSizeCache: csc}
}
type cachingDB struct {
db ethdb.Database
mu sync.Mutex
pastTries []*trie.SecureTrie //trie樹的快取
codeSizeCache *lru.Cache //合約程式碼大小的快取
}
OpenTrie,從快取裡面查詢。如果找到了返回快取的trie的copy, 否則重新構建一顆樹返回。
func (db *cachingDB) OpenTrie(root common.Hash) (Trie, error) {
db.mu.Lock()
defer db.mu.Unlock()
for i := len(db.pastTries) - 1; i >= 0; i-- {
if db.pastTries[i].Hash() == root {
return cachedTrie{db.pastTries[i].Copy(), db}, nil
}
}
tr, err := trie.NewSecure(root, db.db, MaxTrieCacheGen)
if err != nil {
return nil, err
}
return cachedTrie{tr, db}, nil
}
func (db *cachingDB) OpenStorageTrie(addrHash, root common.Hash) (Trie, error) {
return trie.NewSecure(root, db.db, 0)
}
ContractCode 和 ContractCodeSize, ContractCodeSize有快取。
func (db *cachingDB) ContractCode(addrHash, codeHash common.Hash) ([]byte, error) {
code, err := db.db.Get(codeHash[:])
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}
return code, err
}
func (db *cachingDB) ContractCodeSize(addrHash, codeHash common.Hash) (int, error) {
if cached, ok := db.codeSizeCache.Get(codeHash); ok {
return cached.(int), nil
}
code, err := db.ContractCode(addrHash, codeHash)
if err == nil {
db.codeSizeCache.Add(codeHash, len(code))
}
return len(code), err
}
cachedTrie的結構和commit方法,commit的時候會呼叫pushTrie方法把之前的Trie樹快取起來。
// cachedTrie inserts its trie into a cachingDB on commit.
type cachedTrie struct {
*trie.SecureTrie
db *cachingDB
}
func (m cachedTrie) CommitTo(dbw trie.DatabaseWriter) (common.Hash, error) {
root, err := m.SecureTrie.CommitTo(dbw)
if err == nil {
m.db.pushTrie(m.SecureTrie)
}
return root, err
}
func (db *cachingDB) pushTrie(t *trie.SecureTrie) {
db.mu.Lock()
defer db.mu.Unlock()
if len(db.pastTries) >= maxPastTries {
copy(db.pastTries, db.pastTries[1:])
db.pastTries[len(db.pastTries)-1] = t
} else {
db.pastTries = append(db.pastTries, t)
}
}
## journal.go
journal代表了操作日誌, 並針對各種操作的日誌提供了對應的回滾功能。 可以基於這個日誌來做一些事務型別的操作。
型別定義,定義了journalEntry這個介面,提供了undo的功能。 journal 就是journalEntry的列表。
type journalEntry interface {
undo(*StateDB)
}
type journal []journalEntry
各種不同的日誌型別以及undo方法。
createObjectChange struct { //建立物件的日誌。 undo方法就是從StateDB中刪除建立的物件。
account *common.Address
}
func (ch createObjectChange) undo(s *StateDB) {
delete(s.stateObjects, *ch.account)
delete(s.stateObjectsDirty, *ch.account)
}
// 對於stateObject的修改, undo方法就是把值改為原來的物件。
resetObjectChange struct {
prev *stateObject
}
func (ch resetObjectChange) undo(s *StateDB) {
s.setStateObject(ch.prev)
}
// 自殺的更改。自殺應該是刪除賬號,但是如果沒有commit的化,物件還沒有從stateDB刪除。
suicideChange struct {
account *common.Address
prev bool // whether account had already suicided
prevbalance *big.Int
}
func (ch suicideChange) undo(s *StateDB) {
obj := s.getStateObject(*ch.account)
if obj != nil {
obj.suicided = ch.prev
obj.setBalance(ch.prevbalance)
}
}
// Changes to individual accounts.
balanceChange struct {
account *common.Address
prev *big.Int
}
nonceChange struct {
account *common.Address
prev uint64
}
storageChange struct {
account *common.Address
key, prevalue common.Hash
}
codeChange struct {
account *common.Address
prevcode, prevhash []byte
}
func (ch balanceChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setBalance(ch.prev)
}
func (ch nonceChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setNonce(ch.prev)
}
func (ch codeChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
}
func (ch storageChange) undo(s *StateDB) {
s.getStateObject(*ch.account).setState(ch.key, ch.prevalue)
}
// 我理解是DAO事件的退款處理
refundChange struct {
prev *big.Int
}
func (ch refundChange) undo(s *StateDB) {
s.refund = ch.prev
}
// 增加了日誌的修改
addLogChange struct {
txhash common.Hash
}
func (ch addLogChange) undo(s *StateDB) {
logs := s.logs[ch.txhash]
if len(logs) == 1 {
delete(s.logs, ch.txhash)
} else {
s.logs[ch.txhash] = logs[:len(logs)-1]
}
s.logSize--
}
// 這個是增加 VM看到的 SHA3的 原始byte[], 增加SHA3 hash -> byte[] 的對應關係
addPreimageChange struct {
hash common.Hash
}
func (ch addPreimageChange) undo(s *StateDB) {
delete(s.preimages, ch.hash)
}
touchChange struct {
account *common.Address
prev bool
prevDirty bool
}
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
func (ch touchChange) undo(s *StateDB) {
if !ch.prev && *ch.account != ripemd {
s.getStateObject(*ch.account).touched = ch.prev
if !ch.prevDirty {
delete(s.stateObjectsDirty, *ch.account)
}
}
}
## state_object.go
stateObject表示正在修改的以太坊帳戶。
資料結構
type Storage map[common.Hash]common.Hash
// stateObject represents an Ethereum account which is being modified.
// stateObject表示正在修改的以太坊帳戶。
// The usage pattern is as follows:
// First you need to obtain a state object.
// Account values can be accessed and modified through the object.
// Finally, call CommitTrie to write the modified storage trie into a database.
使用模式如下:
首先你需要獲得一個state_object。
帳戶值可以通過物件訪問和修改。
最後,呼叫CommitTrie將修改後的儲存trie寫入資料庫。
type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account 以太坊賬號地址的hash值
data Account // 這個是實際的以太坊賬號的資訊
db *StateDB //狀態資料庫
// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
// during a database read is memoized here and will eventually be returned
// by StateDB.Commit.
//
資料庫錯誤。
stateObject會被共識演算法的核心和VM使用,在這些程式碼內部無法處理資料庫級別的錯誤。
在資料庫讀取期間發生的任何錯誤都會在這裡被儲存,最終將由StateDB.Commit返回。
dbErr error
// Write caches. 寫快取
trie Trie // storage trie, which becomes non-nil on first access 使用者的儲存trie ,在第一次訪問的時候變得非空
code Code // contract bytecode, which gets set when code is loaded 合約程式碼,當程式碼被載入的時候被設定
cachedStorage Storage // Storage entry cache to avoid duplicate reads 使用者儲存物件的快取,用來避免重複讀
dirtyStorage Storage // Storage entries that need to be flushed to disk 需要刷入磁碟的使用者儲存物件
// Cache flags. Cache 標誌
// When an object is marked suicided it will be delete from the trie
// during the "update" phase of the state transition.
// 當一個物件被標記為自殺時,它將在狀態轉換的“更新”階段期間從樹中刪除。
dirtyCode bool // true if the code was updated 如果程式碼被更新,會設定為true
suicided bool
touched bool
deleted bool
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty 第一次被設定為drity的時候會被呼叫。
}
// Account is the Ethereum consensus representation of accounts.
// These objects are stored in the main account trie.
// 帳戶是以太坊共識表示的帳戶。 這些物件儲存在main account trie。
type Account struct {
Nonce uint64
Balance *big.Int
Root common.Hash // merkle root of the storage trie
CodeHash []byte
}
建構函式
// newObject creates a state object.
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
if data.CodeHash == nil {
data.CodeHash = emptyCodeHash
}
return &stateObject{
db: db,
address: address,
addrHash: crypto.Keccak256Hash(address[:]),
data: data,
cachedStorage: make(Storage),
dirtyStorage: make(Storage),
onDirty: onDirty,
}
}
RLP的編碼方式,只會編碼 Account物件。
// EncodeRLP implements rlp.Encoder.
func (c *stateObject) EncodeRLP(w io.Writer) error {
return rlp.Encode(w, c.data)
}
一些狀態改變的函式。
func (self *stateObject) markSuicided() {
self.suicided = true
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
func (c *stateObject) touch() {
c.db.journal = append(c.db.journal, touchChange{
account: &c.address,
prev: c.touched,
prevDirty: c.onDirty == nil,
})
if c.onDirty != nil {
c.onDirty(c.Address())
c.onDirty = nil
}
c.touched = true
}
Storage的處理
// getTrie返回賬戶的Storage Trie
func (c *stateObject) getTrie(db Database) Trie {
if c.trie == nil {
var err error
c.trie, err = db.OpenStorageTrie(c.addrHash, c.data.Root)
if err != nil {
c.trie, _ = db.OpenStorageTrie(c.addrHash, common.Hash{})
c.setError(fmt.Errorf("can't create storage trie: %v", err))
}
}
return c.trie
}
// GetState returns a value in account storage.
// GetState 返回account storage 的一個值,這個值的型別是Hash型別。
// 說明account storage裡面只能儲存hash值?
// 如果快取裡面存在就從快取裡查詢,否則從資料庫裡面查詢。然後儲存到快取裡面。
func (self *stateObject) GetState(db Database, key common.Hash) common.Hash {
value, exists := self.cachedStorage[key]
if exists {
return value
}
// Load from DB in case it is missing.
enc, err := self.getTrie(db).TryGet(key[:])
if err != nil {
self.setError(err)
return common.Hash{}
}
if len(enc) > 0 {
_, content, _, err := rlp.Split(enc)
if err != nil {
self.setError(err)
}
value.SetBytes(content)
}
if (value != common.Hash{}) {
self.cachedStorage[key] = value
}
return value
}
// SetState updates a value in account storage.
// 往 account storeage 裡面設定一個值 key value 的型別都是Hash型別。
func (self *stateObject) SetState(db Database, key, value common.Hash) {
self.db.journal = append(self.db.journal, storageChange{
account: &self.address,
key: key,
prevalue: self.GetState(db, key),
})
self.setState(key, value)
}
func (self *stateObject) setState(key, value common.Hash) {
self.cachedStorage[key] = value
self.dirtyStorage[key] = value
if self.onDirty != nil {
self.onDirty(self.Address())
self.onDirty = nil
}
}
提交 Commit
// CommitTrie the storage trie of the object to dwb.
// This updates the trie root.
// 步驟,首先開啟,然後修改,然後提交或者回滾
func (self *stateObject) CommitTrie(db Database, dbw trie.DatabaseWriter) error {
self.updateTrie(db) // updateTrie把修改過的快取寫入Trie樹
if self.dbErr != nil {
return self.dbErr
}
root, err := self.trie.CommitTo(dbw)
if err == nil {
self.data.Root = root
}
return err
}
// updateTrie writes cached storage modifications into the object's storage trie.
func (self *stateObject) updateTrie(db Database) Trie {
tr := self.getTrie(db)
for key, value := range self.dirtyStorage {
delete(self.dirtyStorage, key)
if (value == common.Hash{}) {
self.setError(tr.TryDelete(key[:]))
continue
}
// Encoding []byte cannot fail, ok to ignore the error.
v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00"))
self.setError(tr.TryUpdate(key[:], v))
}
return tr
}
// UpdateRoot sets the trie root to the current root hash of
// 把賬號的root設定為當前的trie樹的跟。
func (self *stateObject) updateRoot(db Database) {
self.updateTrie(db)
self.data.Root = self.trie.Hash()
}
額外的一些功能 ,deepCopy提供了state_object的深拷貝。
func (self *stateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *stateObject {
stateObject := newObject(db, self.address, self.data, onDirty)
if self.trie != nil {
stateObject.trie = db.db.CopyTrie(self.trie)
}
stateObject.code = self.code
stateObject.dirtyStorage = self.dirtyStorage.Copy()
stateObject.cachedStorage = self.dirtyStorage.Copy()
stateObject.suicided = self.suicided
stateObject.dirtyCode = self.dirtyCode
stateObject.deleted = self.deleted
return stateObject
}
## statedb.go
stateDB用來儲存以太坊中關於merkle trie的所有內容。 StateDB負責快取和儲存巢狀狀態。 這是檢索合約和賬戶的一般查詢介面:
資料結構
type StateDB struct {
db Database // 後端的資料庫
trie Trie // trie樹 main account trie
// This map holds 'live' objects, which will get modified while processing a state transition.
// 下面的Map用來儲存當前活動的物件,這些物件在狀態轉換的時候會被修改。
// stateObjects 用來快取物件
// stateObjectsDirty用來快取被修改過的物件。
stateObjects map[common.Address]*stateObject
stateObjectsDirty map[common.Address]struct{}
// DB error.
// State objects are used by the consensus core and VM which are
// unable to deal with database-level errors. Any error that occurs
// during a database read is memoized here and will eventually be returned
// by StateDB.Commit.
dbErr error
// The refund counter, also used by state transitioning.
// refund計數器。 暫時還不清楚功能。
refund *big.Int
thash, bhash common.Hash //當前的transaction hash 和block hash
txIndex int // 當前的交易的index
logs map[common.Hash][]*types.Log // 日誌 key是交易的hash值
logSize uint
preimages map[common.Hash][]byte // EVM計算的 SHA3->byte[]的對映關係
// Journal of state modifications. This is the backbone of
// Snapshot and RevertToSnapshot.
// 狀態修改日誌。 這是Snapshot和RevertToSnapshot的支柱。
journal journal
validRevisions []revision
nextRevisionId int
lock sync.Mutex
}
建構函式
// 一般的用法 statedb, _ := state.New(common.Hash{}, state.NewDatabase(db))
// Create a new state from a given trie
func New(root common.Hash, db Database) (*StateDB, error) {
tr, err := db.OpenTrie(root)
if err != nil {
return nil, err
}
return &StateDB{
db: db,
trie: tr,
stateObjects: make(map[common.Address]*stateObject),
stateObjectsDirty: make(map[common.Address]struct{}),
refund: new(big.Int),
logs: make(map[common.Hash][]*types.Log),
preimages: make(map[common.Hash][]byte),
}, nil
}
### 對於Log的處理
state提供了Log的處理,這比較意外,因為Log實際上是儲存在區塊鏈中的,並沒有儲存在state trie中, state提供Log的處理, 使用了基於下面的幾個函式。 奇怪的是暫時沒看到如何刪除logs裡面的資訊,如果不刪除的話,應該會越積累越多。 TODO logs 刪除
Prepare函式,在交易執行開始被執行。
AddLog函式,在交易執行過程中被VM執行。新增日誌。同時把日誌和交易關聯起來,新增部分交易的資訊。
GetLogs函式,交易完成取走。
// Prepare sets the current transaction hash and index and block hash which is
// used when the EVM emits new state logs.
func (self *StateDB) Prepare(thash, bhash common.Hash, ti int) {
self.thash = thash
self.bhash = bhash
self.txIndex = ti
}
func (self *StateDB) AddLog(log *types.Log) {
self.journal = append(self.journal, addLogChange{txhash: self.thash})
log.TxHash = self.thash
log.BlockHash = self.bhash
log.TxIndex = uint(self.txIndex)
log.Index = self.logSize
self.logs[self.thash] = append(self.logs[self.thash], log)
self.logSize++
}
func (self *StateDB) GetLogs(hash common.Hash) []*types.Log {
return self.logs[hash]
}
func (self *StateDB) Logs() []*types.Log {
var logs []*types.Log
for _, lgs := range self.logs {
logs = append(logs, lgs...)
}
return logs
}
### stateObject處理
getStateObject,首先從快取裡面獲取,如果沒有就從trie樹裡面獲取,並載入到快取。
// Retrieve a state object given my the address. Returns nil if not found.
func (self *StateDB) getStateObject(addr common.Address) (stateObject *stateObject) {
// Prefer 'live' objects.
if obj := self.stateObjects[addr]; obj != nil {
if obj.deleted {
return nil
}
return obj
}
// Load the object from the database.
enc, err := self.trie.TryGet(addr[:])
if len(enc) == 0 {
self.setError(err)
return nil
}
var data Account
if err := rlp.DecodeBytes(enc, &data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
}
// Insert into the live set.
obj := newObject(self, addr, data, self.MarkStateObjectDirty)
self.setStateObject(obj)
return obj
}
MarkStateObjectDirty, 設定一個stateObject為Dirty。 直接往stateObjectDirty對應的地址插入一個空結構體。
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
// state object cache iteration to find a handful of modified ones.
func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
self.stateObjectsDirty[addr] = struct{}{}
}
### 快照和回滾功能
Snapshot可以建立一個快照, 然後通過 RevertToSnapshot可以回滾到哪個狀態,這個功能是通過journal來做到的。 每一步的修改都會往journal裡面新增一個undo日誌。 如果需要回滾只需要執行undo日誌就行了。
// Snapshot returns an identifier for the current revision of the state.
func (self *StateDB) Snapshot() int {
id := self.nextRevisionId
self.nextRevisionId++
self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
return id
}
// RevertToSnapshot reverts all state changes made since the given revision.
func (self *StateDB) RevertToSnapshot(revid int) {
// Find the snapshot in the stack of valid snapshots.
idx := sort.Search(len(self.validRevisions), func(i int) bool {
return self.validRevisions[i].id >= revid
})
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
}
snapshot := self.validRevisions[idx].journalIndex
// Replay the journal to undo changes.
for i := len(self.journal) - 1; i >= snapshot; i-- {
self.journal[i].undo(self)
}
self.journal = self.journal[:snapshot]
// Remove invalidated snapshots from the stack.
self.validRevisions = self.validRevisions[:idx]
}
### 獲取中間狀態的 root hash值
IntermediateRoot 用來計算當前的state trie的root的hash值。這個方法會在交易執行的過程中被呼叫。會被存入 transaction receipt
Finalise方法會呼叫update方法把存放在cache層的修改寫入到trie資料庫裡面。 但是這個時候還沒有寫入底層的資料庫。 還沒有呼叫commit,資料還在記憶體裡面,還沒有落地成檔案。
// Finalise finalises the state by removing the self destructed objects
// and clears the journal as well as the refunds.
func (s *StateDB) Finalise(deleteEmptyObjects bool) {
for addr := range s.stateObjectsDirty {
stateObject := s.stateObjects[addr]
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
s.deleteStateObject(stateObject)
} else {
stateObject.updateRoot(s.db)
s.updateStateObject(stateObject)
}
}
// Invalidate journal because reverting across transactions is not allowed.
s.clearJournalAndRefund()
}
// IntermediateRoot computes the current root hash of the state trie.
// It is called in between transactions to get the root hash that
// goes into transaction receipts.
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
s.Finalise(deleteEmptyObjects)
return s.trie.Hash()
}
### commit方法
CommitTo用來提交更改。
// CommitTo writes the state to the given database.
func (s *StateDB) CommitTo(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
defer s.clearJournalAndRefund()
// Commit objects to the trie.
for addr, stateObject := range s.stateObjects {
_, isDirty := s.stateObjectsDirty[addr]
switch {
case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
// If the object has been removed, don't bother syncing it
// and just mark it for deletion in the trie.
s.deleteStateObject(stateObject)
case isDirty:
// Write any contract code associated with the state object
if stateObject.code != nil && stateObject.dirtyCode {
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
return common.Hash{}, err
}
stateObject.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie.
if err := stateObject.CommitTrie(s.db, dbw); err != nil {
return common.Hash{}, err
}
// Update the object in the main account trie.
s.updateStateObject(stateObject)
}
delete(s.stateObjectsDirty, addr)
}
// Write trie changes.
root, err = s.trie.CommitTo(dbw)
log.Debug("Trie cache stats after commit", "misses", trie.CacheMisses(), "unloads", trie.CacheUnloads())
return root, err
}
### 總結
state包提供了使用者和合約的狀態管理的功能。 管理了狀態和合約的各種狀態轉換。 cache, trie, 資料庫。 日誌和回滾功能。
相關文章
- 以太坊原始碼分析(36)ethdb原始碼分析原始碼
- 以太坊原始碼分析(38)event原始碼分析原始碼
- 以太坊原始碼分析(41)hashimoto原始碼分析原始碼
- 以太坊原始碼分析(43)node原始碼分析原始碼
- 以太坊原始碼分析(52)trie原始碼分析原始碼
- 以太坊原始碼分析(51)rpc原始碼分析原始碼RPC
- 以太坊原始碼分析(20)core-bloombits原始碼分析原始碼OOM
- 以太坊原始碼分析(29)core-vm原始碼分析原始碼
- 以太坊原始碼分析(23)core-state-process原始碼分析原始碼
- 以太坊原始碼分析(34)eth-downloader原始碼分析原始碼
- 以太坊原始碼分析(35)eth-fetcher原始碼分析原始碼
- 以太坊原始碼分析(5)accounts程式碼分析原始碼
- 以太坊原始碼分析(26)core-txpool交易池原始碼分析原始碼
- 以太坊原始碼分析(28)core-vm-stack-memory原始碼分析原始碼
- 以太坊原始碼分析(30)eth-bloombits和filter原始碼分析原始碼OOMFilter
- 以太坊原始碼分析(31)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(32)eth-downloader-peer原始碼分析原始碼
- 以太坊原始碼分析(33)eth-downloader-statesync原始碼分析原始碼
- 以太坊原始碼分析(8)區塊分析原始碼
- 以太坊原始碼分析(9)cmd包分析原始碼
- 以太坊原始碼分析(13)RPC分析原始碼RPC
- 以太坊原始碼分析(16)挖礦分析原始碼
- 以太坊交易池原始碼分析原始碼
- 以太坊原始碼分析(18)以太坊交易執行分析原始碼
- 以太坊原始碼分析(37)eth以太坊協議分析原始碼協議
- 以太坊原始碼分析(27)core-vm-jumptable-instruction原始碼分析原始碼Struct
- 以太坊原始碼分析(44)p2p-database.go原始碼分析原始碼DatabaseGo
- 以太坊原始碼分析(45)p2p-dial.go原始碼分析原始碼Go
- 以太坊原始碼分析(46)p2p-peer.go原始碼分析原始碼Go
- 以太坊原始碼分析(48)p2p-server.go原始碼分析原始碼ServerGo
- 以太坊原始碼分析(49)p2p-table.go原始碼分析原始碼Go
- 以太坊原始碼分析(50)p2p-udp.go原始碼分析原始碼UDPGo
- 以太坊原始碼分析(10)CMD深入分析原始碼
- 以太坊原始碼分析(12)交易資料分析原始碼
- 以太坊原始碼分析(19)core-blockchain分析原始碼Blockchain
- 以太坊原始碼分析(22)core-genesis創世區塊原始碼分析原始碼
- 以太坊原始碼分析(42)miner挖礦部分原始碼分析CPU挖礦原始碼
- 以太坊原始碼分析(6)accounts賬戶管理分析原始碼