database: Combine xpub and index concepts.
Fee xpub key and last used address index are now wrapped into a struct, with both fields being set and retrieved together rather than individually. The underlying format for storing these values in the database does not change. The only change is the interface between the database code and the caller.
This commit is contained in:
parent
be31d54dac
commit
0633260a3c
@ -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
|
|
||||||
}
|
|
||||||
@ -138,7 +138,11 @@ func CreateNew(dbFile, feeXPub string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Store fee xpub.
|
// Store fee xpub.
|
||||||
err = vspBkt.Put(feeXPubK, []byte(feeXPub))
|
xpub := FeeXPub{
|
||||||
|
Key: feeXPub,
|
||||||
|
LastUsedIdx: 0,
|
||||||
|
}
|
||||||
|
err = insertFeeXPub(tx, xpub)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -311,26 +315,6 @@ func (vdb *VspDatabase) KeyPair() (ed25519.PrivateKey, ed25519.PublicKey, error)
|
|||||||
return signKey, pubKey, err
|
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
|
// CookieSecret retrieves the generated cookie store secret key from the
|
||||||
// database.
|
// database.
|
||||||
func (vdb *VspDatabase) CookieSecret() ([]byte, error) {
|
func (vdb *VspDatabase) CookieSecret() ([]byte, error) {
|
||||||
|
|||||||
@ -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
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ func TestDatabase(t *testing.T) {
|
|||||||
"testTicketFeeExpired": testTicketFeeExpired,
|
"testTicketFeeExpired": testTicketFeeExpired,
|
||||||
"testFilterTickets": testFilterTickets,
|
"testFilterTickets": testFilterTickets,
|
||||||
"testCountTickets": testCountTickets,
|
"testCountTickets": testCountTickets,
|
||||||
"testAddressIndex": testAddressIndex,
|
"testFeeXPub": testFeeXPub,
|
||||||
"testDeleteTicket": testDeleteTicket,
|
"testDeleteTicket": testDeleteTicket,
|
||||||
"testVoteChangeRecords": testVoteChangeRecords,
|
"testVoteChangeRecords": testVoteChangeRecords,
|
||||||
"testHTTPBackup": testHTTPBackup,
|
"testHTTPBackup": testHTTPBackup,
|
||||||
@ -134,16 +134,6 @@ func testCreateNew(t *testing.T) {
|
|||||||
if len(secret) != 32 {
|
if len(secret) != 32 {
|
||||||
t.Fatalf("expected a 32 byte cookie secret, got %d bytes", len(secret))
|
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) {
|
func testHTTPBackup(t *testing.T) {
|
||||||
|
|||||||
75
database/feexpub.go
Normal file
75
database/feexpub.go
Normal file
@ -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)
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -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
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@ -8,30 +8,35 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
func testAddressIndex(t *testing.T) {
|
func testFeeXPub(t *testing.T) {
|
||||||
|
// A newly created DB should store the fee xpub it was initialized with, and
|
||||||
// Getting index before it has been set should return 0.
|
// the last used index should be 0.
|
||||||
idx, err := db.GetLastAddressIndex()
|
retrievedXPub, err := db.FeeXPub()
|
||||||
if err != nil {
|
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")
|
t.Fatalf("retrieved addr index value didnt match expected")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update address index.
|
// Update address index.
|
||||||
idx = uint32(99)
|
idx := uint32(99)
|
||||||
err = db.SetLastAddressIndex(idx)
|
err = db.SetLastAddressIndex(idx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error setting address index: %v", err)
|
t.Fatalf("error setting address index: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for updated value.
|
// Check for updated value.
|
||||||
retrievedIdx, err := db.GetLastAddressIndex()
|
retrievedXPub, err = db.FeeXPub()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("error getting address index: %v", err)
|
t.Fatalf("error getting address index: %v", err)
|
||||||
}
|
}
|
||||||
if idx != retrievedIdx {
|
if idx != retrievedXPub.LastUsedIdx {
|
||||||
t.Fatalf("retrieved addr index value didnt match expected")
|
t.Fatalf("retrieved addr index value didnt match expected")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -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
|
// Use of this source code is governed by an ISC
|
||||||
// license that can be found in the LICENSE file.
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
@ -11,6 +11,7 @@ import (
|
|||||||
"github.com/decred/dcrd/hdkeychain/v3"
|
"github.com/decred/dcrd/hdkeychain/v3"
|
||||||
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
"github.com/decred/dcrd/txscript/v4/stdaddr"
|
||||||
"github.com/decred/slog"
|
"github.com/decred/slog"
|
||||||
|
"github.com/decred/vspd/database"
|
||||||
)
|
)
|
||||||
|
|
||||||
type addressGenerator struct {
|
type addressGenerator struct {
|
||||||
@ -20,8 +21,8 @@ type addressGenerator struct {
|
|||||||
log slog.Logger
|
log slog.Logger
|
||||||
}
|
}
|
||||||
|
|
||||||
func newAddressGenerator(xPub string, netParams *chaincfg.Params, lastUsedIdx uint32, log slog.Logger) (*addressGenerator, error) {
|
func newAddressGenerator(xPub database.FeeXPub, netParams *chaincfg.Params, log slog.Logger) (*addressGenerator, error) {
|
||||||
xPubKey, err := hdkeychain.NewKeyFromString(xPub, netParams)
|
xPubKey, err := hdkeychain.NewKeyFromString(xPub.Key, netParams)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -39,7 +40,7 @@ func newAddressGenerator(xPub string, netParams *chaincfg.Params, lastUsedIdx ui
|
|||||||
return &addressGenerator{
|
return &addressGenerator{
|
||||||
external: external,
|
external: external,
|
||||||
netParams: netParams,
|
netParams: netParams,
|
||||||
lastUsedIndex: lastUsedIdx,
|
lastUsedIndex: xPub.LastUsedIdx,
|
||||||
log: log,
|
log: log,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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)
|
log.Errorf("Could not initialize VSP stats cache: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the last used address index and the feeXpub from the database, and
|
// Get the current fee xpub details from the database.
|
||||||
// use them to initialize the address generator.
|
|
||||||
idx, err := vdb.GetLastAddressIndex()
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("db.GetLastAddressIndex error: %w", err)
|
|
||||||
}
|
|
||||||
feeXPub, err := vdb.FeeXPub()
|
feeXPub, err := vdb.FeeXPub()
|
||||||
if err != nil {
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to initialize fee address generator: %w", err)
|
return nil, fmt.Errorf("failed to initialize fee address generator: %w", err)
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user