Repository: kaspanet/kaspad
Pull request #1432 (2021-01-19 11:11:32)

Restructure database to prevent double-slashes in keys, causing bugs in cursors
This bug has eventually caused the deletion of old virtual utxo-set in `commitVirtualUTXOSet` to malfunction, ending up with an invalid UTXO-Set.

codecov[bot]: # [Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=h1) Report
> Merging [#1432](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=desc) (42f8378) into [v0.8.6-dev](https://codecov.io/gh/kaspanet/kaspad/commit/799eb7515c66fe005ff19f13a81851a638aac0ba?el=desc) (799eb75) will **increase** coverage by `0.22%`.
> The diff coverage is `68.75%`.

[![Impacted file tree graph](https://codecov.io/gh/kaspanet/kaspad/pull/1432/graphs/tree.svg?width=650&height=150&src=pr&token=2FBIEEJTNU)](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=tree)

```diff
@@              Coverage Diff               @@
##           v0.8.6-dev    #1432      +/-   ##
==============================================
+ Coverage       33.45%   33.67%   +0.22%     
==============================================
  Files             506      506              
  Lines           23351    23333      -18     
==============================================
+ Hits             7812     7858      +46     
+ Misses          13993    13931      -62     
+ Partials         1546     1544       -2     
```


| [Impacted Files](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [domain/consensus/consensus.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9jb25zZW5zdXMuZ28=) | `48.61% <ø> (ø)` | |
| [domain/consensus/database/errors.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhYmFzZS9lcnJvcnMuZ28=) | `0.00% <0.00%> (ø)` | |
| [domain/consensus/database/key.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhYmFzZS9rZXkuZ28=) | `57.14% <0.00%> (+37.14%)` | :arrow_up: |
| [...uctures/acceptancedatastore/acceptancedatastore.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9hY2NlcHRhbmNlZGF0YXN0b3JlL2FjY2VwdGFuY2VkYXRhc3RvcmUuZ28=) | `42.22% <ø> (ø)` | |
| [...atastructures/blockheaderstore/blockheaderstore.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9ibG9ja2hlYWRlcnN0b3JlL2Jsb2NraGVhZGVyc3RvcmUuZ28=) | `39.58% <ø> (ø)` | |
| [...tructures/blockrelationstore/blockrelationstore.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9ibG9ja3JlbGF0aW9uc3RvcmUvYmxvY2tyZWxhdGlvbnN0b3JlLmdv) | `55.00% <ø> (ø)` | |
| [...atastructures/blockstatusstore/blockstatusstore.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9ibG9ja3N0YXR1c3N0b3JlL2Jsb2Nrc3RhdHVzc3RvcmUuZ28=) | `55.81% <ø> (ø)` | |
| [.../consensus/datastructures/blockstore/blockstore.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9ibG9ja3N0b3JlL2Jsb2Nrc3RvcmUuZ28=) | `43.75% <ø> (ø)` | |
| [...nsensus/datastructures/consensusstatestore/tips.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9jb25zZW5zdXNzdGF0ZXN0b3JlL3RpcHMuZ28=) | `35.48% <ø> (ø)` | |
| [...nsensus/datastructures/consensusstatestore/utxo.go](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree#diff-ZG9tYWluL2NvbnNlbnN1cy9kYXRhc3RydWN0dXJlcy9jb25zZW5zdXNzdGF0ZXN0b3JlL3V0eG8uZ28=) | `25.54% <ø> (ø)` | |
| ... and [88 more](https://codecov.io/gh/kaspanet/kaspad/pull/1432/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=footer). Last update [799eb75...8e9bb58](https://codecov.io/gh/kaspanet/kaspad/pull/1432?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/domain/consensus/consensus.go b/domain/consensus/consensus.go
index 20f81176..fbd50e50 100644
--- a/domain/consensus/consensus.go
+++ b/domain/consensus/consensus.go
@@ -3,8 +3,7 @@ package consensus
 import (
 	"sync"
 
-	"github.com/kaspanet/kaspad/infrastructure/db/database"
-
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
diff --git a/domain/consensus/database/bucket.go b/domain/consensus/database/bucket.go
index 80a7c66a..348e1b8d 100644
--- a/domain/consensus/database/bucket.go
+++ b/domain/consensus/database/bucket.go
@@ -6,9 +6,18 @@ import (
 )
 
 func dbBucketToDatabaseBucket(bucket model.DBBucket) *database.Bucket {
+	if bucket, ok := bucket.(dbBucket); ok {
+		return bucket.bucket
+	}
+	// This assumes that MakeBucket(src).Path() == src. which is not promised anywhere.
 	return database.MakeBucket(bucket.Path())
 }
 
+// MakeBucket creates a new Bucket using the given path of buckets.
+func MakeBucket(path []byte) model.DBBucket {
+	return dbBucket{bucket: database.MakeBucket(path)}
+}
+
 type dbBucket struct {
 	bucket *database.Bucket
 }
@@ -26,5 +35,5 @@ func (d dbBucket) Path() []byte {
 }
 
 func newDBBucket(bucket *database.Bucket) model.DBBucket {
-	return &dbBucket{bucket: bucket}
+	return dbBucket{bucket: bucket}
 }
diff --git a/domain/consensus/database/errors.go b/domain/consensus/database/errors.go
new file mode 100644
index 00000000..025a624a
--- /dev/null
+++ b/domain/consensus/database/errors.go
@@ -0,0 +1,14 @@
+package database
+
+import (
+	"github.com/kaspanet/kaspad/infrastructure/db/database"
+)
+
+// ErrNotFound denotes that the requested item was not
+// found in the database.
+var ErrNotFound = database.ErrNotFound
+
+// IsNotFoundError checks whether an error is an ErrNotFound.
+func IsNotFoundError(err error) bool {
+	return database.IsNotFoundError(err)
+}
diff --git a/domain/consensus/database/key.go b/domain/consensus/database/key.go
index 2524c3b8..5f75471f 100644
--- a/domain/consensus/database/key.go
+++ b/domain/consensus/database/key.go
@@ -6,6 +6,9 @@ import (
 )
 
 func dbKeyToDatabaseKey(key model.DBKey) *database.Key {
+	if key, ok := key.(dbKey); ok {
+		return key.key
+	}
 	return dbBucketToDatabaseBucket(key.Bucket()).Key(key.Suffix())
 }
 
diff --git a/domain/consensus/datastructures/acceptancedatastore/acceptancedatastore.go b/domain/consensus/datastructures/acceptancedatastore/acceptancedatastore.go
index 2cbb76ac..8698904b 100644
--- a/domain/consensus/datastructures/acceptancedatastore/acceptancedatastore.go
+++ b/domain/consensus/datastructures/acceptancedatastore/acceptancedatastore.go
@@ -1,15 +1,15 @@
 package acceptancedatastore
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 	"google.golang.org/protobuf/proto"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("acceptance-data"))
+var bucket = database.MakeBucket([]byte("acceptance-data"))
 
 // acceptanceDataStore represents a store of AcceptanceData
 type acceptanceDataStore struct {
diff --git a/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go b/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
index e0169fec..2d5a9d25 100644
--- a/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
+++ b/domain/consensus/datastructures/blockheaderstore/blockheaderstore.go
@@ -2,15 +2,15 @@ package blockheaderstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("block-headers"))
-var countKey = dbkeys.MakeBucket().Key([]byte("block-headers-count"))
+var bucket = database.MakeBucket([]byte("block-headers"))
+var countKey = database.MakeBucket(nil).Key([]byte("block-headers-count"))
 
 // blockHeaderStore represents a store of blocks
 type blockHeaderStore struct {
diff --git a/domain/consensus/datastructures/blockrelationstore/blockrelationstore.go b/domain/consensus/datastructures/blockrelationstore/blockrelationstore.go
index d1c6a75c..ec7a2e56 100644
--- a/domain/consensus/datastructures/blockrelationstore/blockrelationstore.go
+++ b/domain/consensus/datastructures/blockrelationstore/blockrelationstore.go
@@ -2,14 +2,14 @@ package blockrelationstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("block-relations"))
+var bucket = database.MakeBucket([]byte("block-relations"))
 
 // blockRelationStore represents a store of BlockRelations
 type blockRelationStore struct {
diff --git a/domain/consensus/datastructures/blockstatusstore/blockstatusstore.go b/domain/consensus/datastructures/blockstatusstore/blockstatusstore.go
index 40c97433..e6fc1ed1 100644
--- a/domain/consensus/datastructures/blockstatusstore/blockstatusstore.go
+++ b/domain/consensus/datastructures/blockstatusstore/blockstatusstore.go
@@ -2,14 +2,14 @@ package blockstatusstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("block-statuses"))
+var bucket = database.MakeBucket([]byte("block-statuses"))
 
 // blockStatusStore represents a store of BlockStatuses
 type blockStatusStore struct {
diff --git a/domain/consensus/datastructures/blockstore/blockstore.go b/domain/consensus/datastructures/blockstore/blockstore.go
index a688d085..fd332c4a 100644
--- a/domain/consensus/datastructures/blockstore/blockstore.go
+++ b/domain/consensus/datastructures/blockstore/blockstore.go
@@ -2,15 +2,15 @@ package blockstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("blocks"))
-var countKey = dbkeys.MakeBucket().Key([]byte("blocks-count"))
+var bucket = database.MakeBucket([]byte("blocks"))
+var countKey = database.MakeBucket(nil).Key([]byte("blocks-count"))
 
 // blockStore represents a store of blocks
 type blockStore struct {
diff --git a/domain/consensus/datastructures/finalitystore/finality_store.go b/domain/consensus/datastructures/finalitystore/finality_store.go
index 002f1d30..3d726ada 100644
--- a/domain/consensus/datastructures/finalitystore/finality_store.go
+++ b/domain/consensus/datastructures/finalitystore/finality_store.go
@@ -1,13 +1,13 @@
 package finalitystore
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("finality-points"))
+var bucket = database.MakeBucket([]byte("finality-points"))
 
 type finalityStore struct {
 	staging  map[externalapi.DomainHash]*externalapi.DomainHash
diff --git a/domain/consensus/datastructures/ghostdagdatastore/ghostdagdatastore.go b/domain/consensus/datastructures/ghostdagdatastore/ghostdagdatastore.go
index 8c61ba06..41f0ba58 100644
--- a/domain/consensus/datastructures/ghostdagdatastore/ghostdagdatastore.go
+++ b/domain/consensus/datastructures/ghostdagdatastore/ghostdagdatastore.go
@@ -2,14 +2,14 @@ package ghostdagdatastore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("block-ghostdag-data"))
+var bucket = database.MakeBucket([]byte("block-ghostdag-data"))
 
 // ghostdagDataStore represents a store of BlockGHOSTDAGData
 type ghostdagDataStore struct {
diff --git a/domain/consensus/datastructures/headersselectedchainstore/headersselectedchainstore.go b/domain/consensus/datastructures/headersselectedchainstore/headersselectedchainstore.go
index 34406ec9..f6b2334a 100644
--- a/domain/consensus/datastructures/headersselectedchainstore/headersselectedchainstore.go
+++ b/domain/consensus/datastructures/headersselectedchainstore/headersselectedchainstore.go
@@ -2,19 +2,18 @@ package headersselectedchainstore
 
 import (
 	"encoding/binary"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/binaryserialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucacheuint64tohash"
-	"github.com/kaspanet/kaspad/infrastructure/db/database"
 	"github.com/pkg/errors"
 )
 
-var bucketChainBlockHashByIndex = dbkeys.MakeBucket([]byte("chain-block-hash-by-index"))
-var bucketChainBlockIndexByHash = dbkeys.MakeBucket([]byte("chain-block-index-by-hash"))
-var highestChainBlockIndexKey = dbkeys.MakeBucket().Key([]byte("highest-chain-block-index"))
+var bucketChainBlockHashByIndex = database.MakeBucket([]byte("chain-block-hash-by-index"))
+var bucketChainBlockIndexByHash = database.MakeBucket([]byte("chain-block-index-by-hash"))
+var highestChainBlockIndexKey = database.MakeBucket(nil).Key([]byte("highest-chain-block-index"))
 
 type headersSelectedChainStore struct {
 	stagingAddedByHash          map[externalapi.DomainHash]uint64
diff --git a/domain/consensus/datastructures/headersselectedtipstore/headertipsstore.go b/domain/consensus/datastructures/headersselectedtipstore/headertipsstore.go
index f0df6910..dd4b4b0a 100644
--- a/domain/consensus/datastructures/headersselectedtipstore/headertipsstore.go
+++ b/domain/consensus/datastructures/headersselectedtipstore/headertipsstore.go
@@ -2,13 +2,13 @@ package headersselectedtipstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 )
 
-var headerSelectedTipKey = dbkeys.MakeBucket().Key([]byte("headers-selected-tip"))
+var headerSelectedTipKey = database.MakeBucket(nil).Key([]byte("headers-selected-tip"))
 
 type headerSelectedTipStore struct {
 	staging *externalapi.DomainHash
diff --git a/domain/consensus/datastructures/multisetstore/multisetstore.go b/domain/consensus/datastructures/multisetstore/multisetstore.go
index 20beca60..b6a565d2 100644
--- a/domain/consensus/datastructures/multisetstore/multisetstore.go
+++ b/domain/consensus/datastructures/multisetstore/multisetstore.go
@@ -2,14 +2,14 @@ package multisetstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var bucket = dbkeys.MakeBucket([]byte("multisets"))
+var bucket = database.MakeBucket([]byte("multisets"))
 
 // multisetStore represents a store of Multisets
 type multisetStore struct {
diff --git a/domain/consensus/datastructures/pruningstore/pruningstore.go b/domain/consensus/datastructures/pruningstore/pruningstore.go
index 24a25258..31eefdc9 100644
--- a/domain/consensus/datastructures/pruningstore/pruningstore.go
+++ b/domain/consensus/datastructures/pruningstore/pruningstore.go
@@ -2,15 +2,15 @@ package pruningstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 )
 
-var pruningBlockHashKey = dbkeys.MakeBucket().Key([]byte("pruning-block-hash"))
-var candidatePruningPointHashKey = dbkeys.MakeBucket().Key([]byte("candidate-pruning-point-hash"))
-var pruningSerializedUTXOSetKey = dbkeys.MakeBucket().Key([]byte("pruning-utxo-set"))
+var pruningBlockHashKey = database.MakeBucket(nil).Key([]byte("pruning-block-hash"))
+var candidatePruningPointHashKey = database.MakeBucket(nil).Key([]byte("candidate-pruning-point-hash"))
+var pruningSerializedUTXOSetKey = database.MakeBucket(nil).Key([]byte("pruning-utxo-set"))
 
 // pruningStore represents a store for the current pruning state
 type pruningStore struct {
diff --git a/domain/consensus/datastructures/reachabilitydatastore/reachabilitydatastore.go b/domain/consensus/datastructures/reachabilitydatastore/reachabilitydatastore.go
index 18751045..7d72e0e9 100644
--- a/domain/consensus/datastructures/reachabilitydatastore/reachabilitydatastore.go
+++ b/domain/consensus/datastructures/reachabilitydatastore/reachabilitydatastore.go
@@ -2,15 +2,15 @@ package reachabilitydatastore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 )
 
-var reachabilityDataBucket = dbkeys.MakeBucket([]byte("reachability-data"))
-var reachabilityReindexRootKey = dbkeys.MakeBucket().Key([]byte("reachability-reindex-root"))
+var reachabilityDataBucket = database.MakeBucket([]byte("reachability-data"))
+var reachabilityReindexRootKey = database.MakeBucket(nil).Key([]byte("reachability-reindex-root"))
 
 // reachabilityDataStore represents a store of ReachabilityData
 type reachabilityDataStore struct {
diff --git a/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go b/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
index 9fa0adc1..caab31e3 100644
--- a/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
+++ b/domain/consensus/datastructures/utxodiffstore/utxodiffstore.go
@@ -2,16 +2,16 @@ package utxodiffstore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/lrucache"
 	"github.com/pkg/errors"
 )
 
-var utxoDiffBucket = dbkeys.MakeBucket([]byte("utxo-diffs"))
-var utxoDiffChildBucket = dbkeys.MakeBucket([]byte("utxo-diff-children"))
+var utxoDiffBucket = database.MakeBucket([]byte("utxo-diffs"))
+var utxoDiffChildBucket = database.MakeBucket([]byte("utxo-diff-children"))
 
 // utxoDiffStore represents a store of UTXODiffs
 type utxoDiffStore struct {
diff --git a/domain/consensus/processes/finalitymanager/finality_manager.go b/domain/consensus/processes/finalitymanager/finality_manager.go
index c383dcf2..7ba1dfe7 100644
--- a/domain/consensus/processes/finalitymanager/finality_manager.go
+++ b/domain/consensus/processes/finalitymanager/finality_manager.go
@@ -3,9 +3,9 @@ package finalitymanager
 import (
 	"errors"
 
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/infrastructure/db/database"
 )
 
 type finalityManager struct {
diff --git a/domain/consensus/processes/reachabilitymanager/fetch.go b/domain/consensus/processes/reachabilitymanager/fetch.go
index 561105ee..77ec8220 100644
--- a/domain/consensus/processes/reachabilitymanager/fetch.go
+++ b/domain/consensus/processes/reachabilitymanager/fetch.go
@@ -1,10 +1,10 @@
 package reachabilitymanager
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/reachabilitydata"
-	"github.com/kaspanet/kaspad/infrastructure/db/database"
 	"github.com/pkg/errors"
 )
 
diff --git a/domain/consensus/processes/syncmanager/blocklocator.go b/domain/consensus/processes/syncmanager/blocklocator.go
index aaa205fa..dac5abe6 100644
--- a/domain/consensus/processes/syncmanager/blocklocator.go
+++ b/domain/consensus/processes/syncmanager/blocklocator.go
@@ -1,9 +1,9 @@
 package syncmanager
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/infrastructure/db/database"
 	"github.com/pkg/errors"
 )
 
diff --git a/domain/consensus/utils/dbkeys/dbkeys.go b/domain/consensus/utils/dbkeys/dbkeys.go
deleted file mode 100644
index b78a602b..00000000
--- a/domain/consensus/utils/dbkeys/dbkeys.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package dbkeys
-
-import (
-	"bytes"
-	"encoding/hex"
-	"github.com/kaspanet/kaspad/domain/consensus/model"
-)
-
-var bucketSeparator = []byte("/")
-
-// Key is a helper type meant to combine prefix
-// and suffix into a single database key.
-type Key struct {
-	bucket *Bucket
-	suffix []byte
-}
-
-// Bytes returns the full key bytes that are consisted
-// from the bucket path concatenated to the suffix.
-func (k *Key) Bytes() []byte {
-	bucketPath := k.bucket.Path()
-	keyBytes := make([]byte, len(bucketPath)+len(k.suffix))
-	copy(keyBytes, bucketPath)
-	copy(keyBytes[len(bucketPath):], k.suffix)
-	return keyBytes
-}
-
-func (k *Key) String() string {
-	return hex.EncodeToString(k.Bytes())
-}
-
-// Bucket returns the key bucket.
-func (k *Key) Bucket() model.DBBucket {
-	return k.bucket
-}
-
-// Suffix returns the key suffix.
-func (k *Key) Suffix() []byte {
-	return k.suffix
-}
-
-// newKey returns a new key composed
-// of the given bucket and suffix
-func newKey(bucket *Bucket, suffix []byte) model.DBKey {
-	return &Key{bucket: bucket, suffix: suffix}
-}
-
-// Bucket is a helper type meant to combine buckets
-// and sub-buckets that can be used to create database
-// keys and prefix-based cursors.
-type Bucket struct {
-	path [][]byte
-}
-
-// MakeBucket creates a new Bucket using the given path
-// of buckets.
-func MakeBucket(path ...[]byte) model.DBBucket {
-	return &Bucket{path: path}
-}
-
-// Bucket returns the sub-bucket of the current bucket
-// defined by bucketBytes.
-func (b *Bucket) Bucket(bucketBytes []byte) model.DBBucket {
-	newPath := make([][]byte, len(b.path)+1)
-	copy(newPath, b.path)
-	copy(newPath[len(b.path):], [][]byte{bucketBytes})
-
-	return MakeBucket(newPath...)
-}
-
-// Key returns a key in the current bucket with the
-// given suffix.
-func (b *Bucket) Key(suffix []byte) model.DBKey {
-	return newKey(b, suffix)
-}
-
-// Path returns the full path of the current bucket.
-func (b *Bucket) Path() []byte {
-	bucketPath := bytes.Join(b.path, bucketSeparator)
-
-	bucketPathWithFinalSeparator := make([]byte, len(bucketPath)+len(bucketSeparator))
-	copy(bucketPathWithFinalSeparator, bucketPath)
-	copy(bucketPathWithFinalSeparator[len(bucketPath):], bucketSeparator)
-
-	return bucketPathWithFinalSeparator
-}
diff --git a/infrastructure/db/database/keys.go b/infrastructure/db/database/keys.go
index 6b9aabdb..e7450a27 100644
--- a/infrastructure/db/database/keys.go
+++ b/infrastructure/db/database/keys.go
@@ -1,11 +1,10 @@
 package database
 
 import (
-	"bytes"
 	"encoding/hex"
 )
 
-var bucketSeparator = []byte("/")
+var bucketSeparator = byte('/')
 
 // Key is a helper type meant to combine prefix
 // and suffix into a single database key.
@@ -48,23 +47,25 @@ func newKey(bucket *Bucket, suffix []byte) *Key {
 // and sub-buckets that can be used to create database
 // keys and prefix-based cursors.
 type Bucket struct {
-	path [][]byte
+	path []byte
 }
 
 // MakeBucket creates a new Bucket using the given path
 // of buckets.
-func MakeBucket(path ...[]byte) *Bucket {
+func MakeBucket(path []byte) *Bucket {
+	if len(path) > 0 && path[len(path)-1] != bucketSeparator {
+		path = append(path, bucketSeparator)
+	}
 	return &Bucket{path: path}
 }
 
 // Bucket returns the sub-bucket of the current bucket
 // defined by bucketBytes.
 func (b *Bucket) Bucket(bucketBytes []byte) *Bucket {
-	newPath := make([][]byte, len(b.path)+1)
-	copy(newPath, b.path)
-	copy(newPath[len(b.path):], [][]byte{bucketBytes})
-
-	return MakeBucket(newPath...)
+	newPath := make([]byte, 0, len(b.path)+len(bucketBytes)+1) // +1 for the separator in MakeBucket
+	newPath = append(newPath, b.path...)
+	newPath = append(newPath, bucketBytes...)
+	return MakeBucket(newPath)
 }
 
 // Key returns a key in the current bucket with the
@@ -75,11 +76,5 @@ func (b *Bucket) Key(suffix []byte) *Key {
 
 // Path returns the full path of the current bucket.
 func (b *Bucket) Path() []byte {
-	bucketPath := bytes.Join(b.path, bucketSeparator)
-
-	bucketPathWithFinalSeparator := make([]byte, len(bucketPath)+len(bucketSeparator))
-	copy(bucketPathWithFinalSeparator, bucketPath)
-	copy(bucketPathWithFinalSeparator[len(bucketPath):], bucketSeparator)
-
-	return bucketPathWithFinalSeparator
+	return b.path
 }


Test patch:
diff --git a/domain/consensus/datastructures/consensusstatestore/tips.go b/domain/consensus/datastructures/consensusstatestore/tips.go
index c9cb4400..f8e74831 100644
--- a/domain/consensus/datastructures/consensusstatestore/tips.go
+++ b/domain/consensus/datastructures/consensusstatestore/tips.go
@@ -2,13 +2,13 @@ package consensusstatestore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 )
 
-var tipsKey = dbkeys.MakeBucket().Key([]byte("tips"))
+var tipsKey = database.MakeBucket(nil).Key([]byte("tips"))
 
 func (css *consensusStateStore) Tips(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
 	if css.tipsStaging != nil {
diff --git a/domain/consensus/datastructures/consensusstatestore/utxo.go b/domain/consensus/datastructures/consensusstatestore/utxo.go
index 8c1dc285..f25de5fd 100644
--- a/domain/consensus/datastructures/consensusstatestore/utxo.go
+++ b/domain/consensus/datastructures/consensusstatestore/utxo.go
@@ -1,14 +1,14 @@
 package consensusstatestore
 
 import (
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/utxo"
 	"github.com/pkg/errors"
 )
 
-var utxoSetBucket = dbkeys.MakeBucket([]byte("virtual-utxo-set"))
+var utxoSetBucket = database.MakeBucket([]byte("virtual-utxo-set"))
 
 func utxoKey(outpoint *externalapi.DomainOutpoint) (model.DBKey, error) {
 	serializedOutpoint, err := serializeOutpoint(outpoint)
diff --git a/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go b/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go
index c834a191..2baac4b0 100644
--- a/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go
+++ b/domain/consensus/datastructures/consensusstatestore/virtual_diff_parents.go
@@ -2,13 +2,13 @@ package consensusstatestore
 
 import (
 	"github.com/golang/protobuf/proto"
+	"github.com/kaspanet/kaspad/domain/consensus/database"
 	"github.com/kaspanet/kaspad/domain/consensus/database/serialization"
 	"github.com/kaspanet/kaspad/domain/consensus/model"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/dbkeys"
 )
 
-var virtualDiffParentsKey = dbkeys.MakeBucket().Key([]byte("virtual-diff-parents"))
+var virtualDiffParentsKey = database.MakeBucket(nil).Key([]byte("virtual-diff-parents"))
 
 func (css *consensusStateStore) VirtualDiffParents(dbContext model.DBReader) ([]*externalapi.DomainHash, error) {
 	if css.virtualDiffParentsStaging != nil {
diff --git a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint_test.go b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint_test.go
index 0d899c80..059112dd 100644
--- a/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint_test.go
+++ b/domain/consensus/processes/blockprocessor/validateandinsertpruningpoint_test.go
@@ -1,8 +1,12 @@
 package blockprocessor_test
 
 import (
+	"testing"
+	"time"
+
 	"github.com/kaspanet/kaspad/domain/consensus"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/model/testapi"
 	"github.com/kaspanet/kaspad/domain/consensus/ruleerrors"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/testutils"
@@ -11,10 +15,30 @@ import (
 	"github.com/kaspanet/kaspad/domain/dagconfig"
 	"github.com/pkg/errors"
 	"google.golang.org/protobuf/proto"
-	"testing"
-	"time"
 )
 
+func addBlock(tcSyncer, tcSyncee testapi.TestConsensus, parentHashes []*externalapi.DomainHash, t *testing.T) *externalapi.DomainHash {
+	block, _, err := tcSyncer.BuildBlockWithParents(parentHashes, nil, nil)
+	if err != nil {
+		t.Fatalf("BuildBlockWithParents: %+v", err)
+	}
+
+	_, err = tcSyncer.ValidateAndInsertBlock(block)
+	if err != nil {
+		t.Fatalf("ValidateAndInsertBlock: %+v", err)
+	}
+
+	_, err = tcSyncee.ValidateAndInsertBlock(&externalapi.DomainBlock{
+		Header:       block.Header,
+		Transactions: nil,
+	})
+	if err != nil {
+		t.Fatalf("ValidateAndInsertBlock: %+v", err)
+	}
+
+	return consensushashing.BlockHash(block)
+}
+
 func TestValidateAndInsertPruningPoint(t *testing.T) {
 	testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
 		// This is done to reduce the pruning depth to 6 blocks
@@ -36,43 +60,21 @@ func TestValidateAndInsertPruningPoint(t *testing.T) {
 		}
 		defer teardownSyncee(false)
 
-		addBlock := func(parentHashes []*externalapi.DomainHash) *externalapi.DomainHash {
-			block, _, err := tcSyncer.BuildBlockWithParents(parentHashes, nil, nil)
-			if err != nil {
-				t.Fatalf("BuildBlockWithParents: %+v", err)
-			}
-
-			_, err = tcSyncer.ValidateAndInsertBlock(block)
-			if err != nil {
-				t.Fatalf("ValidateAndInsertBlock: %+v", err)
-			}
-
-			_, err = tcSyncee.ValidateAndInsertBlock(&externalapi.DomainBlock{
-				Header:       block.Header,
-				Transactions: nil,
-			})
-			if err != nil {
-				t.Fatalf("ValidateAndInsertBlock: %+v", err)
-			}
-
-			return consensushashing.BlockHash(block)
-		}
-
 		tipHash := params.GenesisHash
 		for i := 0; i < finalityDepth-2; i++ {
-			tipHash = addBlock([]*externalapi.DomainHash{tipHash})
+			tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
 		}
 
 		// Add block in the anticone of the pruning point to test such situation
-		pruningPointAnticoneBlock := addBlock([]*externalapi.DomainHash{tipHash})
-		tipHash = addBlock([]*externalapi.DomainHash{tipHash})
-		nextPruningPoint := addBlock([]*externalapi.DomainHash{tipHash})
+		pruningPointAnticoneBlock := addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+		tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+		nextPruningPoint := addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
 
-		tipHash = addBlock([]*externalapi.DomainHash{pruningPointAnticoneBlock, nextPruningPoint})
+		tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{pruningPointAnticoneBlock, nextPruningPoint}, t)
 
 		// Add blocks until the pruning point changes
 		for {
-			tipHash = addBlock([]*externalapi.DomainHash{tipHash})
+			tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
 
 			pruningPoint, err := tcSyncer.PruningPoint()
 			if err != nil {
@@ -187,6 +189,179 @@ func TestValidateAndInsertPruningPoint(t *testing.T) {
 	})
 }
 
+// TestValidateAndInsertPruningPointWithSideBlocks makes sure that when a node applies a UTXO-Set downloaded during
+// IBD, while it already has a non-empty UTXO-Set originating from blocks mined on top of genesis - the resulting
+// UTXO set is correct
+func TestValidateAndInsertPruningPointWithSideBlocks(t *testing.T) {
+	testutils.ForAllNets(t, true, func(t *testing.T, params *dagconfig.Params) {
+		// This is done to reduce the pruning depth to 6 blocks
+		finalityDepth := 3
+		params.FinalityDuration = time.Duration(finalityDepth) * params.TargetTimePerBlock
+		params.K = 0
+
+		factory := consensus.NewFactory()
+
+		tcSyncer, teardownSyncer, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncer")
+		if err != nil {
+			t.Fatalf("Error setting up tcSyncer: %+v", err)
+		}
+		defer teardownSyncer(false)
+
+		tcSyncee, teardownSyncee, err := factory.NewTestConsensus(params, false, "TestValidateAndInsertPruningPointSyncee")
+		if err != nil {
+			t.Fatalf("Error setting up tcSyncee: %+v", err)
+		}
+		defer teardownSyncee(false)
+
+		// Mine 2 block in the syncee on top of genesis
+		side, _, err := tcSyncee.AddBlock([]*externalapi.DomainHash{params.GenesisHash}, &externalapi.DomainCoinbaseData{ScriptPublicKey: &externalapi.ScriptPublicKey{}, ExtraData: []byte{1, 2}}, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+		_, _, err = tcSyncee.AddBlock([]*externalapi.DomainHash{side}, nil, nil)
+		if err != nil {
+			t.Fatal(err)
+		}
+
+		tipHash := params.GenesisHash
+		for i := 0; i < finalityDepth-2; i++ {
+			tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+		}
+
+		tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+		nextPruningPoint := addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+
+		tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{nextPruningPoint}, t)
+
+		// Add blocks until the pruning point changes
+		for {
+			tipHash = addBlock(tcSyncer, tcSyncee, []*externalapi.DomainHash{tipHash}, t)
+
+			pruningPoint, err := tcSyncer.PruningPoint()
+			if err != nil {
+				t.Fatalf("PruningPoint: %+v", err)
+			}
+
+			if !pruningPoint.Equal(params.GenesisHash) {
+				break
+			}
+		}
+
+		pruningPoint, err := tcSyncer.PruningPoint()
+		if err != nil {
+			t.Fatalf("PruningPoint: %+v", err)
+		}
+
+		if !pruningPoint.Equal(nextPruningPoint) {
+			t.Fatalf("Unexpected pruning point %s", pruningPoint)
+		}
+
+		pruningPointUTXOSet, err := tcSyncer.GetPruningPointUTXOSet(pruningPoint)
+		if err != nil {
+			t.Fatalf("GetPruningPointUTXOSet: %+v", err)
+		}
+
+		// Check that ValidateAndInsertPruningPoint works.
+		pruningPointBlock, err := tcSyncer.GetBlock(pruningPoint)
+		if err != nil {
+			t.Fatalf("GetBlock: %+v", err)
+		}
+		err = tcSyncee.ValidateAndInsertPruningPoint(pruningPointBlock, pruningPointUTXOSet)
+		if err != nil {
+			t.Fatalf("ValidateAndInsertPruningPoint: %+v", err)
+		}
+
+		// Insert the rest of the blocks atop pruning point
+		virtualSelectedParent, err := tcSyncer.GetVirtualSelectedParent()
+		if err != nil {
+			t.Fatalf("GetVirtualSelectedParent: %+v", err)
+		}
+
+		missingBlockBodyHashes, err := tcSyncee.GetMissingBlockBodyHashes(virtualSelectedParent)
+		if err != nil {
+			t.Fatalf("GetMissingBlockBodyHashes: %+v", err)
+		}
+
+		for _, missingHash := range missingBlockBodyHashes {
+			block, err := tcSyncer.GetBlock(missingHash)
+			if err != nil {
+				t.Fatalf("GetBlock: %+v", err)
+			}
+
+			_, err = tcSyncee.ValidateAndInsertBlock(block)
+			if err != nil {
+				t.Fatalf("ValidateAndInsertBlock: %+v", err)
+			}
+		}
+
+		// Verify that syncee and syncer tips are equal
+		synceeTips, err := tcSyncee.Tips()
+		if err != nil {
+			t.Fatalf("Tips: %+v", err)
+		}
+
+		syncerTips, err := tcSyncer.Tips()
+		if err != nil {
+			t.Fatalf("Tips: %+v", err)
+		}
+
+		if !externalapi.HashesEqual(synceeTips, syncerTips) {
+			t.Fatalf("Syncee's tips are %s while syncer's are %s", synceeTips, syncerTips)
+		}
+
+		// Verify that syncee and syncer pruning points are equal
+		synceePruningPoint, err := tcSyncee.PruningPoint()
+		if err != nil {
+			t.Fatalf("PruningPoint: %+v", err)
+		}
+
+		if !synceePruningPoint.Equal(pruningPoint) {
+			t.Fatalf("The syncee pruning point has not changed as exepcted")
+		}
+
+		pruningPointOld := pruningPoint
+
+		// Add blocks until the pruning point moves, and verify it moved to the same point on both syncer and syncee
+		for {
+			block, _, err := tcSyncer.BuildBlockWithParents([]*externalapi.DomainHash{tipHash}, nil, nil)
+			if err != nil {
+				t.Fatalf("BuildBlockWithParents: %+v", err)
+			}
+
+			_, err = tcSyncer.ValidateAndInsertBlock(block)
+			if err != nil {
+				t.Fatalf("ValidateAndInsertBlock: %+v", err)
+			}
+
+			_, err = tcSyncee.ValidateAndInsertBlock(block)
+			if err != nil {
+				t.Fatalf("ValidateAndInsertBlock: %+v", err)
+			}
+
+			tipHash = consensushashing.BlockHash(block)
+
+			pruningPoint, err = tcSyncer.PruningPoint()
+			if err != nil {
+				t.Fatalf("PruningPoint: %+v", err)
+			}
+
+			if !pruningPoint.Equal(pruningPointOld) {
+				break
+			}
+		}
+
+		synceePruningPoint, err = tcSyncee.PruningPoint()
+		if err != nil {
+			t.Fatalf("PruningPoint: %+v", err)
+		}
+
+		if !synceePruningPoint.Equal(pruningPoint) {
+			t.Fatalf("The syncee pruning point(%s) is not equal to syncer pruning point (%s) after it moved. "+
+				"pruning point before move: %s", synceePruningPoint, pruningPoint, pruningPointOld)
+		}
+	})
+}
+
 type fakeUTXOSetIterator struct {
 	nextCalled bool
 }
diff --git a/infrastructure/db/database/common_test.go b/infrastructure/db/database/common_test.go
index 06ce3b15..2b16f9b3 100644
--- a/infrastructure/db/database/common_test.go
+++ b/infrastructure/db/database/common_test.go
@@ -67,7 +67,7 @@ func populateDatabaseForTest(t *testing.T, db database.Database, testName string
 	// Prepare a list of key/value pairs
 	entries := make([]keyValuePair, 10)
 	for i := 0; i < 10; i++ {
-		key := database.MakeBucket().Key([]byte(fmt.Sprintf("key%d", i)))
+		key := database.MakeBucket(nil).Key([]byte(fmt.Sprintf("key%d", i)))
 		value := []byte("value")
 		entries[i] = keyValuePair{key: key, value: value}
 	}
diff --git a/infrastructure/db/database/cursor_test.go b/infrastructure/db/database/cursor_test.go
index c0082d94..65e908f7 100644
--- a/infrastructure/db/database/cursor_test.go
+++ b/infrastructure/db/database/cursor_test.go
@@ -15,7 +15,7 @@ import (
 )
 
 func prepareCursorForTest(t *testing.T, db database.Database, testName string) database.Cursor {
-	cursor, err := db.Cursor(database.MakeBucket())
+	cursor, err := db.Cursor(database.MakeBucket(nil))
 	if err != nil {
 		t.Fatalf("%s: Cursor unexpectedly "+
 			"failed: %s", testName, err)
@@ -241,7 +241,7 @@ func testCursorSeek(t *testing.T, db database.Database, testName string) {
 
 	// Seek to a value that doesn't exist and make sure that
 	// the returned error is ErrNotFound
-	err = cursor.Seek(database.MakeBucket().Key([]byte("doesn't exist")))
+	err = cursor.Seek(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err == nil {
 		t.Fatalf("%s: Seek unexpectedly "+
 			"succeeded", testName)
@@ -274,7 +274,7 @@ func testCursorCloseErrors(t *testing.T, db database.Database, testName string)
 		{
 			name: "Seek",
 			function: func() error {
-				return cursor.Seek(database.MakeBucket().Key([]byte{}))
+				return cursor.Seek(database.MakeBucket(nil).Key([]byte{}))
 			},
 		},
 		{
diff --git a/infrastructure/db/database/database_test.go b/infrastructure/db/database/database_test.go
index 55c4b88e..2f7f3d99 100644
--- a/infrastructure/db/database/database_test.go
+++ b/infrastructure/db/database/database_test.go
@@ -18,7 +18,7 @@ func TestDatabasePut(t *testing.T) {
 
 func testDatabasePut(t *testing.T, db database.Database, testName string) {
 	// Put value1 into the database
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value1 := []byte("value1")
 	err := db.Put(key, value1)
 	if err != nil {
@@ -65,7 +65,7 @@ func TestDatabaseGet(t *testing.T) {
 
 func testDatabaseGet(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err := db.Put(key, value)
 	if err != nil {
@@ -87,7 +87,7 @@ func testDatabaseGet(t *testing.T, db database.Database, testName string) {
 
 	// Try getting a non-existent value and make sure
 	// the returned error is ErrNotFound
-	_, err = db.Get(database.MakeBucket().Key([]byte("doesn't exist")))
+	_, err = db.Get(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err == nil {
 		t.Fatalf("%s: Get "+
 			"unexpectedly succeeded", testName)
@@ -104,7 +104,7 @@ func TestDatabaseHas(t *testing.T) {
 
 func testDatabaseHas(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err := db.Put(key, value)
 	if err != nil {
@@ -124,7 +124,7 @@ func testDatabaseHas(t *testing.T, db database.Database, testName string) {
 	}
 
 	// Make sure that Has returns false for a non-existent value
-	exists, err = db.Has(database.MakeBucket().Key([]byte("doesn't exist")))
+	exists, err = db.Has(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err != nil {
 		t.Fatalf("%s: Has "+
 			"unexpectedly failed: %s", testName, err)
@@ -141,7 +141,7 @@ func TestDatabaseDelete(t *testing.T) {
 
 func testDatabaseDelete(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err := db.Put(key, value)
 	if err != nil {
diff --git a/infrastructure/db/database/keys_test.go b/infrastructure/db/database/keys_test.go
index 4c0e8b36..87f9566b 100644
--- a/infrastructure/db/database/keys_test.go
+++ b/infrastructure/db/database/keys_test.go
@@ -6,6 +6,10 @@ import (
 	"testing"
 )
 
+func makeBucketJoin(path ...[]byte) *Bucket {
+	return MakeBucket(bytes.Join(path, []byte{bucketSeparator}))
+}
+
 func TestBucketPath(t *testing.T) {
 	tests := []struct {
 		bucketByteSlices [][]byte
@@ -23,14 +27,14 @@ func TestBucketPath(t *testing.T) {
 
 	for _, test := range tests {
 		// Build a result using the MakeBucket function alone
-		resultKey := MakeBucket(test.bucketByteSlices...).Path()
+		resultKey := makeBucketJoin(test.bucketByteSlices...).Path()
 		if !reflect.DeepEqual(resultKey, test.expectedPath) {
 			t.Errorf("TestBucketPath: got wrong path using MakeBucket. "+
 				"Want: %s, got: %s", string(test.expectedPath), string(resultKey))
 		}
 
 		// Build a result using sub-Bucket calls
-		bucket := MakeBucket()
+		bucket := MakeBucket(nil)
 		for _, bucketBytes := range test.bucketByteSlices {
 			bucket = bucket.Bucket(bucketBytes)
 		}
@@ -63,14 +67,14 @@ func TestBucketKey(t *testing.T) {
 			key:              []byte("test"),
 			expectedKeyBytes: []byte("hello/world/test"),
 			expectedKey: &Key{
-				bucket: MakeBucket([]byte("hello"), []byte("world")),
+				bucket: makeBucketJoin([]byte("hello"), []byte("world")),
 				suffix: []byte("test"),
 			},
 		},
 	}
 
 	for _, test := range tests {
-		resultKey := MakeBucket(test.bucketByteSlices...).Key(test.key)
+		resultKey := makeBucketJoin(test.bucketByteSlices...).Key(test.key)
 		if !reflect.DeepEqual(resultKey, test.expectedKey) {
 			t.Errorf("TestBucketKey: got wrong key. Want: %s, got: %s",
 				test.expectedKeyBytes, resultKey)
diff --git a/infrastructure/db/database/ldb/cursor_test.go b/infrastructure/db/database/ldb/cursor_test.go
index f9232daf..ae410b06 100644
--- a/infrastructure/db/database/ldb/cursor_test.go
+++ b/infrastructure/db/database/ldb/cursor_test.go
@@ -95,7 +95,7 @@ func TestCursorSanity(t *testing.T) {
 	validateCurrentCursorKeyAndValue(t, "TestCursorSanity", cursor, expectedKey, expectedValue)
 
 	// Seek to a non-existant key
-	err = cursor.Seek(database.MakeBucket().Key([]byte("doesn't exist")))
+	err = cursor.Seek(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err == nil {
 		t.Fatalf("TestCursorSanity: Seek " +
 			"unexpectedly succeeded")
@@ -155,7 +155,7 @@ func TestCursorCloseErrors(t *testing.T) {
 		{
 			name: "Seek",
 			function: func(cursor database.Cursor) error {
-				return cursor.Seek(database.MakeBucket().Key([]byte{}))
+				return cursor.Seek(database.MakeBucket(nil).Key([]byte{}))
 			},
 		},
 		{
@@ -186,7 +186,7 @@ func TestCursorCloseErrors(t *testing.T) {
 			defer teardownFunc()
 
 			// Open a new cursor
-			cursor, err := ldb.Cursor(database.MakeBucket())
+			cursor, err := ldb.Cursor(database.MakeBucket(nil))
 			if err != nil {
 				t.Fatalf("TestCursorCloseErrors: ldb.Cursor "+
 					"unexpectedly failed: %s", err)
diff --git a/infrastructure/db/database/ldb/leveldb_test.go b/infrastructure/db/database/ldb/leveldb_test.go
index cc91639a..51f3aab3 100644
--- a/infrastructure/db/database/ldb/leveldb_test.go
+++ b/infrastructure/db/database/ldb/leveldb_test.go
@@ -35,7 +35,7 @@ func TestLevelDBSanity(t *testing.T) {
 	defer teardownFunc()
 
 	// Put something into the db
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	putData := []byte("Hello world!")
 	err := ldb.Put(key, putData)
 	if err != nil {
@@ -71,7 +71,7 @@ func TestLevelDBTransactionSanity(t *testing.T) {
 	}
 
 	// Put something into the transaction
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	putData := []byte("Hello world!")
 	err = tx.Put(key, putData)
 	if err != nil {
@@ -115,7 +115,7 @@ func TestLevelDBTransactionSanity(t *testing.T) {
 
 	// Case 2. Write directly to the DB and then read from a tx
 	// Put something into the db
-	key = database.MakeBucket().Key([]byte("key2"))
+	key = database.MakeBucket(nil).Key([]byte("key2"))
 	putData = []byte("Goodbye world!")
 	err = ldb.Put(key, putData)
 	if err != nil {
diff --git a/infrastructure/db/database/ldb/transaction_test.go b/infrastructure/db/database/ldb/transaction_test.go
index 3b81b0ae..bf2c9a7a 100644
--- a/infrastructure/db/database/ldb/transaction_test.go
+++ b/infrastructure/db/database/ldb/transaction_test.go
@@ -20,14 +20,14 @@ func TestTransactionCloseErrors(t *testing.T) {
 		{
 			name: "Put",
 			function: func(dbTx *LevelDBTransaction) error {
-				return dbTx.Put(database.MakeBucket().Key([]byte("key")), []byte("value"))
+				return dbTx.Put(database.MakeBucket(nil).Key([]byte("key")), []byte("value"))
 			},
 			shouldReturnError: true,
 		},
 		{
 			name: "Get",
 			function: func(dbTx *LevelDBTransaction) error {
-				_, err := dbTx.Get(database.MakeBucket().Key([]byte("key")))
+				_, err := dbTx.Get(database.MakeBucket(nil).Key([]byte("key")))
 				return err
 			},
 			shouldReturnError: true,
@@ -35,7 +35,7 @@ func TestTransactionCloseErrors(t *testing.T) {
 		{
 			name: "Has",
 			function: func(dbTx *LevelDBTransaction) error {
-				_, err := dbTx.Has(database.MakeBucket().Key([]byte("key")))
+				_, err := dbTx.Has(database.MakeBucket(nil).Key([]byte("key")))
 				return err
 			},
 			shouldReturnError: true,
@@ -43,7 +43,7 @@ func TestTransactionCloseErrors(t *testing.T) {
 		{
 			name: "Delete",
 			function: func(dbTx *LevelDBTransaction) error {
-				return dbTx.Delete(database.MakeBucket().Key([]byte("key")))
+				return dbTx.Delete(database.MakeBucket(nil).Key([]byte("key")))
 			},
 			shouldReturnError: true,
 		},
diff --git a/infrastructure/db/database/transaction_test.go b/infrastructure/db/database/transaction_test.go
index eda5b137..e8a44170 100644
--- a/infrastructure/db/database/transaction_test.go
+++ b/infrastructure/db/database/transaction_test.go
@@ -33,7 +33,7 @@ func testTransactionPut(t *testing.T, db database.Database, testName string) {
 	}()
 
 	// Put value1 into the transaction
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value1 := []byte("value1")
 	err = dbTx.Put(key, value1)
 	if err != nil {
@@ -75,7 +75,7 @@ func TestTransactionGet(t *testing.T) {
 
 func testTransactionGet(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key1 := database.MakeBucket().Key([]byte("key1"))
+	key1 := database.MakeBucket(nil).Key([]byte("key1"))
 	value1 := []byte("value1")
 	err := db.Put(key1, value1)
 	if err != nil {
@@ -111,7 +111,7 @@ func testTransactionGet(t *testing.T, db database.Database, testName string) {
 
 	// Try getting a non-existent value and make sure
 	// the returned error is ErrNotFound
-	_, err = dbTx.Get(database.MakeBucket().Key([]byte("doesn't exist")))
+	_, err = dbTx.Get(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err == nil {
 		t.Fatalf("%s: Get "+
 			"unexpectedly succeeded", testName)
@@ -122,7 +122,7 @@ func testTransactionGet(t *testing.T, db database.Database, testName string) {
 	}
 
 	// Put a new value into the database outside of the transaction
-	key2 := database.MakeBucket().Key([]byte("key2"))
+	key2 := database.MakeBucket(nil).Key([]byte("key2"))
 	value2 := []byte("value2")
 	err = db.Put(key2, value2)
 	if err != nil {
@@ -141,7 +141,7 @@ func testTransactionGet(t *testing.T, db database.Database, testName string) {
 	}
 
 	// Put a new value into the transaction
-	key3 := database.MakeBucket().Key([]byte("key3"))
+	key3 := database.MakeBucket(nil).Key([]byte("key3"))
 	value3 := []byte("value3")
 	err = dbTx.Put(key3, value3)
 	if err != nil {
@@ -167,7 +167,7 @@ func TestTransactionHas(t *testing.T) {
 
 func testTransactionHas(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key1 := database.MakeBucket().Key([]byte("key1"))
+	key1 := database.MakeBucket(nil).Key([]byte("key1"))
 	value1 := []byte("value1")
 	err := db.Put(key1, value1)
 	if err != nil {
@@ -201,7 +201,7 @@ func testTransactionHas(t *testing.T, db database.Database, testName string) {
 	}
 
 	// Make sure that Has returns false for a non-existent value
-	exists, err = dbTx.Has(database.MakeBucket().Key([]byte("doesn't exist")))
+	exists, err = dbTx.Has(database.MakeBucket(nil).Key([]byte("doesn't exist")))
 	if err != nil {
 		t.Fatalf("%s: Has "+
 			"unexpectedly failed: %s", testName, err)
@@ -212,7 +212,7 @@ func testTransactionHas(t *testing.T, db database.Database, testName string) {
 	}
 
 	// Put a new value into the database outside of the transaction
-	key2 := database.MakeBucket().Key([]byte("key2"))
+	key2 := database.MakeBucket(nil).Key([]byte("key2"))
 	value2 := []byte("value2")
 	err = db.Put(key2, value2)
 	if err != nil {
@@ -238,7 +238,7 @@ func TestTransactionDelete(t *testing.T) {
 
 func testTransactionDelete(t *testing.T, db database.Database, testName string) {
 	// Put a value into the database
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err := db.Put(key, value)
 	if err != nil {
@@ -327,7 +327,7 @@ func testTransactionCommit(t *testing.T, db database.Database, testName string)
 	}()
 
 	// Put a value into the transaction
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err = dbTx.Put(key, value)
 	if err != nil {
@@ -388,7 +388,7 @@ func testTransactionRollback(t *testing.T, db database.Database, testName string
 	}()
 
 	// Put a value into the transaction
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err = dbTx.Put(key, value)
 	if err != nil {
@@ -448,7 +448,7 @@ func testTransactionRollbackUnlessClosed(t *testing.T, db database.Database, tes
 	}()
 
 	// Put a value into the transaction
-	key := database.MakeBucket().Key([]byte("key"))
+	key := database.MakeBucket(nil).Key([]byte("key"))
 	value := []byte("value")
 	err = dbTx.Put(key, value)
 	if err != nil {