From 245d143e9a4836ffe2eba64e9cffbc6d8b9aaab2 Mon Sep 17 00:00:00 2001 From: Kim Date: Fri, 7 Mar 2025 18:14:52 +0900 Subject: [PATCH 1/5] database: update driver functions to include dbType parameter --- database/driver.go | 8 +++---- database/driver_test.go | 4 ++-- database/ffldb/db.go | 8 ++++--- database/ffldb/driver.go | 38 ++++++++++++++++++--------------- database/ffldb/whitebox_test.go | 8 +++---- 5 files changed, 36 insertions(+), 30 deletions(-) diff --git a/database/driver.go b/database/driver.go index cb76d2fc..9b9a8a6a 100644 --- a/database/driver.go +++ b/database/driver.go @@ -20,12 +20,12 @@ type Driver struct { // Create is the function that will be invoked with all user-specified // arguments to create the database. This function must return // ErrDbExists if the database already exists. - Create func(args ...interface{}) (DB, error) + Create func(dbType string, args ...interface{}) (DB, error) // Open is the function that will be invoked with all user-specified // arguments to open the database. This function must return // ErrDbDoesNotExist if the database has not already been created. - Open func(args ...interface{}) (DB, error) + Open func(dbType string, args ...interface{}) (DB, error) // UseLogger uses a specified Logger to output package logging info. UseLogger func(logger btclog.Logger) @@ -70,7 +70,7 @@ func Create(dbType string, args ...interface{}) (DB, error) { return nil, makeError(ErrDbUnknownType, str, nil) } - return drv.Create(args...) + return drv.Create(dbType, args...) } // Open opens an existing database for the specified type. The arguments are @@ -85,5 +85,5 @@ func Open(dbType string, args ...interface{}) (DB, error) { return nil, makeError(ErrDbUnknownType, str, nil) } - return drv.Open(args...) + return drv.Open(dbType, args...) } diff --git a/database/driver_test.go b/database/driver_test.go index 3bb48de1..201b3a11 100644 --- a/database/driver_test.go +++ b/database/driver_test.go @@ -53,7 +53,7 @@ func TestAddDuplicateDriver(t *testing.T) { // driver function and intentionally returns a failure that can be // detected if the interface allows a duplicate driver to overwrite an // existing one. - bogusCreateDB := func(args ...interface{}) (database.DB, error) { + bogusCreateDB := func(dbType string, args ...interface{}) (database.DB, error) { return nil, fmt.Errorf("duplicate driver allowed for database "+ "type [%v]", dbType) } @@ -82,7 +82,7 @@ func TestCreateOpenFail(t *testing.T) { dbType := "createopenfail" openError := fmt.Errorf("failed to create or open database for "+ "database type [%v]", dbType) - bogusCreateDB := func(args ...interface{}) (database.DB, error) { + bogusCreateDB := func(dbType string, args ...interface{}) (database.DB, error) { return nil, openError } diff --git a/database/ffldb/db.go b/database/ffldb/db.go index 60103aaa..ec30996f 100644 --- a/database/ffldb/db.go +++ b/database/ffldb/db.go @@ -1860,6 +1860,8 @@ func (tx *transaction) Rollback() error { // the database.DB interface. All database access is performed through // transactions which are obtained through the specific Namespace. type db struct { + dbType string + writeLock sync.Mutex // Limit to one write transaction at a time. closeLock sync.RWMutex // Make database close block while txns active. closed bool // Is the database closed? @@ -1875,7 +1877,7 @@ var _ database.DB = (*db)(nil) // // This function is part of the database.DB interface implementation. func (db *db) Type() string { - return dbType + return db.dbType } // begin is the implementation function for the Begin database method. See its @@ -2115,7 +2117,7 @@ func initDB(ldb *leveldb.DB) error { // openDB opens the database at the provided path. database.ErrDbDoesNotExist // is returned if the database doesn't exist and the create flag is not set. -func openDB(dbPath string, network wire.BitcoinNet, create bool) (database.DB, error) { +func openDB(dbType string, dbPath string, network wire.BitcoinNet, create bool) (database.DB, error) { // Error if the database doesn't exist and the create flag is not set. metadataDbPath := filepath.Join(dbPath, metadataDbName) dbExists := fileExists(metadataDbPath) @@ -2154,7 +2156,7 @@ func openDB(dbPath string, network wire.BitcoinNet, create bool) (database.DB, e return nil, convertErr(err.Error(), err) } cache := newDbCache(ldb, store, defaultCacheSize, defaultFlushSecs) - pdb := &db{store: store, cache: cache} + pdb := &db{dbType: dbType, store: store, cache: cache} // Perform any reconciliation needed between the block and metadata as // well as database initialization, if needed. diff --git a/database/ffldb/driver.go b/database/ffldb/driver.go index 01290bf0..cdcd7d63 100644 --- a/database/ffldb/driver.go +++ b/database/ffldb/driver.go @@ -15,11 +15,11 @@ import ( var log = btclog.Disabled const ( - dbType = "ffldb" + LevelDB = "ffldb" ) // parseArgs parses the arguments from the database Open/Create methods. -func parseArgs(funcName string, args ...interface{}) (string, wire.BitcoinNet, error) { +func parseArgs(dbType, funcName string, args ...interface{}) (string, wire.BitcoinNet, error) { if len(args) != 2 { return "", 0, fmt.Errorf("invalid arguments to %s.%s -- "+ "expected database path and block network", dbType, @@ -43,24 +43,24 @@ func parseArgs(funcName string, args ...interface{}) (string, wire.BitcoinNet, e // openDBDriver is the callback provided during driver registration that opens // an existing database for use. -func openDBDriver(args ...interface{}) (database.DB, error) { - dbPath, network, err := parseArgs("Open", args...) +func openDBDriver(dbType string, args ...interface{}) (database.DB, error) { + dbPath, network, err := parseArgs(dbType, "Open", args...) if err != nil { return nil, err } - return openDB(dbPath, network, false) + return openDB(dbType, dbPath, network, false) } // createDBDriver is the callback provided during driver registration that // creates, initializes, and opens a database for use. -func createDBDriver(args ...interface{}) (database.DB, error) { - dbPath, network, err := parseArgs("Create", args...) +func createDBDriver(dbType string, args ...interface{}) (database.DB, error) { + dbPath, network, err := parseArgs(dbType, "Create", args...) if err != nil { return nil, err } - return openDB(dbPath, network, true) + return openDB(dbType, dbPath, network, true) } // useLogger is the callback provided during driver registration that sets the @@ -70,15 +70,19 @@ func useLogger(logger btclog.Logger) { } func init() { - // Register the driver. - driver := database.Driver{ - DbType: dbType, - Create: createDBDriver, - Open: openDBDriver, - UseLogger: useLogger, + // Register the drivers. + drivers := []database.Driver{ + { + DbType: LevelDB, + Create: createDBDriver, + Open: openDBDriver, + UseLogger: useLogger, + }, } - if err := database.RegisterDriver(driver); err != nil { - panic(fmt.Sprintf("Failed to register database driver '%s': %v", - dbType, err)) + for _, driver := range drivers { + if err := database.RegisterDriver(driver); err != nil { + panic(fmt.Sprintf("Failed to register database driver '%s': %v", + driver.DbType, err)) + } } } diff --git a/database/ffldb/whitebox_test.go b/database/ffldb/whitebox_test.go index cac49840..5f4b5dff 100644 --- a/database/ffldb/whitebox_test.go +++ b/database/ffldb/whitebox_test.go @@ -179,7 +179,7 @@ func TestCornerCases(t *testing.T) { // directory is needed. testName := "openDB: fail due to file at target location" wantErrCode := database.ErrDriverSpecific - idb, err := openDB(dbPath, blockDataNet, true) + idb, err := openDB(LevelDB, dbPath, blockDataNet, true) if !checkDbError(t, testName, err, wantErrCode) { if err == nil { idb.Close() @@ -191,7 +191,7 @@ func TestCornerCases(t *testing.T) { // Remove the file and create the database to run tests against. It // should be successful this time. _ = os.RemoveAll(dbPath) - idb, err = openDB(dbPath, blockDataNet, true) + idb, err = openDB(LevelDB, dbPath, blockDataNet, true) if err != nil { t.Errorf("openDB: unexpected error: %v", err) return @@ -605,9 +605,9 @@ func TestFailureScenarios(t *testing.T) { // Create a new database to run tests against. dbPath := filepath.Join(os.TempDir(), "ffldb-failurescenarios") _ = os.RemoveAll(dbPath) - idb, err := database.Create(dbType, dbPath, blockDataNet) + idb, err := database.Create(LevelDB, dbPath, blockDataNet) if err != nil { - t.Errorf("Failed to create test database (%s) %v", dbType, err) + t.Errorf("Failed to create test database (%s) %v", LevelDB, err) return } defer os.RemoveAll(dbPath) From ab0ce7dd5e7a9cf7b37f986ede38f48df7bde577 Mon Sep 17 00:00:00 2001 From: Kim Date: Fri, 7 Mar 2025 18:16:11 +0900 Subject: [PATCH 2/5] database: init engine interface for ffldb --- database/engine/engine.go | 25 ++ database/engine/iterator.go | 430 +++++++++++++++++++++++++ database/engine/leveldb/leveldb.go | 46 +++ database/engine/leveldb/snapshot.go | 34 ++ database/engine/leveldb/transaction.go | 30 ++ 5 files changed, 565 insertions(+) create mode 100644 database/engine/engine.go create mode 100644 database/engine/iterator.go create mode 100644 database/engine/leveldb/leveldb.go create mode 100644 database/engine/leveldb/snapshot.go create mode 100644 database/engine/leveldb/transaction.go diff --git a/database/engine/engine.go b/database/engine/engine.go new file mode 100644 index 00000000..bf173cb4 --- /dev/null +++ b/database/engine/engine.go @@ -0,0 +1,25 @@ +package engine + +type Engine interface { + Transaction() (Transaction, error) + Snapshot() (Snapshot, error) + Close() error +} + +type Transaction interface { + Put(key, value []byte) error + Delete(key []byte) error + Commit() error + Discard() +} + +type Snapshot interface { + Get(key []byte) ([]byte, error) + Has(key []byte) (bool, error) + NewIterator(*Range) Iterator + Releaser +} + +type Releaser interface { + Release() +} diff --git a/database/engine/iterator.go b/database/engine/iterator.go new file mode 100644 index 00000000..56d632f8 --- /dev/null +++ b/database/engine/iterator.go @@ -0,0 +1,430 @@ +package engine + +import ( + "bytes" + + "github.com/syndtr/goleveldb/leveldb/errors" + "github.com/syndtr/goleveldb/leveldb/util" +) + +type Iterator interface { + // First moves the iterator to the first key/value pair. If the iterator + // only contains one key/value pair then First and Last would moves + // to the same key/value pair. + // It returns whether such pair exist. + First() bool + + // Last moves the iterator to the last key/value pair. If the iterator + // only contains one key/value pair then First and Last would moves + // to the same key/value pair. + // It returns whether such pair exist. + Last() bool + + // Seek moves the iterator to the first key/value pair whose key is greater + // than or equal to the given key. + // It returns whether such pair exist. + // + // It is safe to modify the contents of the argument after Seek returns. + Seek(key []byte) bool + + // Next moves the iterator to the next key/value pair. + // It returns false if the iterator is exhausted. + Next() bool + + // Prev moves the iterator to the previous key/value pair. + // It returns false if the iterator is exhausted. + Prev() bool + + // TODO: Remove this when ready. + Valid() bool + + // Error returns any accumulated error. Exhausting all the key/value pairs + // is not considered to be an error. + Error() error + + // Key returns the key of the current key/value pair, or nil if done. + // The caller should not modify the contents of the returned slice, and + // its contents may change on the next call to any 'seeks method'. + Key() []byte + + // Value returns the value of the current key/value pair, or nil if done. + // The caller should not modify the contents of the returned slice, and + // its contents may change on the next call to any 'seeks method'. + Value() []byte + + Releaser +} + +var ( + ErrIterReleased = errors.New("iterator: iterator released") +) + +type dir int + +const ( + dirReleased dir = iota - 1 + dirSOI + dirEOI + dirBackward + dirForward +) + +var _ Iterator = (*mergedIterator)(nil) + +type mergedIterator struct { + cmp Comparer + iters []Iterator + strict bool + + keys [][]byte + index int + dir dir + err error + errf func(err error) + releaser util.Releaser +} + +func assertKey(key []byte) []byte { + if key == nil { + panic("leveldb/iterator: nil key") + } + return key +} + +func (i *mergedIterator) iterErr(iter Iterator) bool { + if err := iter.Error(); err != nil { + if i.errf != nil { + i.errf(err) + } + if i.strict || !errors.IsCorrupted(err) { + i.err = err + return true + } + } + return false +} + +func (i *mergedIterator) Valid() bool { + return i.err == nil && i.dir > dirEOI +} + +func (i *mergedIterator) First() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.First(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirSOI + return i.next() +} + +func (i *mergedIterator) Last() bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.Last(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirEOI + return i.prev() +} + +func (i *mergedIterator) Seek(key []byte) bool { + if i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + for x, iter := range i.iters { + switch { + case iter.Seek(key): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + i.dir = dirSOI + return i.next() +} + +func (i *mergedIterator) next() bool { + var key []byte + if i.dir == dirForward { + key = i.keys[i.index] + } + for x, tkey := range i.keys { + if tkey != nil && (key == nil || i.cmp.Compare(tkey, key) < 0) { + key = tkey + i.index = x + } + } + if key == nil { + i.dir = dirEOI + return false + } + i.dir = dirForward + return true +} + +func (i *mergedIterator) Next() bool { + if i.dir == dirEOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + switch i.dir { + case dirSOI: + return i.First() + case dirBackward: + key := append([]byte{}, i.keys[i.index]...) + if !i.Seek(key) { + return false + } + return i.Next() + } + + x := i.index + iter := i.iters[x] + switch { + case iter.Next(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + return i.next() +} + +func (i *mergedIterator) prev() bool { + var key []byte + if i.dir == dirBackward { + key = i.keys[i.index] + } + for x, tkey := range i.keys { + if tkey != nil && (key == nil || i.cmp.Compare(tkey, key) > 0) { + key = tkey + i.index = x + } + } + if key == nil { + i.dir = dirSOI + return false + } + i.dir = dirBackward + return true +} + +func (i *mergedIterator) Prev() bool { + if i.dir == dirSOI || i.err != nil { + return false + } else if i.dir == dirReleased { + i.err = ErrIterReleased + return false + } + + switch i.dir { + case dirEOI: + return i.Last() + case dirForward: + key := append([]byte{}, i.keys[i.index]...) + for x, iter := range i.iters { + if x == i.index { + continue + } + seek := iter.Seek(key) + switch { + case seek && iter.Prev(), !seek && iter.Last(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + } + } + + x := i.index + iter := i.iters[x] + switch { + case iter.Prev(): + i.keys[x] = assertKey(iter.Key()) + case i.iterErr(iter): + return false + default: + i.keys[x] = nil + } + return i.prev() +} + +func (i *mergedIterator) Key() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.keys[i.index] +} + +func (i *mergedIterator) Value() []byte { + if i.err != nil || i.dir <= dirEOI { + return nil + } + return i.iters[i.index].Value() +} + +func (i *mergedIterator) Release() { + if i.dir != dirReleased { + i.dir = dirReleased + for _, iter := range i.iters { + iter.Release() + } + i.iters = nil + i.keys = nil + if i.releaser != nil { + i.releaser.Release() + i.releaser = nil + } + } +} + +func (i *mergedIterator) SetReleaser(releaser util.Releaser) { + if i.dir == dirReleased { + panic(util.ErrReleased) + } + if i.releaser != nil && releaser != nil { + panic(util.ErrHasReleaser) + } + i.releaser = releaser +} + +func (i *mergedIterator) Error() error { + return i.err +} + +func (i *mergedIterator) SetErrorCallback(f func(err error)) { + i.errf = f +} + +// NewMergedIterator returns an iterator that merges its input. Walking the +// resultant iterator will return all key/value pairs of all input iterators +// in strictly increasing key order, as defined by cmp. +// The input's key ranges may overlap, but there are assumed to be no duplicate +// keys: if iters[i] contains a key k then iters[j] will not contain that key k. +// None of the iters may be nil. +// +// If strict is true the any 'corruption errors' (i.e errors.IsCorrupted(err) == true) +// won't be ignored and will halt 'merged iterator', otherwise the iterator will +// continue to the next 'input iterator'. +func NewMergedIterator(iters []Iterator, cmp Comparer, strict bool) Iterator { + return &mergedIterator{ + iters: iters, + cmp: cmp, + strict: strict, + keys: make([][]byte, len(iters)), + } +} + +type Comparer interface { + Compare(a, b []byte) int + + Name() string + + Separator(dst, a, b []byte) []byte + + Successor(dst, b []byte) []byte +} + +type bytesComparer struct{} + +func (bytesComparer) Compare(a, b []byte) int { + return bytes.Compare(a, b) +} + +func (bytesComparer) Name() string { + return "leveldb.BytewiseComparator" +} + +func (bytesComparer) Separator(dst, a, b []byte) []byte { + i, n := 0, len(a) + if n > len(b) { + n = len(b) + } + for ; i < n && a[i] == b[i]; i++ { + } + if i >= n { + // Do not shorten if one string is a prefix of the other + } else if c := a[i]; c < 0xff && c+1 < b[i] { + dst = append(dst, a[:i+1]...) + dst[len(dst)-1]++ + return dst + } + return nil +} + +func (bytesComparer) Successor(dst, b []byte) []byte { + for i, c := range b { + if c != 0xff { + dst = append(dst, b[:i+1]...) + dst[len(dst)-1]++ + return dst + } + } + return nil +} + +// DefaultComparer are default implementation of the Comparer interface. +// It uses the natural ordering, consistent with bytes.Compare. +var DefaultComparer = bytesComparer{} + +// Range is a key range. +type Range struct { + // Start of the key range, include in the range. + Start []byte + + // Limit of the key range, not include in the range. + Limit []byte +} + +// BytesPrefix returns key range that satisfy the given prefix. +// This only applicable for the standard 'bytes comparer'. +func BytesPrefix(prefix []byte) *Range { + var limit []byte + for i := len(prefix) - 1; i >= 0; i-- { + c := prefix[i] + if c < 0xff { + limit = make([]byte, i+1) + copy(limit, prefix) + limit[i] = c + 1 + break + } + } + return &Range{prefix, limit} +} diff --git a/database/engine/leveldb/leveldb.go b/database/engine/leveldb/leveldb.go new file mode 100644 index 00000000..7b47a00c --- /dev/null +++ b/database/engine/leveldb/leveldb.go @@ -0,0 +1,46 @@ +package leveldb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/filter" + "github.com/syndtr/goleveldb/leveldb/opt" +) + +func NewDB(dbPath string, create bool) (engine.Engine, error) { + opts := opt.Options{ + ErrorIfExist: create, + Strict: opt.DefaultStrict, + Compression: opt.NoCompression, + Filter: filter.NewBloomFilter(10), + } + ldb, err := leveldb.OpenFile(dbPath, &opts) + if err != nil { + return nil, err + } + return &DB{DB: ldb}, nil +} + +type DB struct { + *leveldb.DB +} + +func (d *DB) Transaction() (engine.Transaction, error) { + tx, err := d.DB.OpenTransaction() + if err != nil { + return nil, err + } + return NewTransaction(tx), nil +} + +func (d *DB) Snapshot() (engine.Snapshot, error) { + snapshot, err := d.DB.GetSnapshot() + if err != nil { + return nil, err + } + return NewSnapshot(snapshot), nil +} + +func (d *DB) Close() error { + return d.DB.Close() +} diff --git a/database/engine/leveldb/snapshot.go b/database/engine/leveldb/snapshot.go new file mode 100644 index 00000000..980b2047 --- /dev/null +++ b/database/engine/leveldb/snapshot.go @@ -0,0 +1,34 @@ +package leveldb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/syndtr/goleveldb/leveldb" + "github.com/syndtr/goleveldb/leveldb/util" +) + +func NewSnapshot(snapshot *leveldb.Snapshot) engine.Snapshot { + return &Snapshot{Snapshot: snapshot} +} + +type Snapshot struct { + *leveldb.Snapshot +} + +func (s *Snapshot) Has(key []byte) (bool, error) { + return s.Snapshot.Has(key, nil) +} + +func (s *Snapshot) Get(key []byte) (val []byte, err error) { + return s.Snapshot.Get(key, nil) +} + +func (s *Snapshot) Release() { + s.Snapshot.Release() +} + +func (s *Snapshot) NewIterator(slice *engine.Range) engine.Iterator { + return s.Snapshot.NewIterator(&util.Range{ + Start: slice.Start, + Limit: slice.Limit, + }, nil) +} diff --git a/database/engine/leveldb/transaction.go b/database/engine/leveldb/transaction.go new file mode 100644 index 00000000..0489e552 --- /dev/null +++ b/database/engine/leveldb/transaction.go @@ -0,0 +1,30 @@ +package leveldb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/syndtr/goleveldb/leveldb" +) + +func NewTransaction(tx *leveldb.Transaction) engine.Transaction { + return &Transaction{Transaction: tx} +} + +type Transaction struct { + *leveldb.Transaction +} + +func (t *Transaction) Put(key, value []byte) error { + return t.Transaction.Put(key, value, nil) +} + +func (t *Transaction) Delete(key []byte) error { + return t.Transaction.Delete(key, nil) +} + +func (t *Transaction) Discard() { + t.Transaction.Discard() +} + +func (t *Transaction) Commit() error { + return t.Transaction.Commit() +} From c0550840465485c4a46af535a03d218c92d84ebe Mon Sep 17 00:00:00 2001 From: Kim Date: Fri, 7 Mar 2025 18:18:32 +0900 Subject: [PATCH 3/5] database: refactor ffldb to use db engine instead of leveldb --- database/ffldb/README.md | 4 +- database/ffldb/db.go | 104 ++++++++++++++-------------- database/ffldb/dbcache.go | 118 ++++++++++++++++---------------- database/ffldb/doc.go | 4 +- database/ffldb/ldbtreapiter.go | 21 +++--- database/ffldb/reconcile.go | 2 +- database/ffldb/whitebox_test.go | 16 ++--- 7 files changed, 133 insertions(+), 136 deletions(-) diff --git a/database/ffldb/README.md b/database/ffldb/README.md index 5b855faa..fe9b6cf9 100644 --- a/database/ffldb/README.md +++ b/database/ffldb/README.md @@ -6,10 +6,10 @@ ffldb [![GoDoc](https://pkg.go.dev/github.com/btcsuite/btcd/database/ffldb?status.png)](https://pkg.go.dev/github.com/btcsuite/btcd/database/ffldb) ======= -Package ffldb implements a driver for the database package that uses leveldb for +Package ffldb implements a driver for the database package that uses db engine for the backing metadata and flat files for block storage. -This driver is the recommended driver for use with btcd. It makes use leveldb +This driver is the recommended driver for use with btcd. It makes use db engine for the metadata, flat files for block storage, and checksums in key areas to ensure data integrity. diff --git a/database/ffldb/db.go b/database/ffldb/db.go index ec30996f..72fe07f9 100644 --- a/database/ffldb/db.go +++ b/database/ffldb/db.go @@ -17,15 +17,13 @@ import ( "github.com/btcsuite/btcd/btcutil" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/database" + "github.com/btcsuite/btcd/database/engine" + "github.com/btcsuite/btcd/database/engine/leveldb" "github.com/btcsuite/btcd/database/internal/treap" "github.com/btcsuite/btcd/wire" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/comparer" + + ldb "github.com/syndtr/goleveldb/leveldb" ldberrors "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/syndtr/goleveldb/leveldb/filter" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/opt" - "github.com/syndtr/goleveldb/leveldb/util" ) const ( @@ -131,35 +129,35 @@ func makeDbErr(c database.ErrorCode, desc string, err error) database.Error { return database.Error{ErrorCode: c, Description: desc, Err: err} } -// convertErr converts the passed leveldb error into a database error with an +// convertErr converts the passed db engine error into a database error with an // equivalent error code and the passed description. It also sets the passed // error as the underlying error. -func convertErr(desc string, ldbErr error) database.Error { +func convertErr(desc string, dbErr error) database.Error { // Use the driver-specific error code by default. The code below will // update this with the converted error if it's recognized. var code = database.ErrDriverSpecific switch { // Database corruption errors. - case ldberrors.IsCorrupted(ldbErr): + case ldberrors.IsCorrupted(dbErr): code = database.ErrCorruption // Database open/create errors. - case ldbErr == leveldb.ErrClosed: + case dbErr == ldb.ErrClosed: code = database.ErrDbNotOpen // Transaction errors. - case ldbErr == leveldb.ErrSnapshotReleased: + case dbErr == ldb.ErrSnapshotReleased: code = database.ErrTxClosed - case ldbErr == leveldb.ErrIterReleased: + case dbErr == ldb.ErrIterReleased: code = database.ErrTxClosed } - return database.Error{ErrorCode: code, Description: desc, Err: ldbErr} + return database.Error{ErrorCode: code, Description: desc, Err: dbErr} } // copySlice returns a copy of the passed slice. This is mostly used to copy -// leveldb iterator keys and values since they are only valid until the iterator +// db iterator keys and values since they are only valid until the iterator // is moved instead of during the entirety of the transaction. func copySlice(slice []byte) []byte { ret := make([]byte, len(slice)) @@ -171,9 +169,9 @@ func copySlice(slice []byte) []byte { // and nested buckets of a bucket and implements the database.Cursor interface. type cursor struct { bucket *bucket - dbIter iterator.Iterator - pendingIter iterator.Iterator - currentIter iterator.Iterator + dbIter engine.Iterator + pendingIter engine.Iterator + currentIter engine.Iterator } // Enforce cursor implements the database.Cursor interface. @@ -491,10 +489,10 @@ func cursorFinalizer(c *cursor) { // NOTE: The caller is responsible for calling the cursorFinalizer function on // the returned cursor. func newCursor(b *bucket, bucketID []byte, cursorTyp cursorType) *cursor { - var dbIter, pendingIter iterator.Iterator + var dbIter, pendingIter engine.Iterator switch cursorTyp { case ctKeys: - keyRange := util.BytesPrefix(bucketID) + keyRange := engine.BytesPrefix(bucketID) dbIter = b.tx.snapshot.NewIterator(keyRange) pendingKeyIter := newLdbTreapIter(b.tx, keyRange) pendingIter = pendingKeyIter @@ -509,7 +507,7 @@ func newCursor(b *bucket, bucketID []byte, cursorTyp cursorType) *cursor { prefix := make([]byte, len(bucketIndexPrefix)+4) copy(prefix, bucketIndexPrefix) copy(prefix[len(bucketIndexPrefix):], bucketID) - bucketRange := util.BytesPrefix(prefix) + bucketRange := engine.BytesPrefix(prefix) dbIter = b.tx.snapshot.NewIterator(bucketRange) pendingBucketIter := newLdbTreapIter(b.tx, bucketRange) @@ -523,26 +521,26 @@ func newCursor(b *bucket, bucketID []byte, cursorTyp cursorType) *cursor { prefix := make([]byte, len(bucketIndexPrefix)+4) copy(prefix, bucketIndexPrefix) copy(prefix[len(bucketIndexPrefix):], bucketID) - bucketRange := util.BytesPrefix(prefix) - keyRange := util.BytesPrefix(bucketID) + bucketRange := engine.BytesPrefix(prefix) + keyRange := engine.BytesPrefix(bucketID) // Since both keys and buckets are needed from the database, // create an individual iterator for each prefix and then create // a merged iterator from them. dbKeyIter := b.tx.snapshot.NewIterator(keyRange) dbBucketIter := b.tx.snapshot.NewIterator(bucketRange) - iters := []iterator.Iterator{dbKeyIter, dbBucketIter} - dbIter = iterator.NewMergedIterator(iters, - comparer.DefaultComparer, true) + iters := []engine.Iterator{dbKeyIter, dbBucketIter} + dbIter = engine.NewMergedIterator(iters, + engine.DefaultComparer, true) // Since both keys and buckets are needed from the pending keys, // create an individual iterator for each prefix and then create // a merged iterator from them. pendingKeyIter := newLdbTreapIter(b.tx, keyRange) pendingBucketIter := newLdbTreapIter(b.tx, bucketRange) - iters = []iterator.Iterator{pendingKeyIter, pendingBucketIter} - pendingIter = iterator.NewMergedIterator(iters, - comparer.DefaultComparer, true) + iters = []engine.Iterator{pendingKeyIter, pendingBucketIter} + pendingIter = engine.NewMergedIterator(iters, + engine.DefaultComparer, true) } // Create the cursor using the iterators. @@ -1866,7 +1864,7 @@ type db struct { closeLock sync.RWMutex // Make database close block while txns active. closed bool // Is the database closed? store *blockStore // Handles read/writing blocks to flat files. - cache *dbCache // Cache layer which wraps underlying leveldb DB. + cache *dbCache // Cache layer which wraps underlying DB engine. } // Enforce db implements the database.DB interface. @@ -2054,7 +2052,7 @@ func (db *db) Close() error { // cache and clear all state without the individual locks. // Close the database cache which will flush any existing entries to - // disk and close the underlying leveldb database. Any error is saved + // disk and close the underlying database. Any error is saved // and returned at the end after the remaining cleanup since the // database will be marked closed even if this fails given there is no // good way for the caller to recover from a failure here anyways. @@ -2088,11 +2086,13 @@ func fileExists(name string) bool { // initDB creates the initial buckets and values used by the package. This is // mainly in a separate function for testing purposes. -func initDB(ldb *leveldb.DB) error { - // The starting block file write cursor location is file num 0, offset - // 0. - batch := new(leveldb.Batch) - batch.Put(bucketizedKey(metadataBucketID, writeLocKeyName), +func initDB(engine engine.Engine) error { + // The starting block file write cursor location is file num 0, offset 0. + tx, err := engine.Transaction() + if err != nil { + return convertErr(err.Error(), err) + } + tx.Put(bucketizedKey(metadataBucketID, writeLocKeyName), serializeWriteRow(0, 0)) // Create block index bucket and set the current bucket id. @@ -2101,14 +2101,13 @@ func initDB(ldb *leveldb.DB) error { // there is no need to store the bucket index data for the metadata // bucket in the database. However, the first bucket ID to use does // need to account for it to ensure there are no key collisions. - batch.Put(bucketIndexKey(metadataBucketID, blockIdxBucketName), + tx.Put(bucketIndexKey(metadataBucketID, blockIdxBucketName), blockIdxBucketID[:]) - batch.Put(curBucketIDKeyName, blockIdxBucketID[:]) + tx.Put(curBucketIDKeyName, blockIdxBucketID[:]) - // Write everything as a single batch. - if err := ldb.Write(batch, nil); err != nil { - str := fmt.Sprintf("failed to initialize metadata database: %v", - err) + // Apply the batch write. + if err := tx.Commit(); err != nil { + str := fmt.Sprintf("failed to initialize metadata database: %v", err) return convertErr(str, err) } @@ -2129,19 +2128,20 @@ func openDB(dbType string, dbPath string, network wire.BitcoinNet, create bool) // Ensure the full path to the database exists. if !dbExists { // The error can be ignored here since the call to - // leveldb.OpenFile will fail if the directory couldn't be + // db Open will fail if the directory couldn't be // created. _ = os.MkdirAll(dbPath, 0700) } // Open the metadata database (will create it if needed). - opts := opt.Options{ - ErrorIfExist: create, - Strict: opt.DefaultStrict, - Compression: opt.NoCompression, - Filter: filter.NewBloomFilter(10), + var dbEngine engine.Engine + var err error + switch dbType { + case LevelDB: + dbEngine, err = leveldb.NewDB(metadataDbPath, create) + default: + err = fmt.Errorf("driver %q is not registered", dbType) } - ldb, err := leveldb.OpenFile(metadataDbPath, &opts) if err != nil { return nil, convertErr(err.Error(), err) } @@ -2149,16 +2149,16 @@ func openDB(dbType string, dbPath string, network wire.BitcoinNet, create bool) // Create the block store which includes scanning the existing flat // block files to find what the current write cursor position is // according to the data that is actually on disk. Also create the - // database cache which wraps the underlying leveldb database to provide + // database cache which wraps the underlying database to provide // write caching. store, err := newBlockStore(dbPath, network) if err != nil { return nil, convertErr(err.Error(), err) } - cache := newDbCache(ldb, store, defaultCacheSize, defaultFlushSecs) - pdb := &db{dbType: dbType, store: store, cache: cache} + cache := newDbCache(dbEngine, store, defaultCacheSize, defaultFlushSecs) + db := &db{dbType: dbType, store: store, cache: cache} // Perform any reconciliation needed between the block and metadata as // well as database initialization, if needed. - return reconcileDB(pdb, create) + return reconcileDB(db, create) } diff --git a/database/ffldb/dbcache.go b/database/ffldb/dbcache.go index ec42ee96..37d7a286 100644 --- a/database/ffldb/dbcache.go +++ b/database/ffldb/dbcache.go @@ -10,10 +10,8 @@ import ( "sync" "time" + "github.com/btcsuite/btcd/database/engine" "github.com/btcsuite/btcd/database/internal/treap" - "github.com/syndtr/goleveldb/leveldb" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/util" ) const ( @@ -40,18 +38,18 @@ const ( ) // ldbCacheIter wraps a treap iterator to provide the additional functionality -// needed to satisfy the leveldb iterator.Iterator interface. +// needed to satisfy the engine.Iterator interface. type ldbCacheIter struct { *treap.Iterator } -// Enforce ldbCacheIterator implements the leveldb iterator.Iterator interface. -var _ iterator.Iterator = (*ldbCacheIter)(nil) +// Enforce ldbCacheIterator implements the engine.Iterator interface. +var _ engine.Iterator = (*ldbCacheIter)(nil) // Error is only provided to satisfy the iterator interface as there are no // errors for this memory-only structure. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *ldbCacheIter) Error() error { return nil } @@ -59,20 +57,20 @@ func (iter *ldbCacheIter) Error() error { // SetReleaser is only provided to satisfy the iterator interface as there is no // need to override it. // -// This is part of the leveldb iterator.Iterator interface implementation. -func (iter *ldbCacheIter) SetReleaser(releaser util.Releaser) { +// This is part of the engine.Iterator interface implementation. +func (iter *ldbCacheIter) SetReleaser(releaser engine.Releaser) { } // Release is only provided to satisfy the iterator interface. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *ldbCacheIter) Release() { } // newLdbCacheIter creates a new treap iterator for the given slice against the // pending keys for the passed cache snapshot and returns it wrapped in an -// ldbCacheIter so it can be used as a leveldb iterator. -func newLdbCacheIter(snap *dbCacheSnapshot, slice *util.Range) *ldbCacheIter { +// ldbCacheIter so it can be used as a db iterator. +func newLdbCacheIter(snap *dbCacheSnapshot, slice *engine.Range) *ldbCacheIter { iter := snap.pendingKeys.Iterator(slice.Start, slice.Limit) return &ldbCacheIter{Iterator: iter} } @@ -81,14 +79,14 @@ func newLdbCacheIter(snap *dbCacheSnapshot, slice *util.Range) *ldbCacheIter { // cache and underlying database. type dbCacheIterator struct { cacheSnapshot *dbCacheSnapshot - dbIter iterator.Iterator - cacheIter iterator.Iterator - currentIter iterator.Iterator + dbIter engine.Iterator + cacheIter engine.Iterator + currentIter engine.Iterator released bool } -// Enforce dbCacheIterator implements the leveldb iterator.Iterator interface. -var _ iterator.Iterator = (*dbCacheIterator)(nil) +// Enforce dbCacheIterator implements the engine.Iterator interface. +var _ engine.Iterator = (*dbCacheIterator)(nil) // skipPendingUpdates skips any keys at the current database iterator position // that are being updated by the cache. The forwards flag indicates the @@ -157,7 +155,7 @@ func (iter *dbCacheIterator) chooseIterator(forwards bool) bool { // First positions the iterator at the first key/value pair and returns whether // or not the pair exists. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) First() bool { // Seek to the first key in both the database and cache iterators and // choose the iterator that is both valid and has the smaller key. @@ -169,7 +167,7 @@ func (iter *dbCacheIterator) First() bool { // Last positions the iterator at the last key/value pair and returns whether or // not the pair exists. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Last() bool { // Seek to the last key in both the database and cache iterators and // choose the iterator that is both valid and has the larger key. @@ -181,7 +179,7 @@ func (iter *dbCacheIterator) Last() bool { // Next moves the iterator one key/value pair forward and returns whether or not // the pair exists. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Next() bool { // Nothing to return if cursor is exhausted. if iter.currentIter == nil { @@ -197,7 +195,7 @@ func (iter *dbCacheIterator) Next() bool { // Prev moves the iterator one key/value pair backward and returns whether or // not the pair exists. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Prev() bool { // Nothing to return if cursor is exhausted. if iter.currentIter == nil { @@ -213,7 +211,7 @@ func (iter *dbCacheIterator) Prev() bool { // Seek positions the iterator at the first key/value pair that is greater than // or equal to the passed seek key. Returns false if no suitable key was found. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Seek(key []byte) bool { // Seek to the provided key in both the database and cache iterators // then choose the iterator that is both valid and has the larger key. @@ -225,14 +223,14 @@ func (iter *dbCacheIterator) Seek(key []byte) bool { // Valid indicates whether the iterator is positioned at a valid key/value pair. // It will be considered invalid when the iterator is newly created or exhausted. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Valid() bool { return iter.currentIter != nil } // Key returns the current key the iterator is pointing to. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Key() []byte { // Nothing to return if iterator is exhausted. if iter.currentIter == nil { @@ -244,7 +242,7 @@ func (iter *dbCacheIterator) Key() []byte { // Value returns the current value the iterator is pointing to. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Value() []byte { // Nothing to return if iterator is exhausted. if iter.currentIter == nil { @@ -257,14 +255,14 @@ func (iter *dbCacheIterator) Value() []byte { // SetReleaser is only provided to satisfy the iterator interface as there is no // need to override it. // -// This is part of the leveldb iterator.Iterator interface implementation. -func (iter *dbCacheIterator) SetReleaser(releaser util.Releaser) { +// This is part of the engine.Iterator interface implementation. +func (iter *dbCacheIterator) SetReleaser(releaser engine.Releaser) { } // Release releases the iterator by removing the underlying treap iterator from // the list of active iterators against the pending keys treap. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Release() { if !iter.released { iter.dbIter.Release() @@ -277,7 +275,7 @@ func (iter *dbCacheIterator) Release() { // Error is only provided to satisfy the iterator interface as there are no // errors for this memory-only structure. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *dbCacheIterator) Error() error { return nil } @@ -285,7 +283,7 @@ func (iter *dbCacheIterator) Error() error { // dbCacheSnapshot defines a snapshot of the database cache and underlying // database at a particular point in time. type dbCacheSnapshot struct { - dbSnapshot *leveldb.Snapshot + dbSnapshot engine.Snapshot pendingKeys *treap.Immutable pendingRemove *treap.Immutable } @@ -301,7 +299,7 @@ func (snap *dbCacheSnapshot) Has(key []byte) bool { } // Consult the database. - hasKey, _ := snap.dbSnapshot.Has(key, nil) + hasKey, _ := snap.dbSnapshot.Has(key) return hasKey } @@ -317,7 +315,7 @@ func (snap *dbCacheSnapshot) Get(key []byte) []byte { } // Consult the database. - value, err := snap.dbSnapshot.Get(key, nil) + value, err := snap.dbSnapshot.Get(key) if err != nil { return nil } @@ -338,9 +336,9 @@ func (snap *dbCacheSnapshot) Release() { // The slice parameter allows the iterator to be limited to a range of keys. // The start key is inclusive and the limit key is exclusive. Either or both // can be nil if the functionality is not desired. -func (snap *dbCacheSnapshot) NewIterator(slice *util.Range) *dbCacheIterator { +func (snap *dbCacheSnapshot) NewIterator(slice *engine.Range) *dbCacheIterator { return &dbCacheIterator{ - dbIter: snap.dbSnapshot.NewIterator(slice, nil), + dbIter: snap.dbSnapshot.NewIterator(slice), cacheIter: newLdbCacheIter(snap, slice), cacheSnapshot: snap, } @@ -354,8 +352,8 @@ func (snap *dbCacheSnapshot) NewIterator(slice *util.Range) *dbCacheIterator { // can commit transactions at will without incurring large performance hits due // to frequent disk syncs. type dbCache struct { - // ldb is the underlying leveldb DB for metadata. - ldb *leveldb.DB + // dbEngine is the underlying DB for metadata. + dbEngine engine.Engine // store is used to sync blocks to flat files. store *blockStore @@ -396,7 +394,7 @@ type dbCache struct { // // The snapshot must be released after use by calling Release. func (c *dbCache) Snapshot() (*dbCacheSnapshot, error) { - dbSnapshot, err := c.ldb.GetSnapshot() + dbSnapshot, err := c.dbEngine.Snapshot() if err != nil { str := "failed to open transaction" return nil, convertErr(str, err) @@ -415,26 +413,26 @@ func (c *dbCache) Snapshot() (*dbCacheSnapshot, error) { return cacheSnapshot, nil } -// updateDB invokes the passed function in the context of a managed leveldb +// updateDB invokes the passed function in the context of a managed db // transaction. Any errors returned from the user-supplied function will cause // the transaction to be rolled back and are returned from this function. // Otherwise, the transaction is committed when the user-supplied function // returns a nil error. -func (c *dbCache) updateDB(fn func(ldbTx *leveldb.Transaction) error) error { - // Start a leveldb transaction. - ldbTx, err := c.ldb.OpenTransaction() +func (c *dbCache) updateDB(fn func(tx engine.Transaction) error) error { + // Start a db transaction. + tx, err := c.dbEngine.Transaction() if err != nil { return convertErr("failed to open ldb transaction", err) } - if err := fn(ldbTx); err != nil { - ldbTx.Discard() + if err := fn(tx); err != nil { + tx.Discard() return err } - // Commit the leveldb transaction and convert any errors as needed. - if err := ldbTx.Commit(); err != nil { - return convertErr("failed to commit leveldb transaction", err) + // Commit the db transaction and convert any errors as needed. + if err := tx.Commit(); err != nil { + return convertErr("failed to commit db transaction", err) } return nil } @@ -450,11 +448,11 @@ type TreapForEacher interface { // commitTreaps atomically commits all of the passed pending add/update/remove // updates to the underlying database. func (c *dbCache) commitTreaps(pendingKeys, pendingRemove TreapForEacher) error { - // Perform all leveldb updates using an atomic transaction. - return c.updateDB(func(ldbTx *leveldb.Transaction) error { + // Perform all db updates using an atomic transaction. + return c.updateDB(func(tx engine.Transaction) error { var innerErr error pendingKeys.ForEach(func(k, v []byte) bool { - if dbErr := ldbTx.Put(k, v, nil); dbErr != nil { + if dbErr := tx.Put(k, v); dbErr != nil { str := fmt.Sprintf("failed to put key %q to "+ "ldb transaction", k) innerErr = convertErr(str, dbErr) @@ -467,7 +465,7 @@ func (c *dbCache) commitTreaps(pendingKeys, pendingRemove TreapForEacher) error } pendingRemove.ForEach(func(k, v []byte) bool { - if dbErr := ldbTx.Delete(k, nil); dbErr != nil { + if dbErr := tx.Delete(k); dbErr != nil { str := fmt.Sprintf("failed to delete "+ "key %q from ldb transaction", k) @@ -509,7 +507,7 @@ func (c *dbCache) flush() error { return nil } - // Perform all leveldb updates using an atomic transaction. + // Perform all db updates using an atomic transaction. if err := c.commitTreaps(cachedKeys, cachedRemove); err != nil { return err } @@ -572,7 +570,7 @@ func (c *dbCache) commitTx(tx *transaction) error { return err } - // Perform all leveldb updates using an atomic transaction. + // Perform all db updates using an atomic transaction. err := c.commitTreaps(tx.pendingKeys, tx.pendingRemove) if err != nil { return err @@ -621,7 +619,7 @@ func (c *dbCache) commitTx(tx *transaction) error { } // Close cleanly shuts down the database cache by syncing all data and closing -// the underlying leveldb database. +// the underlying database. // // This function MUST be called with the database write lock held. func (c *dbCache) Close() error { @@ -630,13 +628,13 @@ func (c *dbCache) Close() error { // Even if there is an error while flushing, attempt to close // the underlying database. The error is ignored since it would // mask the flush error. - _ = c.ldb.Close() + _ = c.dbEngine.Close() return err } - // Close the underlying leveldb database. - if err := c.ldb.Close(); err != nil { - str := "failed to close underlying leveldb database" + // Close the underlying database. + if err := c.dbEngine.Close(); err != nil { + str := "failed to close underlying database" return convertErr(str, err) } @@ -644,12 +642,12 @@ func (c *dbCache) Close() error { } // newDbCache returns a new database cache instance backed by the provided -// leveldb instance. The cache will be flushed to leveldb when the max size +// db instance. The cache will be flushed to db when the max size // exceeds the provided value or it has been longer than the provided interval // since the last flush. -func newDbCache(ldb *leveldb.DB, store *blockStore, maxSize uint64, flushIntervalSecs uint32) *dbCache { +func newDbCache(dbEngine engine.Engine, store *blockStore, maxSize uint64, flushIntervalSecs uint32) *dbCache { return &dbCache{ - ldb: ldb, + dbEngine: dbEngine, store: store, maxSize: maxSize, flushInterval: time.Second * time.Duration(flushIntervalSecs), diff --git a/database/ffldb/doc.go b/database/ffldb/doc.go index 00011967..71bcb625 100644 --- a/database/ffldb/doc.go +++ b/database/ffldb/doc.go @@ -3,10 +3,10 @@ // license that can be found in the LICENSE file. /* -Package ffldb implements a driver for the database package that uses leveldb +Package ffldb implements a driver for the database package that uses db engine for the backing metadata and flat files for block storage. -This driver is the recommended driver for use with btcd. It makes use leveldb +This driver is the recommended driver for use with btcd. It makes use db engine for the metadata, flat files for block storage, and checksums in key areas to ensure data integrity. diff --git a/database/ffldb/ldbtreapiter.go b/database/ffldb/ldbtreapiter.go index 10ce207c..3b5655b1 100644 --- a/database/ffldb/ldbtreapiter.go +++ b/database/ffldb/ldbtreapiter.go @@ -5,26 +5,25 @@ package ffldb import ( + "github.com/btcsuite/btcd/database/engine" "github.com/btcsuite/btcd/database/internal/treap" - "github.com/syndtr/goleveldb/leveldb/iterator" - "github.com/syndtr/goleveldb/leveldb/util" ) // ldbTreapIter wraps a treap iterator to provide the additional functionality -// needed to satisfy the leveldb iterator.Iterator interface. +// needed to satisfy the engine.Iterator interface. type ldbTreapIter struct { *treap.Iterator tx *transaction released bool } -// Enforce ldbTreapIter implements the leveldb iterator.Iterator interface. -var _ iterator.Iterator = (*ldbTreapIter)(nil) +// Enforce ldbTreapIter implements the engine.Iterator interface. +var _ engine.Iterator = (*ldbTreapIter)(nil) // Error is only provided to satisfy the iterator interface as there are no // errors for this memory-only structure. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *ldbTreapIter) Error() error { return nil } @@ -32,14 +31,14 @@ func (iter *ldbTreapIter) Error() error { // SetReleaser is only provided to satisfy the iterator interface as there is no // need to override it. // -// This is part of the leveldb iterator.Iterator interface implementation. -func (iter *ldbTreapIter) SetReleaser(releaser util.Releaser) { +// This is part of the engine.Iterator interface implementation. +func (iter *ldbTreapIter) SetReleaser(releaser engine.Releaser) { } // Release releases the iterator by removing the underlying treap iterator from // the list of active iterators against the pending keys treap. // -// This is part of the leveldb iterator.Iterator interface implementation. +// This is part of the engine.Iterator interface implementation. func (iter *ldbTreapIter) Release() { if !iter.released { iter.tx.removeActiveIter(iter.Iterator) @@ -49,9 +48,9 @@ func (iter *ldbTreapIter) Release() { // newLdbTreapIter creates a new treap iterator for the given slice against the // pending keys for the passed transaction and returns it wrapped in an -// ldbTreapIter so it can be used as a leveldb iterator. It also adds the new +// ldbTreapIter so it can be used as a db iterator. It also adds the new // iterator to the list of active iterators for the transaction. -func newLdbTreapIter(tx *transaction, slice *util.Range) *ldbTreapIter { +func newLdbTreapIter(tx *transaction, slice *engine.Range) *ldbTreapIter { iter := tx.pendingKeys.Iterator(slice.Start, slice.Limit) tx.addActiveIter(iter) return &ldbTreapIter{Iterator: iter, tx: tx} diff --git a/database/ffldb/reconcile.go b/database/ffldb/reconcile.go index e2c4d6bb..8ab9bff0 100644 --- a/database/ffldb/reconcile.go +++ b/database/ffldb/reconcile.go @@ -53,7 +53,7 @@ func reconcileDB(pdb *db, create bool) (database.DB, error) { // Perform initial internal bucket and value creation during database // creation. if create { - if err := initDB(pdb.cache.ldb); err != nil { + if err := initDB(pdb.cache.dbEngine); err != nil { return nil, err } } diff --git a/database/ffldb/whitebox_test.go b/database/ffldb/whitebox_test.go index 5f4b5dff..fc0bb8cb 100644 --- a/database/ffldb/whitebox_test.go +++ b/database/ffldb/whitebox_test.go @@ -21,7 +21,7 @@ import ( "github.com/btcsuite/btcd/chaincfg" "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/wire" - "github.com/syndtr/goleveldb/leveldb" + ldb "github.com/syndtr/goleveldb/leveldb" ldberrors "github.com/syndtr/goleveldb/leveldb/errors" ) @@ -135,7 +135,7 @@ type testContext struct { blocks []*btcutil.Block } -// TestConvertErr ensures the leveldb error to database error conversion works +// TestConvertErr ensures the db error to database error conversion works // as expected. func TestConvertErr(t *testing.T) { t.Parallel() @@ -145,9 +145,9 @@ func TestConvertErr(t *testing.T) { wantErrCode database.ErrorCode }{ {&ldberrors.ErrCorrupted{}, database.ErrCorruption}, - {leveldb.ErrClosed, database.ErrDbNotOpen}, - {leveldb.ErrSnapshotReleased, database.ErrTxClosed}, - {leveldb.ErrIterReleased, database.ErrTxClosed}, + {ldb.ErrClosed, database.ErrDbNotOpen}, + {ldb.ErrSnapshotReleased, database.ErrTxClosed}, + {ldb.ErrIterReleased, database.ErrTxClosed}, } for i, test := range tests { @@ -215,14 +215,14 @@ func TestCornerCases(t *testing.T) { _ = os.RemoveAll(filePath) // Close the underlying leveldb database out from under the database. - ldb := idb.(*db).cache.ldb - ldb.Close() + db := idb.(*db).cache.dbEngine + db.Close() // Ensure initialization errors in the underlying database work as // expected. testName = "initDB: reinitialization" wantErrCode = database.ErrDbNotOpen - err = initDB(ldb) + err = initDB(db) if !checkDbError(t, testName, err, wantErrCode) { return } From 4e9d4445344f88e0fef903b6a7c7c28921c6ee6a Mon Sep 17 00:00:00 2001 From: Kim Date: Fri, 7 Mar 2025 18:23:24 +0900 Subject: [PATCH 4/5] database: add pebbledb support --- database/engine/pebbledb/iterator.go | 47 +++ database/engine/pebbledb/pebbledb.go | 92 +++++ database/engine/pebbledb/snapshot.go | 65 ++++ database/engine/pebbledb/transaction.go | 44 +++ database/ffldb/db.go | 9 +- database/ffldb/driver.go | 9 +- go.mod | 29 +- go.sum | 492 +++++++++++++++++++++++- 8 files changed, 772 insertions(+), 15 deletions(-) create mode 100644 database/engine/pebbledb/iterator.go create mode 100644 database/engine/pebbledb/pebbledb.go create mode 100644 database/engine/pebbledb/snapshot.go create mode 100644 database/engine/pebbledb/transaction.go diff --git a/database/engine/pebbledb/iterator.go b/database/engine/pebbledb/iterator.go new file mode 100644 index 00000000..f073b190 --- /dev/null +++ b/database/engine/pebbledb/iterator.go @@ -0,0 +1,47 @@ +package pebbledb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/cockroachdb/pebble" +) + +func NewIterator(iter *pebble.Iterator) engine.Iterator { + return &Iterator{Iterator: iter} +} + +type Iterator struct { + *pebble.Iterator + released bool +} + +func (i *Iterator) Seek(key []byte) bool { + return i.Iterator.SeekGE(key) +} + +func (i *Iterator) Key() []byte { + if !i.Iterator.Valid() { // return nil if the iterator is exhausted + return nil + } + return i.Iterator.Key() +} + +func (i *Iterator) Value() []byte { + if !i.Iterator.Valid() { // return nil if the iterator is exhausted + return nil + } + return i.Iterator.Value() +} + +func (i *Iterator) Release() { + if !i.released { + i.released = true + i.Iterator.Close() + } +} + +func (i *Iterator) Error() error { + if i.released { + return engine.ErrIterReleased + } + return i.Iterator.Error() +} diff --git a/database/engine/pebbledb/pebbledb.go b/database/engine/pebbledb/pebbledb.go new file mode 100644 index 00000000..8078a557 --- /dev/null +++ b/database/engine/pebbledb/pebbledb.go @@ -0,0 +1,92 @@ +package pebbledb + +import ( + "errors" + "runtime" + "sync/atomic" + + "github.com/btcsuite/btcd/database/engine" + "github.com/cockroachdb/pebble" + "github.com/cockroachdb/pebble/bloom" +) + +var ( + ErrDbClosed = errors.New("pebbledb: closed") + ErrTxClosed = errors.New("pebbledb: transaction already closed") + ErrSnapshotReleased = errors.New("pebbledb: snapshot released") + ErrIteratorReleased = errors.New("pebbledb: iterator released") +) + +const ( + DefaultCache = 64 + DefaultHandles = 16 +) + +func NewDB(dbPath string, create bool, cache, handles int) (engine.Engine, error) { + if cache <= 0 { + cache = DefaultCache + } + if handles <= 0 { + handles = DefaultHandles + } + + opts := &pebble.Options{ + Cache: pebble.NewCache(int64(cache * 1024 * 1024)), // cache MB + ErrorIfExists: create, // Fail if the database exists and create is true + MaxOpenFiles: handles, + MaxConcurrentCompactions: runtime.NumCPU, + Levels: []pebble.LevelOptions{ + {TargetFileSize: 2 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 4 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 8 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 16 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 32 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 64 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + {TargetFileSize: 128 * 1024 * 1024, FilterPolicy: bloom.FilterPolicy(10)}, + }, + } + opts.Experimental.ReadSamplingMultiplier = -1 + dbEngine, err := pebble.Open(dbPath, opts) + if err != nil { + return nil, err + } + + return &DB{DB: dbEngine}, nil +} + +type DB struct { + *pebble.DB + + closed atomic.Bool +} + +// Set closed flag; return true if not already closed. +func (db *DB) setClosed() bool { + return !db.closed.Swap(true) +} + +// Check whether DB was closed. +func (db *DB) isClosed() bool { + return db.closed.Load() +} + +func (d *DB) Transaction() (engine.Transaction, error) { + if d.isClosed() { + return nil, ErrDbClosed + } + return NewTransaction(d.DB.NewBatch()), nil +} + +func (d *DB) Snapshot() (engine.Snapshot, error) { + if d.isClosed() { + return nil, ErrDbClosed + } + return NewSnapshot(d.DB.NewSnapshot()), nil +} + +func (d *DB) Close() error { + if !d.setClosed() { + return ErrDbClosed + } + return d.DB.Close() +} diff --git a/database/engine/pebbledb/snapshot.go b/database/engine/pebbledb/snapshot.go new file mode 100644 index 00000000..e9888e0b --- /dev/null +++ b/database/engine/pebbledb/snapshot.go @@ -0,0 +1,65 @@ +package pebbledb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/cockroachdb/pebble" +) + +func NewSnapshot(snapshot *pebble.Snapshot) engine.Snapshot { + return &Snapshot{Snapshot: snapshot} +} + +type Snapshot struct { + *pebble.Snapshot + released bool +} + +func (s *Snapshot) Has(key []byte) (bool, error) { + if s.released { + return false, ErrSnapshotReleased + } + + val, err := s.Get(key) + if err == pebble.ErrNotFound { + return false, nil + } else if err != nil { + return false, err + } + return val != nil, nil +} + +func (s *Snapshot) Get(key []byte) (val []byte, err error) { + if s.released { + return nil, ErrSnapshotReleased + } + + ori, closer, err := s.Snapshot.Get(key) + if err != nil { + return nil, err + } + defer closer.Close() + + val = make([]byte, len(ori)) + copy(val, ori) + return val, nil +} + +func (s *Snapshot) Release() { + if !s.released { + s.released = true + s.Close() + } +} + +func (s *Snapshot) NewIterator(slice *engine.Range) engine.Iterator { + if s.released { + return nil + } + + iter, _ := s.Snapshot.NewIter(&pebble.IterOptions{ + LowerBound: slice.Start, + UpperBound: slice.Limit, + }) + iter.SeekLT(slice.Start) + return NewIterator(iter) +} diff --git a/database/engine/pebbledb/transaction.go b/database/engine/pebbledb/transaction.go new file mode 100644 index 00000000..ae00f599 --- /dev/null +++ b/database/engine/pebbledb/transaction.go @@ -0,0 +1,44 @@ +package pebbledb + +import ( + "github.com/btcsuite/btcd/database/engine" + "github.com/cockroachdb/pebble" +) + +func NewTransaction(batch *pebble.Batch) engine.Transaction { + return &Transaction{Batch: batch} +} + +type Transaction struct { + *pebble.Batch + released bool +} + +func (t *Transaction) Put(key, value []byte) error { + if t.released { + return ErrTxClosed + } + return t.Batch.Set(key, value, pebble.NoSync) +} + +func (t *Transaction) Delete(key []byte) error { + if t.released { + return ErrTxClosed + } + + return t.Batch.Delete(key, pebble.NoSync) +} + +func (t *Transaction) Discard() { + if !t.released { + t.released = true + t.Batch.Close() + } +} + +func (t *Transaction) Commit() error { + if t.released { + return ErrTxClosed + } + return t.Batch.Commit(pebble.Sync) +} diff --git a/database/ffldb/db.go b/database/ffldb/db.go index 72fe07f9..dbba7d62 100644 --- a/database/ffldb/db.go +++ b/database/ffldb/db.go @@ -19,6 +19,7 @@ import ( "github.com/btcsuite/btcd/database" "github.com/btcsuite/btcd/database/engine" "github.com/btcsuite/btcd/database/engine/leveldb" + "github.com/btcsuite/btcd/database/engine/pebbledb" "github.com/btcsuite/btcd/database/internal/treap" "github.com/btcsuite/btcd/wire" @@ -143,13 +144,13 @@ func convertErr(desc string, dbErr error) database.Error { code = database.ErrCorruption // Database open/create errors. - case dbErr == ldb.ErrClosed: + case dbErr == ldb.ErrClosed, dbErr == pebbledb.ErrDbClosed: code = database.ErrDbNotOpen // Transaction errors. - case dbErr == ldb.ErrSnapshotReleased: + case dbErr == ldb.ErrSnapshotReleased, dbErr == pebbledb.ErrSnapshotReleased: code = database.ErrTxClosed - case dbErr == ldb.ErrIterReleased: + case dbErr == ldb.ErrIterReleased, dbErr == pebbledb.ErrIteratorReleased: code = database.ErrTxClosed } @@ -2137,6 +2138,8 @@ func openDB(dbType string, dbPath string, network wire.BitcoinNet, create bool) var dbEngine engine.Engine var err error switch dbType { + case PebbleDB: + dbEngine, err = pebbledb.NewDB(metadataDbPath, create, 0, 0) case LevelDB: dbEngine, err = leveldb.NewDB(metadataDbPath, create) default: diff --git a/database/ffldb/driver.go b/database/ffldb/driver.go index cdcd7d63..7bbaba41 100644 --- a/database/ffldb/driver.go +++ b/database/ffldb/driver.go @@ -15,7 +15,8 @@ import ( var log = btclog.Disabled const ( - LevelDB = "ffldb" + LevelDB = "ffldb" + PebbleDB = "pebbledb" ) // parseArgs parses the arguments from the database Open/Create methods. @@ -78,6 +79,12 @@ func init() { Open: openDBDriver, UseLogger: useLogger, }, + { + DbType: PebbleDB, + Create: createDBDriver, + Open: openDBDriver, + UseLogger: useLogger, + }, } for _, driver := range drivers { if err := database.RegisterDriver(driver); err != nil { diff --git a/go.mod b/go.mod index 04110578..ccf55cf2 100644 --- a/go.mod +++ b/go.mod @@ -8,26 +8,51 @@ require ( github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 github.com/btcsuite/winsvc v1.0.0 + github.com/cockroachdb/pebble v1.1.4 github.com/davecgh/go-spew v1.1.1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 github.com/decred/dcrd/lru v1.0.0 github.com/gorilla/websocket v1.5.0 github.com/jessevdk/go-flags v1.4.0 github.com/jrick/logrotate v1.0.0 - github.com/stretchr/testify v1.8.4 + github.com/stretchr/testify v1.9.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 golang.org/x/crypto v0.22.0 golang.org/x/sys v0.19.0 ) require ( + github.com/DataDog/zstd v1.4.5 // indirect github.com/aead/siphash v1.0.1 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cockroachdb/errors v1.11.3 // indirect + github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce // indirect + github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect + github.com/cockroachdb/redact v1.1.5 // indirect + github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect github.com/decred/dcrd/crypto/blake256 v1.0.0 // indirect + github.com/getsentry/sentry-go v0.27.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/golang/snappy v0.0.4 // indirect github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 // indirect + github.com/klauspost/compress v1.16.0 // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/kr/text v0.2.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - github.com/stretchr/objx v0.5.0 // indirect + github.com/prometheus/client_golang v1.12.0 // indirect + github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a // indirect + github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/procfs v0.7.3 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/stretchr/objx v0.5.2 // indirect + golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df // indirect golang.org/x/net v0.24.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index b92ab408..d87916d2 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,51 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= github.com/aead/siphash v1.0.1 h1:FwHfE/T45KPKYuuSAKyyvE+oPWcaQ+CUmFW0bPlM+kg= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= @@ -28,6 +74,31 @@ github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792 h1:R8vQdOQdZ9Y3 github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0 h1:J9B4L7e3oqhXOcm+2IuNApwzQec85lE+QaikUcCs+dk= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= +github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= +github.com/cockroachdb/errors v1.11.3 h1:5bA+k2Y6r+oz/6Z/RFlNeVCesGARKuC6YymtcDrbC/I= +github.com/cockroachdb/errors v1.11.3/go.mod h1:m4UIW4CDjx+R5cybPsNrRbreomiFqt8o1h1wUVazSd8= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce h1:giXvy4KSc/6g/esnpM7Geqxka4WSqI1SZc7sMJFd3y4= +github.com/cockroachdb/fifo v0.0.0-20240606204812-0bbfbd93a7ce/go.mod h1:9/y3cnZ5GKakj/H4y9r9GTjCvAFta7KLgSHPJJYc52M= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= +github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= +github.com/cockroachdb/pebble v1.1.4 h1:5II1uEP4MyHLDnsrbv/EZ36arcb9Mxg3n+owhZ3GrG8= +github.com/cockroachdb/pebble v1.1.4/go.mod h1:4exszw1r40423ZsmkG/09AFEG83I0uDgfujJdbL6kYU= +github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= +github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -38,31 +109,133 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/lru v1.0.0 h1:Kbsb1SFDsIlaupWPwsPp+dkxiBY1frcS07PCPgotKz8= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= +github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= +github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= +github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0 h1:4IU2WS7AumrZ/40jfhf4QVDMsQwqA7VEHozFRrGARJA= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jrick/logrotate v1.0.0 h1:lQ1bL/n9mBNeIXoTUoYRlK4dHuNJVofX9oWqBtPnSzI= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23 h1:FOOIBWrEkLgmlgGfMuZT83xIwfPDxEI2OHu6xUmJMFE= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/klauspost/compress v1.16.0 h1:iULayQNOReoYUe+1qtKOqw9CwJv3aNQu8ivo7lw1HU4= +github.com/klauspost/compress v1.16.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -75,67 +248,368 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= +github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.0 h1:C+UIj/QWtmqY13Arb8kwMt5j34/0Z2iKamrJ+ryC0Gg= +github.com/prometheus/client_golang v1.12.0/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a h1:CmF68hwI0XsOQ5UwlBopMi2Ow4Pbg32akc4KIVCOm+Y= +github.com/prometheus/client_model v0.2.1-0.20210607210712-147c58e9608a/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df h1:UA2aFVmmsIlefxMk29Dp2juaUSth8Pyn3Tq5Y5mJGME= +golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= From 670c0817ea407388dd2a52505f216b9b89f6b506 Mon Sep 17 00:00:00 2001 From: Kim Date: Fri, 7 Mar 2025 18:24:12 +0900 Subject: [PATCH 5/5] database: add test suite for LevelDB and PebbleDB engines --- database/engine/leveldb/leveldb_test.go | 19 +++ database/engine/pebbledb/pebbledb_test.go | 19 +++ database/engine/testsuite.go | 167 ++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 database/engine/leveldb/leveldb_test.go create mode 100644 database/engine/pebbledb/pebbledb_test.go create mode 100644 database/engine/testsuite.go diff --git a/database/engine/leveldb/leveldb_test.go b/database/engine/leveldb/leveldb_test.go new file mode 100644 index 00000000..b520b8a2 --- /dev/null +++ b/database/engine/leveldb/leveldb_test.go @@ -0,0 +1,19 @@ +package leveldb + +import ( + "path/filepath" + "testing" + + "github.com/btcsuite/btcd/database/engine" + "github.com/stretchr/testify/require" +) + +func TestSuiteLevelDB(t *testing.T) { + engine.TestSuiteEngine(t, func() engine.Engine { + dbPath := filepath.Join(t.TempDir(), "leveldb-testsuite") + + leveldb, err := NewDB(dbPath, true) + require.NoErrorf(t, err, "failed to create leveldb") + return leveldb + }) +} diff --git a/database/engine/pebbledb/pebbledb_test.go b/database/engine/pebbledb/pebbledb_test.go new file mode 100644 index 00000000..d2e96611 --- /dev/null +++ b/database/engine/pebbledb/pebbledb_test.go @@ -0,0 +1,19 @@ +package pebbledb + +import ( + "path/filepath" + "testing" + + "github.com/btcsuite/btcd/database/engine" + "github.com/stretchr/testify/require" +) + +func TestSuitePebbleDB(t *testing.T) { + engine.TestSuiteEngine(t, func() engine.Engine { + dbPath := filepath.Join(t.TempDir(), "pebbledb-testsuite") + + pebbledb, err := NewDB(dbPath, true, 0, 0) + require.NoErrorf(t, err, "failed to create pebbledb") + return pebbledb + }) +} diff --git a/database/engine/testsuite.go b/database/engine/testsuite.go new file mode 100644 index 00000000..0e5944a2 --- /dev/null +++ b/database/engine/testsuite.go @@ -0,0 +1,167 @@ +package engine + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestSuiteEngine(t *testing.T, new func() Engine) { + t.Run("TransactionSnapshot", func(t *testing.T) { + engine := new() + defer engine.Close() + + // Create new transaction + tx, err := engine.Transaction() + require.NoErrorf(t, err, "failed to create transaction") + + // Put some data into the transaction + key := []byte("key1") + value := []byte("value1") + err = tx.Put(key, value) + require.NoErrorf(t, err, "failed to put data into transaction") + + // Create a snapshot and find empty data + snapshot, err := engine.Snapshot() + require.NoErrorf(t, err, "failed to create snapshot") + + has, err := snapshot.Has(key) + require.NoErrorf(t, err, "failed to check if key exists in snapshot") + require.Falsef(t, has, "expected key to not exist in snapshot") + + gotValue, err := snapshot.Get(key) + require.Errorf(t, err, "expected to get error when getting value from snapshot") + require.Nil(t, gotValue, "expected to get nil value from snapshot") + snapshot.Release() + + // Commit the transaction + err = tx.Commit() + require.NoErrorf(t, err, "failed to commit transaction") + + // Create a snapshot and verify the data + snapshot, err = engine.Snapshot() + require.NoErrorf(t, err, "failed to create snapshot") + + gotValue, err = snapshot.Get(key) + require.NoErrorf(t, err, "failed to get value from snapshot") + require.Equalf(t, value, gotValue, "snapshot value mismatch") + snapshot.Release() + }) + + t.Run("TransactionIterator", func(t *testing.T) { + for _, test := range []struct { + kvs map[string]string // random order of key-value pairs + ranges *Range + expectkvs [][2]string + }{ + { + kvs: map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, + ranges: &Range{Start: []byte("key0"), Limit: []byte("key1")}, + expectkvs: nil, + }, + { + kvs: map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, + ranges: &Range{Start: []byte("key0"), Limit: []byte("key2")}, + expectkvs: [][2]string{{"key1", "value1"}}, + }, + { + kvs: map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, + ranges: &Range{Start: []byte("key1"), Limit: []byte("key3")}, + expectkvs: [][2]string{{"key1", "value1"}, {"key2", "value2"}}, + }, + { + kvs: map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, + ranges: &Range{Start: []byte("key10"), Limit: []byte("key30")}, + expectkvs: [][2]string{{"key2", "value2"}, {"key3", "value3"}}, + }, + { + kvs: map[string]string{"key1": "value1", "key2": "value2", "key3": "value3"}, + ranges: &Range{Start: []byte("key2"), Limit: []byte("key2")}, + expectkvs: nil, + }, + { + kvs: map[string]string{"key10": "value10", "key11": "value11", "key20": "value20", "key21": "value21"}, + ranges: BytesPrefix([]byte("key1")), + expectkvs: [][2]string{{"key10", "value10"}, {"key11", "value11"}}, + }, + } { + engine := new() + defer engine.Close() + + // Create new transaction + tx, err := engine.Transaction() + require.NoErrorf(t, err, "failed to create transaction") + + // Put some data into the transaction + for k, v := range test.kvs { + err = tx.Put([]byte(k), []byte(v)) + require.NoErrorf(t, err, "failed to put data into transaction") + } + // Commit the transaction + err = tx.Commit() + require.NoErrorf(t, err, "failed to commit transaction") + + // Iterate over the data + snapshot, err := engine.Snapshot() + require.NoErrorf(t, err, "failed to create snapshot") + + iter := snapshot.NewIterator(test.ranges) + var idx int + for iter.Next() { + if idx >= len(test.expectkvs) { + require.FailNowf(t, "unexpected key-value pair", "key: %s, value: %s", iter.Key(), iter.Value()) + } + + require.Equalf(t, []byte(test.expectkvs[idx][0]), iter.Key(), "key mismatch") + require.Equalf(t, []byte(test.expectkvs[idx][1]), iter.Value(), "value mismatch") + idx++ + } + require.Equalf(t, len(test.expectkvs), idx, "key-value pair count mismatch") + + iter.Release() + snapshot.Release() + } + }) + + t.Run("DbClose", func(t *testing.T) { + engine := new() + + // release + transaction, err := engine.Transaction() + require.NoErrorf(t, err, "failed to create transaction") + + transaction.Discard() + transaction.Discard() // multiple calls to discard should be safe + err = transaction.Commit() + require.Errorf(t, err, "expected to get error when committing discarded transaction") + + snapshot, err := engine.Snapshot() + require.NoErrorf(t, err, "failed to create snapshot") + + iterator := snapshot.NewIterator(&Range{}) + require.NoErrorf(t, iterator.Error(), "failed to create iterator") + iterator.Release() + iterator.Release() // multiple calls to release should be safe + + snapshot.Release() + snapshot.Release() // multiple calls to release should be safe + _, err = snapshot.Get([]byte("key")) + require.Errorf(t, err, "expected to get error when getting value from released snapshot") + + err = engine.Close() + require.NoErrorf(t, err, "failed to close engine") + + // Ensure that the engine is closed + err = engine.Close() + require.Errorf(t, err, "expected to get error when closing closed engine") + + // Get a transaction from a closed engine + _, err = engine.Transaction() + require.Errorf(t, err, "expected to get error when creating transaction from closed engine") + + // Get a snapshot from a closed engine + _, err = engine.Snapshot() + require.Errorf(t, err, "expected to get error when creating snapshot from closed engine") + }) + +}