From b4806d9e0f751d2d9d4d4cae9680425ab10dcdf2 Mon Sep 17 00:00:00 2001 From: Ryan Staudt Date: Tue, 4 May 2021 20:49:13 -0500 Subject: [PATCH] blockchain: Remove UtxoCacher DB Tx dependency. This removes the dependency that the UtxoCacher interface and UtxoCache implementation currently have on the database.Tx interface. This is necessary since the UtxoCache will move away from the current database interface in favor of an interface that is better suited for the UTXO database in a subsequent commit. --- blockchain/stakeext.go | 54 +++++++++-------------- blockchain/utxocache.go | 84 ++++++++++++++++-------------------- blockchain/utxocache_test.go | 8 +--- 3 files changed, 59 insertions(+), 87 deletions(-) diff --git a/blockchain/stakeext.go b/blockchain/stakeext.go index d09e1290..f16df3ca 100644 --- a/blockchain/stakeext.go +++ b/blockchain/stakeext.go @@ -7,7 +7,6 @@ package blockchain import ( "github.com/decred/dcrd/chaincfg/chainhash" - "github.com/decred/dcrd/database/v2" "github.com/decred/dcrd/dcrutil/v4" "github.com/decred/dcrd/txscript/v4" "github.com/decred/dcrd/txscript/v4/stdaddr" @@ -111,27 +110,22 @@ func (b *BlockChain) TicketsWithAddress(address stdaddr.Address, isTreasuryEnabl encodedAddr := address.String() var ticketsWithAddr []chainhash.Hash - err := b.utxoDb.View(func(dbTx database.Tx) error { - for _, hash := range tickets { - outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake} - utxo, err := b.utxoCache.FetchEntry(dbTx, outpoint) - if err != nil { - return err - } - _, addrs, _, err := txscript.ExtractPkScriptAddrs(utxo.ScriptVersion(), - utxo.PkScript(), b.chainParams, isTreasuryEnabled) - if err != nil { - return err - } - if addrs[0].String() == encodedAddr { - ticketsWithAddr = append(ticketsWithAddr, hash) - } + for _, hash := range tickets { + outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake} + utxo, err := b.utxoCache.FetchEntry(outpoint) + if err != nil { + return nil, err + } + + _, addrs, _, err := txscript.ExtractPkScriptAddrs(utxo.ScriptVersion(), + utxo.PkScript(), b.chainParams, isTreasuryEnabled) + if err != nil { + return nil, err + } + if addrs[0].String() == encodedAddr { + ticketsWithAddr = append(ticketsWithAddr, hash) } - return nil - }) - if err != nil { - return nil, err } return ticketsWithAddr, nil @@ -223,20 +217,14 @@ func (b *BlockChain) TicketPoolValue() (dcrutil.Amount, error) { b.chainLock.RUnlock() var amt int64 - err := b.utxoDb.View(func(dbTx database.Tx) error { - for _, hash := range sn.LiveTickets() { - outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake} - utxo, err := b.utxoCache.FetchEntry(dbTx, outpoint) - if err != nil { - return err - } - - amt += utxo.Amount() + for _, hash := range sn.LiveTickets() { + outpoint := wire.OutPoint{Hash: hash, Index: 0, Tree: wire.TxTreeStake} + utxo, err := b.utxoCache.FetchEntry(outpoint) + if err != nil { + return 0, err } - return nil - }) - if err != nil { - return 0, err + + amt += utxo.Amount() } return dcrutil.Amount(amt), nil } diff --git a/blockchain/utxocache.go b/blockchain/utxocache.go index a33cc9ef..eb894a21 100644 --- a/blockchain/utxocache.go +++ b/blockchain/utxocache.go @@ -58,7 +58,7 @@ const ( periodicFlushInterval = time.Minute * 2 ) -// UtxoCacher represents a utxo cache that sits on top of the utxo set database. +// UtxoCacher represents a utxo cache that sits on top of a utxo set backend. // // The interface contract requires that all of these methods are safe for // concurrent access. @@ -73,7 +73,7 @@ type UtxoCacher interface { // FetchEntries adds the requested transaction outputs to the provided view. // It first checks the cache for each output, and if an output does not exist - // in the cache, it will fetch it from the database. + // in the cache, it will fetch it from the backend. // // Upon completion of this function, the view will contain an entry for each // requested outpoint. Spent outputs, or those which otherwise don't exist, @@ -82,20 +82,19 @@ type UtxoCacher interface { // FetchEntry returns the specified transaction output from the utxo set. If // the output exists in the cache, it is returned immediately. Otherwise, it - // uses an existing database transaction to fetch the output from the - // database, cache it, and return it to the caller. The entry that is - // returned can safely be mutated by the caller without invalidating the - // cache. + // fetches the output from the backend, caches it, and returns it to the + // caller. The entry that is returned can safely be mutated by the caller + // without invalidating the cache. // // When there is no entry for the provided output, nil will be returned for // both the entry and the error. - FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) + FetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error) // Initialize initializes the utxo cache by ensuring that the utxo set is // caught up to the tip of the best chain. Initialize(b *BlockChain, tip *blockNode) error - // MaybeFlush conditionally flushes the cache to the database. A flush can be + // MaybeFlush conditionally flushes the cache to the backend. A flush can be // forced by setting the force flush parameter. MaybeFlush(bestHash *chainhash.Hash, bestHeight uint32, forceFlush bool, logFlush bool) error @@ -335,19 +334,19 @@ func (c *UtxoCache) SpendEntry(outpoint wire.OutPoint) { // fetchEntry returns the specified transaction output from the utxo set. If // the output exists in the cache, it is returned immediately. Otherwise, it -// uses an existing database transaction to fetch the output from the database, -// cache it, and return it to the caller. A cloned copy of the entry is -// returned so it can safely be mutated by the caller without invalidating the -// cache. +// fetches the output from the database, caches it, and returns it to the +// caller. A cloned copy of the entry is returned so it can safely be mutated +// by the caller without invalidating the cache. // // When there is no entry for the provided output, nil will be returned for both // the entry and the error. // // This function MUST be called with the cache lock held. -func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) { +func (c *UtxoCache) fetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error) { // If the cache already has the entry, return it immediately. A cloned copy // of the entry is returned so it can safely be mutated by the caller without // invalidating the cache. + var entry *UtxoEntry if entry, found := c.entries[outpoint]; found { c.hits++ return entry.Clone(), nil @@ -362,7 +361,11 @@ func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE // will result in nil entries in the view. This is intentionally done // so other code can use the presence of an entry in the view as a way // to unnecessarily avoid attempting to reload it from the database. - entry, err := dbFetchUtxoEntry(dbTx, outpoint) + err := c.db.View(func(dbTx database.Tx) error { + var err error + entry, err = dbFetchUtxoEntry(dbTx, outpoint) + return err + }) if err != nil { return nil, err } @@ -381,18 +384,17 @@ func (c *UtxoCache) fetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE // FetchEntry returns the specified transaction output from the utxo set. If // the output exists in the cache, it is returned immediately. Otherwise, it -// uses an existing database transaction to fetch the output from the database, -// cache it, and return it to the caller. A cloned copy of the entry is -// returned so it can safely be mutated by the caller without invalidating the -// cache. +// fetches the output from the database, caches it, and returns it to the +// caller. A cloned copy of the entry is returned so it can safely be mutated +// by the caller without invalidating the cache. // // When there is no entry for the provided output, nil will be returned for both // the entry and the error. // // This function is safe for concurrent access. -func (c *UtxoCache) FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoEntry, error) { +func (c *UtxoCache) FetchEntry(outpoint wire.OutPoint) (*UtxoEntry, error) { c.cacheLock.Lock() - entry, err := c.fetchEntry(dbTx, outpoint) + entry, err := c.fetchEntry(outpoint) c.cacheLock.Unlock() return entry, err } @@ -408,25 +410,21 @@ func (c *UtxoCache) FetchEntry(dbTx database.Tx, outpoint wire.OutPoint) (*UtxoE // This function is safe for concurrent access. func (c *UtxoCache) FetchEntries(filteredSet viewFilteredSet, view *UtxoViewpoint) error { c.cacheLock.Lock() - err := c.db.View(func(dbTx database.Tx) error { - for outpoint := range filteredSet { - entry, err := c.fetchEntry(dbTx, outpoint) - if err != nil { - return err - } - - // NOTE: Missing entries are not considered an error here and instead - // will result in nil entries in the view. This is intentionally done - // so other code can use the presence of an entry in the view as a way - // to unnecessarily avoid attempting to reload it from the database. - view.entries[outpoint] = entry + for outpoint := range filteredSet { + entry, err := c.fetchEntry(outpoint) + if err != nil { + return err } - return nil - }) + // NOTE: Missing entries are not considered an error here and instead + // will result in nil entries in the view. This is intentionally done + // so other code can use the presence of an entry in the view as a way + // to unnecessarily avoid attempting to reload it from the database. + view.entries[outpoint] = entry + } c.cacheLock.Unlock() - return err + return nil } // Commit updates the cache based on the state of each entry in the provided @@ -952,19 +950,9 @@ func (b *BlockChain) ShutdownUtxoCache() { // any) is NOT. func (b *BlockChain) FetchUtxoEntry(outpoint wire.OutPoint) (*UtxoEntry, error) { b.chainLock.RLock() - defer b.chainLock.RUnlock() - - var entry *UtxoEntry - err := b.utxoDb.View(func(dbTx database.Tx) error { - var err error - entry, err = b.utxoCache.FetchEntry(dbTx, outpoint) - return err - }) - if err != nil { - return nil, err - } - - return entry, nil + entry, err := b.utxoCache.FetchEntry(outpoint) + b.chainLock.RUnlock() + return entry, err } // UtxoStats represents unspent output statistics on the current utxo set. diff --git a/blockchain/utxocache_test.go b/blockchain/utxocache_test.go index f678dc6c..e1f85c3f 100644 --- a/blockchain/utxocache_test.go +++ b/blockchain/utxocache_test.go @@ -545,6 +545,7 @@ func TestFetchEntry(t *testing.T) { for _, test := range tests { // Create a utxo cache with the cached entries specified by the test. utxoCache := createTestUtxoCache(t, test.cachedEntries) + utxoCache.db = db wantTotalEntrySize := utxoCache.totalEntrySize // Add entries specified by the test to the test database. @@ -563,12 +564,7 @@ func TestFetchEntry(t *testing.T) { } // Attempt to fetch the entry for the outpoint specified by the test. - var entry *UtxoEntry - err = db.View(func(dbTx database.Tx) error { - var err error - entry, err = utxoCache.FetchEntry(dbTx, test.outpoint) - return err - }) + entry, err = utxoCache.FetchEntry(test.outpoint) if err != nil { t.Fatalf("%q: unexpected error fetching entry: %v", test.name, err) }