작성자:

초기에 대충 본거라 정확하지 않을수 있습니다.. 이후 수정하지않음
헷갈릴만한 용어dirty, stateObject, stateDB관련JournalTrie nodeselfdestruct(suicide) - 현재 (trie에서) CA를 지우는 유일한 방법empty, touch TD: total difficultiesCode내용 기록
헷갈릴만한 용어
dirty, stateObject, stateDB관련
- dirty라는건, 수정된 상태를 말함
- stateObject는 account의 수정중인 상태를 저장
(address를 통해 접근가능함
s *stateObject
s[address]
)- s.trie는 수정된 storage trie를 의미함
- s.originStorage는 마지막 state commit에 따른 storage 상태
- s.dirtyStorage 는 수정된 상태를 저장
(SetState로 value에 변화가 있을시에 storageChange를, s.dirtyStorage에 저장하고, 아래에 설명할 journal에도 저장해요. )
(CommitTrie와 updateRoot함수는, updateTrie를 통해서 s.dirtyStorage를 s.originStorage를 거쳐 s.trie에 저장해요.)
- Account는 main account trie에 저장되며, Account.Root 는 storage trie의 root를 의미함
- block.Header에 (state trie)Root 저장
- stateObject에는 Account등이 저장, Account에는(storage trie)Root 저장.
- stateDB에 (storage)trie, stateObject, journal 등을 저장
- s.stateDB에 proofList가 존재함
Journal
- dirty account는 마지막 state commit 이후 상태가 수정된 account를 말함
- journalEntry는 state commit 이후 적용된 state 수정사항
- journal은 journalEntry를 포함하고 있으며, revert해야할 경우를 대비해 entry를 트래킹
따라서 state commit에 의해 수정이 일어날때마다 journal에서는 dirties에 account별로 counting을 해요. (dirties에 address를 매핑)
j.dirties[*addr]++
Trie node
shortNode : expansion node 혹은 leaf node
fullNode : branch node
hashNode : 각 node의 hash
valueNode : leaf node의 value 혹은 expansion node의 value??
selfdestruct(suicide) - 현재 (trie에서) CA를 지우는 유일한 방법
- stateObject.suicided는 selfdestruct된 contract account를 위한 플래그 (suicide에서 selfdestruct로 용어가 변경됨)
- selfdestruct시 trie에서는 제거되나, db에는 유지됨.
(StateDB.go에서 Suicide함수는 account의 balance를 clear하고, stateObject의 suicided에 true표시함.
Finalise함수에서 stateObject.sucided = true인 object를 trie에서 삭제하고, (기타내용 생략)
Exist함수는 address로 stateDB에 해당 주소의 계정이 존재하는지 확인가능하나, suicided account에 대해서도 true를 반환한다.)
empty, touch
-empty Account란 nonce, balance, code가 모두 0인 Account
(state_object.go의 AddBalance함수에서는, amount가 0이고, stateObject.empty() (nonce=balance=code==0)이면 touch함수를 통해, journal의 dirties에 추가한다. )
-touch event는 journal.dirties에는 존재하지만 stateObject에는 존재하지 않는다(stateDB.go Finalise()의
stateObject, exist := s.stateObjects[addr]
if !exist
*parity에서는 실행시 옵션으로 touched empty account 삭제하고, min_balance이하이면서 balance가 줄어든 모든 계정 삭제.
TD: total difficulties
Code내용 기록
세모는 클릭하면 나와요.
Block → (stateRoot) → state trie → account* → storage trie
geth/core/types/block.go 중 Header에서 Root(stateRoot)를 확인할 수 있음.
// Header represents a block header in the Ethereum blockchain. type Header struct { ParentHash common.Hash `json:"parentHash" gencodec:"required"` UncleHash common.Hash `json:"sha3Uncles" gencodec:"required"` Coinbase common.Address `json:"miner" gencodec:"required"` Root common.Hash `json:"stateRoot" gencodec:"required"` TxHash common.Hash `json:"transactionsRoot" gencodec:"required"` ReceiptHash common.Hash `json:"receiptsRoot" gencodec:"required"` Bloom Bloom `json:"logsBloom" gencodec:"required"` Difficulty *big.Int `json:"difficulty" gencodec:"required"` Number *big.Int `json:"number" gencodec:"required"` GasLimit uint64 `json:"gasLimit" gencodec:"required"` GasUsed uint64 `json:"gasUsed" gencodec:"required"` Time uint64 `json:"timestamp" gencodec:"required"` Extra []byte `json:"extraData" gencodec:"required"` MixDigest common.Hash `json:"mixHash"` Nonce BlockNonce `json:"nonce"` }
core/state directory
datatabase.go
Trie.Commit() : writes all node to the trie's memory db, tracking the internal and external references.
NewDatabase() : creats a backing store for state, 메모리의 최근 trie node는 보유할수 없음. 메모리의 hitorical state를 유지하기위해 NewDatabasWithCache 사용
NewDatabasWithCache() : 많은양의 이전 RLP trie node를 메모리 캐시에 저장 가능 .
TrieDB() : 중간 트리이노드 캐싱 레이어 검색
journal.go
state modifications. this is backbone of snapshot and revertTosnapshot.
journal은 마지막 state commit 이후 적용된 state 수정 목록(journalEntry)을 포함하며, 이들은 요청시 revert하기위해 저널에 의해 트래킹됨.
- dirty account란 수정된 account
append() :수정이 일어날때마다 dirties에 counting :
j.dirties[*addr]++
account trie, individual accounts(Account), other state values(refund,addLog,addPreimage,touch)에 대한 revert, dirtied 함수가 존재함.
/state_object.go
type stateObject struct { address common.Address addrHash common.Hash // hash of ethereum address of the account data Account db *StateDB dbErr error // Write caches. trie Trie // storage trie, which becomes non-nil on first access code Code // contract bytecode, which gets set when code is loaded originStorage Storage // Storage cache of original entries to dedup rewrites dirtyStorage Storage // Storage entries that need to be flushed to disk // Cache flags. // 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 suicided bool deleted bool }
stateObject 는 수정중인 계정을 나타내며,(address,addrHash,data(Account),db(StateDB)) 등을 저장함.
주로 address를 통해 stateObject에 접근
- SetState시 value에 변화가 있으면, storagChange를 journal에 저장하고, dirtyStorage에도 반영한다.
- updateRoot()는 updateTrie를 통해 originStorag에 수정사항을 반영하고
Root를 재계산한다.(trie.Hash())
// updateTrie writes cached storage modifications into the object's storage trie. func (s *stateObject) updateTrie(db Database) Trie { // Track the amount of time wasted on updating the storge trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageUpdates += time.Since(start) }(time.Now()) } // Update all the dirty slots in the trie tr := s.getTrie(db) for key, value := range s.dirtyStorage { delete(s.dirtyStorage, key) // Skip noop changes, persist actual changes if value == s.originStorage[key] { continue } s.originStorage[key] = value if (value == common.Hash{}) { s.setError(tr.TryDelete(key[:])) continue } // Encoding []byte cannot fail, ok to ignore the error. v, _ := rlp.EncodeToBytes(bytes.TrimLeft(value[:], "\x00")) s.setError(tr.TryUpdate(key[:], v)) } return tr } // UpdateRoot sets the trie root to the current root hash of func (s *stateObject) updateRoot(db Database) { s.updateTrie(db) // Track the amount of time wasted on hashing the storge trie if metrics.EnabledExpensive { defer func(start time.Time) { s.db.StorageHashes += time.Since(start) }(time.Now()) } s.data.Root = s.trie.Hash() }
Account는 main account trie에 저장됨.
type Account struct { Nonce uint64 Balance *big.Int Root common.Hash // merkle root of the storage trie CodeHash []byte }
- CommitTrie를 하면 updateTrie를 통해 db에 수정된 storage trie(아래코드중 trie)가 저장된다.
StateDB
- (storage)trie, stateObject, journal 등을 저장.
proofList [][]byte
- New()에서 주어진 trie로새로운 stateDB를 생성 :
tr, err := db.OpenTrie(root)
- Reset() : 이전의 state trie는 그대로 두고 journal과 txindex등은 clear
- Exist(address) : stateDB에 해당 주소의 계정이 존재하는지 확인가능. 단, suicided account에 대해서도 true를 반환.
- GetBalance(address)시에 db에서 balance 반환, stateobject가 없으면 0을 반환함
- GetNonce(address) db 에서 stateObjct의 Nonce를 반환
- GetState(address,common.hash)에서 storage trie를 반환. stateObject.GetState를 이용.
- stateObject.GetState는 state entry에 dirty value가 있다면, dirty storage에서 그 값을 반환하고, 그렇지않으면, GetCommittedState()를통해 original storage에서 값을 반환함.
- Account 에 대한 setter와 getter 존재
Suicide는 account balance를 clear하고 stateObject의 suicided에 마크한다.
updateStateObject()는 trie에 stateobject 를 추가
setStateObject()는 stateDB에 저장
createObject()는 새 state object 생성, 이미 있는 주소의 account라면, overwrite
createAccount()는 createObject()를 호출함.
- Snapshot()은 현재 state의 개정(revision) id를 반환(stateDB에서)
type revision struct { id int journalIndex int }
- Finalise 는 suicided = true인 object를 삭제하고, journal과 refund clear
- touch event 는 journal.dirties에 존재하지만 stateobject에 존재하지 않는다.
- Commit() 하면 변경된 state object가 trie db에 저장됨. suicided된 object는 trie에서 제거, dirty가 존재할때, object.code ≠ nill && object.dirtyCode 이면
s.db.TrieDB().InsertBlob(common.BytesToHash(stateObject.CodeHash()), stateObject.code)
stateObject.CommitTrie(s.db)
s.updateStateObject(stateObject)
trie.Commit
managedState.go
sync.go
NewStateSync.go
// NewStateSync create a new state trie download scheduler. func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.SyncBloom) *trie.Sync { var syncer *trie.Sync callback := func(leaf []byte, parent common.Hash) error { var obj Account if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil { return err } syncer.AddSubTrie(obj.Root, 64, parent, nil) syncer.AddRawEntry(common.BytesToHash(obj.CodeHash), 64, parent) return nil }block -> state trie -> account* -> storage trie syncer = trie.NewSync(root, database, callback, bloom) return syncer }
blockchain.go
// CacheConfig contains the configuration values for the trie caching/pruning // that's resident in a blockchain. type CacheConfig struct { TrieCleanLimit int // Memory allowance (MB) to use for caching trie nodes in memory TrieCleanNoPrefetch bool // Whether to disable heuristic state prefetching for followup blocks TrieDirtyLimit int // Memory limit (MB) at which to start flushing dirty trie nodes to disk TrieDirtyDisabled bool // Whether to disable trie write caching and GC altogether (archive node) TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk }
type BlockChain struct { ... stateCache state.Database // State database to reuse between imports (contains state cache) bodyCache *lru.Cache // Cache for the most recent block bodies bodyRLPCache *lru.Cache // Cache for the most recent block bodies in RLP encoded format receiptsCache *lru.Cache // Cache for the most recent receipts per block blockCache *lru.Cache // Cache for the most recent entire blocks futureBlocks *lru.Cache // future blocks are blocks added for later processing ... validator Validator // Block and state validator interface prefetcher Prefetcher // Block state prefetcher interface processor Processor // Block transaction processor interface
triegc : prioty queue를 사용. block number를 tries와 매핑함
triegc *prque.Prque // Priority queue mapping block numbers to tries to gc
TrieCleanLimit : trie의 node 캐싱에 사용할 여유 메모리
TrieCleanLimit: 256
mb TrieDirtyLimit : dirty node(상태변화가 일어난 노드)를 디스크에 flush하기 시작하는 메모리 시점
TrieDirtyLimit: 256
mb stateCache : NewDatabaseWithCache는 state backing store를 생성. large memory cache에 trie의 지난 노드들을 저장한다.
stateCache: state.NewDatabaseWithCache(db, cacheConfig.TrieCleanLimit),
secure tree 생성
exit전까지는 recent block의 상태가 127개까지 db에 저장되어있으나,
stop 전에 TrieDirtyDisabled(archive node)가 false면
triegc가 empty일때까지 triedb에서도Dereference해야함.
반대로,
full이지만 archive node가 아닌경우
triedb.Reference를 통해 triegc에 push함
TD : Total difficulties
다른 블록데이터(receipt)는 batch를 이용해 rawdb에 저장
trie.go
필요하다 생각되는 부분 있을때마다 볼것임.
tryGet(value,newnode,didResolve,error)
hashNode : roothash를 의미하는듯
valueNode : value Node
shortNode: expansion node 혹은 leaf node
fullNode: branch node
Update→TryUpdate→ value의 길이가 0이아니면 insert 그렇지않으면 delete