mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-02-26 07:37:20 +00:00
This pull request removes the node copy operation to reduce memory allocation. Key Changes as below: **(a) Use `decodeNodeUnsafe` for decoding nodes retrieved from the trie node reader** In the current implementation of the MPT, once a trie node blob is retrieved, it is passed to `decodeNode` for decoding. However, `decodeNode` assumes the supplied byte slice might be mutated later, so it performs a deep copy internally before parsing the node. Given that the node reader is implemented by the path database and the hash database, both of which guarantee the immutability of the returned byte slice. By restricting the node reader interface to explicitly guarantee that the returned byte slice will not be modified, we can safely replace `decodeNode` with `decodeNodeUnsafe`. This eliminates the need for a redundant byte copy during each node resolution. **(b) Modify the trie in place** In the current implementation of the MPT, a copy of a trie node is created before any modifications are made. These modifications include: - Node resolution: Converting the value from a hash to the actual node. - Node hashing: Tagging the hash into its cache. - Node commit: Replacing the children with its hash. - Structural changes: For example, adding a new child to a fullNode or replacing a child of a shortNode. This mechanism ensures that modifications only affect the live tree, leaving all previously created copies unaffected. Unfortunately, this property leads to a huge memory allocation requirement. For example, if we want to modify the fullNode for n times, the node will be copied for n times. In this pull request, all the trie modifications are made in place. In order to make sure all previously created copies are unaffected, the `Copy` function now will deep-copy all the live nodes rather than the root node itself. With this change, while the `Copy` function becomes more expensive, it's totally acceptable as it's not a frequently used one. For the normal trie operations (Get, GetNode, Hash, Commit, Insert, Delete), the node copy is not required anymore.
70 lines
2.9 KiB
Go
70 lines
2.9 KiB
Go
// Copyright 2024 The go-ethereum Authors
|
|
// This file is part of the go-ethereum library.
|
|
//
|
|
// The go-ethereum library is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Lesser General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// The go-ethereum library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Lesser General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Lesser General Public License
|
|
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package database
|
|
|
|
import (
|
|
"github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/core/types"
|
|
)
|
|
|
|
// NodeReader wraps the Node method of a backing trie reader.
|
|
type NodeReader interface {
|
|
// Node retrieves the trie node blob with the provided trie identifier,
|
|
// node path and the corresponding node hash. No error will be returned
|
|
// if the node is not found.
|
|
//
|
|
// The returned node content won't be changed after the call.
|
|
//
|
|
// Don't modify the returned byte slice since it's not deep-copied and
|
|
// still be referenced by database.
|
|
Node(owner common.Hash, path []byte, hash common.Hash) ([]byte, error)
|
|
}
|
|
|
|
// NodeDatabase wraps the methods of a backing trie store.
|
|
type NodeDatabase interface {
|
|
// NodeReader returns a node reader associated with the specific state.
|
|
// An error will be returned if the specified state is not available.
|
|
NodeReader(stateRoot common.Hash) (NodeReader, error)
|
|
}
|
|
|
|
// StateReader wraps the Account and Storage method of a backing state reader.
|
|
type StateReader interface {
|
|
// Account directly retrieves the account associated with a particular hash in
|
|
// the slim data format. An error will be returned if the read operation exits
|
|
// abnormally. Specifically, if the layer is already stale.
|
|
//
|
|
// Note:
|
|
// - the returned account object is safe to modify
|
|
// - no error will be returned if the requested account is not found in database
|
|
Account(hash common.Hash) (*types.SlimAccount, error)
|
|
|
|
// Storage directly retrieves the storage data associated with a particular hash,
|
|
// within a particular account. An error will be returned if the read operation
|
|
// exits abnormally.
|
|
//
|
|
// Note:
|
|
// - the returned storage data is not a copy, please don't modify it
|
|
// - no error will be returned if the requested slot is not found in database
|
|
Storage(accountHash, storageHash common.Hash) ([]byte, error)
|
|
}
|
|
|
|
// StateDatabase wraps the methods of a backing state store.
|
|
type StateDatabase interface {
|
|
// StateReader returns a state reader associated with the specific state.
|
|
// An error will be returned if the specified state is not available.
|
|
StateReader(stateRoot common.Hash) (StateReader, error)
|
|
}
|