Repository: coinbase/rosetta-cli
Pull request #193 (2020-12-01 19:15:31)

[chore] Update `rosetta-sdk-go`
Related: https://github.com/coinbase/rosetta-cli/tree/patrick/lock-profiling
Closes: #191 
Closes: #189 
Closes: #170 
Blocked By: https://github.com/coinbase/rosetta-sdk-go/pull/259

This PR includes significant improvements to the `storage` package. In our own testing, we observed over a 20x improvement in sync speed and reconciliation rate (mostly due to a reduction in lock contention).

### Changes
- [x] use `EstimatedReconciliationCoverage`
- [x] remove unnecessary `mocks`
- [x] update [`rosetta-sdk-go` to official release](https://github.com/coinbase/rosetta-sdk-go/releases/tag/v0.6.2) + run `go mod tidy`
  - [x] fix breaking changes
- [x] update version
- [x] Add pruning frequency to config

### rosetta-sdk-go Changes
- [asserter] Backwards Compatibility for Operation.Status [`#260`](https://github.com/coinbase/rosetta-sdk-go/pull/260)
- [chore] Miscellaneous Nits Before Release [`#259`](https://github.com/coinbase/rosetta-sdk-go/pull/259)
- Bump github.com/vmihailenco/msgpack/v5 from 5.0.0 to 5.1.0 [`#256`](https://github.com/coinbase/rosetta-sdk-go/pull/256)
- [reconciler] Performance Overhaul [`#258`](https://github.com/coinbase/rosetta-sdk-go/pull/258)
- feat: support schnorr_1 for zilliqa [`#257`](https://github.com/coinbase/rosetta-sdk-go/pull/257)
- [storage] Small Nits [`#255`](https://github.com/coinbase/rosetta-sdk-go/pull/255)
- [storage] Balance Storage Overhaul [`#252`](https://github.com/coinbase/rosetta-sdk-go/pull/252)
- [storage] Interface Refactor + Test Cleanup [`#254`](https://github.com/coinbase/rosetta-sdk-go/pull/254)
- [storage] Interface Overhaul [`#251`](https://github.com/coinbase/rosetta-sdk-go/pull/251)
- [statefulsyncer] Add Variadic Initializer [`#250`](https://github.com/coinbase/rosetta-sdk-go/pull/250)
- [utils] Implement ShardedMap [`#248`](https://github.com/coinbase/rosetta-sdk-go/pull/248)
- [utils] Add MutexMap [`#246`](https://github.com/coinbase/rosetta-sdk-go/pull/246)
- [utils] Implement PriorityMutex [`#244`](https://github.com/coinbase/rosetta-sdk-go/pull/244)
- [chore] Update msgpack [`#245`](https://github.com/coinbase/rosetta-sdk-go/pull/245)



Patch:
diff --git a/Makefile b/Makefile
index 84e8fc8..2242922 100644
--- a/Makefile
+++ b/Makefile
@@ -63,8 +63,3 @@ build:
 
 install:
 	go install ./...
-
-mocks:
-	rm -rf mocks;
-	mockery --dir pkg/constructor --all --case underscore --outpkg constructor --output mocks/constructor;
-	${ADDLICENCE_SCRIPT} .;
diff --git a/cmd/root.go b/cmd/root.go
index 58f440a..ae8a2d3 100644
--- a/cmd/root.go
+++ b/cmd/root.go
@@ -257,6 +257,6 @@ var versionCmd = &cobra.Command{
 	Use:   "version",
 	Short: "Print rosetta-cli version",
 	Run: func(cmd *cobra.Command, args []string) {
-		fmt.Println("v0.6.0")
+		fmt.Println("v0.6.1")
 	},
 }
diff --git a/cmd/utils_train_zstd.go b/cmd/utils_train_zstd.go
index bc715bc..5a338f0 100644
--- a/cmd/utils_train_zstd.go
+++ b/cmd/utils_train_zstd.go
@@ -20,7 +20,8 @@ import (
 	"path"
 	"strconv"
 
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/encoder"
 	"github.com/fatih/color"
 	"github.com/spf13/cobra"
 )
@@ -58,9 +59,9 @@ func runTrainZstdCmd(cmd *cobra.Command, args []string) error {
 		return fmt.Errorf("%w: unable to convert max items to integer", err)
 	}
 
-	compressorEntries := []*storage.CompressorEntry{}
+	compressorEntries := []*encoder.CompressorEntry{}
 	if len(args) > trainArgs {
-		compressorEntries = append(compressorEntries, &storage.CompressorEntry{
+		compressorEntries = append(compressorEntries, &encoder.CompressorEntry{
 			Namespace:      namespace,
 			DictionaryPath: args[4],
 		})
@@ -70,7 +71,7 @@ func runTrainZstdCmd(cmd *cobra.Command, args []string) error {
 
 	log.Printf("Running zstd training (this could take a while)...")
 
-	_, _, err = storage.BadgerTrain(
+	_, _, err = database.BadgerTrain(
 		Context,
 		namespace,
 		databasePath,
diff --git a/configuration/types.go b/configuration/types.go
index 8feb383..5a25f67 100644
--- a/configuration/types.go
+++ b/configuration/types.go
@@ -16,7 +16,7 @@ package configuration
 
 import (
 	"github.com/coinbase/rosetta-sdk-go/constructor/job"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -115,7 +115,7 @@ type ConstructionConfiguration struct {
 
 	// PrefundedAccounts is an array of prefunded accounts
 	// to use while testing.
-	PrefundedAccounts []*storage.PrefundedAccount `json:"prefunded_accounts,omitempty"`
+	PrefundedAccounts []*modules.PrefundedAccount `json:"prefunded_accounts,omitempty"`
 
 	// Workflows are executed by the rosetta-cli to test
 	// certain construction flows.
@@ -315,6 +315,11 @@ type DataConfiguration struct {
 	// previously synced block.
 	PruningDisabled bool `json:"pruning_disabled"`
 
+	// PruningFrequency is the frequency (in seconds) that we attempt
+	// to prune blocks. If not populated, we use the default value
+	// provided in the `statefulsyncer` package.
+	PruningFrequency *int `json:"pruning_frequency,omitempty"`
+
 	// InitialBalanceFetchDisabled configures rosetta-cli
 	// not to lookup the balance of newly seen accounts at the
 	// parent block before applying operations. Disabling
diff --git a/go.mod b/go.mod
index f862c5a..b19152f 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/coinbase/rosetta-cli
 go 1.13
 
 require (
-	github.com/coinbase/rosetta-sdk-go v0.6.0
+	github.com/coinbase/rosetta-sdk-go v0.6.2
 	github.com/fatih/color v1.10.0
 	github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c
 	github.com/spf13/cobra v1.1.1
diff --git a/go.sum b/go.sum
index a40eac1..6d51898 100644
--- a/go.sum
+++ b/go.sum
@@ -36,6 +36,8 @@ github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIO
 github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
 github.com/VictoriaMetrics/fastcache v1.5.7 h1:4y6y0G8PRzszQUYIQHHssv/jgPHAb5qQuuDNdCbyAgw=
 github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
+github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6 h1:1d9pzdbkth4D9AX6ndKSl7of3UTV0RYl3z64U2dXMGo=
+github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1:eSYp2T6f0apnuW8TzhV3f6Aff2SE8Dwio++U4ha4yEM=
 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/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
@@ -52,11 +54,13 @@ github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+Ce
 github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
 github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
 github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
+github.com/btcsuite/btcd v0.0.0-20190315201642-aa6e0f35703c/go.mod h1:DrZx5ec/dmnfpw9KyYoQyYo7d0KEvTkk/5M/vbZjAr8=
 github.com/btcsuite/btcd v0.20.1-beta h1:Ik4hyJqN8Jfyv3S4AGBOmyouMsYE3EdYODkMbQjwPGw=
 github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
 github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
 github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
 github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190207003914-4c204d697803/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
 github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
 github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
 github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
@@ -76,8 +80,8 @@ github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
 github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
-github.com/coinbase/rosetta-sdk-go v0.6.0 h1:U8/NhkPo7CGJR8Ud82Y0HqtujXSVzGZKscmxOHsmS54=
-github.com/coinbase/rosetta-sdk-go v0.6.0/go.mod h1:pqBibzTcpz0mLqb7is8fo2qf93tcHjItJ3u7Yud8RI4=
+github.com/coinbase/rosetta-sdk-go v0.6.2 h1:vgynGMbRkYv8S3/swrLGlww1iOKfurILLRCGJOrtLMk=
+github.com/coinbase/rosetta-sdk-go v0.6.2/go.mod h1:MvQfsL2KlJ5786OdDviRIJE3agui2YcvS1CaQPDl1Yo=
 github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
 github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -115,8 +119,8 @@ github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJ
 github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw=
-github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
+github.com/ethereum/go-ethereum v1.9.24 h1:6AK+ORt3EMDO+FTjzXy/AQwHMbu52J2nYHIjyQX9azQ=
+github.com/ethereum/go-ethereum v1.9.24/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
 github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
 github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
@@ -173,11 +177,13 @@ github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXi
 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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
+github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
 github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
 github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
+github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
 github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
 github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
@@ -269,6 +275,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
 github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
+github.com/neilotoole/errgroup v0.1.5 h1:DxEGoIfFm5ooGicidR+okiHjoOaGRKFaSxDPVZuuu2I=
+github.com/neilotoole/errgroup v0.1.5/go.mod h1:Q2nLGf+594h0CLBs/Mbg6qOr7GtqDK7C2S41udRnToE=
 github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
 github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
 github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
@@ -315,6 +323,8 @@ github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
+github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
+github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
 github.com/shirou/gopsutil v2.20.5+incompatible h1:tYH07UPoQt0OCQdgWWMgYHy3/a9bcxNpBIysykNIP7I=
 github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
@@ -350,12 +360,15 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
 github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
 github.com/syndtr/goleveldb v1.0.1-0.20200815110645-5c35d600f0ca/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
 github.com/tidwall/gjson v1.6.1 h1:LRbvNuNuvAiISWg6gxLEFuCe72UKy5hDqhxW/8183ws=
 github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
+github.com/tidwall/gjson v1.6.3 h1:aHoiiem0dr7GHkW001T1SMTJ7X5PvyekH5WX0whWGnI=
+github.com/tidwall/gjson v1.6.3/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
 github.com/tidwall/match v1.0.1 h1:PnKP62LPNxHKTwvHHZZzdOAOCtsJTjo6dZLCwpKm5xc=
 github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
 github.com/tidwall/pretty v1.0.2 h1:Z7S3cePv9Jwm1KwS0513MRaoUe3S01WPbLNV40pwWZU=
@@ -364,15 +377,17 @@ github.com/tidwall/sjson v1.1.2 h1:NC5okI+tQ8OG/oyzchvwXXxRxCV/FVdhODbPKkQ25jQ=
 github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY=
 github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
 github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
+github.com/tyler-smith/go-bip39 v1.0.2/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
 github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
 github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9 h1:iBRIniTnWOo0kqkg3k3XR8Vn6OCkVlIuZNo0UoBrKx4=
-github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo=
+github.com/vmihailenco/msgpack/v5 v5.1.0 h1:+od5YbEXxW95SPlW6beocmt8nOtlh83zqat5Ip9Hwdc=
+github.com/vmihailenco/msgpack/v5 v5.1.0/go.mod h1:C5gboKD0TJPqWDTVTtrQNfRbiBwHZGo8UTqP/9/XvLI=
 github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
 github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
 github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
 github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/ybbus/jsonrpc v2.1.2+incompatible/go.mod h1:XJrh1eMSzdIYFbM08flv0wp5G35eRniyeGut1z+LSiE=
 go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
 go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
 go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
@@ -384,6 +399,7 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
 golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/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-20190426145343-a29dc8fdc734/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 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 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
diff --git a/mocks/constructor/handler.go b/mocks/constructor/handler.go
deleted file mode 100644
index 4afe30e..0000000
--- a/mocks/constructor/handler.go
+++ /dev/null
@@ -1,43 +0,0 @@
-// Code generated by mockery v1.0.0. DO NOT EDIT.
-
-package constructor
-
-import (
-	context "context"
-
-	types "github.com/coinbase/rosetta-sdk-go/types"
-	mock "github.com/stretchr/testify/mock"
-)
-
-// Handler is an autogenerated mock type for the Handler type
-type Handler struct {
-	mock.Mock
-}
-
-// AddressCreated provides a mock function with given fields: _a0, _a1
-func (_m *Handler) AddressCreated(_a0 context.Context, _a1 string) error {
-	ret := _m.Called(_a0, _a1)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, string) error); ok {
-		r0 = rf(_a0, _a1)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// TransactionCreated provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Handler) TransactionCreated(_a0 context.Context, _a1 string, _a2 *types.TransactionIdentifier) error {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, string, *types.TransactionIdentifier) error); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
diff --git a/mocks/constructor/helper.go b/mocks/constructor/helper.go
deleted file mode 100644
index be7a65c..0000000
--- a/mocks/constructor/helper.go
+++ /dev/null
@@ -1,441 +0,0 @@
-// Code generated by mockery v1.0.0. DO NOT EDIT.
-
-package constructor
-
-import (
-	big "math/big"
-
-	context "context"
-
-	keys "github.com/coinbase/rosetta-sdk-go/keys"
-
-	mock "github.com/stretchr/testify/mock"
-
-	storage "github.com/coinbase/rosetta-sdk-go/storage"
-
-	types "github.com/coinbase/rosetta-sdk-go/types"
-)
-
-// Helper is an autogenerated mock type for the Helper type
-type Helper struct {
-	mock.Mock
-}
-
-// AccountBalance provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) AccountBalance(_a0 context.Context, _a1 *types.AccountIdentifier, _a2 *types.Currency) (*big.Int, error) {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 *big.Int
-	if rf, ok := ret.Get(0).(func(context.Context, *types.AccountIdentifier, *types.Currency) *big.Int); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*big.Int)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.AccountIdentifier, *types.Currency) error); ok {
-		r1 = rf(_a0, _a1, _a2)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// AllAddresses provides a mock function with given fields: ctx
-func (_m *Helper) AllAddresses(ctx context.Context) ([]string, error) {
-	ret := _m.Called(ctx)
-
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context) []string); ok {
-		r0 = rf(ctx)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
-		r1 = rf(ctx)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// AllBroadcasts provides a mock function with given fields: ctx
-func (_m *Helper) AllBroadcasts(ctx context.Context) ([]*storage.Broadcast, error) {
-	ret := _m.Called(ctx)
-
-	var r0 []*storage.Broadcast
-	if rf, ok := ret.Get(0).(func(context.Context) []*storage.Broadcast); ok {
-		r0 = rf(ctx)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*storage.Broadcast)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
-		r1 = rf(ctx)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Broadcast provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4
-func (_m *Helper) Broadcast(_a0 context.Context, _a1 string, _a2 []*types.Operation, _a3 *types.TransactionIdentifier, _a4 string) error {
-	ret := _m.Called(_a0, _a1, _a2, _a3, _a4)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, string, []*types.Operation, *types.TransactionIdentifier, string) error); ok {
-		r0 = rf(_a0, _a1, _a2, _a3, _a4)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
-
-// ClearBroadcasts provides a mock function with given fields: ctx
-func (_m *Helper) ClearBroadcasts(ctx context.Context) ([]*storage.Broadcast, error) {
-	ret := _m.Called(ctx)
-
-	var r0 []*storage.Broadcast
-	if rf, ok := ret.Get(0).(func(context.Context) []*storage.Broadcast); ok {
-		r0 = rf(ctx)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*storage.Broadcast)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
-		r1 = rf(ctx)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// CoinBalance provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) CoinBalance(_a0 context.Context, _a1 *types.AccountIdentifier, _a2 *types.Currency) (*big.Int, *types.CoinIdentifier, error) {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 *big.Int
-	if rf, ok := ret.Get(0).(func(context.Context, *types.AccountIdentifier, *types.Currency) *big.Int); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*big.Int)
-		}
-	}
-
-	var r1 *types.CoinIdentifier
-	if rf, ok := ret.Get(1).(func(context.Context, *types.AccountIdentifier, *types.Currency) *types.CoinIdentifier); ok {
-		r1 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(1) != nil {
-			r1 = ret.Get(1).(*types.CoinIdentifier)
-		}
-	}
-
-	var r2 error
-	if rf, ok := ret.Get(2).(func(context.Context, *types.AccountIdentifier, *types.Currency) error); ok {
-		r2 = rf(_a0, _a1, _a2)
-	} else {
-		r2 = ret.Error(2)
-	}
-
-	return r0, r1, r2
-}
-
-// Combine provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Combine(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 string, _a3 []*types.Signature) (string, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
-
-	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, string, []*types.Signature) string); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r0 = ret.Get(0).(string)
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, string, []*types.Signature) error); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Derive provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 *types.PublicKey, _a3 map[string]interface{}) (string, map[string]interface{}, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
-
-	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) string); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r0 = ret.Get(0).(string)
-	}
-
-	var r1 map[string]interface{}
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) map[string]interface{}); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(1) != nil {
-			r1 = ret.Get(1).(map[string]interface{})
-		}
-	}
-
-	var r2 error
-	if rf, ok := ret.Get(2).(func(context.Context, *types.NetworkIdentifier, *types.PublicKey, map[string]interface{}) error); ok {
-		r2 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r2 = ret.Error(2)
-	}
-
-	return r0, r1, r2
-}
-
-// Hash provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) Hash(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 string) (*types.TransactionIdentifier, error) {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 *types.TransactionIdentifier
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, string) *types.TransactionIdentifier); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*types.TransactionIdentifier)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, string) error); ok {
-		r1 = rf(_a0, _a1, _a2)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// HeadBlockExists provides a mock function with given fields: _a0
-func (_m *Helper) HeadBlockExists(_a0 context.Context) bool {
-	ret := _m.Called(_a0)
-
-	var r0 bool
-	if rf, ok := ret.Get(0).(func(context.Context) bool); ok {
-		r0 = rf(_a0)
-	} else {
-		r0 = ret.Get(0).(bool)
-	}
-
-	return r0
-}
-
-// LockedAddresses provides a mock function with given fields: _a0
-func (_m *Helper) LockedAddresses(_a0 context.Context) ([]string, error) {
-	ret := _m.Called(_a0)
-
-	var r0 []string
-	if rf, ok := ret.Get(0).(func(context.Context) []string); ok {
-		r0 = rf(_a0)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]string)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
-		r1 = rf(_a0)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Metadata provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) Metadata(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 map[string]interface{}) (map[string]interface{}, error) {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 map[string]interface{}
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, map[string]interface{}) map[string]interface{}); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(map[string]interface{})
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, map[string]interface{}) error); ok {
-		r1 = rf(_a0, _a1, _a2)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// Parse provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Parse(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 bool, _a3 string) ([]*types.Operation, []string, map[string]interface{}, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
-
-	var r0 []*types.Operation
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, bool, string) []*types.Operation); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*types.Operation)
-		}
-	}
-
-	var r1 []string
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, bool, string) []string); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(1) != nil {
-			r1 = ret.Get(1).([]string)
-		}
-	}
-
-	var r2 map[string]interface{}
-	if rf, ok := ret.Get(2).(func(context.Context, *types.NetworkIdentifier, bool, string) map[string]interface{}); ok {
-		r2 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(2) != nil {
-			r2 = ret.Get(2).(map[string]interface{})
-		}
-	}
-
-	var r3 error
-	if rf, ok := ret.Get(3).(func(context.Context, *types.NetworkIdentifier, bool, string) error); ok {
-		r3 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r3 = ret.Error(3)
-	}
-
-	return r0, r1, r2, r3
-}
-
-// Payloads provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Payloads(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 []*types.Operation, _a3 map[string]interface{}) (string, []*types.SigningPayload, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
-
-	var r0 string
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) string); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r0 = ret.Get(0).(string)
-	}
-
-	var r1 []*types.SigningPayload
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) []*types.SigningPayload); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(1) != nil {
-			r1 = ret.Get(1).([]*types.SigningPayload)
-		}
-	}
-
-	var r2 error
-	if rf, ok := ret.Get(2).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) error); ok {
-		r2 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r2 = ret.Error(2)
-	}
-
-	return r0, r1, r2
-}
-
-// Preprocess provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Preprocess(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 []*types.Operation, _a3 map[string]interface{}) (map[string]interface{}, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
-
-	var r0 map[string]interface{}
-	if rf, ok := ret.Get(0).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) map[string]interface{}); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(map[string]interface{})
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) error); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// RandomAmount provides a mock function with given fields: _a0, _a1
-func (_m *Helper) RandomAmount(_a0 *big.Int, _a1 *big.Int) *big.Int {
-	ret := _m.Called(_a0, _a1)
-
-	var r0 *big.Int
-	if rf, ok := ret.Get(0).(func(*big.Int, *big.Int) *big.Int); ok {
-		r0 = rf(_a0, _a1)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*big.Int)
-		}
-	}
-
-	return r0
-}
-
-// Sign provides a mock function with given fields: _a0, _a1
-func (_m *Helper) Sign(_a0 context.Context, _a1 []*types.SigningPayload) ([]*types.Signature, error) {
-	ret := _m.Called(_a0, _a1)
-
-	var r0 []*types.Signature
-	if rf, ok := ret.Get(0).(func(context.Context, []*types.SigningPayload) []*types.Signature); ok {
-		r0 = rf(_a0, _a1)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).([]*types.Signature)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, []*types.SigningPayload) error); ok {
-		r1 = rf(_a0, _a1)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
-// StoreKey provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) StoreKey(_a0 context.Context, _a1 string, _a2 *keys.KeyPair) error {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 error
-	if rf, ok := ret.Get(0).(func(context.Context, string, *keys.KeyPair) error); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		r0 = ret.Error(0)
-	}
-
-	return r0
-}
diff --git a/pkg/processor/balance_storage_handler.go b/pkg/processor/balance_storage_handler.go
index 0e7038f..84e0df1 100644
--- a/pkg/processor/balance_storage_handler.go
+++ b/pkg/processor/balance_storage_handler.go
@@ -16,23 +16,26 @@ package processor
 
 import (
 	"context"
+	"math/big"
 
 	"github.com/coinbase/rosetta-cli/pkg/logger"
 
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
-var _ storage.BalanceStorageHandler = (*BalanceStorageHandler)(nil)
+var _ modules.BalanceStorageHandler = (*BalanceStorageHandler)(nil)
 
 // BalanceStorageHandler is invoked whenever a block is added
 // or removed from block storage so that balance changes
 // can be sent to other functions (ex: reconciler).
 type BalanceStorageHandler struct {
-	logger     *logger.Logger
-	reconciler *reconciler.Reconciler
+	logger         *logger.Logger
+	reconciler     *reconciler.Reconciler
+	counterStorage *modules.CounterStorage
 
 	reconcile          bool
 	interestingAccount *types.AccountCurrency
@@ -42,12 +45,14 @@ type BalanceStorageHandler struct {
 func NewBalanceStorageHandler(
 	logger *logger.Logger,
 	reconciler *reconciler.Reconciler,
+	counterStorage *modules.CounterStorage,
 	reconcile bool,
 	interestingAccount *types.AccountCurrency,
 ) *BalanceStorageHandler {
 	return &BalanceStorageHandler{
 		logger:             logger,
 		reconciler:         reconciler,
+		counterStorage:     counterStorage,
 		reconcile:          reconcile,
 		interestingAccount: interestingAccount,
 	}
@@ -106,3 +111,33 @@ func (h *BalanceStorageHandler) BlockRemoved(
 	// not removed
 	return nil
 }
+
+// AccountsReconciled updates the total accounts reconciled by count.
+func (h *BalanceStorageHandler) AccountsReconciled(
+	ctx context.Context,
+	dbTx database.Transaction,
+	count int,
+) error {
+	_, err := h.counterStorage.UpdateTransactional(
+		ctx,
+		dbTx,
+		modules.ReconciledAccounts,
+		big.NewInt(int64(count)),
+	)
+	return err
+}
+
+// AccountsSeen updates the total accounts seen by count.
+func (h *BalanceStorageHandler) AccountsSeen(
+	ctx context.Context,
+	dbTx database.Transaction,
+	count int,
+) error {
+	_, err := h.counterStorage.UpdateTransactional(
+		ctx,
+		dbTx,
+		modules.SeenAccounts,
+		big.NewInt(int64(count)),
+	)
+	return err
+}
diff --git a/pkg/processor/balance_storage_helper.go b/pkg/processor/balance_storage_helper.go
index c28098b..f862093 100644
--- a/pkg/processor/balance_storage_helper.go
+++ b/pkg/processor/balance_storage_helper.go
@@ -17,23 +17,26 @@ package processor
 import (
 	"context"
 	"fmt"
+	"math/big"
 
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/parser"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
 )
 
-var _ storage.BalanceStorageHelper = (*BalanceStorageHelper)(nil)
+var _ modules.BalanceStorageHelper = (*BalanceStorageHelper)(nil)
 
 // BalanceStorageHelper implements the storage.Helper
 // interface.
 type BalanceStorageHelper struct {
-	network *types.NetworkIdentifier
-	fetcher *fetcher.Fetcher
+	network        *types.NetworkIdentifier
+	fetcher        *fetcher.Fetcher
+	counterStorage *modules.CounterStorage
 
 	// Configuration settings
 	lookupBalanceByBlock bool
@@ -50,6 +53,7 @@ type BalanceStorageHelper struct {
 func NewBalanceStorageHelper(
 	network *types.NetworkIdentifier,
 	fetcher *fetcher.Fetcher,
+	counterStorage *modules.CounterStorage,
 	lookupBalanceByBlock bool,
 	exemptAccounts []*types.AccountCurrency,
 	interestingOnly bool,
@@ -67,6 +71,7 @@ func NewBalanceStorageHelper(
 	return &BalanceStorageHelper{
 		network:              network,
 		fetcher:              fetcher,
+		counterStorage:       counterStorage,
 		lookupBalanceByBlock: lookupBalanceByBlock,
 		exemptAccounts:       exemptMap,
 		interestingAddresses: map[string]struct{}{},
@@ -154,3 +159,19 @@ func (h *BalanceStorageHelper) ExemptFunc() parser.ExemptOperation {
 func (h *BalanceStorageHelper) BalanceExemptions() []*types.BalanceExemption {
 	return h.balanceExemptions
 }
+
+// AccountsReconciled returns the total accounts reconciled by count.
+func (h *BalanceStorageHelper) AccountsReconciled(
+	ctx context.Context,
+	dbTx database.Transaction,
+) (*big.Int, error) {
+	return h.counterStorage.GetTransactional(ctx, dbTx, modules.ReconciledAccounts)
+}
+
+// AccountsSeen returns the total accounts seen by count.
+func (h *BalanceStorageHelper) AccountsSeen(
+	ctx context.Context,
+	dbTx database.Transaction,
+) (*big.Int, error) {
+	return h.counterStorage.GetTransactional(ctx, dbTx, modules.SeenAccounts)
+}
diff --git a/pkg/processor/broadcast_storage_handler.go b/pkg/processor/broadcast_storage_handler.go
index 4bfafe7..57b881c 100644
--- a/pkg/processor/broadcast_storage_handler.go
+++ b/pkg/processor/broadcast_storage_handler.go
@@ -23,18 +23,19 @@ import (
 
 	"github.com/coinbase/rosetta-sdk-go/constructor/coordinator"
 	"github.com/coinbase/rosetta-sdk-go/parser"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
-var _ storage.BroadcastStorageHandler = (*BroadcastStorageHandler)(nil)
+var _ modules.BroadcastStorageHandler = (*BroadcastStorageHandler)(nil)
 
 // BroadcastStorageHandler is invoked whenever a block is added
 // or removed from block storage so that balance changes
 // can be sent to other functions (ex: reconciler).
 type BroadcastStorageHandler struct {
 	config         *configuration.Configuration
-	counterStorage *storage.CounterStorage
+	counterStorage *modules.CounterStorage
 	coordinator    *coordinator.Coordinator
 	parser         *parser.Parser
 }
@@ -42,7 +43,7 @@ type BroadcastStorageHandler struct {
 // NewBroadcastStorageHandler returns a new *BroadcastStorageHandler.
 func NewBroadcastStorageHandler(
 	config *configuration.Configuration,
-	counterStorage *storage.CounterStorage,
+	counterStorage *modules.CounterStorage,
 	coordinator *coordinator.Coordinator,
 	parser *parser.Parser,
 ) *BroadcastStorageHandler {
@@ -58,7 +59,7 @@ func NewBroadcastStorageHandler(
 // last time at a block height < current block height - confirmationDepth.
 func (h *BroadcastStorageHandler) TransactionConfirmed(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	identifier string,
 	blockIdentifier *types.BlockIdentifier,
 	transaction *types.Transaction,
@@ -71,7 +72,7 @@ func (h *BroadcastStorageHandler) TransactionConfirmed(
 	_, _ = h.counterStorage.UpdateTransactional(
 		ctx,
 		dbTx,
-		storage.TransactionsConfirmedCounter,
+		modules.TransactionsConfirmedCounter,
 		big.NewInt(1),
 	)
 
@@ -92,14 +93,14 @@ func (h *BroadcastStorageHandler) TransactionConfirmed(
 // current block height - last broadcast > staleDepth.
 func (h *BroadcastStorageHandler) TransactionStale(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	identifier string,
 	transactionIdentifier *types.TransactionIdentifier,
 ) error {
 	_, _ = h.counterStorage.UpdateTransactional(
 		ctx,
 		dbTx,
-		storage.StaleBroadcastsCounter,
+		modules.StaleBroadcastsCounter,
 		big.NewInt(1),
 	)
 
@@ -110,7 +111,7 @@ func (h *BroadcastStorageHandler) TransactionStale(
 // put it over the provided broadcast limit.
 func (h *BroadcastStorageHandler) BroadcastFailed(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	identifier string,
 	transactionIdentifier *types.TransactionIdentifier,
 	intent []*types.Operation,
@@ -118,7 +119,7 @@ func (h *BroadcastStorageHandler) BroadcastFailed(
 	_, _ = h.counterStorage.UpdateTransactional(
 		ctx,
 		dbTx,
-		storage.FailedBroadcastsCounter,
+		modules.FailedBroadcastsCounter,
 		big.NewInt(1),
 	)
 
diff --git a/pkg/processor/broadcast_storage_helper.go b/pkg/processor/broadcast_storage_helper.go
index f8e4f13..91eecb2 100644
--- a/pkg/processor/broadcast_storage_helper.go
+++ b/pkg/processor/broadcast_storage_helper.go
@@ -19,22 +19,23 @@ import (
 	"fmt"
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
-var _ storage.BroadcastStorageHelper = (*BroadcastStorageHelper)(nil)
+var _ modules.BroadcastStorageHelper = (*BroadcastStorageHelper)(nil)
 
 // BroadcastStorageHelper implements the storage.Helper
 // interface.
 type BroadcastStorageHelper struct {
-	blockStorage *storage.BlockStorage
+	blockStorage *modules.BlockStorage
 	fetcher      *fetcher.Fetcher
 }
 
 // NewBroadcastStorageHelper returns a new BroadcastStorageHelper.
 func NewBroadcastStorageHelper(
-	blockStorage *storage.BlockStorage,
+	blockStorage *modules.BlockStorage,
 	fetcher *fetcher.Fetcher,
 ) *BroadcastStorageHelper {
 	return &BroadcastStorageHelper{
@@ -75,7 +76,7 @@ func (h *BroadcastStorageHelper) CurrentBlockIdentifier(
 func (h *BroadcastStorageHelper) FindTransaction(
 	ctx context.Context,
 	transactionIdentifier *types.TransactionIdentifier,
-	txn storage.DatabaseTransaction,
+	txn database.Transaction,
 ) (*types.BlockIdentifier, *types.Transaction, error) {
 	newestBlock, transaction, err := h.blockStorage.FindTransaction(ctx, transactionIdentifier, txn)
 	if err != nil {
diff --git a/pkg/processor/coin_storage_helper.go b/pkg/processor/coin_storage_helper.go
index dae4b41..afad8c5 100644
--- a/pkg/processor/coin_storage_helper.go
+++ b/pkg/processor/coin_storage_helper.go
@@ -17,20 +17,21 @@ package processor
 import (
 	"context"
 
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
-var _ storage.CoinStorageHelper = (*CoinStorageHelper)(nil)
+var _ modules.CoinStorageHelper = (*CoinStorageHelper)(nil)
 
 // CoinStorageHelper implements the storage.CoinStorageHelper
 // interface.
 type CoinStorageHelper struct {
-	blockStorage *storage.BlockStorage
+	blockStorage *modules.BlockStorage
 }
 
 // NewCoinStorageHelper returns a new *CoinStorageHelper.
-func NewCoinStorageHelper(blockStorage *storage.BlockStorage) *CoinStorageHelper {
+func NewCoinStorageHelper(blockStorage *modules.BlockStorage) *CoinStorageHelper {
 	return &CoinStorageHelper{blockStorage: blockStorage}
 }
 
@@ -38,7 +39,7 @@ func NewCoinStorageHelper(blockStorage *storage.BlockStorage) *CoinStorageHelper
 // the context of a storage.DatabaseTransaction.
 func (c *CoinStorageHelper) CurrentBlockIdentifier(
 	ctx context.Context,
-	transaction storage.DatabaseTransaction,
+	transaction database.Transaction,
 ) (*types.BlockIdentifier, error) {
 	return c.blockStorage.GetHeadBlockIdentifierTransactional(ctx, transaction)
 }
diff --git a/pkg/processor/coordinator_handler.go b/pkg/processor/coordinator_handler.go
index 2201800..e78ae76 100644
--- a/pkg/processor/coordinator_handler.go
+++ b/pkg/processor/coordinator_handler.go
@@ -19,7 +19,7 @@ import (
 	"math/big"
 
 	"github.com/coinbase/rosetta-sdk-go/constructor/coordinator"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -28,13 +28,13 @@ var _ coordinator.Handler = (*CoordinatorHandler)(nil)
 // CoordinatorHandler is invoked by the Coordinator
 // when addresses are created or transactions are created.
 type CoordinatorHandler struct {
-	counterStorage *storage.CounterStorage
+	counterStorage *modules.CounterStorage
 }
 
 // NewCoordinatorHandler returns a new
 // *CoordinatorHandler.
 func NewCoordinatorHandler(
-	counterStorage *storage.CounterStorage,
+	counterStorage *modules.CounterStorage,
 ) *CoordinatorHandler {
 	return &CoordinatorHandler{
 		counterStorage: counterStorage,
@@ -50,7 +50,7 @@ func (h *CoordinatorHandler) TransactionCreated(
 ) error {
 	_, _ = h.counterStorage.Update(
 		ctx,
-		storage.TransactionsCreatedCounter,
+		modules.TransactionsCreatedCounter,
 		big.NewInt(1),
 	)
 
diff --git a/pkg/processor/coordinator_helper.go b/pkg/processor/coordinator_helper.go
index f21b8a8..c017e8c 100644
--- a/pkg/processor/coordinator_helper.go
+++ b/pkg/processor/coordinator_helper.go
@@ -24,7 +24,8 @@ import (
 	"github.com/coinbase/rosetta-sdk-go/constructor/coordinator"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/keys"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -62,13 +63,13 @@ type CoordinatorHelper struct {
 	offlineFetcher *fetcher.Fetcher
 	onlineFetcher  *fetcher.Fetcher
 
-	database         storage.Database
-	blockStorage     *storage.BlockStorage
-	keyStorage       *storage.KeyStorage
-	balanceStorage   *storage.BalanceStorage
-	coinStorage      *storage.CoinStorage
-	broadcastStorage *storage.BroadcastStorage
-	counterStorage   *storage.CounterStorage
+	database         database.Database
+	blockStorage     *modules.BlockStorage
+	keyStorage       *modules.KeyStorage
+	balanceStorage   *modules.BalanceStorage
+	coinStorage      *modules.CoinStorage
+	broadcastStorage *modules.BroadcastStorage
+	counterStorage   *modules.CounterStorage
 
 	balanceStorageHelper *BalanceStorageHelper
 
@@ -81,14 +82,14 @@ type CoordinatorHelper struct {
 func NewCoordinatorHelper(
 	offlineFetcher *fetcher.Fetcher,
 	onlineFetcher *fetcher.Fetcher,
-	database storage.Database,
-	blockStorage *storage.BlockStorage,
-	keyStorage *storage.KeyStorage,
-	balanceStorage *storage.BalanceStorage,
-	coinStorage *storage.CoinStorage,
-	broadcastStorage *storage.BroadcastStorage,
+	database database.Database,
+	blockStorage *modules.BlockStorage,
+	keyStorage *modules.KeyStorage,
+	balanceStorage *modules.BalanceStorage,
+	coinStorage *modules.CoinStorage,
+	broadcastStorage *modules.BroadcastStorage,
 	balanceStorageHelper *BalanceStorageHelper,
-	counterStorage *storage.CounterStorage,
+	counterStorage *modules.CounterStorage,
 	quiet bool,
 ) *CoordinatorHelper {
 	return &CoordinatorHelper{
@@ -106,9 +107,9 @@ func NewCoordinatorHelper(
 	}
 }
 
-// DatabaseTransaction returns a new write-ready storage.DatabaseTransaction.
-func (c *CoordinatorHelper) DatabaseTransaction(ctx context.Context) storage.DatabaseTransaction {
-	return c.database.NewDatabaseTransaction(ctx, true)
+// DatabaseTransaction returns a new write-ready database.Transaction.
+func (c *CoordinatorHelper) DatabaseTransaction(ctx context.Context) database.Transaction {
+	return c.database.Transaction(ctx)
 }
 
 type arg struct {
@@ -359,7 +360,7 @@ func (c *CoordinatorHelper) Sign(
 // associated with an address.
 func (c *CoordinatorHelper) GetKey(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	account *types.AccountIdentifier,
 ) (*keys.KeyPair, error) {
 	return c.keyStorage.GetTransactional(ctx, dbTx, account)
@@ -369,7 +370,7 @@ func (c *CoordinatorHelper) GetKey(
 // in KeyStorage.
 func (c *CoordinatorHelper) StoreKey(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	account *types.AccountIdentifier,
 	keyPair *keys.KeyPair,
 ) error {
@@ -379,7 +380,7 @@ func (c *CoordinatorHelper) StoreKey(
 	_, _ = c.counterStorage.UpdateTransactional(
 		ctx,
 		dbTx,
-		storage.AddressesCreatedCounter,
+		modules.AddressesCreatedCounter,
 		big.NewInt(1),
 	)
 	return c.keyStorage.StoreTransactional(ctx, account, keyPair, dbTx)
@@ -391,7 +392,7 @@ func (c *CoordinatorHelper) StoreKey(
 // 0 will be returned.
 func (c *CoordinatorHelper) Balance(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	accountIdentifier *types.AccountIdentifier,
 	currency *types.Currency,
 ) (*types.Amount, error) {
@@ -416,7 +417,7 @@ func (c *CoordinatorHelper) Balance(
 // an account.
 func (c *CoordinatorHelper) Coins(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	accountIdentifier *types.AccountIdentifier,
 	currency *types.Currency,
 ) ([]*types.Coin, error) {
@@ -445,25 +446,25 @@ func (c *CoordinatorHelper) Coins(
 // funds.
 func (c *CoordinatorHelper) LockedAccounts(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 ) ([]*types.AccountIdentifier, error) {
 	return c.broadcastStorage.LockedAccounts(ctx, dbTx)
 }
 
 // AllBroadcasts returns a slice of all in-progress broadcasts in BroadcastStorage.
-func (c *CoordinatorHelper) AllBroadcasts(ctx context.Context) ([]*storage.Broadcast, error) {
+func (c *CoordinatorHelper) AllBroadcasts(ctx context.Context) ([]*modules.Broadcast, error) {
 	return c.broadcastStorage.GetAllBroadcasts(ctx)
 }
 
 // ClearBroadcasts deletes all pending broadcasts.
-func (c *CoordinatorHelper) ClearBroadcasts(ctx context.Context) ([]*storage.Broadcast, error) {
+func (c *CoordinatorHelper) ClearBroadcasts(ctx context.Context) ([]*modules.Broadcast, error) {
 	return c.broadcastStorage.ClearBroadcasts(ctx)
 }
 
 // Broadcast enqueues a particular intent for broadcast.
 func (c *CoordinatorHelper) Broadcast(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	identifier string,
 	network *types.NetworkIdentifier,
 	intent []*types.Operation,
@@ -499,7 +500,7 @@ func (c *CoordinatorHelper) BroadcastAll(
 // AllAccounts returns a slice of all known accounts.
 func (c *CoordinatorHelper) AllAccounts(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 ) ([]*types.AccountIdentifier, error) {
 	return c.keyStorage.GetAllAccountsTransactional(ctx, dbTx)
 }
diff --git a/pkg/processor/reconciler_handler.go b/pkg/processor/reconciler_handler.go
index 6ce94f4..1352150 100644
--- a/pkg/processor/reconciler_handler.go
+++ b/pkg/processor/reconciler_handler.go
@@ -25,7 +25,7 @@ import (
 	"github.com/coinbase/rosetta-cli/pkg/results"
 
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
@@ -37,19 +37,19 @@ var _ reconciler.Handler = (*ReconcilerHandler)(nil)
 
 var (
 	countKeys = []string{
-		storage.FailedReconciliationCounter,
-		storage.SkippedReconciliationsCounter,
-		storage.ExemptReconciliationCounter,
-		storage.ActiveReconciliationCounter,
-		storage.InactiveReconciliationCounter,
+		modules.FailedReconciliationCounter,
+		modules.SkippedReconciliationsCounter,
+		modules.ExemptReconciliationCounter,
+		modules.ActiveReconciliationCounter,
+		modules.InactiveReconciliationCounter,
 	}
 )
 
 // ReconcilerHandler implements the Reconciler.Handler interface.
 type ReconcilerHandler struct {
 	logger                    *logger.Logger
-	counterStorage            *storage.CounterStorage
-	balanceStorage            *storage.BalanceStorage
+	counterStorage            *modules.CounterStorage
+	balanceStorage            *modules.BalanceStorage
 	haltOnReconciliationError bool
 
 	InactiveFailure      *types.AccountCurrency
@@ -64,8 +64,8 @@ type ReconcilerHandler struct {
 // NewReconcilerHandler creates a new ReconcilerHandler.
 func NewReconcilerHandler(
 	logger *logger.Logger,
-	counterStorage *storage.CounterStorage,
-	balanceStorage *storage.BalanceStorage,
+	counterStorage *modules.CounterStorage,
+	balanceStorage *modules.BalanceStorage,
 	haltOnReconciliationError bool,
 ) *ReconcilerHandler {
 	counts := map[string]int64{}
@@ -82,7 +82,7 @@ func NewReconcilerHandler(
 	}
 }
 
-// Updater periodically updates storage with cached counts.
+// Updater periodically updates modules.with cached counts.
 func (h *ReconcilerHandler) Updater(ctx context.Context) error {
 	tc := time.NewTicker(updateFrequency)
 	defer tc.Stop()
@@ -99,7 +99,7 @@ func (h *ReconcilerHandler) Updater(ctx context.Context) error {
 	}
 }
 
-// UpdateCounts forces cached counts to be written to storage.
+// UpdateCounts forces cached counts to be written to modules.
 func (h *ReconcilerHandler) UpdateCounts(ctx context.Context) error {
 	for _, key := range countKeys {
 		h.counterLock.Lock()
@@ -132,7 +132,7 @@ func (h *ReconcilerHandler) ReconciliationFailed(
 	block *types.BlockIdentifier,
 ) error {
 	h.counterLock.Lock()
-	h.counts[storage.FailedReconciliationCounter]++
+	h.counts[modules.FailedReconciliationCounter]++
 	h.counterLock.Unlock()
 
 	err := h.logger.ReconcileFailureStream(
@@ -202,7 +202,7 @@ func (h *ReconcilerHandler) ReconciliationExempt(
 	exemption *types.BalanceExemption,
 ) error {
 	h.counterLock.Lock()
-	h.counts[storage.ExemptReconciliationCounter]++
+	h.counts[modules.ExemptReconciliationCounter]++
 	h.counterLock.Unlock()
 
 	// Although the reconciliation was exempt (non-zero difference that was ignored),
@@ -224,7 +224,7 @@ func (h *ReconcilerHandler) ReconciliationSkipped(
 	cause string,
 ) error {
 	h.counterLock.Lock()
-	h.counts[storage.SkippedReconciliationsCounter]++
+	h.counts[modules.SkippedReconciliationsCounter]++
 	h.counterLock.Unlock()
 
 	return nil
@@ -240,9 +240,9 @@ func (h *ReconcilerHandler) ReconciliationSucceeded(
 	block *types.BlockIdentifier,
 ) error {
 	// Update counters
-	counter := storage.ActiveReconciliationCounter
+	counter := modules.ActiveReconciliationCounter
 	if reconciliationType == reconciler.InactiveReconciliation {
-		counter = storage.InactiveReconciliationCounter
+		counter = modules.InactiveReconciliationCounter
 	}
 
 	h.counterLock.Lock()
diff --git a/pkg/processor/reconciler_helper.go b/pkg/processor/reconciler_helper.go
index 3fbdbdc..5254d4b 100644
--- a/pkg/processor/reconciler_helper.go
+++ b/pkg/processor/reconciler_helper.go
@@ -21,7 +21,8 @@ import (
 
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
 )
@@ -36,9 +37,9 @@ type ReconcilerHelper struct {
 	network *types.NetworkIdentifier
 	fetcher *fetcher.Fetcher
 
-	database       storage.Database
-	blockStorage   *storage.BlockStorage
-	balanceStorage *storage.BalanceStorage
+	database       database.Database
+	blockStorage   *modules.BlockStorage
+	balanceStorage *modules.BalanceStorage
 }
 
 // NewReconcilerHelper returns a new ReconcilerHelper.
@@ -46,9 +47,9 @@ func NewReconcilerHelper(
 	config *configuration.Configuration,
 	network *types.NetworkIdentifier,
 	fetcher *fetcher.Fetcher,
-	database storage.Database,
-	blockStorage *storage.BlockStorage,
-	balanceStorage *storage.BalanceStorage,
+	database database.Database,
+	blockStorage *modules.BlockStorage,
+	balanceStorage *modules.BalanceStorage,
 ) *ReconcilerHelper {
 	return &ReconcilerHelper{
 		config:         config,
@@ -60,11 +61,11 @@ func NewReconcilerHelper(
 	}
 }
 
-// DatabaseTransaction returns a new read-only storage.DatabaseTransaction.
+// DatabaseTransaction returns a new read-only database.Transaction.
 func (h *ReconcilerHelper) DatabaseTransaction(
 	ctx context.Context,
-) storage.DatabaseTransaction {
-	return h.database.NewDatabaseTransaction(ctx, false)
+) database.Transaction {
+	return h.database.ReadTransaction(ctx)
 }
 
 // CanonicalBlock returns a boolean indicating if a block
@@ -73,7 +74,7 @@ func (h *ReconcilerHelper) DatabaseTransaction(
 // does not exist, reconciliation will be skipped.
 func (h *ReconcilerHelper) CanonicalBlock(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	block *types.BlockIdentifier,
 ) (bool, error) {
 	return h.blockStorage.CanonicalBlockTransactional(ctx, block, dbTx)
@@ -100,7 +101,7 @@ func (h *ReconcilerHelper) IndexAtTip(
 // inactive reconciliation.
 func (h *ReconcilerHelper) CurrentBlock(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 ) (*types.BlockIdentifier, error) {
 	return h.blockStorage.GetHeadBlockIdentifierTransactional(ctx, dbTx)
 }
@@ -110,7 +111,7 @@ func (h *ReconcilerHelper) CurrentBlock(
 // package to allow for separation from a default storage backend.
 func (h *ReconcilerHelper) ComputedBalance(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
+	dbTx database.Transaction,
 	account *types.AccountIdentifier,
 	currency *types.Currency,
 	index int64,
diff --git a/pkg/results/construction_results.go b/pkg/results/construction_results.go
index 83e4413..22f8b65 100644
--- a/pkg/results/construction_results.go
+++ b/pkg/results/construction_results.go
@@ -23,7 +23,7 @@ import (
 
 	"github.com/coinbase/rosetta-cli/configuration"
 
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/fatih/color"
@@ -73,8 +73,8 @@ func (c *CheckConstructionResults) Output(path string) {
 func ComputeCheckConstructionResults(
 	cfg *configuration.Configuration,
 	err error,
-	counterStorage *storage.CounterStorage,
-	jobStorage *storage.JobStorage,
+	counterStorage *modules.CounterStorage,
+	jobStorage *modules.JobStorage,
 ) *CheckConstructionResults {
 	ctx := context.Background()
 	stats := ComputeCheckConstructionStats(ctx, cfg, counterStorage, jobStorage)
@@ -169,38 +169,38 @@ func (c *CheckConstructionStats) Print() {
 func ComputeCheckConstructionStats(
 	ctx context.Context,
 	config *configuration.Configuration,
-	counters *storage.CounterStorage,
-	jobs *storage.JobStorage,
+	counters *modules.CounterStorage,
+	jobs *modules.JobStorage,
 ) *CheckConstructionStats {
 	if counters == nil || jobs == nil {
 		return nil
 	}
 
-	transactionsCreated, err := counters.Get(ctx, storage.TransactionsCreatedCounter)
+	transactionsCreated, err := counters.Get(ctx, modules.TransactionsCreatedCounter)
 	if err != nil {
 		log.Printf("%s cannot get transactions created counter\n", err.Error())
 		return nil
 	}
 
-	transactionsConfirmed, err := counters.Get(ctx, storage.TransactionsConfirmedCounter)
+	transactionsConfirmed, err := counters.Get(ctx, modules.TransactionsConfirmedCounter)
 	if err != nil {
 		log.Printf("%s cannot get transactions confirmed counter\n", err.Error())
 		return nil
 	}
 
-	staleBroadcasts, err := counters.Get(ctx, storage.StaleBroadcastsCounter)
+	staleBroadcasts, err := counters.Get(ctx, modules.StaleBroadcastsCounter)
 	if err != nil {
 		log.Printf("%s cannot get stale broadcasts counter\n", err)
 		return nil
 	}
 
-	failedBroadcasts, err := counters.Get(ctx, storage.FailedBroadcastsCounter)
+	failedBroadcasts, err := counters.Get(ctx, modules.FailedBroadcastsCounter)
 	if err != nil {
 		log.Printf("%s cannot get failed broadcasts counter\n", err.Error())
 		return nil
 	}
 
-	addressesCreated, err := counters.Get(ctx, storage.AddressesCreatedCounter)
+	addressesCreated, err := counters.Get(ctx, modules.AddressesCreatedCounter)
 	if err != nil {
 		log.Printf("%s cannot get addresses created counter\n", err.Error())
 		return nil
@@ -239,8 +239,8 @@ type CheckConstructionProgress struct {
 // *CheckConstructionProgress.
 func ComputeCheckConstructionProgress(
 	ctx context.Context,
-	broadcasts *storage.BroadcastStorage,
-	jobs *storage.JobStorage,
+	broadcasts *modules.BroadcastStorage,
+	jobs *modules.JobStorage,
 ) *CheckConstructionProgress {
 	inflight, err := broadcasts.GetAllBroadcasts(ctx)
 	if err != nil {
@@ -271,9 +271,9 @@ type CheckConstructionStatus struct {
 func ComputeCheckConstructionStatus(
 	ctx context.Context,
 	config *configuration.Configuration,
-	counters *storage.CounterStorage,
-	broadcasts *storage.BroadcastStorage,
-	jobs *storage.JobStorage,
+	counters *modules.CounterStorage,
+	broadcasts *modules.BroadcastStorage,
+	jobs *modules.JobStorage,
 ) *CheckConstructionStatus {
 	return &CheckConstructionStatus{
 		Stats:    ComputeCheckConstructionStats(ctx, config, counters, jobs),
@@ -295,8 +295,8 @@ func FetchCheckConstructionStatus(url string) (*CheckConstructionStatus, error)
 // and to a provided output path.
 func ExitConstruction(
 	config *configuration.Configuration,
-	counterStorage *storage.CounterStorage,
-	jobStorage *storage.JobStorage,
+	counterStorage *modules.CounterStorage,
+	jobStorage *modules.JobStorage,
 	err error,
 ) error {
 	results := ComputeCheckConstructionResults(
diff --git a/pkg/results/data_results.go b/pkg/results/data_results.go
index 950c97c..1afdcdd 100644
--- a/pkg/results/data_results.go
+++ b/pkg/results/data_results.go
@@ -28,7 +28,8 @@ import (
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	storageErrs "github.com/coinbase/rosetta-sdk-go/storage/errors"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
@@ -175,62 +176,62 @@ func (c *CheckDataStats) Print() {
 // ComputeCheckDataStats returns a populated CheckDataStats.
 func ComputeCheckDataStats(
 	ctx context.Context,
-	counters *storage.CounterStorage,
-	balances *storage.BalanceStorage,
+	counters *modules.CounterStorage,
+	balances *modules.BalanceStorage,
 ) *CheckDataStats {
 	if counters == nil {
 		return nil
 	}
 
-	blocks, err := counters.Get(ctx, storage.BlockCounter)
+	blocks, err := counters.Get(ctx, modules.BlockCounter)
 	if err != nil {
 		log.Printf("%s: cannot get block counter", err.Error())
 		return nil
 	}
 
-	orphans, err := counters.Get(ctx, storage.OrphanCounter)
+	orphans, err := counters.Get(ctx, modules.OrphanCounter)
 	if err != nil {
 		log.Printf("%s: cannot get orphan counter", err.Error())
 		return nil
 	}
 
-	txs, err := counters.Get(ctx, storage.TransactionCounter)
+	txs, err := counters.Get(ctx, modules.TransactionCounter)
 	if err != nil {
 		log.Printf("%s: cannot get transaction counter", err.Error())
 		return nil
 	}
 
-	ops, err := counters.Get(ctx, storage.OperationCounter)
+	ops, err := counters.Get(ctx, modules.OperationCounter)
 	if err != nil {
 		log.Printf("%s: cannot get operations counter", err.Error())
 		return nil
 	}
 
-	activeReconciliations, err := counters.Get(ctx, storage.ActiveReconciliationCounter)
+	activeReconciliations, err := counters.Get(ctx, modules.ActiveReconciliationCounter)
 	if err != nil {
 		log.Printf("%s: cannot get active reconciliations counter", err.Error())
 		return nil
 	}
 
-	inactiveReconciliations, err := counters.Get(ctx, storage.InactiveReconciliationCounter)
+	inactiveReconciliations, err := counters.Get(ctx, modules.InactiveReconciliationCounter)
 	if err != nil {
 		log.Printf("%s: cannot get inactive reconciliations counter", err.Error())
 		return nil
 	}
 
-	exemptReconciliations, err := counters.Get(ctx, storage.ExemptReconciliationCounter)
+	exemptReconciliations, err := counters.Get(ctx, modules.ExemptReconciliationCounter)
 	if err != nil {
 		log.Printf("%s: cannot get exempt reconciliations counter", err.Error())
 		return nil
 	}
 
-	failedReconciliations, err := counters.Get(ctx, storage.FailedReconciliationCounter)
+	failedReconciliations, err := counters.Get(ctx, modules.FailedReconciliationCounter)
 	if err != nil {
 		log.Printf("%s: cannot get failed reconciliations counter", err.Error())
 		return nil
 	}
 
-	skippedReconciliations, err := counters.Get(ctx, storage.SkippedReconciliationsCounter)
+	skippedReconciliations, err := counters.Get(ctx, modules.SkippedReconciliationsCounter)
 	if err != nil {
 		log.Printf("%s: cannot get skipped reconciliations counter", err.Error())
 		return nil
@@ -249,7 +250,7 @@ func ComputeCheckDataStats(
 	}
 
 	if balances != nil {
-		coverage, err := balances.ReconciliationCoverage(ctx, 0)
+		coverage, err := balances.EstimatedReconciliationCoverage(ctx)
 		if err != nil {
 			log.Printf("%s: cannot get reconcile coverage", err.Error())
 			return nil
@@ -279,8 +280,8 @@ func ComputeCheckDataProgress(
 	ctx context.Context,
 	fetcher *fetcher.Fetcher,
 	network *types.NetworkIdentifier,
-	counters *storage.CounterStorage,
-	blockStorage *storage.BlockStorage,
+	counters *modules.CounterStorage,
+	blockStorage *modules.BlockStorage,
 	reconciler *reconciler.Reconciler,
 ) *CheckDataProgress {
 	networkStatus, fetchErr := fetcher.NetworkStatusRetry(ctx, network, nil)
@@ -293,7 +294,7 @@ func ComputeCheckDataProgress(
 	// Get current tip in the case that re-orgs occurred
 	// or a custom start index was provied.
 	headBlock, err := blockStorage.GetHeadBlockIdentifier(ctx)
-	if errors.Is(err, storage.ErrHeadBlockNotFound) {
+	if errors.Is(err, storageErrs.ErrHeadBlockNotFound) {
 		return nil
 	}
 	if err != nil {
@@ -301,7 +302,7 @@ func ComputeCheckDataProgress(
 		return nil
 	}
 
-	blocks, err := counters.Get(ctx, storage.BlockCounter)
+	blocks, err := counters.Get(ctx, modules.BlockCounter)
 	if err != nil {
 		fmt.Printf("%s: cannot get block counter", err.Error())
 		return nil
@@ -311,7 +312,7 @@ func ComputeCheckDataProgress(
 		return nil
 	}
 
-	orphans, err := counters.Get(ctx, storage.OrphanCounter)
+	orphans, err := counters.Get(ctx, modules.OrphanCounter)
 	if err != nil {
 		fmt.Printf("%s: cannot get orphan counter", err.Error())
 		return nil
@@ -375,9 +376,9 @@ type CheckDataStatus struct {
 // *CheckDataStatus.
 func ComputeCheckDataStatus(
 	ctx context.Context,
-	blocks *storage.BlockStorage,
-	counters *storage.CounterStorage,
-	balances *storage.BalanceStorage,
+	blocks *modules.BlockStorage,
+	counters *modules.CounterStorage,
+	balances *modules.BalanceStorage,
 	fetcher *fetcher.Fetcher,
 	network *types.NetworkIdentifier,
 	reconciler *reconciler.Reconciler,
@@ -503,9 +504,9 @@ func ResponseAssertionTest(err error) bool {
 // blocks.
 func BlockSyncingTest(err error, blocksSynced bool) *bool {
 	syncPass := true
-	storageFailed, _ := storage.Err(err)
+	storageFailed, _ := storageErrs.Err(err)
 	if syncer.Err(err) ||
-		(storageFailed && !errors.Is(err, storage.ErrNegativeBalance)) {
+		(storageFailed && !errors.Is(err, storageErrs.ErrNegativeBalance)) {
 		syncPass = false
 	}
 
@@ -521,7 +522,7 @@ func BlockSyncingTest(err error, blocksSynced bool) *bool {
 // while syncing.
 func BalanceTrackingTest(cfg *configuration.Configuration, err error, operationsSeen bool) *bool {
 	balancePass := true
-	for _, balanceStorageErr := range storage.BalanceStorageErrs {
+	for _, balanceStorageErr := range storageErrs.BalanceStorageErrs {
 		if errors.Is(err, balanceStorageErr) {
 			balancePass = false
 			break
@@ -565,31 +566,31 @@ func ComputeCheckDataTests( // nolint:gocognit
 	ctx context.Context,
 	cfg *configuration.Configuration,
 	err error,
-	counterStorage *storage.CounterStorage,
+	counterStorage *modules.CounterStorage,
 ) *CheckDataTests {
 	operationsSeen := false
 	reconciliationsPerformed := false
 	reconciliationsFailed := false
 	blocksSynced := false
 	if counterStorage != nil {
-		blocks, err := counterStorage.Get(ctx, storage.BlockCounter)
+		blocks, err := counterStorage.Get(ctx, modules.BlockCounter)
 		if err == nil && blocks.Int64() > 0 {
 			blocksSynced = true
 		}
 
-		ops, err := counterStorage.Get(ctx, storage.OperationCounter)
+		ops, err := counterStorage.Get(ctx, modules.OperationCounter)
 		if err == nil && ops.Int64() > 0 {
 			operationsSeen = true
 		}
 
-		activeReconciliations, err := counterStorage.Get(ctx, storage.ActiveReconciliationCounter)
+		activeReconciliations, err := counterStorage.Get(ctx, modules.ActiveReconciliationCounter)
 		if err == nil && activeReconciliations.Int64() > 0 {
 			reconciliationsPerformed = true
 		}
 
 		inactiveReconciliations, err := counterStorage.Get(
 			ctx,
-			storage.InactiveReconciliationCounter,
+			modules.InactiveReconciliationCounter,
 		)
 		if err == nil && inactiveReconciliations.Int64() > 0 {
 			reconciliationsPerformed = true
@@ -597,7 +598,7 @@ func ComputeCheckDataTests( // nolint:gocognit
 
 		exemptReconciliations, err := counterStorage.Get(
 			ctx,
-			storage.ExemptReconciliationCounter,
+			modules.ExemptReconciliationCounter,
 		)
 		if err == nil && exemptReconciliations.Int64() > 0 {
 			reconciliationsPerformed = true
@@ -605,7 +606,7 @@ func ComputeCheckDataTests( // nolint:gocognit
 
 		failedReconciliations, err := counterStorage.Get(
 			ctx,
-			storage.FailedReconciliationCounter,
+			modules.FailedReconciliationCounter,
 		)
 		if err == nil && failedReconciliations.Int64() > 0 {
 			reconciliationsPerformed = true
@@ -631,8 +632,8 @@ func ComputeCheckDataTests( // nolint:gocognit
 func ComputeCheckDataResults(
 	cfg *configuration.Configuration,
 	err error,
-	counterStorage *storage.CounterStorage,
-	balanceStorage *storage.BalanceStorage,
+	counterStorage *modules.CounterStorage,
+	balanceStorage *modules.BalanceStorage,
 	endCondition configuration.CheckDataEndCondition,
 	endConditionDetail string,
 ) *CheckDataResults {
@@ -677,8 +678,8 @@ func ComputeCheckDataResults(
 // and to a provided output path.
 func ExitData(
 	config *configuration.Configuration,
-	counterStorage *storage.CounterStorage,
-	balanceStorage *storage.BalanceStorage,
+	counterStorage *modules.CounterStorage,
+	balanceStorage *modules.BalanceStorage,
 	err error,
 	endCondition configuration.CheckDataEndCondition,
 	endConditionDetail string,


Test patch:
diff --git a/configuration/configuration_test.go b/configuration/configuration_test.go
index 7cc2c50..5170340 100644
--- a/configuration/configuration_test.go
+++ b/configuration/configuration_test.go
@@ -21,7 +21,7 @@ import (
 	"testing"
 
 	"github.com/coinbase/rosetta-sdk-go/constructor/job"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/stretchr/testify/assert"
@@ -105,7 +105,7 @@ var (
 	}
 	invalidPrefundedAccounts = &Configuration{
 		Construction: &ConstructionConfiguration{
-			PrefundedAccounts: []*storage.PrefundedAccount{
+			PrefundedAccounts: []*modules.PrefundedAccount{
 				{
 					PrivateKeyHex: "hello",
 				},
diff --git a/pkg/processor/balance_storage_helper_test.go b/pkg/processor/balance_storage_helper_test.go
index 928efe4..2e7cc5e 100644
--- a/pkg/processor/balance_storage_helper_test.go
+++ b/pkg/processor/balance_storage_helper_test.go
@@ -78,6 +78,7 @@ func TestExemptFuncExemptAccounts(t *testing.T) {
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
 			helper := NewBalanceStorageHelper(
+				nil,
 				nil,
 				nil,
 				false,
@@ -121,6 +122,7 @@ func TestExemptFuncInterestingParsing(t *testing.T) {
 	for name, test := range tests {
 		t.Run(name, func(t *testing.T) {
 			helper := NewBalanceStorageHelper(
+				nil,
 				nil,
 				nil,
 				false,
diff --git a/pkg/results/data_results_test.go b/pkg/results/data_results_test.go
index 9addec7..d5b92c1 100644
--- a/pkg/results/data_results_test.go
+++ b/pkg/results/data_results_test.go
@@ -26,13 +26,47 @@ import (
 
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	sdkMocks "github.com/coinbase/rosetta-sdk-go/mocks/storage/modules"
+	"github.com/coinbase/rosetta-sdk-go/parser"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	storageErrs "github.com/coinbase/rosetta-sdk-go/storage/errors"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
 	"github.com/stretchr/testify/assert"
+	"github.com/stretchr/testify/mock"
 )
 
+func baseAsserter() *asserter.Asserter {
+	a, _ := asserter.NewClientWithOptions(
+		&types.NetworkIdentifier{
+			Blockchain: "bitcoin",
+			Network:    "mainnet",
+		},
+		&types.BlockIdentifier{
+			Hash:  "block 0",
+			Index: 0,
+		},
+		[]string{"Transfer"},
+		[]*types.OperationStatus{
+			{
+				Status:     "Success",
+				Successful: true,
+			},
+		},
+		[]*types.Error{},
+		nil,
+	)
+	return a
+}
+
+func exemptFunc() parser.ExemptOperation {
+	return func(op *types.Operation) bool {
+		return false
+	}
+}
+
 func TestComputeCheckDataResults(t *testing.T) {
 	var tests = map[string]struct {
 		cfg *configuration.Configuration
@@ -115,8 +149,8 @@ func TestComputeCheckDataResults(t *testing.T) {
 			err: []error{
 				syncer.ErrCannotRemoveGenesisBlock,
 				syncer.ErrOutOfOrder,
-				storage.ErrDuplicateKey,
-				storage.ErrDuplicateTransactionHash,
+				storageErrs.ErrDuplicateKey,
+				storageErrs.ErrDuplicateTransactionHash,
 			},
 			result: &CheckDataResults{
 				Tests: &CheckDataTests{
@@ -129,7 +163,7 @@ func TestComputeCheckDataResults(t *testing.T) {
 		"default configuration, counter storage no blocks, balance errors": {
 			cfg:                   configuration.DefaultConfiguration(),
 			provideCounterStorage: true,
-			err:                   []error{storage.ErrNegativeBalance},
+			err:                   []error{storageErrs.ErrNegativeBalance},
 			result: &CheckDataResults{
 				Tests: &CheckDataTests{
 					RequestResponse:   true,
@@ -143,7 +177,7 @@ func TestComputeCheckDataResults(t *testing.T) {
 			cfg:                   configuration.DefaultConfiguration(),
 			provideCounterStorage: true,
 			blockCount:            100,
-			err:                   []error{storage.ErrNegativeBalance},
+			err:                   []error{storageErrs.ErrNegativeBalance},
 			result: &CheckDataResults{
 				Tests: &CheckDataTests{
 					RequestResponse:   true,
@@ -279,7 +313,7 @@ func TestComputeCheckDataResults(t *testing.T) {
 		},
 		"default configuration, no storage, balance errors": {
 			cfg: configuration.DefaultConfiguration(),
-			err: []error{storage.ErrNegativeBalance},
+			err: []error{storageErrs.ErrNegativeBalance},
 			result: &CheckDataResults{
 				Tests: &CheckDataTests{
 					RequestResponse:   true,
@@ -358,88 +392,81 @@ func TestComputeCheckDataResults(t *testing.T) {
 				assert.NoError(t, err)
 
 				ctx := context.Background()
-				localStore, err := storage.NewBadgerStorage(
+				localStore, err := database.NewBadgerDatabase(
 					ctx,
 					dir,
-					storage.WithIndexCacheSize(storage.TinyIndexCacheSize),
+					database.WithIndexCacheSize(database.TinyIndexCacheSize),
 				)
 				assert.NoError(t, err)
 
 				logPath := path.Join(dir, "results.json")
 
-				var counterStorage *storage.CounterStorage
+				var counterStorage *modules.CounterStorage
 				if test.provideCounterStorage {
-					counterStorage = storage.NewCounterStorage(localStore)
+					counterStorage = modules.NewCounterStorage(localStore)
 					_, err = counterStorage.Update(
 						ctx,
-						storage.BlockCounter,
+						modules.BlockCounter,
 						big.NewInt(test.blockCount),
 					)
 					assert.NoError(t, err)
 
 					_, err = counterStorage.Update(
 						ctx,
-						storage.OperationCounter,
+						modules.OperationCounter,
 						big.NewInt(test.operationCount),
 					)
 					assert.NoError(t, err)
 
 					_, err = counterStorage.Update(
 						ctx,
-						storage.ActiveReconciliationCounter,
+						modules.ActiveReconciliationCounter,
 						big.NewInt(test.activeReconciliations),
 					)
 					assert.NoError(t, err)
 
 					_, err = counterStorage.Update(
 						ctx,
-						storage.InactiveReconciliationCounter,
+						modules.InactiveReconciliationCounter,
 						big.NewInt(test.inactiveReconciliations),
 					)
 					assert.NoError(t, err)
 
 					_, err = counterStorage.Update(
 						ctx,
-						storage.FailedReconciliationCounter,
+						modules.FailedReconciliationCounter,
 						big.NewInt(test.reconciliationFailures),
 					)
 					assert.NoError(t, err)
 				}
 
-				var balanceStorage *storage.BalanceStorage
+				var balanceStorage *modules.BalanceStorage
 				if test.provideBalanceStorage {
-					balanceStorage = storage.NewBalanceStorage(localStore)
-
-					j := 0
-					currency := &types.Currency{Symbol: "BLAH"}
-					block := &types.BlockIdentifier{Hash: "0", Index: 0}
-					for i := 0; i < test.totalAccounts; i++ {
-						dbTransaction := localStore.NewDatabaseTransaction(ctx, true)
-						acct := &types.AccountIdentifier{
-							Address: fmt.Sprintf("account %d", i),
-						}
-						assert.NoError(t, balanceStorage.SetBalance(
-							ctx,
-							dbTransaction,
-							acct,
-							&types.Amount{Value: "1", Currency: currency},
-							block,
-						))
-						assert.NoError(t, dbTransaction.Commit(ctx))
-
-						if j >= test.reconciledAccounts {
-							continue
-						}
-
-						assert.NoError(t, balanceStorage.Reconciled(
-							ctx,
-							acct,
-							currency,
-							block,
-						))
-
-						j++
-					}
+					balanceStorage = modules.NewBalanceStorage(localStore)
+					mockHelper := &sdkMocks.BalanceStorageHelper{}
+					mockHelper.On("Asserter").Return(baseAsserter())
+					mockHelper.On("ExemptFunc").Return(exemptFunc())
+					mockHelper.On("BalanceExemptions").Return([]*types.BalanceExemption{})
+					mockHelper.On(
+						"AccountsSeen",
+						mock.Anything,
+						mock.Anything,
+						mock.Anything,
+					).Return(
+						big.NewInt(int64(test.totalAccounts)),
+						nil,
+					)
+					mockHelper.On(
+						"AccountsReconciled",
+						mock.Anything,
+						mock.Anything,
+						mock.Anything,
+					).Return(
+						big.NewInt(int64(test.reconciledAccounts)),
+						nil,
+					)
+					mockHandler := &sdkMocks.BalanceStorageHandler{}
+					balanceStorage.Initialize(mockHelper, mockHandler)
 				}
 
 				t.Run(testName, func(t *testing.T) {
diff --git a/pkg/tester/construction.go b/pkg/tester/construction.go
index 194198a..11b40f1 100644
--- a/pkg/tester/construction.go
+++ b/pkg/tester/construction.go
@@ -32,7 +32,9 @@ import (
 	"github.com/coinbase/rosetta-sdk-go/fetcher"
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/statefulsyncer"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	storageErrs "github.com/coinbase/rosetta-sdk-go/storage/errors"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
@@ -54,15 +56,15 @@ var _ http.Handler = (*ConstructionTester)(nil)
 // ConstructionTester coordinates the `check:construction` test.
 type ConstructionTester struct {
 	network          *types.NetworkIdentifier
-	database         storage.Database
+	database         database.Database
 	config           *configuration.Configuration
 	syncer           *statefulsyncer.StatefulSyncer
 	logger           *logger.Logger
 	onlineFetcher    *fetcher.Fetcher
-	broadcastStorage *storage.BroadcastStorage
-	blockStorage     *storage.BlockStorage
-	jobStorage       *storage.JobStorage
-	counterStorage   *storage.CounterStorage
+	broadcastStorage *modules.BroadcastStorage
+	blockStorage     *modules.BlockStorage
+	jobStorage       *modules.JobStorage
+	counterStorage   *modules.CounterStorage
 	coordinator      *coordinator.Coordinator
 	cancel           context.CancelFunc
 	signalReceived   *bool
@@ -84,15 +86,18 @@ func InitializeConstruction(
 		log.Fatalf("%s: cannot create command path", err.Error())
 	}
 
-	opts := []storage.BadgerOption{}
+	opts := []database.BadgerOption{}
 	if config.CompressionDisabled {
-		opts = append(opts, storage.WithoutCompression())
+		opts = append(opts, database.WithoutCompression())
 	}
 	if config.MemoryLimitDisabled {
-		opts = append(opts, storage.WithCustomSettings(storage.PerformanceBadgerOptions(dataPath)))
+		opts = append(
+			opts,
+			database.WithCustomSettings(database.PerformanceBadgerOptions(dataPath)),
+		)
 	}
 
-	localStore, err := storage.NewBadgerStorage(ctx, dataPath, opts...)
+	localStore, err := database.NewBadgerDatabase(ctx, dataPath, opts...)
 	if err != nil {
 		log.Fatalf("%s: unable to initialize database", err.Error())
 	}
@@ -107,7 +112,7 @@ func InitializeConstruction(
 		log.Fatal("found balance exemptions but initial balance fetch disabled")
 	}
 
-	counterStorage := storage.NewCounterStorage(localStore)
+	counterStorage := modules.NewCounterStorage(localStore)
 	logger := logger.NewLogger(
 		dataPath,
 		false,
@@ -116,15 +121,16 @@ func InitializeConstruction(
 		false,
 	)
 
-	blockStorage := storage.NewBlockStorage(localStore)
-	keyStorage := storage.NewKeyStorage(localStore)
+	blockStorage := modules.NewBlockStorage(localStore)
+	keyStorage := modules.NewKeyStorage(localStore)
 	coinStorageHelper := processor.NewCoinStorageHelper(blockStorage)
-	coinStorage := storage.NewCoinStorage(localStore, coinStorageHelper, onlineFetcher.Asserter)
-	balanceStorage := storage.NewBalanceStorage(localStore)
+	coinStorage := modules.NewCoinStorage(localStore, coinStorageHelper, onlineFetcher.Asserter)
+	balanceStorage := modules.NewBalanceStorage(localStore)
 
 	balanceStorageHelper := processor.NewBalanceStorageHelper(
 		network,
 		onlineFetcher,
+		counterStorage,
 		false,
 		nil,
 		true,
@@ -135,13 +141,14 @@ func InitializeConstruction(
 	balanceStorageHandler := processor.NewBalanceStorageHandler(
 		logger,
 		nil,
+		counterStorage,
 		false,
 		nil,
 	)
 
 	balanceStorage.Initialize(balanceStorageHelper, balanceStorageHandler)
 
-	broadcastStorage := storage.NewBroadcastStorage(
+	broadcastStorage := modules.NewBroadcastStorage(
 		localStore,
 		config.Construction.StaleDepth,
 		config.Construction.BroadcastLimit,
@@ -209,7 +216,7 @@ func InitializeConstruction(
 		return nil, fmt.Errorf("%w: unable to set coin balances", err)
 	}
 
-	jobStorage := storage.NewJobStorage(localStore)
+	jobStorage := modules.NewJobStorage(localStore)
 	coordinatorHelper := processor.NewCoordinatorHelper(
 		offlineFetcher,
 		onlineFetcher,
@@ -255,10 +262,10 @@ func InitializeConstruction(
 		counterStorage,
 		logger,
 		cancel,
-		[]storage.BlockWorker{balanceStorage, coinStorage, broadcastStorage},
-		syncer.DefaultCacheSize,
-		config.MaxSyncConcurrency,
-		config.MaxReorgDepth,
+		[]modules.BlockWorker{counterStorage, balanceStorage, coinStorage, broadcastStorage},
+		statefulsyncer.WithCacheSize(syncer.DefaultCacheSize),
+		statefulsyncer.WithMaxConcurrency(config.MaxSyncConcurrency),
+		statefulsyncer.WithPastBlockLimit(config.MaxReorgDepth),
 	)
 
 	return &ConstructionTester{
@@ -360,7 +367,7 @@ func (t *ConstructionTester) StartSyncer(
 ) error {
 	startIndex := int64(-1)
 	_, err := t.blockStorage.GetHeadBlockIdentifier(ctx)
-	if errors.Is(err, storage.ErrHeadBlockNotFound) {
+	if errors.Is(err, storageErrs.ErrHeadBlockNotFound) {
 		// If no head block exists, ensure we are at tip before starting. Otherwise,
 		// we will unnecessarily sync tons of blocks before reaching any that matter.
 		startIndex, err = t.waitForTip(ctx)
diff --git a/pkg/tester/data.go b/pkg/tester/data.go
index c851d8f..1ecabc9 100644
--- a/pkg/tester/data.go
+++ b/pkg/tester/data.go
@@ -33,7 +33,9 @@ import (
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/reconciler"
 	"github.com/coinbase/rosetta-sdk-go/statefulsyncer"
-	"github.com/coinbase/rosetta-sdk-go/storage"
+	"github.com/coinbase/rosetta-sdk-go/storage/database"
+	storageErrs "github.com/coinbase/rosetta-sdk-go/storage/errors"
+	"github.com/coinbase/rosetta-sdk-go/storage/modules"
 	"github.com/coinbase/rosetta-sdk-go/syncer"
 	"github.com/coinbase/rosetta-sdk-go/types"
 	"github.com/coinbase/rosetta-sdk-go/utils"
@@ -71,14 +73,14 @@ var _ statefulsyncer.PruneHelper = (*DataTester)(nil)
 // DataTester coordinates the `check:data` test.
 type DataTester struct {
 	network                  *types.NetworkIdentifier
-	database                 storage.Database
+	database                 database.Database
 	config                   *configuration.Configuration
 	syncer                   *statefulsyncer.StatefulSyncer
 	reconciler               *reconciler.Reconciler
 	logger                   *logger.Logger
-	balanceStorage           *storage.BalanceStorage
-	blockStorage             *storage.BlockStorage
-	counterStorage           *storage.CounterStorage
+	balanceStorage           *modules.BalanceStorage
+	blockStorage             *modules.BlockStorage
+	counterStorage           *modules.CounterStorage
 	reconcilerHandler        *processor.ReconcilerHandler
 	fetcher                  *fetcher.Fetcher
 	signalReceived           *bool
@@ -148,15 +150,18 @@ func InitializeData(
 		log.Fatalf("%s: cannot create command path", err.Error())
 	}
 
-	opts := []storage.BadgerOption{}
+	opts := []database.BadgerOption{}
 	if config.CompressionDisabled {
-		opts = append(opts, storage.WithoutCompression())
+		opts = append(opts, database.WithoutCompression())
 	}
 	if config.MemoryLimitDisabled {
-		opts = append(opts, storage.WithCustomSettings(storage.PerformanceBadgerOptions(dataPath)))
+		opts = append(
+			opts,
+			database.WithCustomSettings(database.PerformanceBadgerOptions(dataPath)),
+		)
 	}
 
-	localStore, err := storage.NewBadgerStorage(ctx, dataPath, opts...)
+	localStore, err := database.NewBadgerDatabase(ctx, dataPath, opts...)
 	if err != nil {
 		log.Fatalf("%s: unable to initialize database", err.Error())
 	}
@@ -171,9 +176,9 @@ func InitializeData(
 		log.Fatalf("%s: unable to load interesting accounts", err.Error())
 	}
 
-	counterStorage := storage.NewCounterStorage(localStore)
-	blockStorage := storage.NewBlockStorage(localStore)
-	balanceStorage := storage.NewBalanceStorage(localStore)
+	counterStorage := modules.NewCounterStorage(localStore)
+	blockStorage := modules.NewBlockStorage(localStore)
+	balanceStorage := modules.NewBalanceStorage(localStore)
 
 	// Bootstrap balances, if provided. We need to do before initializing
 	// the reconciler otherwise we won't reconcile bootstrapped accounts
@@ -181,7 +186,7 @@ func InitializeData(
 	if len(config.Data.BootstrapBalances) > 0 {
 		_, err := blockStorage.GetHeadBlockIdentifier(ctx)
 		switch {
-		case err == storage.ErrHeadBlockNotFound:
+		case err == storageErrs.ErrHeadBlockNotFound:
 			err = balanceStorage.BootstrapBalances(
 				ctx,
 				config.Data.BootstrapBalances,
@@ -275,11 +280,12 @@ func InitializeData(
 		rOpts...,
 	)
 
-	blockWorkers := []storage.BlockWorker{}
+	blockWorkers := []modules.BlockWorker{counterStorage}
 	if !config.Data.BalanceTrackingDisabled {
 		balanceStorageHelper := processor.NewBalanceStorageHelper(
 			network,
 			fetcher,
+			counterStorage,
 			historicalBalanceEnabled,
 			exemptAccounts,
 			false,
@@ -290,6 +296,7 @@ func InitializeData(
 		balanceStorageHandler := processor.NewBalanceStorageHandler(
 			logger,
 			r,
+			counterStorage,
 			shouldReconcile(config),
 			interestingAccount,
 		)
@@ -301,11 +308,23 @@ func InitializeData(
 
 	if !config.Data.CoinTrackingDisabled {
 		coinStorageHelper := processor.NewCoinStorageHelper(blockStorage)
-		coinStorage := storage.NewCoinStorage(localStore, coinStorageHelper, fetcher.Asserter)
+		coinStorage := modules.NewCoinStorage(localStore, coinStorageHelper, fetcher.Asserter)
 
 		blockWorkers = append(blockWorkers, coinStorage)
 	}
 
+	statefulSyncerOptions := []statefulsyncer.Option{
+		statefulsyncer.WithCacheSize(syncer.DefaultCacheSize),
+		statefulsyncer.WithMaxConcurrency(config.MaxSyncConcurrency),
+		statefulsyncer.WithPastBlockLimit(config.MaxReorgDepth),
+	}
+	if config.Data.PruningFrequency != nil {
+		statefulSyncerOptions = append(
+			statefulSyncerOptions,
+			statefulsyncer.WithPruneSleepTime(*config.Data.PruningFrequency),
+		)
+	}
+
 	syncer := statefulsyncer.New(
 		ctx,
 		network,
@@ -315,9 +334,7 @@ func InitializeData(
 		logger,
 		cancel,
 		blockWorkers,
-		syncer.DefaultCacheSize,
-		config.MaxSyncConcurrency,
-		config.MaxReorgDepth,
+		statefulSyncerOptions...,
 	)
 
 	return &DataTester{
@@ -512,7 +529,7 @@ func (t *DataTester) EndReconciliationCoverage( // nolint:gocognit
 
 		case <-tc.C:
 			headBlock, err := t.blockStorage.GetBlock(ctx, nil)
-			if errors.Is(err, storage.ErrHeadBlockNotFound) {
+			if errors.Is(err, storageErrs.ErrHeadBlockNotFound) {
 				continue
 			}
 			if err != nil {
@@ -665,22 +682,22 @@ func (t *DataTester) WatchEndConditions(
 // CompleteReconciliations returns the sum of all failed, exempt, and successful
 // reconciliations.
 func (t *DataTester) CompleteReconciliations(ctx context.Context) (int64, error) {
-	activeReconciliations, err := t.counterStorage.Get(ctx, storage.ActiveReconciliationCounter)
+	activeReconciliations, err := t.counterStorage.Get(ctx, modules.ActiveReconciliationCounter)
 	if err != nil {
 		return -1, fmt.Errorf("%w: cannot get active reconciliations counter", err)
 	}
 
-	exemptReconciliations, err := t.counterStorage.Get(ctx, storage.ExemptReconciliationCounter)
+	exemptReconciliations, err := t.counterStorage.Get(ctx, modules.ExemptReconciliationCounter)
 	if err != nil {
 		return -1, fmt.Errorf("%w: cannot get exempt reconciliations counter", err)
 	}
 
-	failedReconciliations, err := t.counterStorage.Get(ctx, storage.FailedReconciliationCounter)
+	failedReconciliations, err := t.counterStorage.Get(ctx, modules.FailedReconciliationCounter)
 	if err != nil {
 		return -1, fmt.Errorf("%w: cannot get failed reconciliations counter", err)
 	}
 
-	skippedReconciliations, err := t.counterStorage.Get(ctx, storage.SkippedReconciliationsCounter)
+	skippedReconciliations, err := t.counterStorage.Get(ctx, modules.SkippedReconciliationsCounter)
 	if err != nil {
 		return -1, fmt.Errorf("%w: cannot get skipped reconciliations counter", err)
 	}
@@ -952,14 +969,14 @@ func (t *DataTester) recursiveOpSearch(
 	}
 	defer utils.RemoveTempDir(tmpDir)
 
-	localStore, err := storage.NewBadgerStorage(ctx, tmpDir)
+	localStore, err := database.NewBadgerDatabase(ctx, tmpDir)
 	if err != nil {
 		return nil, fmt.Errorf("%w: unable to initialize database", err)
 	}
 
-	counterStorage := storage.NewCounterStorage(localStore)
-	blockStorage := storage.NewBlockStorage(localStore)
-	balanceStorage := storage.NewBalanceStorage(localStore)
+	counterStorage := modules.NewCounterStorage(localStore)
+	blockStorage := modules.NewBlockStorage(localStore)
+	balanceStorage := modules.NewBalanceStorage(localStore)
 
 	logger := logger.NewLogger(
 		tmpDir,
@@ -1005,6 +1022,7 @@ func (t *DataTester) recursiveOpSearch(
 	balanceStorageHelper := processor.NewBalanceStorageHelper(
 		t.network,
 		t.fetcher,
+		counterStorage,
 		t.historicalBalanceEnabled,
 		nil,
 		false,
@@ -1015,6 +1033,7 @@ func (t *DataTester) recursiveOpSearch(
 	balanceStorageHandler := processor.NewBalanceStorageHandler(
 		logger,
 		r,
+		counterStorage,
 		true,
 		accountCurrency,
 	)
@@ -1029,10 +1048,10 @@ func (t *DataTester) recursiveOpSearch(
 		counterStorage,
 		logger,
 		cancel,
-		[]storage.BlockWorker{balanceStorage},
-		syncer.DefaultCacheSize,
-		t.config.MaxSyncConcurrency,
-		t.config.MaxReorgDepth,
+		[]modules.BlockWorker{balanceStorage},
+		statefulsyncer.WithCacheSize(syncer.DefaultCacheSize),
+		statefulsyncer.WithMaxConcurrency(t.config.MaxSyncConcurrency),
+		statefulsyncer.WithPastBlockLimit(t.config.MaxReorgDepth),
 	)
 
 	g, ctx := errgroup.WithContext(ctx)