diff --git a/database/addressindex.go b/database/addressindex.go deleted file mode 100644 index 8338d6da..00000000 --- a/database/addressindex.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright (c) 2020 The Decred developers -// Use of this source code is governed by an ISC -// license that can be found in the LICENSE file. - -package database - -import ( - bolt "go.etcd.io/bbolt" -) - -// GetLastAddressIndex retrieves the last index used to derive a new fee -// address from the fee xpub key. -func (vdb *VspDatabase) GetLastAddressIndex() (uint32, error) { - var idx uint32 - err := vdb.db.View(func(tx *bolt.Tx) error { - vspBkt := tx.Bucket(vspBktK) - - idxBytes := vspBkt.Get(lastAddressIndexK) - if idxBytes == nil { - return nil - } - - idx = bytesToUint32(idxBytes) - - return nil - }) - - return idx, err -} - -// SetLastAddressIndex updates the last index used to derive a new fee address -// from the fee xpub key. -func (vdb *VspDatabase) SetLastAddressIndex(idx uint32) error { - err := vdb.db.Update(func(tx *bolt.Tx) error { - vspBkt := tx.Bucket(vspBktK) - return vspBkt.Put(lastAddressIndexK, uint32ToBytes(idx)) - - }) - - return err -} diff --git a/database/database.go b/database/database.go index 2c0fcff3..11d3cfad 100644 --- a/database/database.go +++ b/database/database.go @@ -138,7 +138,11 @@ func CreateNew(dbFile, feeXPub string) error { } // Store fee xpub. - err = vspBkt.Put(feeXPubK, []byte(feeXPub)) + xpub := FeeXPub{ + Key: feeXPub, + LastUsedIdx: 0, + } + err = insertFeeXPub(tx, xpub) if err != nil { return err } @@ -311,26 +315,6 @@ func (vdb *VspDatabase) KeyPair() (ed25519.PrivateKey, ed25519.PublicKey, error) return signKey, pubKey, err } -// FeeXPub retrieves the extended pubkey used for generating fee addresses -// from the database. -func (vdb *VspDatabase) FeeXPub() (string, error) { - var feeXPub string - err := vdb.db.View(func(tx *bolt.Tx) error { - vspBkt := tx.Bucket(vspBktK) - - xpubBytes := vspBkt.Get(feeXPubK) - if xpubBytes == nil { - return nil - } - - feeXPub = string(xpubBytes) - - return nil - }) - - return feeXPub, err -} - // CookieSecret retrieves the generated cookie store secret key from the // database. func (vdb *VspDatabase) CookieSecret() ([]byte, error) { diff --git a/database/database_test.go b/database/database_test.go index a340f7a4..be5bdb58 100644 --- a/database/database_test.go +++ b/database/database_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2022 The Decred developers +// Copyright (c) 2020-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -77,7 +77,7 @@ func TestDatabase(t *testing.T) { "testTicketFeeExpired": testTicketFeeExpired, "testFilterTickets": testFilterTickets, "testCountTickets": testCountTickets, - "testAddressIndex": testAddressIndex, + "testFeeXPub": testFeeXPub, "testDeleteTicket": testDeleteTicket, "testVoteChangeRecords": testVoteChangeRecords, "testHTTPBackup": testHTTPBackup, @@ -134,16 +134,6 @@ func testCreateNew(t *testing.T) { if len(secret) != 32 { t.Fatalf("expected a 32 byte cookie secret, got %d bytes", len(secret)) } - - // A newly created DB should store the fee xpub it was initialized with. - retrievedXPub, err := db.FeeXPub() - if err != nil { - t.Fatalf("error getting fee xpub: %v", err) - } - - if retrievedXPub != feeXPub { - t.Fatalf("expected fee xpub %v, got %v", feeXPub, retrievedXPub) - } } func testHTTPBackup(t *testing.T) { diff --git a/database/feexpub.go b/database/feexpub.go new file mode 100644 index 00000000..df3ab7a6 --- /dev/null +++ b/database/feexpub.go @@ -0,0 +1,75 @@ +// Copyright (c) 2020-2024 The Decred developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package database + +import ( + "fmt" + + bolt "go.etcd.io/bbolt" +) + +type FeeXPub struct { + Key string + LastUsedIdx uint32 +} + +// insertFeeXPub stores the provided pubkey in the database, regardless of +// whether a value pre-exists. +func insertFeeXPub(tx *bolt.Tx, xpub FeeXPub) error { + vspBkt := tx.Bucket(vspBktK) + + err := vspBkt.Put(feeXPubK, []byte(xpub.Key)) + if err != nil { + return err + } + + return vspBkt.Put(lastAddressIndexK, uint32ToBytes(xpub.LastUsedIdx)) +} + +// FeeXPub retrieves the extended pubkey used for generating fee addresses +// from the database. +func (vdb *VspDatabase) FeeXPub() (FeeXPub, error) { + var feeXPub string + var idx uint32 + err := vdb.db.View(func(tx *bolt.Tx) error { + vspBkt := tx.Bucket(vspBktK) + + // Get the key. + xpubBytes := vspBkt.Get(feeXPubK) + if xpubBytes == nil { + return nil + } + feeXPub = string(xpubBytes) + + // Get the last used address index. + idxBytes := vspBkt.Get(lastAddressIndexK) + if idxBytes == nil { + return nil + } + idx = bytesToUint32(idxBytes) + + return nil + }) + if err != nil { + return FeeXPub{}, fmt.Errorf("could not retrieve fee xpub: %w", err) + } + + return FeeXPub{Key: feeXPub, LastUsedIdx: idx}, nil +} + +// SetLastAddressIndex updates the last index used to derive a new fee address +// from the fee xpub key. +func (vdb *VspDatabase) SetLastAddressIndex(idx uint32) error { + current, err := vdb.FeeXPub() + if err != nil { + return err + } + + current.LastUsedIdx = idx + + return vdb.db.Update(func(tx *bolt.Tx) error { + return insertFeeXPub(tx, current) + }) +} diff --git a/database/addressindex_test.go b/database/feexpub_test.go similarity index 50% rename from database/addressindex_test.go rename to database/feexpub_test.go index 75c1a58c..26804607 100644 --- a/database/addressindex_test.go +++ b/database/feexpub_test.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020 The Decred developers +// Copyright (c) 2020-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -8,30 +8,35 @@ import ( "testing" ) -func testAddressIndex(t *testing.T) { - - // Getting index before it has been set should return 0. - idx, err := db.GetLastAddressIndex() +func testFeeXPub(t *testing.T) { + // A newly created DB should store the fee xpub it was initialized with, and + // the last used index should be 0. + retrievedXPub, err := db.FeeXPub() if err != nil { - t.Fatalf("error getting address index: %v", err) + t.Fatalf("error getting fee xpub: %v", err) } - if idx != 0 { + + if retrievedXPub.Key != feeXPub { + t.Fatalf("expected fee xpub %v, got %v", feeXPub, retrievedXPub.Key) + } + + if retrievedXPub.LastUsedIdx != 0 { t.Fatalf("retrieved addr index value didnt match expected") } // Update address index. - idx = uint32(99) + idx := uint32(99) err = db.SetLastAddressIndex(idx) if err != nil { t.Fatalf("error setting address index: %v", err) } // Check for updated value. - retrievedIdx, err := db.GetLastAddressIndex() + retrievedXPub, err = db.FeeXPub() if err != nil { t.Fatalf("error getting address index: %v", err) } - if idx != retrievedIdx { + if idx != retrievedXPub.LastUsedIdx { t.Fatalf("retrieved addr index value didnt match expected") } } diff --git a/internal/webapi/addressgenerator.go b/internal/webapi/addressgenerator.go index 7d84cc94..73d7c7af 100644 --- a/internal/webapi/addressgenerator.go +++ b/internal/webapi/addressgenerator.go @@ -1,4 +1,4 @@ -// Copyright (c) 2020-2022 The Decred developers +// Copyright (c) 2020-2024 The Decred developers // Use of this source code is governed by an ISC // license that can be found in the LICENSE file. @@ -11,6 +11,7 @@ import ( "github.com/decred/dcrd/hdkeychain/v3" "github.com/decred/dcrd/txscript/v4/stdaddr" "github.com/decred/slog" + "github.com/decred/vspd/database" ) type addressGenerator struct { @@ -20,8 +21,8 @@ type addressGenerator struct { log slog.Logger } -func newAddressGenerator(xPub string, netParams *chaincfg.Params, lastUsedIdx uint32, log slog.Logger) (*addressGenerator, error) { - xPubKey, err := hdkeychain.NewKeyFromString(xPub, netParams) +func newAddressGenerator(xPub database.FeeXPub, netParams *chaincfg.Params, log slog.Logger) (*addressGenerator, error) { + xPubKey, err := hdkeychain.NewKeyFromString(xPub.Key, netParams) if err != nil { return nil, err } @@ -39,7 +40,7 @@ func newAddressGenerator(xPub string, netParams *chaincfg.Params, lastUsedIdx ui return &addressGenerator{ external: external, netParams: netParams, - lastUsedIndex: lastUsedIdx, + lastUsedIndex: xPub.LastUsedIdx, log: log, }, nil } diff --git a/internal/webapi/webapi.go b/internal/webapi/webapi.go index 081899f2..9b501fd3 100644 --- a/internal/webapi/webapi.go +++ b/internal/webapi/webapi.go @@ -95,17 +95,15 @@ func New(vdb *database.VspDatabase, log slog.Logger, dcrd rpc.DcrdConnect, log.Errorf("Could not initialize VSP stats cache: %v", err) } - // Get the last used address index and the feeXpub from the database, and - // use them to initialize the address generator. - idx, err := vdb.GetLastAddressIndex() - if err != nil { - return nil, fmt.Errorf("db.GetLastAddressIndex error: %w", err) - } + // Get the current fee xpub details from the database. feeXPub, err := vdb.FeeXPub() if err != nil { - return nil, fmt.Errorf("db.GetFeeXPub error: %w", err) + return nil, fmt.Errorf("db.FeeXPub error: %w", err) } - addrGen, err := newAddressGenerator(feeXPub, cfg.Network.Params, idx, log) + + // Use the retrieved pubkey to initialize an address generator which can + // later be used to derive new fee addresses. + addrGen, err := newAddressGenerator(feeXPub, cfg.Network.Params, log) if err != nil { return nil, fmt.Errorf("failed to initialize fee address generator: %w", err) }