gitlab.com/yannislg/go-pulse@v0.0.0-20210722055913-a3e24e95638d/core/state/snapshot/iterator.go (about) 1 // Copyright 2019 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package snapshot 18 19 import ( 20 "bytes" 21 "fmt" 22 "sort" 23 24 "github.com/ethereum/go-ethereum/common" 25 "github.com/ethereum/go-ethereum/core/rawdb" 26 "github.com/ethereum/go-ethereum/ethdb" 27 ) 28 29 // AccountIterator is an iterator to step over all the accounts in a snapshot, 30 // which may or may npt be composed of multiple layers. 31 type AccountIterator interface { 32 // Next steps the iterator forward one element, returning false if exhausted, 33 // or an error if iteration failed for some reason (e.g. root being iterated 34 // becomes stale and garbage collected). 35 Next() bool 36 37 // Error returns any failure that occurred during iteration, which might have 38 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 39 Error() error 40 41 // Hash returns the hash of the account the iterator is currently at. 42 Hash() common.Hash 43 44 // Account returns the RLP encoded slim account the iterator is currently at. 45 // An error will be returned if the iterator becomes invalid (e.g. snaph 46 Account() []byte 47 48 // Release releases associated resources. Release should always succeed and 49 // can be called multiple times without causing error. 50 Release() 51 } 52 53 // diffAccountIterator is an account iterator that steps over the accounts (both 54 // live and deleted) contained within a single diff layer. Higher order iterators 55 // will use the deleted accounts to skip deeper iterators. 56 type diffAccountIterator struct { 57 // curHash is the current hash the iterator is positioned on. The field is 58 // explicitly tracked since the referenced diff layer might go stale after 59 // the iterator was positioned and we don't want to fail accessing the old 60 // hash as long as the iterator is not touched any more. 61 curHash common.Hash 62 63 layer *diffLayer // Live layer to retrieve values from 64 keys []common.Hash // Keys left in the layer to iterate 65 fail error // Any failures encountered (stale) 66 } 67 68 // AccountIterator creates an account iterator over a single diff layer. 69 func (dl *diffLayer) AccountIterator(seek common.Hash) AccountIterator { 70 // Seek out the requested starting account 71 hashes := dl.AccountList() 72 index := sort.Search(len(hashes), func(i int) bool { 73 return bytes.Compare(seek[:], hashes[i][:]) < 0 74 }) 75 // Assemble and returned the already seeked iterator 76 return &diffAccountIterator{ 77 layer: dl, 78 keys: hashes[index:], 79 } 80 } 81 82 // Next steps the iterator forward one element, returning false if exhausted. 83 func (it *diffAccountIterator) Next() bool { 84 // If the iterator was already stale, consider it a programmer error. Although 85 // we could just return false here, triggering this path would probably mean 86 // somebody forgot to check for Error, so lets blow up instead of undefined 87 // behavior that's hard to debug. 88 if it.fail != nil { 89 panic(fmt.Sprintf("called Next of failed iterator: %v", it.fail)) 90 } 91 // Stop iterating if all keys were exhausted 92 if len(it.keys) == 0 { 93 return false 94 } 95 if it.layer.Stale() { 96 it.fail, it.keys = ErrSnapshotStale, nil 97 return false 98 } 99 // Iterator seems to be still alive, retrieve and cache the live hash 100 it.curHash = it.keys[0] 101 // key cached, shift the iterator and notify the user of success 102 it.keys = it.keys[1:] 103 return true 104 } 105 106 // Error returns any failure that occurred during iteration, which might have 107 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 108 func (it *diffAccountIterator) Error() error { 109 return it.fail 110 } 111 112 // Hash returns the hash of the account the iterator is currently at. 113 func (it *diffAccountIterator) Hash() common.Hash { 114 return it.curHash 115 } 116 117 // Account returns the RLP encoded slim account the iterator is currently at. 118 // This method may _fail_, if the underlying layer has been flattened between 119 // the call to Next and Acccount. That type of error will set it.Err. 120 // This method assumes that flattening does not delete elements from 121 // the accountdata mapping (writing nil into it is fine though), and will panic 122 // if elements have been deleted. 123 func (it *diffAccountIterator) Account() []byte { 124 it.layer.lock.RLock() 125 blob, ok := it.layer.accountData[it.curHash] 126 if !ok { 127 if _, ok := it.layer.destructSet[it.curHash]; ok { 128 return nil 129 } 130 panic(fmt.Sprintf("iterator referenced non-existent account: %x", it.curHash)) 131 } 132 it.layer.lock.RUnlock() 133 if it.layer.Stale() { 134 it.fail, it.keys = ErrSnapshotStale, nil 135 } 136 return blob 137 } 138 139 // Release is a noop for diff account iterators as there are no held resources. 140 func (it *diffAccountIterator) Release() {} 141 142 // diskAccountIterator is an account iterator that steps over the live accounts 143 // contained within a disk layer. 144 type diskAccountIterator struct { 145 layer *diskLayer 146 it ethdb.Iterator 147 } 148 149 // AccountIterator creates an account iterator over a disk layer. 150 func (dl *diskLayer) AccountIterator(seek common.Hash) AccountIterator { 151 pos := common.TrimRightZeroes(seek[:]) 152 return &diskAccountIterator{ 153 layer: dl, 154 it: dl.diskdb.NewIterator(rawdb.SnapshotAccountPrefix, pos), 155 } 156 } 157 158 // Next steps the iterator forward one element, returning false if exhausted. 159 func (it *diskAccountIterator) Next() bool { 160 // If the iterator was already exhausted, don't bother 161 if it.it == nil { 162 return false 163 } 164 // Try to advance the iterator and release it if we reached the end 165 for { 166 if !it.it.Next() || !bytes.HasPrefix(it.it.Key(), rawdb.SnapshotAccountPrefix) { 167 it.it.Release() 168 it.it = nil 169 return false 170 } 171 if len(it.it.Key()) == len(rawdb.SnapshotAccountPrefix)+common.HashLength { 172 break 173 } 174 } 175 return true 176 } 177 178 // Error returns any failure that occurred during iteration, which might have 179 // caused a premature iteration exit (e.g. snapshot stack becoming stale). 180 // 181 // A diff layer is immutable after creation content wise and can always be fully 182 // iterated without error, so this method always returns nil. 183 func (it *diskAccountIterator) Error() error { 184 return it.it.Error() 185 } 186 187 // Hash returns the hash of the account the iterator is currently at. 188 func (it *diskAccountIterator) Hash() common.Hash { 189 return common.BytesToHash(it.it.Key()) 190 } 191 192 // Account returns the RLP encoded slim account the iterator is currently at. 193 func (it *diskAccountIterator) Account() []byte { 194 return it.it.Value() 195 } 196 197 // Release releases the database snapshot held during iteration. 198 func (it *diskAccountIterator) Release() { 199 // The iterator is auto-released on exhaustion, so make sure it's still alive 200 if it.it != nil { 201 it.it.Release() 202 it.it = nil 203 } 204 }