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  }