github.com/tacshi/go-ethereum@v0.0.0-20230616113857-84a434e20921/core/rawdb/accessors_trie.go (about) 1 // Copyright 2022 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 rawdb 18 19 import ( 20 "fmt" 21 "sync" 22 23 "github.com/tacshi/go-ethereum/common" 24 "github.com/tacshi/go-ethereum/crypto" 25 "github.com/tacshi/go-ethereum/ethdb" 26 "github.com/tacshi/go-ethereum/log" 27 "golang.org/x/crypto/sha3" 28 ) 29 30 // HashScheme is the legacy hash-based state scheme with which trie nodes are 31 // stored in the disk with node hash as the database key. The advantage of this 32 // scheme is that different versions of trie nodes can be stored in disk, which 33 // is very beneficial for constructing archive nodes. The drawback is it will 34 // store different trie nodes on the same path to different locations on the disk 35 // with no data locality, and it's unfriendly for designing state pruning. 36 // 37 // Now this scheme is still kept for backward compatibility, and it will be used 38 // for archive node and some other tries(e.g. light trie). 39 const HashScheme = "hashScheme" 40 41 // PathScheme is the new path-based state scheme with which trie nodes are stored 42 // in the disk with node path as the database key. This scheme will only store one 43 // version of state data in the disk, which means that the state pruning operation 44 // is native. At the same time, this scheme will put adjacent trie nodes in the same 45 // area of the disk with good data locality property. But this scheme needs to rely 46 // on extra state diffs to survive deep reorg. 47 const PathScheme = "pathScheme" 48 49 // nodeHasher used to derive the hash of trie node. 50 type nodeHasher struct{ sha crypto.KeccakState } 51 52 var hasherPool = sync.Pool{ 53 New: func() interface{} { return &nodeHasher{sha: sha3.NewLegacyKeccak256().(crypto.KeccakState)} }, 54 } 55 56 func newNodeHasher() *nodeHasher { return hasherPool.Get().(*nodeHasher) } 57 func returnHasherToPool(h *nodeHasher) { hasherPool.Put(h) } 58 59 func (h *nodeHasher) hashData(data []byte) (n common.Hash) { 60 h.sha.Reset() 61 h.sha.Write(data) 62 h.sha.Read(n[:]) 63 return n 64 } 65 66 // ReadAccountTrieNode retrieves the account trie node and the associated node 67 // hash with the specified node path. 68 func ReadAccountTrieNode(db ethdb.KeyValueReader, path []byte) ([]byte, common.Hash) { 69 data, err := db.Get(accountTrieNodeKey(path)) 70 if err != nil { 71 return nil, common.Hash{} 72 } 73 hasher := newNodeHasher() 74 defer returnHasherToPool(hasher) 75 return data, hasher.hashData(data) 76 } 77 78 // HasAccountTrieNode checks the account trie node presence with the specified 79 // node path and the associated node hash. 80 func HasAccountTrieNode(db ethdb.KeyValueReader, path []byte, hash common.Hash) bool { 81 data, err := db.Get(accountTrieNodeKey(path)) 82 if err != nil { 83 return false 84 } 85 hasher := newNodeHasher() 86 defer returnHasherToPool(hasher) 87 return hasher.hashData(data) == hash 88 } 89 90 // WriteAccountTrieNode writes the provided account trie node into database. 91 func WriteAccountTrieNode(db ethdb.KeyValueWriter, path []byte, node []byte) { 92 if err := db.Put(accountTrieNodeKey(path), node); err != nil { 93 log.Crit("Failed to store account trie node", "err", err) 94 } 95 } 96 97 // DeleteAccountTrieNode deletes the specified account trie node from the database. 98 func DeleteAccountTrieNode(db ethdb.KeyValueWriter, path []byte) { 99 if err := db.Delete(accountTrieNodeKey(path)); err != nil { 100 log.Crit("Failed to delete account trie node", "err", err) 101 } 102 } 103 104 // ReadStorageTrieNode retrieves the storage trie node and the associated node 105 // hash with the specified node path. 106 func ReadStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte) ([]byte, common.Hash) { 107 data, err := db.Get(storageTrieNodeKey(accountHash, path)) 108 if err != nil { 109 return nil, common.Hash{} 110 } 111 hasher := newNodeHasher() 112 defer returnHasherToPool(hasher) 113 return data, hasher.hashData(data) 114 } 115 116 // HasStorageTrieNode checks the storage trie node presence with the provided 117 // node path and the associated node hash. 118 func HasStorageTrieNode(db ethdb.KeyValueReader, accountHash common.Hash, path []byte, hash common.Hash) bool { 119 data, err := db.Get(storageTrieNodeKey(accountHash, path)) 120 if err != nil { 121 return false 122 } 123 hasher := newNodeHasher() 124 defer returnHasherToPool(hasher) 125 return hasher.hashData(data) == hash 126 } 127 128 // WriteStorageTrieNode writes the provided storage trie node into database. 129 func WriteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte, node []byte) { 130 if err := db.Put(storageTrieNodeKey(accountHash, path), node); err != nil { 131 log.Crit("Failed to store storage trie node", "err", err) 132 } 133 } 134 135 // DeleteStorageTrieNode deletes the specified storage trie node from the database. 136 func DeleteStorageTrieNode(db ethdb.KeyValueWriter, accountHash common.Hash, path []byte) { 137 if err := db.Delete(storageTrieNodeKey(accountHash, path)); err != nil { 138 log.Crit("Failed to delete storage trie node", "err", err) 139 } 140 } 141 142 // ReadLegacyTrieNode retrieves the legacy trie node with the given 143 // associated node hash. 144 func ReadLegacyTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte { 145 data, err := db.Get(hash.Bytes()) 146 if err != nil { 147 return nil 148 } 149 return data 150 } 151 152 // HasLegacyTrieNode checks if the trie node with the provided hash is present in db. 153 func HasLegacyTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool { 154 ok, _ := db.Has(hash.Bytes()) 155 return ok 156 } 157 158 // WriteLegacyTrieNode writes the provided legacy trie node to database. 159 func WriteLegacyTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) { 160 if err := db.Put(hash.Bytes(), node); err != nil { 161 log.Crit("Failed to store legacy trie node", "err", err) 162 } 163 } 164 165 // DeleteLegacyTrieNode deletes the specified legacy trie node from database. 166 func DeleteLegacyTrieNode(db ethdb.KeyValueWriter, hash common.Hash) { 167 if err := db.Delete(hash.Bytes()); err != nil { 168 log.Crit("Failed to delete legacy trie node", "err", err) 169 } 170 } 171 172 // HasTrieNode checks the trie node presence with the provided node info and 173 // the associated node hash. 174 func HasTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) bool { 175 switch scheme { 176 case HashScheme: 177 return HasLegacyTrieNode(db, hash) 178 case PathScheme: 179 if owner == (common.Hash{}) { 180 return HasAccountTrieNode(db, path, hash) 181 } 182 return HasStorageTrieNode(db, owner, path, hash) 183 default: 184 panic(fmt.Sprintf("Unknown scheme %v", scheme)) 185 } 186 } 187 188 // ReadTrieNode retrieves the trie node from database with the provided node info 189 // and associated node hash. 190 // hashScheme-based lookup requires the following: 191 // - hash 192 // 193 // pathScheme-based lookup requires the following: 194 // - owner 195 // - path 196 func ReadTrieNode(db ethdb.KeyValueReader, owner common.Hash, path []byte, hash common.Hash, scheme string) []byte { 197 switch scheme { 198 case HashScheme: 199 return ReadLegacyTrieNode(db, hash) 200 case PathScheme: 201 var ( 202 blob []byte 203 nHash common.Hash 204 ) 205 if owner == (common.Hash{}) { 206 blob, nHash = ReadAccountTrieNode(db, path) 207 } else { 208 blob, nHash = ReadStorageTrieNode(db, owner, path) 209 } 210 if nHash != hash { 211 return nil 212 } 213 return blob 214 default: 215 panic(fmt.Sprintf("Unknown scheme %v", scheme)) 216 } 217 } 218 219 // WriteTrieNode writes the trie node into database with the provided node info 220 // and associated node hash. 221 // hashScheme-based lookup requires the following: 222 // - hash 223 // 224 // pathScheme-based lookup requires the following: 225 // - owner 226 // - path 227 func WriteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, node []byte, scheme string) { 228 switch scheme { 229 case HashScheme: 230 WriteLegacyTrieNode(db, hash, node) 231 case PathScheme: 232 if owner == (common.Hash{}) { 233 WriteAccountTrieNode(db, path, node) 234 } else { 235 WriteStorageTrieNode(db, owner, path, node) 236 } 237 default: 238 panic(fmt.Sprintf("Unknown scheme %v", scheme)) 239 } 240 } 241 242 // DeleteTrieNode deletes the trie node from database with the provided node info 243 // and associated node hash. 244 // hashScheme-based lookup requires the following: 245 // - hash 246 // 247 // pathScheme-based lookup requires the following: 248 // - owner 249 // - path 250 func DeleteTrieNode(db ethdb.KeyValueWriter, owner common.Hash, path []byte, hash common.Hash, scheme string) { 251 switch scheme { 252 case HashScheme: 253 DeleteLegacyTrieNode(db, hash) 254 case PathScheme: 255 if owner == (common.Hash{}) { 256 DeleteAccountTrieNode(db, path) 257 } else { 258 DeleteStorageTrieNode(db, owner, path) 259 } 260 default: 261 panic(fmt.Sprintf("Unknown scheme %v", scheme)) 262 } 263 }