mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 01:43:47 +00:00
cmd, core, eth, light, trie: gcmode flag + supress archive logs
This commit is contained in:
parent
49bdc8b121
commit
54b8ded249
13 changed files with 98 additions and 67 deletions
|
|
@ -85,6 +85,7 @@ var (
|
||||||
utils.FastSyncFlag,
|
utils.FastSyncFlag,
|
||||||
utils.LightModeFlag,
|
utils.LightModeFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
|
utils.GCModeFlag,
|
||||||
utils.LightServFlag,
|
utils.LightServFlag,
|
||||||
utils.LightPeersFlag,
|
utils.LightPeersFlag,
|
||||||
utils.LightKDFFlag,
|
utils.LightKDFFlag,
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||||
utils.TestnetFlag,
|
utils.TestnetFlag,
|
||||||
utils.RinkebyFlag,
|
utils.RinkebyFlag,
|
||||||
utils.SyncModeFlag,
|
utils.SyncModeFlag,
|
||||||
|
utils.GCModeFlag,
|
||||||
utils.EthStatsURLFlag,
|
utils.EthStatsURLFlag,
|
||||||
utils.IdentityFlag,
|
utils.IdentityFlag,
|
||||||
utils.LightServFlag,
|
utils.LightServFlag,
|
||||||
|
|
|
||||||
|
|
@ -170,7 +170,11 @@ var (
|
||||||
Usage: `Blockchain sync mode ("fast", "full", or "light")`,
|
Usage: `Blockchain sync mode ("fast", "full", or "light")`,
|
||||||
Value: &defaultSyncMode,
|
Value: &defaultSyncMode,
|
||||||
}
|
}
|
||||||
|
GCModeFlag = cli.StringFlag{
|
||||||
|
Name: "gcmode",
|
||||||
|
Usage: `Blockchain garbage collection mode ("full", "archive")`,
|
||||||
|
Value: "full",
|
||||||
|
}
|
||||||
LightServFlag = cli.IntFlag{
|
LightServFlag = cli.IntFlag{
|
||||||
Name: "lightserv",
|
Name: "lightserv",
|
||||||
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
|
Usage: "Maximum percentage of time allowed for serving LES requests (0-90)",
|
||||||
|
|
@ -1036,6 +1040,11 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *eth.Config) {
|
||||||
}
|
}
|
||||||
cfg.DatabaseHandles = makeDatabaseHandles()
|
cfg.DatabaseHandles = makeDatabaseHandles()
|
||||||
|
|
||||||
|
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
||||||
|
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
||||||
|
}
|
||||||
|
cfg.NoPruning = ctx.GlobalString(GCModeFlag.Name) == "archive"
|
||||||
|
|
||||||
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
|
if ctx.GlobalIsSet(CacheFlag.Name) || ctx.GlobalIsSet(CacheGCFlag.Name) {
|
||||||
cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
cfg.TrieCache = ctx.GlobalInt(CacheFlag.Name) * ctx.GlobalInt(CacheGCFlag.Name) / 100
|
||||||
}
|
}
|
||||||
|
|
@ -1222,7 +1231,11 @@ func MakeChain(ctx *cli.Context, stack *node.Node) (chain *core.BlockChain, chai
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if gcmode := ctx.GlobalString(GCModeFlag.Name); gcmode != "full" && gcmode != "archive" {
|
||||||
|
Fatalf("--%s must be either 'full' or 'archive'", GCModeFlag.Name)
|
||||||
|
}
|
||||||
cache := &core.CacheConfig{
|
cache := &core.CacheConfig{
|
||||||
|
Disabled: ctx.GlobalString(GCModeFlag.Name) == "archive",
|
||||||
TrieNodeLimit: eth.DefaultConfig.TrieCache,
|
TrieNodeLimit: eth.DefaultConfig.TrieCache,
|
||||||
TrieTimeLimit: eth.DefaultConfig.TrieTimeout,
|
TrieTimeLimit: eth.DefaultConfig.TrieTimeout,
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ const (
|
||||||
// CacheConfig contains the configuration values for the trie caching/pruning
|
// CacheConfig contains the configuration values for the trie caching/pruning
|
||||||
// that's resident in a blockchain.
|
// that's resident in a blockchain.
|
||||||
type CacheConfig struct {
|
type CacheConfig struct {
|
||||||
|
Disabled bool // Whether to disable trie write caching (archive node)
|
||||||
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
|
TrieNodeLimit int // Memory limit (MB) at which to flush the current in-memory trie to disk
|
||||||
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
|
TrieTimeLimit time.Duration // Time limit after which to flush the current in-memory trie to disk
|
||||||
}
|
}
|
||||||
|
|
@ -655,12 +656,13 @@ func (bc *BlockChain) Stop() {
|
||||||
//
|
//
|
||||||
// This may be tuned a bit on mainnet if its too annoying to reprocess the last
|
// This may be tuned a bit on mainnet if its too annoying to reprocess the last
|
||||||
// N blocks.
|
// N blocks.
|
||||||
|
if !bc.cacheConfig.Disabled {
|
||||||
triedb := bc.stateCache.TrieDB()
|
triedb := bc.stateCache.TrieDB()
|
||||||
if number := bc.CurrentBlock().NumberU64(); number >= triesInMemory {
|
if number := bc.CurrentBlock().NumberU64(); number >= triesInMemory {
|
||||||
recent := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - triesInMemory + 1)
|
recent := bc.GetBlockByNumber(bc.CurrentBlock().NumberU64() - triesInMemory + 1)
|
||||||
|
|
||||||
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
|
log.Info("Writing cached state to disk", "block", recent.Number(), "hash", recent.Hash(), "root", recent.Root())
|
||||||
if err := triedb.Commit(recent.Root()); err != nil {
|
if err := triedb.Commit(recent.Root(), true); err != nil {
|
||||||
log.Error("Failed to commit recent state trie", "err", err)
|
log.Error("Failed to commit recent state trie", "err", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -670,6 +672,7 @@ func (bc *BlockChain) Stop() {
|
||||||
if size := triedb.Size(); size != 0 {
|
if size := triedb.Size(); size != 0 {
|
||||||
log.Error("Dangling trie nodes after full cleanup")
|
log.Error("Dangling trie nodes after full cleanup")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
log.Info("Blockchain manager stopped")
|
log.Info("Blockchain manager stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -896,6 +899,13 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
triedb := bc.stateCache.TrieDB()
|
triedb := bc.stateCache.TrieDB()
|
||||||
|
|
||||||
|
// If we're running an archive node, always flush
|
||||||
|
if bc.cacheConfig.Disabled {
|
||||||
|
if err := triedb.Commit(root, false); err != nil {
|
||||||
|
return NonStatTy, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Full but not archive node, do proper garbage collection
|
||||||
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
triedb.Reference(root, common.Hash{}) // metadata reference to keep trie alive
|
||||||
bc.triegc.Push(root, -float32(block.NumberU64()))
|
bc.triegc.Push(root, -float32(block.NumberU64()))
|
||||||
|
|
||||||
|
|
@ -927,7 +937,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
}
|
}
|
||||||
// If optimum or critical limits reached, write to disk
|
// If optimum or critical limits reached, write to disk
|
||||||
if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
if chosen >= lastWrite+triesInMemory || size >= 2*limit || bc.gcproc >= 2*bc.cacheConfig.TrieTimeLimit {
|
||||||
triedb.Commit(header.Root)
|
triedb.Commit(header.Root, true)
|
||||||
lastWrite = chosen
|
lastWrite = chosen
|
||||||
bc.gcproc = 0
|
bc.gcproc = 0
|
||||||
}
|
}
|
||||||
|
|
@ -942,6 +952,7 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
|
||||||
triedb.Dereference(root.(common.Hash), common.Hash{})
|
triedb.Dereference(root.(common.Hash), common.Hash{})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
if err := WriteBlockReceipts(batch, block.Hash(), block.NumberU64(), receipts); err != nil {
|
||||||
return NonStatTy, err
|
return NonStatTy, err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ func GenerateChain(config *params.ChainConfig, parent *types.Block, engine conse
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Sprintf("state write error: %v", err))
|
panic(fmt.Sprintf("state write error: %v", err))
|
||||||
}
|
}
|
||||||
if err := statedb.Database().TrieDB().Commit(root); err != nil {
|
if err := statedb.Database().TrieDB().Commit(root, false); err != nil {
|
||||||
panic(fmt.Sprintf("trie write error: %v", err))
|
panic(fmt.Sprintf("trie write error: %v", err))
|
||||||
}
|
}
|
||||||
return block, b.receipts
|
return block, b.receipts
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
if _, err := bc.InsertChain(blocks); err != nil {
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||||
}
|
}
|
||||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
|
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||||
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
||||||
}
|
}
|
||||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
|
@ -104,7 +104,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
if _, err := bc.InsertChain(blocks); err != nil {
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||||
}
|
}
|
||||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
|
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||||
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
||||||
}
|
}
|
||||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
|
@ -130,7 +130,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
if _, err := bc.InsertChain(blocks); err != nil {
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
t.Fatalf("failed to import contra-fork chain for expansion: %v", err)
|
||||||
}
|
}
|
||||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
|
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||||
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
t.Fatalf("failed to commit contra-fork head for expansion: %v", err)
|
||||||
}
|
}
|
||||||
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
blocks, _ = GenerateChain(&proConf, conBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
|
@ -150,7 +150,7 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||||
if _, err := bc.InsertChain(blocks); err != nil {
|
if _, err := bc.InsertChain(blocks); err != nil {
|
||||||
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
t.Fatalf("failed to import pro-fork chain for expansion: %v", err)
|
||||||
}
|
}
|
||||||
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root); err != nil {
|
if err := bc.stateCache.TrieDB().Commit(bc.CurrentHeader().Root, true); err != nil {
|
||||||
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
t.Fatalf("failed to commit pro-fork head for expansion: %v", err)
|
||||||
}
|
}
|
||||||
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
blocks, _ = GenerateChain(&conConf, proBc.CurrentBlock(), ethash.NewFaker(), db, 1, func(i int, gen *BlockGen) {})
|
||||||
|
|
|
||||||
|
|
@ -255,7 +255,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
|
||||||
head.Difficulty = params.GenesisDifficulty
|
head.Difficulty = params.GenesisDifficulty
|
||||||
}
|
}
|
||||||
statedb.Commit(false)
|
statedb.Commit(false)
|
||||||
statedb.Database().TrieDB().Commit(root)
|
statedb.Database().TrieDB().Commit(root, true)
|
||||||
|
|
||||||
return types.NewBlock(head, nil, nil, nil)
|
return types.NewBlock(head, nil, nil, nil)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -146,7 +146,7 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
vmConfig = vm.Config{EnablePreimageRecording: config.EnablePreimageRecording}
|
||||||
cacheConfig = &core.CacheConfig{TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
cacheConfig = &core.CacheConfig{Disabled: config.NoPruning, TrieNodeLimit: config.TrieCache, TrieTimeLimit: config.TrieTimeout}
|
||||||
)
|
)
|
||||||
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
|
eth.blockchain, err = core.NewBlockChain(chainDb, cacheConfig, eth.chainConfig, eth.engine, vmConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@ type Config struct {
|
||||||
// Protocol options
|
// Protocol options
|
||||||
NetworkId uint64 // Network ID to use for selecting peers to connect to
|
NetworkId uint64 // Network ID to use for selecting peers to connect to
|
||||||
SyncMode downloader.SyncMode
|
SyncMode downloader.SyncMode
|
||||||
|
NoPruning bool
|
||||||
|
|
||||||
// Light client options
|
// Light client options
|
||||||
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
LightServ int `toml:",omitempty"` // Maximum percentage of time allowed for serving LES requests
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ func (c *ChtIndexerBackend) Commit() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
c.triedb.Commit(root)
|
c.triedb.Commit(root, false)
|
||||||
|
|
||||||
if ((c.section+1)*c.sectionSize)%ChtFrequency == 0 {
|
if ((c.section+1)*c.sectionSize)%ChtFrequency == 0 {
|
||||||
log.Info("Storing CHT", "idx", c.section*c.sectionSize/ChtFrequency, "sectionHead", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root))
|
log.Info("Storing CHT", "idx", c.section*c.sectionSize/ChtFrequency, "sectionHead", fmt.Sprintf("%064x", c.lastHash), "root", fmt.Sprintf("%064x", root))
|
||||||
|
|
@ -291,7 +291,7 @@ func (b *BloomTrieIndexerBackend) Commit() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
b.triedb.Commit(root)
|
b.triedb.Commit(root, false)
|
||||||
|
|
||||||
sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
|
sectionHead := b.sectionHeads[b.bloomTrieRatio-1]
|
||||||
log.Info("Storing BloomTrie", "section", b.section, "sectionHead", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression ratio", float64(compSize)/float64(decompSize))
|
log.Info("Storing BloomTrie", "section", b.section, "sectionHead", fmt.Sprintf("%064x", sectionHead), "root", fmt.Sprintf("%064x", root), "compression ratio", float64(compSize)/float64(decompSize))
|
||||||
|
|
|
||||||
|
|
@ -241,7 +241,7 @@ func (db *Database) dereference(child common.Hash, parent common.Hash) {
|
||||||
// to disk, forcefully tearing down all references in both directions.
|
// to disk, forcefully tearing down all references in both directions.
|
||||||
//
|
//
|
||||||
// As a side effect, all pre-images accumulated up to this point are also written.
|
// As a side effect, all pre-images accumulated up to this point are also written.
|
||||||
func (db *Database) Commit(node common.Hash) error {
|
func (db *Database) Commit(node common.Hash, report bool) error {
|
||||||
// Create a database batch to flush persistent data out. It is important that
|
// Create a database batch to flush persistent data out. It is important that
|
||||||
// outside code doesn't see an inconsistent state (referenced data removed from
|
// outside code doesn't see an inconsistent state (referenced data removed from
|
||||||
// memory cache during commit but not yet in persistent storage). This is ensured
|
// memory cache during commit but not yet in persistent storage). This is ensured
|
||||||
|
|
@ -289,7 +289,11 @@ func (db *Database) Commit(node common.Hash) error {
|
||||||
|
|
||||||
db.uncache(node)
|
db.uncache(node)
|
||||||
|
|
||||||
log.Info("Persisted trie from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.nodesSize, "time", time.Since(start),
|
logger := log.Info
|
||||||
|
if !report {
|
||||||
|
logger = log.Debug
|
||||||
|
}
|
||||||
|
logger("Persisted trie from memory database", "nodes", nodes-len(db.nodes), "size", storage-db.nodesSize, "time", time.Since(start),
|
||||||
"gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.nodesSize)
|
"gcnodes", db.gcnodes, "gcsize", db.gcsize, "gctime", db.gctime, "livenodes", len(db.nodes), "livesize", db.nodesSize)
|
||||||
|
|
||||||
// Reset the garbage collection statistics
|
// Reset the garbage collection statistics
|
||||||
|
|
|
||||||
|
|
@ -298,7 +298,7 @@ func testIteratorContinueAfterError(t *testing.T, memonly bool) {
|
||||||
}
|
}
|
||||||
tr.Commit(nil)
|
tr.Commit(nil)
|
||||||
if !memonly {
|
if !memonly {
|
||||||
triedb.Commit(tr.Hash())
|
triedb.Commit(tr.Hash(), true)
|
||||||
}
|
}
|
||||||
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
|
wantNodeCount := checkIteratorNoDups(t, tr.NodeIterator(nil), nil)
|
||||||
|
|
||||||
|
|
@ -385,7 +385,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
|
||||||
}
|
}
|
||||||
root, _ := ctr.Commit(nil)
|
root, _ := ctr.Commit(nil)
|
||||||
if !memonly {
|
if !memonly {
|
||||||
triedb.Commit(root)
|
triedb.Commit(root, true)
|
||||||
}
|
}
|
||||||
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
|
barNodeHash := common.HexToHash("05041990364eb72fcb1127652ce40d8bab765f2bfe53225b1170d276cc101c2e")
|
||||||
var (
|
var (
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,7 @@ func testMissingNode(t *testing.T, memonly bool) {
|
||||||
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
|
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
|
||||||
root, _ := trie.Commit(nil)
|
root, _ := trie.Commit(nil)
|
||||||
if !memonly {
|
if !memonly {
|
||||||
triedb.Commit(root)
|
triedb.Commit(root, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
trie, _ = New(root, triedb)
|
trie, _ = New(root, triedb)
|
||||||
|
|
@ -339,7 +339,7 @@ func TestCacheUnload(t *testing.T) {
|
||||||
updateString(trie, key2, "this is the branch of key2.")
|
updateString(trie, key2, "this is the branch of key2.")
|
||||||
|
|
||||||
root, _ := trie.Commit(nil)
|
root, _ := trie.Commit(nil)
|
||||||
trie.db.Commit(root)
|
trie.db.Commit(root, true)
|
||||||
|
|
||||||
// Commit the trie repeatedly and access key1.
|
// Commit the trie repeatedly and access key1.
|
||||||
// The branch containing it is loaded from DB exactly two times:
|
// The branch containing it is loaded from DB exactly two times:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue