Skip to content

Commit 8860b39

Browse files
all: prepare for path-based trie storage (#26603)
This PR moves some trie-related db accessor methods to a different file, and also removes the schema type. Instead of the schema type, a string is used to distinguish between hashbased/pathbased db accessors. This also moves some code from trie package to rawdb package. This PR is intended to be a no-functionality-change prep PR for #25963 . --------- Co-authored-by: Gary Rong <garyrong0905@gmail.com>
1 parent 10c1484 commit 8860b39

File tree

20 files changed

+336
-184
lines changed

20 files changed

+336
-184
lines changed

cmd/geth/snapshot.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func traverseRawState(ctx *cli.Context) error {
399399
// Check the present for non-empty hash node(embedded node doesn't
400400
// have their own hash).
401401
if node != (common.Hash{}) {
402-
blob := rawdb.ReadTrieNode(chaindb, node)
402+
blob := rawdb.ReadLegacyTrieNode(chaindb, node)
403403
if len(blob) == 0 {
404404
log.Error("Missing trie node(account)", "hash", node)
405405
return errors.New("missing account")
@@ -436,7 +436,7 @@ func traverseRawState(ctx *cli.Context) error {
436436
// Check the present for non-empty hash node(embedded node doesn't
437437
// have their own hash).
438438
if node != (common.Hash{}) {
439-
blob := rawdb.ReadTrieNode(chaindb, node)
439+
blob := rawdb.ReadLegacyTrieNode(chaindb, node)
440440
if len(blob) == 0 {
441441
log.Error("Missing trie node(storage)", "hash", node)
442442
return errors.New("missing storage")

core/rawdb/accessors_state.go

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ func ReadPreimage(db ethdb.KeyValueReader, hash common.Hash) []byte {
2828
return data
2929
}
3030

31+
// WritePreimages writes the provided set of preimages to the database.
32+
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
33+
for hash, preimage := range preimages {
34+
if err := db.Put(preimageKey(hash), preimage); err != nil {
35+
log.Crit("Failed to store trie preimage", "err", err)
36+
}
37+
}
38+
preimageCounter.Inc(int64(len(preimages)))
39+
preimageHitCounter.Inc(int64(len(preimages)))
40+
}
41+
3142
// ReadCode retrieves the contract code of the provided code hash.
3243
func ReadCode(db ethdb.KeyValueReader, hash common.Hash) []byte {
3344
// Try with the prefixed code scheme first, if not then try with legacy
@@ -48,12 +59,6 @@ func ReadCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) []byte {
4859
return data
4960
}
5061

51-
// ReadTrieNode retrieves the trie node of the provided hash.
52-
func ReadTrieNode(db ethdb.KeyValueReader, hash common.Hash) []byte {
53-
data, _ := db.Get(hash.Bytes())
54-
return data
55-
}
56-
5762
// HasCode checks if the contract code corresponding to the
5863
// provided code hash is present in the db.
5964
func HasCode(db ethdb.KeyValueReader, hash common.Hash) bool {
@@ -74,47 +79,16 @@ func HasCodeWithPrefix(db ethdb.KeyValueReader, hash common.Hash) bool {
7479
return ok
7580
}
7681

77-
// HasTrieNode checks if the trie node with the provided hash is present in db.
78-
func HasTrieNode(db ethdb.KeyValueReader, hash common.Hash) bool {
79-
ok, _ := db.Has(hash.Bytes())
80-
return ok
81-
}
82-
83-
// WritePreimages writes the provided set of preimages to the database.
84-
func WritePreimages(db ethdb.KeyValueWriter, preimages map[common.Hash][]byte) {
85-
for hash, preimage := range preimages {
86-
if err := db.Put(preimageKey(hash), preimage); err != nil {
87-
log.Crit("Failed to store trie preimage", "err", err)
88-
}
89-
}
90-
preimageCounter.Inc(int64(len(preimages)))
91-
preimageHitCounter.Inc(int64(len(preimages)))
92-
}
93-
9482
// WriteCode writes the provided contract code database.
9583
func WriteCode(db ethdb.KeyValueWriter, hash common.Hash, code []byte) {
9684
if err := db.Put(codeKey(hash), code); err != nil {
9785
log.Crit("Failed to store contract code", "err", err)
9886
}
9987
}
10088

101-
// WriteTrieNode writes the provided trie node database.
102-
func WriteTrieNode(db ethdb.KeyValueWriter, hash common.Hash, node []byte) {
103-
if err := db.Put(hash.Bytes(), node); err != nil {
104-
log.Crit("Failed to store trie node", "err", err)
105-
}
106-
}
107-
10889
// DeleteCode deletes the specified contract code from the database.
10990
func DeleteCode(db ethdb.KeyValueWriter, hash common.Hash) {
11091
if err := db.Delete(codeKey(hash)); err != nil {
11192
log.Crit("Failed to delete contract code", "err", err)
11293
}
11394
}
114-
115-
// DeleteTrieNode deletes the specified trie node from the database.
116-
func DeleteTrieNode(db ethdb.KeyValueWriter, hash common.Hash) {
117-
if err := db.Delete(hash.Bytes()); err != nil {
118-
log.Crit("Failed to delete trie node", "err", err)
119-
}
120-
}

core/rawdb/accessors_trie.go

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
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/ethereum/go-ethereum/common"
24+
"github.com/ethereum/go-ethereum/crypto"
25+
"github.com/ethereum/go-ethereum/ethdb"
26+
"github.com/ethereum/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+
}

core/rawdb/schema.go

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,14 @@ var (
100100
CodePrefix = []byte("c") // CodePrefix + code hash -> account code
101101
skeletonHeaderPrefix = []byte("S") // skeletonHeaderPrefix + num (uint64 big endian) -> header
102102

103+
// Path-based trie node scheme.
104+
trieNodeAccountPrefix = []byte("A") // trieNodeAccountPrefix + hexPath -> trie node
105+
trieNodeStoragePrefix = []byte("O") // trieNodeStoragePrefix + accountHash + hexPath -> trie node
106+
103107
PreimagePrefix = []byte("secure-key-") // PreimagePrefix + hash -> preimage
104108
configPrefix = []byte("ethereum-config-") // config prefix for the db
105109
genesisPrefix = []byte("ethereum-genesis-") // genesis state prefix for the db
106110

107-
// Chain index prefixes (use `i` + single byte to avoid mixing data types).
108-
109111
// BloomBitsIndexPrefix is the data table of a chain indexer to track its progress
110112
BloomBitsIndexPrefix = []byte("iB")
111113

@@ -236,3 +238,13 @@ func configKey(hash common.Hash) []byte {
236238
func genesisStateSpecKey(hash common.Hash) []byte {
237239
return append(genesisPrefix, hash.Bytes()...)
238240
}
241+
242+
// accountTrieNodeKey = trieNodeAccountPrefix + nodePath.
243+
func accountTrieNodeKey(path []byte) []byte {
244+
return append(trieNodeAccountPrefix, path...)
245+
}
246+
247+
// storageTrieNodeKey = trieNodeStoragePrefix + accountHash + nodePath.
248+
func storageTrieNodeKey(accountHash common.Hash, path []byte) []byte {
249+
return append(append(trieNodeStoragePrefix, accountHash.Bytes()...), path...)
250+
}

0 commit comments

Comments
 (0)