dcrd/blockchain/example_test.go
Ryan Staudt 2c07da6e8e multi: Update UtxoBackend to use leveldb directly.
This updates the UtxoBackend implementation to use leveldb directly
rather than using the database package.  This results in reduced memory
usage and improved processing time since it avoids the overhead of
things that the database package provides, such as caching with periodic
flushing, that are not needed for the UtxoBackend since it has its own
caching layer (UtxoCache).

An overview of the changes is as follows:

- Introduce simple UTXO key sets to be used for constructing prefixes of
  database keys rather than using the "buckets" concept that the
  database package uses
  - All keys in the UTXO backend now start with a serialized prefix
    consisting of the key set (1 byte) and version of that key set (1
    byte)
- Add migration to move all keys in the UTXO backend from buckets to the
  newly defined key sets
- Add Get function to the UtxoBackend interface and corresponding
  implementation in LevelDbUtxoBackend to allow for directly retrieving
  the value for a given key
- Add NewIterator function to the UtxoBackend interface and
  corresponding implementation in LevelDbUtxoBackend to allow for
  iterating over the key/value pairs in the UtxoBackend
- Add Update function to the UtxoBackend interface and corresponding
  implementation in LevelDbUtxoBackend to allow for directly updating
  values in the UtxoBackend in the context of a transaction
- Update LevelDbUtxoBackend to use an underlying leveldb.DB instance
  rather than a database.DB instance
- Update all LevelDbUtxoBackend methods to use the Get, NewIterator, and
  Update functions rather than the database package functions
- Update all database queries to use keys with the new key set prefixes
  rather than bucketized keys
- Update dbFetchUtxoBackendInfo to check the legacy bucket for database
  info if it doesn't exist in the new location
- Update upgrade logic to use the UtxoBackend interface rather than the
  concrete LevelDbUtxoBackend type
- Update all tests to create leveldb.DB databases instead of database.DB
  databases for the UtxoBackend
2021-07-30 16:20:26 -05:00

104 lines
3.7 KiB
Go

// Copyright (c) 2014-2016 The btcsuite developers
// Copyright (c) 2015-2021 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockchain_test
import (
"context"
"fmt"
"os"
"path/filepath"
"github.com/decred/dcrd/blockchain/v4"
"github.com/decred/dcrd/chaincfg/v3"
"github.com/decred/dcrd/database/v2"
_ "github.com/decred/dcrd/database/v2/ffldb"
"github.com/decred/dcrd/dcrutil/v4"
"github.com/syndtr/goleveldb/leveldb"
"github.com/syndtr/goleveldb/leveldb/filter"
"github.com/syndtr/goleveldb/leveldb/opt"
)
// This example demonstrates how to create a new chain instance and use
// ProcessBlock to attempt to add a block to the chain. As the package
// overview documentation describes, this includes all of the Decred consensus
// rules. This example intentionally attempts to insert a duplicate genesis
// block to illustrate how an invalid block is handled.
func ExampleBlockChain_ProcessBlock() {
// Create a new database to store the accepted blocks into. Typically
// this would be opening an existing database and would not be deleting
// and creating a new database like this, but it is done here so this is
// a complete working example and does not leave temporary files laying
// around.
mainNetParams := chaincfg.MainNetParams()
dbPath := filepath.Join(os.TempDir(), "exampleprocessblock")
_ = os.RemoveAll(dbPath)
db, err := database.Create("ffldb", dbPath, mainNetParams.Net)
if err != nil {
fmt.Printf("Failed to create database: %v\n", err)
return
}
defer os.RemoveAll(dbPath)
defer db.Close()
// Additionally, create a new database to store the UTXO set.
utxoDbPath := filepath.Join(os.TempDir(), "exampleprocessblockutxodb")
_ = os.RemoveAll(utxoDbPath)
opts := opt.Options{
Strict: opt.DefaultStrict,
Compression: opt.NoCompression,
Filter: filter.NewBloomFilter(10),
}
utxoDb, err := leveldb.OpenFile(utxoDbPath, &opts)
if err != nil {
fmt.Printf("Failed to create UTXO database: %v\n", err)
return
}
defer os.RemoveAll(utxoDbPath)
defer utxoDb.Close()
// Create a new BlockChain instance using the underlying database for
// the main bitcoin network. This example does not demonstrate some
// of the other available configuration options such as specifying a
// notification callback and signature cache. Also, the caller would
// ordinarily keep a reference to the median time source and add time
// values obtained from other peers on the network so the local time is
// adjusted to be in agreement with other peers.
chain, err := blockchain.New(context.Background(),
&blockchain.Config{
DB: db,
ChainParams: mainNetParams,
TimeSource: blockchain.NewMedianTime(),
UtxoCache: blockchain.NewUtxoCache(&blockchain.UtxoCacheConfig{
Backend: blockchain.NewLevelDbUtxoBackend(utxoDb),
FlushBlockDB: func() error {
return nil
},
MaxSize: 100 * 1024 * 1024, // 100 MiB
}),
})
if err != nil {
fmt.Printf("Failed to create chain instance: %v\n", err)
return
}
// Process a block. For this example, intentionally cause an error by
// trying to process the genesis block which already exists.
genesisBlock := dcrutil.NewBlock(mainNetParams.GenesisBlock)
forkLen, err := chain.ProcessBlock(genesisBlock,
blockchain.BFNone)
if err != nil {
fmt.Printf("Failed to create chain instance: %v\n", err)
return
}
isMainChain := forkLen == 0
fmt.Printf("Block accepted. Is it on the main chain?: %v", isMainChain)
// This output is dependent on the genesis block, and needs to be
// updated if the mainnet genesis block is updated.
// Output:
// Failed to process block: already have block 267a53b5ee86c24a48ec37aee4f4e7c0c4004892b7259e695e9f5b321f1ab9d2
}