From 4ec3fb4928d6063a45f72b9e1f4cf2c0cfdae031 Mon Sep 17 00:00:00 2001 From: carla Date: Mon, 25 May 2020 10:56:25 +0200 Subject: [PATCH 1/3] wtxmgr: separate PutTxLabel into store-independent function Separate out the logic we use to write labels so that it can be used in dropwtxmgr to re-add labels if we want to keep them when we drop the rest of the db. --- wtxmgr/tx.go | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/wtxmgr/tx.go b/wtxmgr/tx.go index 512fe1a..8c9e574 100644 --- a/wtxmgr/tx.go +++ b/wtxmgr/tx.go @@ -984,6 +984,15 @@ func (s *Store) PutTxLabel(ns walletdb.ReadWriteBucket, txid chainhash.Hash, return err } + return PutTxLabel(labelBucket, txid, label) +} + +// PutTxLabel writes a label for a tx to the bucket provided. Note that it does +// not perform any validation on the label provided, or check whether there is +// an existing label for the txid. +func PutTxLabel(labelBucket walletdb.ReadWriteBucket, txid chainhash.Hash, + label string) error { + // We expect the label length to be limited on creation, so we can // store the label's length as a uint16. labelLen := uint16(len(label)) From ab19740c94aba6a3008981aee66af80843cdc1c3 Mon Sep 17 00:00:00 2001 From: carla Date: Thu, 28 May 2020 09:07:31 +0200 Subject: [PATCH 2/3] wtxmgr: extract label deserialization into separate function Requrired so that we can use the same logic in dropwtxmgr. --- wtxmgr/tx.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/wtxmgr/tx.go b/wtxmgr/tx.go index 8c9e574..9674c9a 100644 --- a/wtxmgr/tx.go +++ b/wtxmgr/tx.go @@ -1025,6 +1025,12 @@ func FetchTxLabel(ns walletdb.ReadBucket, txid chainhash.Hash) (string, error) { return "", ErrTxLabelNotFound } + return DeserializeLabel(v) +} + +// DeserializeLabel reads a deserializes a length-value encoded label from the +// byte array provided. +func DeserializeLabel(v []byte) (string, error) { // If the label is empty, return an error. length := binary.BigEndian.Uint16(v[0:2]) if length == 0 { From 338cf08088bdd662747f5229c07810a8fbf044a0 Mon Sep 17 00:00:00 2001 From: carla Date: Thu, 28 May 2020 09:09:15 +0200 Subject: [PATCH 3/3] dropwtxmgr: keep tx labels, with an optional DropLabels flag --- cmd/dropwtxmgr/main.go | 92 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 90 insertions(+), 2 deletions(-) diff --git a/cmd/dropwtxmgr/main.go b/cmd/dropwtxmgr/main.go index 8f47644..9447e6e 100644 --- a/cmd/dropwtxmgr/main.go +++ b/cmd/dropwtxmgr/main.go @@ -10,6 +10,7 @@ import ( "os" "path/filepath" + "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcutil" "github.com/btcsuite/btcwallet/waddrmgr" "github.com/btcsuite/btcwallet/walletdb" @@ -24,8 +25,9 @@ var datadir = btcutil.AppDataDir("btcwallet", false) // Flags. var opts = struct { - Force bool `short:"f" description:"Force removal without prompt"` - DbPath string `long:"db" description:"Path to wallet database"` + Force bool `short:"f" description:"Force removal without prompt"` + DbPath string `long:"db" description:"Path to wallet database"` + DropLabels bool `long:"droplabels" description:"Drop transaction labels"` }{ Force: false, DbPath: filepath.Join(datadir, defaultNet, "wallet.db"), @@ -42,6 +44,9 @@ var ( // Namespace keys. waddrmgrNamespace = []byte("waddrmgr") wtxmgrNamespace = []byte("wtxmgr") + + // Bucket names. + bucketTxLabels = []byte("l") ) func yes(s string) bool { @@ -109,6 +114,16 @@ func mainInt() int { fmt.Println("Dropping btcwallet transaction history") err = walletdb.Update(db, func(tx walletdb.ReadWriteTx) error { + // If we want to keep our tx labels, we read them out so we + // can re-add them after we have deleted our wtxmgr. + var labels map[chainhash.Hash]string + if !opts.DropLabels { + labels, err = fetchAllLabels(tx) + if err != nil { + return err + } + } + err := tx.DeleteTopLevelBucket(wtxmgrNamespace) if err != nil && err != walletdb.ErrBucketNotFound { return err @@ -122,6 +137,13 @@ func mainInt() int { return err } + // If we want to re-add our labels, we do so now. + if !opts.DropLabels { + if err := putTxLabels(ns, labels); err != nil { + return err + } + } + ns = tx.ReadWriteBucket(waddrmgrNamespace) birthdayBlock, err := waddrmgr.FetchBirthdayBlock(ns) if err != nil { @@ -154,3 +176,69 @@ func mainInt() int { return 0 } + +// fetchAllLabels returns a map of hex-encoded txid to label. +func fetchAllLabels(tx walletdb.ReadWriteTx) (map[chainhash.Hash]string, + error) { + + // Get our top level bucket, if it does not exist we just exit. + txBucket := tx.ReadBucket(wtxmgrNamespace) + if txBucket == nil { + return nil, nil + } + + // If we do not have a labels bucket, there are no labels so we exit. + labelsBucket := txBucket.NestedReadBucket(bucketTxLabels) + if labelsBucket == nil { + return nil, nil + } + + labels := make(map[chainhash.Hash]string) + if err := labelsBucket.ForEach(func(k, v []byte) error { + txid, err := chainhash.NewHash(k) + if err != nil { + return err + } + + label, err := wtxmgr.DeserializeLabel(v) + if err != nil { + return err + } + + // Add an entry to our map of labels. + labels[*txid] = label + + return nil + }); err != nil { + return nil, err + } + + return labels, nil +} + +// putTxLabels re-adds a nested labels bucket and entries to the bucket provided +// if there are any labels present. +func putTxLabels(ns walletdb.ReadWriteBucket, + labels map[chainhash.Hash]string) error { + + // If there are no labels, exit early. + if len(labels) == 0 { + return nil + } + + // First, we create a labels bucket which we will add all labels to. + labelBucket, err := ns.CreateBucketIfNotExists(bucketTxLabels) + if err != nil { + return err + } + + // Next, we re-add every label to the bucket. + for txid, label := range labels { + err := wtxmgr.PutTxLabel(labelBucket, txid, label) + if err != nil { + return err + } + } + + return nil +}