Repository: coinbase/rosetta-sdk-go
Pull request #134 (2020-09-03 22:39:29)

[Parser] typed errors overhaul
Fixes # .

### Motivation
continuing from this PR: [https://github.com/coinbase/rosetta-sdk-go/pull/102](https://github.com/coinbase/rosetta-sdk-go/pull/102)
and this PR: [https://github.com/coinbase/rosetta-sdk-go/pull/129](https://github.com/coinbase/rosetta-sdk-go/pull/129)

we want to use named errors in the SDK to improve the debugging experience

this PR also includes some minor changes in /keys

### Solution
added errors.go and an ErrParser() function to check if an error was thrown by the parser

### Open questions
<!--
(optional) Any open questions or feedback on design desired?
-->



Patch:
diff --git a/Makefile b/Makefile
index eac6166..f5e5605 100644
--- a/Makefile
+++ b/Makefile
@@ -13,7 +13,7 @@ GOVERALLS_CMD=go run github.com/mattn/goveralls
 GOLINT_CMD=go run golang.org/x/lint/golint
 GO_PACKAGES=./asserter/... ./fetcher/... ./types/... ./client/... ./server/... \
 	./parser/... ./syncer/... ./reconciler/... ./keys/... \
-	./statefulsyncer/... ./storage/... ./utils/... ./constructor/...
+	./statefulsyncer/... ./storage/... ./utils/... ./constructor/... ./errors/...
 GO_FOLDERS=$(shell echo ${GO_PACKAGES} | sed -e "s/\.\///g" | sed -e "s/\/\.\.\.//g")
 TEST_SCRIPT=go test ${GO_PACKAGES}
 LINT_SETTINGS=golint,misspell,gocyclo,gocritic,whitespace,goconst,gocognit,bodyclose,unconvert,lll,unparam
diff --git a/asserter/construction.go b/asserter/construction.go
index 90eb75a..693045b 100644
--- a/asserter/construction.go
+++ b/asserter/construction.go
@@ -20,24 +20,6 @@ import (
 	"github.com/coinbase/rosetta-sdk-go/types"
 )
 
-// ConstructionPreprocessResponse returns an error if
-// the request public keys are not valid AccountIdentifiers.
-func ConstructionPreprocessResponse(
-	response *types.ConstructionPreprocessResponse,
-) error {
-	if response == nil {
-		return ErrConstructionPreprocessResponseIsNil
-	}
-
-	for _, accountIdentifier := range response.RequiredPublicKeys {
-		if err := AccountIdentifier(accountIdentifier); err != nil {
-			return err
-		}
-	}
-
-	return nil
-}
-
 // ConstructionMetadataResponse returns an error if
 // the metadata is not a JSON object.
 func ConstructionMetadataResponse(
@@ -205,7 +187,7 @@ func CurveType(
 	curve types.CurveType,
 ) error {
 	switch curve {
-	case types.Secp256k1, types.Secp256r1, types.Edwards25519, types.Tweedle:
+	case types.Secp256k1, types.Edwards25519:
 		return nil
 	default:
 		return fmt.Errorf("%w: %s", ErrCurveTypeNotSupported, curve)
@@ -286,7 +268,7 @@ func SignatureType(
 	signature types.SignatureType,
 ) error {
 	switch signature {
-	case types.Ecdsa, types.EcdsaRecovery, types.Ed25519, types.Schnorr1, types.SchnorrPoseidon:
+	case types.Ecdsa, types.EcdsaRecovery, types.Ed25519:
 		return nil
 	default:
 		return fmt.Errorf("%w: %s", ErrSignatureTypeNotSupported, signature)
diff --git a/asserter/errors.go b/asserter/errors.go
index 6b402d5..4bc7972 100644
--- a/asserter/errors.go
+++ b/asserter/errors.go
@@ -14,7 +14,11 @@
 
 package asserter
 
-import "errors"
+import (
+	"errors"
+
+	utils "github.com/coinbase/rosetta-sdk-go/errors"
+)
 
 // Named error types for Asserter errors
 var (
@@ -146,9 +150,6 @@ var (
 	/* CONSTRUCTION ERRORS */
 	/////////////////////////
 
-	ErrConstructionPreprocessResponseIsNil = errors.New(
-		"ConstructionPreprocessResponse cannot be nil",
-	)
 	ErrConstructionMetadataResponseIsNil = errors.New(
 		"ConstructionMetadataResponse cannot be nil",
 	)
@@ -202,7 +203,6 @@ var (
 	ErrSignatureTypeNotSupported = errors.New("not a supported SignatureType")
 
 	ConstructionErrs = []error{
-		ErrConstructionPreprocessResponseIsNil,
 		ErrConstructionMetadataResponseIsNil,
 		ErrConstructionMetadataResponseMetadataMissing,
 		ErrTxIdentifierResponseIsNil,
@@ -379,19 +379,9 @@ func ErrAsserter(err error) (bool, string) {
 	}
 
 	for key, val := range assertErrs {
-		if findError(val, err) {
+		if utils.FindError(val, err) {
 			return true, key
 		}
 	}
 	return false, ""
 }
-
-// findError
-func findError(errorList []error, err error) bool {
-	for _, k := range errorList {
-		if errors.Is(err, k) {
-			return true
-		}
-	}
-	return false
-}
diff --git a/asserter/server.go b/asserter/server.go
index fc01eeb..788819b 100644
--- a/asserter/server.go
+++ b/asserter/server.go
@@ -163,12 +163,6 @@ func (a *Asserter) ConstructionMetadataRequest(request *types.ConstructionMetada
 		return ErrConstructionMetadataRequestOptionsIsNil
 	}
 
-	for _, publicKey := range request.PublicKeys {
-		if err := PublicKey(publicKey); err != nil {
-			return err
-		}
-	}
-
 	return nil
 }
 
@@ -321,12 +315,6 @@ func (a *Asserter) ConstructionPayloadsRequest(request *types.ConstructionPayloa
 		return err
 	}
 
-	for _, publicKey := range request.PublicKeys {
-		if err := PublicKey(publicKey); err != nil {
-			return err
-		}
-	}
-
 	return nil
 }
 
diff --git a/client/client.go b/client/client.go
index 9f938c7..561ebb7 100644
--- a/client/client.go
+++ b/client/client.go
@@ -36,7 +36,7 @@ var (
 	jsonCheck = regexp.MustCompile(`(?i:(?:application|text)/(?:vnd\.[^;]+\+)?json)`)
 )
 
-// APIClient manages communication with the Rosetta API v1.4.3
+// APIClient manages communication with the Rosetta API v1.4.2
 // In most cases there should be only one, shared, APIClient.
 type APIClient struct {
 	cfg    *Configuration
diff --git a/codegen.sh b/codegen.sh
index 4111b98..8b96f1e 100755
--- a/codegen.sh
+++ b/codegen.sh
@@ -53,7 +53,7 @@ done
 rm -rf tmp;
 
 # Download spec file from releases
-ROSETTA_SPEC_VERSION=v1.4.3
+ROSETTA_SPEC_VERSION=v1.4.2
 curl -L https://github.com/coinbase/rosetta-specifications/releases/download/${ROSETTA_SPEC_VERSION}/api.json -o api.json;
 
 # Generate client + types code
@@ -121,14 +121,10 @@ sed "${SED_IFLAG[@]}" 's/*CoinAction/CoinAction/g' client/* server/*;
 
 # Fix CurveTypes, SignatureTypes, and CoinActions
 sed "${SED_IFLAG[@]}" 's/SECP256K1/Secp256k1/g' client/* server/*;
-sed "${SED_IFLAG[@]}" 's/SECP256R1/Secp256r1/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/EDWARDS25519/Edwards25519/g' client/* server/*;
-sed "${SED_IFLAG[@]}" 's/TWEEDLE/Tweedle/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/ECDSA_RECOVERY/EcdsaRecovery/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/ECDSA/Ecdsa/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/ED25519/Ed25519/g' client/* server/*;
-sed "${SED_IFLAG[@]}" 's/SCHNORR_1/Schnorr1/g' client/* server/*;
-sed "${SED_IFLAG[@]}" 's/SCHNORR_POSEIDON/SchnorrPoseidon/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/CREATED/CoinCreated/g' client/* server/*;
 sed "${SED_IFLAG[@]}" 's/SPENT/CoinSpent/g' client/* server/*;
 
diff --git a/constructor/coordinator/coordinator.go b/constructor/coordinator/coordinator.go
index 4aa5bfa..a91af2f 100644
--- a/constructor/coordinator/coordinator.go
+++ b/constructor/coordinator/coordinator.go
@@ -190,10 +190,9 @@ func (c *Coordinator) findJob(
 // createTransaction constructs and signs a transaction with the provided intent.
 func (c *Coordinator) createTransaction(
 	ctx context.Context,
-	dbTx storage.DatabaseTransaction,
 	broadcast *job.Broadcast,
 ) (*types.TransactionIdentifier, string, error) {
-	metadataRequest, requiredPublicKeys, err := c.helper.Preprocess(
+	metadataRequest, err := c.helper.Preprocess(
 		ctx,
 		broadcast.Network,
 		broadcast.Intent,
@@ -203,25 +202,10 @@ func (c *Coordinator) createTransaction(
 		return nil, "", fmt.Errorf("%w: unable to preprocess", err)
 	}
 
-	publicKeys := make([]*types.PublicKey, len(requiredPublicKeys))
-	for i, accountIdentifier := range requiredPublicKeys {
-		keyPair, err := c.helper.GetKey(ctx, dbTx, accountIdentifier.Address)
-		if err != nil {
-			return nil, "", fmt.Errorf(
-				"%w: unable to find key for address %s",
-				err,
-				accountIdentifier.Address,
-			)
-		}
-
-		publicKeys[i] = keyPair.PublicKey
-	}
-
 	requiredMetadata, err := c.helper.Metadata(
 		ctx,
 		broadcast.Network,
 		metadataRequest,
-		publicKeys,
 	)
 	if err != nil {
 		return nil, "", fmt.Errorf("%w: unable to construct metadata", err)
@@ -232,7 +216,6 @@ func (c *Coordinator) createTransaction(
 		broadcast.Network,
 		broadcast.Intent,
 		requiredMetadata,
-		publicKeys,
 	)
 	if err != nil {
 		return nil, "", fmt.Errorf("%w: unable to construct payloads", err)
@@ -500,11 +483,7 @@ func (c *Coordinator) Process(
 		var transactionCreated *types.TransactionIdentifier
 		if broadcast != nil {
 			// Construct Transaction
-			transactionIdentifier, networkTransaction, err := c.createTransaction(
-				ctx,
-				dbTx,
-				broadcast,
-			)
+			transactionIdentifier, networkTransaction, err := c.createTransaction(ctx, broadcast)
 			if err != nil {
 				return fmt.Errorf("%w: unable to create transaction", err)
 			}
diff --git a/constructor/coordinator/types.go b/constructor/coordinator/types.go
index fa36caa..7916945 100644
--- a/constructor/coordinator/types.go
+++ b/constructor/coordinator/types.go
@@ -58,14 +58,6 @@ type Helper interface {
 		*keys.KeyPair,
 	) error
 
-	// GetKey is called to get the *types.KeyPair
-	// associated with an address.
-	GetKey(
-		context.Context,
-		storage.DatabaseTransaction,
-		string, // address
-	) (*keys.KeyPair, error)
-
 	// AllAddresses returns a slice of all known addresses.
 	AllAddresses(
 		context.Context,
@@ -127,7 +119,7 @@ type Helper interface {
 		*types.NetworkIdentifier,
 		[]*types.Operation,
 		map[string]interface{},
-	) (map[string]interface{}, []*types.AccountIdentifier, error)
+	) (map[string]interface{}, error)
 
 	// Metadata calls the /construction/metadata endpoint
 	// using the online node.
@@ -135,7 +127,6 @@ type Helper interface {
 		context.Context,
 		*types.NetworkIdentifier,
 		map[string]interface{},
-		[]*types.PublicKey,
 	) (map[string]interface{}, error)
 
 	// Payloads calls the /construction/payloads endpoint
@@ -145,7 +136,6 @@ type Helper interface {
 		*types.NetworkIdentifier,
 		[]*types.Operation,
 		map[string]interface{},
-		[]*types.PublicKey,
 	) (string, []*types.SigningPayload, error)
 
 	// Parse calls the /construction/parse endpoint
diff --git a/errors/utils.go b/errors/utils.go
new file mode 100644
index 0000000..6f9242c
--- /dev/null
+++ b/errors/utils.go
@@ -0,0 +1,28 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import "errors"
+
+// FindError is used by the Err functions in each package
+// to determine if a particular error was thrown by that component
+func FindError(errorList []error, err error) bool {
+	for _, k := range errorList {
+		if errors.Is(err, k) {
+			return true
+		}
+	}
+	return false
+}
diff --git a/fetcher/construction.go b/fetcher/construction.go
index 142f2ac..f5d3dc1 100644
--- a/fetcher/construction.go
+++ b/fetcher/construction.go
@@ -132,13 +132,11 @@ func (f *Fetcher) ConstructionMetadata(
 	ctx context.Context,
 	network *types.NetworkIdentifier,
 	options map[string]interface{},
-	publicKeys []*types.PublicKey,
 ) (map[string]interface{}, *Error) {
 	metadata, clientErr, err := f.rosettaClient.ConstructionAPI.ConstructionMetadata(ctx,
 		&types.ConstructionMetadataRequest{
 			NetworkIdentifier: network,
 			Options:           options,
-			PublicKeys:        publicKeys,
 		},
 	)
 	if err != nil {
@@ -211,14 +209,12 @@ func (f *Fetcher) ConstructionPayloads(
 	network *types.NetworkIdentifier,
 	operations []*types.Operation,
 	metadata map[string]interface{},
-	publicKeys []*types.PublicKey,
 ) (string, []*types.SigningPayload, *Error) {
 	response, clientErr, err := f.rosettaClient.ConstructionAPI.ConstructionPayloads(ctx,
 		&types.ConstructionPayloadsRequest{
 			NetworkIdentifier: network,
 			Operations:        operations,
 			Metadata:          metadata,
-			PublicKeys:        publicKeys,
 		},
 	)
 
@@ -252,7 +248,7 @@ func (f *Fetcher) ConstructionPreprocess(
 	network *types.NetworkIdentifier,
 	operations []*types.Operation,
 	metadata map[string]interface{},
-) (map[string]interface{}, []*types.AccountIdentifier, *Error) {
+) (map[string]interface{}, *Error) {
 	response, clientErr, err := f.rosettaClient.ConstructionAPI.ConstructionPreprocess(ctx,
 		&types.ConstructionPreprocessRequest{
 			NetworkIdentifier: network,
@@ -266,17 +262,13 @@ func (f *Fetcher) ConstructionPreprocess(
 			Err:       fmt.Errorf("%w: /construction/preprocess %s", ErrRequestFailed, err.Error()),
 			ClientErr: clientErr,
 		}
-		return nil, nil, fetcherErr
+		return nil, fetcherErr
 	}
 
-	if err := asserter.ConstructionPreprocessResponse(response); err != nil {
-		fetcherErr := &Error{
-			Err: fmt.Errorf("%w: /construction/preprocess %s", ErrAssertionFailed, err.Error()),
-		}
-		return nil, nil, fetcherErr
-	}
+	// We do not assert the response here because the only object
+	// in the response is optional and unstructured.
 
-	return response.Options, response.RequiredPublicKeys, nil
+	return response.Options, nil
 }
 
 // ConstructionSubmit returns the validated response
diff --git a/keys/errors.go b/keys/errors.go
index bd4da75..1f19725 100644
--- a/keys/errors.go
+++ b/keys/errors.go
@@ -14,7 +14,11 @@
 
 package keys
 
-import "errors"
+import (
+	"errors"
+
+	utils "github.com/coinbase/rosetta-sdk-go/errors"
+)
 
 // Named error types for Keys errors
 var (
@@ -24,10 +28,10 @@ var (
 	ErrSignUnsupportedPayloadSignatureType = errors.New(
 		"sign: unexpected payload.SignatureType while signing",
 	)
-	ErrSignUnsupportedSigType = errors.New(
+	ErrSignUnsupportedSignatureType = errors.New(
 		"sign: unexpected Signature type while signing",
 	)
-	ErrSignFailed = errors.New("sign: unable to sign. %w")
+	ErrSignFailed = errors.New("sign: unable to sign")
 
 	ErrVerifyUnsupportedPayloadSignatureType = errors.New(
 		"verify: unexpected payload.SignatureType while verifying",
@@ -37,3 +41,20 @@ var (
 	)
 	ErrVerifyFailed = errors.New("verify: verify returned false")
 )
+
+// ErrKeys takes an error as an argument and returns
+// whether or not the error is one thrown by the keys package
+func ErrKeys(err error) bool {
+	keyErrors := []error{
+		ErrPrivKeyUndecodable,
+		ErrPrivKeyLengthInvalid,
+		ErrSignUnsupportedPayloadSignatureType,
+		ErrSignUnsupportedSignatureType,
+		ErrSignFailed,
+		ErrVerifyUnsupportedPayloadSignatureType,
+		ErrVerifyUnsupportedSignatureType,
+		ErrVerifyFailed,
+	}
+
+	return utils.FindError(keyErrors, err)
+}
diff --git a/keys/signer_edwards25519.go b/keys/signer_edwards25519.go
index 8547983..854387c 100644
--- a/keys/signer_edwards25519.go
+++ b/keys/signer_edwards25519.go
@@ -56,7 +56,7 @@ func (s *SignerEdwards25519) Sign(
 	if sigType != types.Ed25519 {
 		return nil, fmt.Errorf(
 			"%w: expected %v but got %v",
-			ErrSignUnsupportedSigType,
+			ErrSignUnsupportedSignatureType,
 			types.Ed25519,
 			sigType,
 		)
diff --git a/keys/signer_secp256k1.go b/keys/signer_secp256k1.go
index caaf441..703c3d4 100644
--- a/keys/signer_secp256k1.go
+++ b/keys/signer_secp256k1.go
@@ -62,16 +62,16 @@ func (s *SignerSecp256k1) Sign(
 	case types.EcdsaRecovery:
 		sig, err = secp256k1.Sign(payload.Bytes, privKeyBytes)
 		if err != nil {
-			return nil, fmt.Errorf(ErrSignFailed.Error(), err)
+			return nil, fmt.Errorf("%w: %s", ErrSignFailed, err.Error())
 		}
 	case types.Ecdsa:
 		sig, err = secp256k1.Sign(payload.Bytes, privKeyBytes)
 		if err != nil {
-			return nil, fmt.Errorf(ErrSignFailed.Error(), err)
+			return nil, fmt.Errorf("%w: %s", ErrSignFailed, err.Error())
 		}
 		sig = sig[:EcdsaSignatureLen]
 	default:
-		return nil, fmt.Errorf(ErrSignUnsupportedSigType.Error(), err)
+		return nil, fmt.Errorf("%w: %v", ErrSignUnsupportedSignatureType, err)
 	}
 
 	return &types.Signature{
diff --git a/mocks/constructor/coordinator/helper.go b/mocks/constructor/coordinator/helper.go
index 7bc431b..d02d868 100644
--- a/mocks/constructor/coordinator/helper.go
+++ b/mocks/constructor/coordinator/helper.go
@@ -183,29 +183,6 @@ func (_m *Helper) Derive(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2
 	return r0, r1, r2
 }
 
-// GetKey provides a mock function with given fields: _a0, _a1, _a2
-func (_m *Helper) GetKey(_a0 context.Context, _a1 storage.DatabaseTransaction, _a2 string) (*keys.KeyPair, error) {
-	ret := _m.Called(_a0, _a1, _a2)
-
-	var r0 *keys.KeyPair
-	if rf, ok := ret.Get(0).(func(context.Context, storage.DatabaseTransaction, string) *keys.KeyPair); ok {
-		r0 = rf(_a0, _a1, _a2)
-	} else {
-		if ret.Get(0) != nil {
-			r0 = ret.Get(0).(*keys.KeyPair)
-		}
-	}
-
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, storage.DatabaseTransaction, string) error); ok {
-		r1 = rf(_a0, _a1, _a2)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
-}
-
 // 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)
@@ -266,13 +243,13 @@ func (_m *Helper) LockedAddresses(_a0 context.Context, _a1 storage.DatabaseTrans
 	return r0, r1
 }
 
-// Metadata provides a mock function with given fields: _a0, _a1, _a2, _a3
-func (_m *Helper) Metadata(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 map[string]interface{}, _a3 []*types.PublicKey) (map[string]interface{}, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3)
+// 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{}, []*types.PublicKey) map[string]interface{}); ok {
-		r0 = rf(_a0, _a1, _a2, _a3)
+	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{})
@@ -280,8 +257,8 @@ func (_m *Helper) Metadata(_a0 context.Context, _a1 *types.NetworkIdentifier, _a
 	}
 
 	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, map[string]interface{}, []*types.PublicKey) error); ok {
-		r1 = rf(_a0, _a1, _a2, _a3)
+	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)
 	}
@@ -330,20 +307,20 @@ func (_m *Helper) Parse(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 b
 	return r0, r1, r2, r3
 }
 
-// Payloads provides a mock function with given fields: _a0, _a1, _a2, _a3, _a4
-func (_m *Helper) Payloads(_a0 context.Context, _a1 *types.NetworkIdentifier, _a2 []*types.Operation, _a3 map[string]interface{}, _a4 []*types.PublicKey) (string, []*types.SigningPayload, error) {
-	ret := _m.Called(_a0, _a1, _a2, _a3, _a4)
+// 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{}, []*types.PublicKey) string); ok {
-		r0 = rf(_a0, _a1, _a2, _a3, _a4)
+	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.PublicKey) []*types.SigningPayload); ok {
-		r1 = rf(_a0, _a1, _a2, _a3, _a4)
+	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)
@@ -351,8 +328,8 @@ func (_m *Helper) Payloads(_a0 context.Context, _a1 *types.NetworkIdentifier, _a
 	}
 
 	var r2 error
-	if rf, ok := ret.Get(2).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}, []*types.PublicKey) error); ok {
-		r2 = rf(_a0, _a1, _a2, _a3, _a4)
+	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)
 	}
@@ -361,7 +338,7 @@ func (_m *Helper) Payloads(_a0 context.Context, _a1 *types.NetworkIdentifier, _a
 }
 
 // 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{}, []*types.AccountIdentifier, error) {
+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{}
@@ -373,23 +350,14 @@ func (_m *Helper) Preprocess(_a0 context.Context, _a1 *types.NetworkIdentifier,
 		}
 	}
 
-	var r1 []*types.AccountIdentifier
-	if rf, ok := ret.Get(1).(func(context.Context, *types.NetworkIdentifier, []*types.Operation, map[string]interface{}) []*types.AccountIdentifier); ok {
+	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 {
-		if ret.Get(1) != nil {
-			r1 = ret.Get(1).([]*types.AccountIdentifier)
-		}
-	}
-
-	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)
+		r1 = ret.Error(1)
 	}
 
-	return r0, r1, r2
+	return r0, r1
 }
 
 // Sign provides a mock function with given fields: _a0, _a1
diff --git a/parser/errors.go b/parser/errors.go
new file mode 100644
index 0000000..1d9023b
--- /dev/null
+++ b/parser/errors.go
@@ -0,0 +1,131 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package parser
+
+import (
+	"errors"
+
+	utils "github.com/coinbase/rosetta-sdk-go/errors"
+)
+
+// Named error types for Parser errors
+var (
+	///////////////////
+	/* INTENT ERRORS */
+	///////////////////
+	ErrExpectedOperationAccountMismatch = errors.New(
+		"intended account did not match observed account",
+	)
+	ErrExpectedOperationAmountMismatch = errors.New(
+		"intended amount did not match observed amount",
+	)
+	ErrExpectedOperationTypeMismatch = errors.New(
+		"intended type did not match observed type",
+	)
+	ErrExpectedOperationsExtraOperation = errors.New("found extra operation")
+	ErrExpectedSignerDuplicateSigner    = errors.New("found duplicate signer")
+	ErrExpectedSignerUnexpectedSigner   = errors.New("found unexpected signers")
+
+	IntentErrs = []error{
+		ErrExpectedOperationAccountMismatch,
+		ErrExpectedOperationAmountMismatch,
+		ErrExpectedOperationTypeMismatch,
+		ErrExpectedOperationsExtraOperation,
+		ErrExpectedSignerDuplicateSigner,
+		ErrExpectedSignerUnexpectedSigner,
+	}
+
+	/////////////////////////////
+	/* MATCH OPERATIONS ERRORS */
+	/////////////////////////////
+	ErrAccountMatchAccountMissing           = errors.New("account is missing")
+	ErrAccountMatchSubAccountMissing        = errors.New("SubAccountIdentifier.Address is missing")
+	ErrAccountMatchSubAccountPopulated      = errors.New("SubAccount is populated")
+	ErrAccountMatchUnexpectedSubAccountAddr = errors.New("unexpected SubAccountIdentifier.Address")
+
+	ErrMetadataMatchKeyNotFound      = errors.New("key is not present in metadata")
+	ErrMetadataMatchKeyValueMismatch = errors.New("unexpected value associated with key")
+
+	ErrAmountMatchAmountMissing      = errors.New("amount is missing")
+	ErrAmountMatchAmountPopulated    = errors.New("amount is populated")
+	ErrAmountMatchUnexpectedSign     = errors.New("unexpected amount sign")
+	ErrAmountMatchUnexpectedCurrency = errors.New("unexpected currency")
+
+	ErrCoinActionMatchCoinChangeIsNil      = errors.New("coin change is nil")
+	ErrCoinActionMatchUnexpectedCoinAction = errors.New("unexpected coin action")
+
+	ErrEqualAmountsNoOperations = errors.New("cannot check equality of 0 operations")
+	ErrEqualAmountsNotEqual     = errors.New("amounts are not equal")
+
+	ErrOppositeAmountsSameSign       = errors.New("operations have the same sign")
+	ErrOppositeAmountsAbsValMismatch = errors.New("operation absolute values are not equal")
+
+	ErrEqualAddressesTooFewOperations = errors.New("cannot check equality of <= 1 operations")
+	ErrEqualAddressesAccountIsNil     = errors.New("account is nil")
+	ErrEqualAddressesAddrMismatch     = errors.New("addresses do not match")
+
+	ErrMatchIndexValidIndexOutOfRange = errors.New("match index out of range")
+	ErrMatchIndexValidIndexIsNil      = errors.New("match index is nil")
+
+	ErrMatchOperationsNoOperations          = errors.New("unable to match anything to 0 operations")
+	ErrMatchOperationsDescriptionsMissing   = errors.New("no descriptions to match")
+	ErrMatchOperationsMatchNotFound         = errors.New("unable to find match for operation")
+	ErrMatchOperationsDescriptionNotMatched = errors.New("could not find match for description")
+
+	MatchOpsErrs = []error{
+		ErrAccountMatchAccountMissing,
+		ErrAccountMatchSubAccountMissing,
+		ErrAccountMatchSubAccountPopulated,
+		ErrAccountMatchUnexpectedSubAccountAddr,
+		ErrMetadataMatchKeyNotFound,
+		ErrMetadataMatchKeyValueMismatch,
+		ErrAmountMatchAmountMissing,
+		ErrAmountMatchAmountPopulated,
+		ErrAmountMatchUnexpectedSign,
+		ErrAmountMatchUnexpectedCurrency,
+		ErrCoinActionMatchCoinChangeIsNil,
+		ErrCoinActionMatchUnexpectedCoinAction,
+		ErrEqualAmountsNoOperations,
+		ErrEqualAmountsNotEqual,
+		ErrOppositeAmountsSameSign,
+		ErrOppositeAmountsAbsValMismatch,
+		ErrEqualAddressesTooFewOperations,
+		ErrEqualAddressesAccountIsNil,
+		ErrEqualAddressesAddrMismatch,
+		ErrMatchIndexValidIndexOutOfRange,
+		ErrMatchIndexValidIndexIsNil,
+		ErrMatchOperationsNoOperations,
+		ErrMatchOperationsDescriptionsMissing,
+		ErrMatchOperationsMatchNotFound,
+		ErrMatchOperationsDescriptionNotMatched,
+	}
+)
+
+// ErrParser takes an error as an argument and returns
+// whether or not the error is one thrown by the asserter
+// along with the specific source of the error
+func ErrParser(err error) (bool, string) {
+	parserErrs := map[string][]error{
+		"intent error":           IntentErrs,
+		"match operations error": MatchOpsErrs,
+	}
+
+	for key, val := range parserErrs {
+		if utils.FindError(val, err) {
+			return true, key
+		}
+	}
+	return false, ""
+}
diff --git a/parser/intent.go b/parser/intent.go
index f07ca30..0b1d308 100644
--- a/parser/intent.go
+++ b/parser/intent.go
@@ -29,7 +29,8 @@ import (
 func ExpectedOperation(intent *types.Operation, observed *types.Operation) error {
 	if types.Hash(intent.Account) != types.Hash(observed.Account) {
 		return fmt.Errorf(
-			"intended account %s did not match observed account %s",
+			"%w: expected %s but got %s",
+			ErrExpectedOperationAccountMismatch,
 			types.PrettyPrintStruct(intent.Account),
 			types.PrettyPrintStruct(observed.Account),
 		)
@@ -37,7 +38,8 @@ func ExpectedOperation(intent *types.Operation, observed *types.Operation) error
 
 	if types.Hash(intent.Amount) != types.Hash(observed.Amount) {
 		return fmt.Errorf(
-			"intended amount %s did not match observed amount %s",
+			"%w: expected %s but got %s",
+			ErrExpectedOperationAmountMismatch,
 			types.PrettyPrintStruct(intent.Amount),
 			types.PrettyPrintStruct(observed.Amount),
 		)
@@ -45,7 +47,8 @@ func ExpectedOperation(intent *types.Operation, observed *types.Operation) error
 
 	if intent.Type != observed.Type {
 		return fmt.Errorf(
-			"intended type %s did not match observed type %s",
+			"%w: expected %s but got %s",
+			ErrExpectedOperationTypeMismatch,
 			intent.Type,
 			observed.Type,
 		)
@@ -103,7 +106,8 @@ func (p *Parser) ExpectedOperations(
 
 		if !foundMatch && errExtra {
 			return fmt.Errorf(
-				"found extra operation %s",
+				"%w: %s",
+				ErrExpectedOperationsExtraOperation,
 				types.PrettyPrintStruct(obs),
 			)
 		}
@@ -118,7 +122,7 @@ func (p *Parser) ExpectedOperations(
 
 	if len(missingIntent) > 0 {
 		errString := fmt.Sprintf(
-			"could intent match for %v",
+			"could not intent match %v",
 			missingIntent,
 		)
 
@@ -147,7 +151,7 @@ func ExpectedSigners(intent []*types.SigningPayload, observed []string) error {
 	}
 
 	if err := asserter.StringArray("observed signers", observed); err != nil {
-		return fmt.Errorf("%w: found duplicate signer", err)
+		return fmt.Errorf("%w: %s", ErrExpectedSignerDuplicateSigner, err.Error())
 	}
 
 	// Could exist here if len(intent) != len(observed) but
@@ -166,7 +170,8 @@ func ExpectedSigners(intent []*types.SigningPayload, observed []string) error {
 	for k := range intendedSigners {
 		if _, exists := seenSigners[k]; !exists {
 			return fmt.Errorf(
-				"could not find match for intended signer %s",
+				"%w: %s",
+				ErrExpectedSignerDuplicateSigner,
 				k,
 			)
 		}
@@ -174,7 +179,8 @@ func ExpectedSigners(intent []*types.SigningPayload, observed []string) error {
 
 	if len(unmatched) != 0 {
 		return fmt.Errorf(
-			"found unexpected signers: %s",
+			"%w: %s",
+			ErrExpectedSignerUnexpectedSigner,
 			types.PrettyPrintStruct(unmatched),
 		)
 	}
diff --git a/parser/match_operations.go b/parser/match_operations.go
index 74aa29a..21bd313 100644
--- a/parser/match_operations.go
+++ b/parser/match_operations.go
@@ -15,7 +15,6 @@
 package parser
 
 import (
-	"errors"
 	"fmt"
 	"math/big"
 	"reflect"
@@ -160,11 +159,16 @@ func metadataMatch(reqs []*MetadataDescription, metadata map[string]interface{})
 	for _, req := range reqs {
 		val, ok := metadata[req.Key]
 		if !ok {
-			return fmt.Errorf("%s is not present in metadata", req.Key)
+			return fmt.Errorf("%w: %s", ErrMetadataMatchKeyNotFound, req.Key)
 		}
 
 		if reflect.TypeOf(val).Kind() != req.ValueKind {
-			return fmt.Errorf("%s value is not %s", req.Key, req.ValueKind)
+			return fmt.Errorf(
+				"%w: value of %s is not of type %s",
+				ErrMetadataMatchKeyValueMismatch,
+				req.Key,
+				req.ValueKind,
+			)
 		}
 	}
 
@@ -180,7 +184,7 @@ func accountMatch(req *AccountDescription, account *types.AccountIdentifier) err
 
 	if account == nil {
 		if req.Exists {
-			return errors.New("account is missing")
+			return ErrAccountMatchAccountMissing
 		}
 
 		return nil
@@ -188,22 +192,23 @@ func accountMatch(req *AccountDescription, account *types.AccountIdentifier) err
 
 	if account.SubAccount == nil {
 		if req.SubAccountExists {
-			return errors.New("SubAccountIdentifier.Address is missing")
+			return ErrAccountMatchSubAccountMissing
 		}
 
 		return nil
 	}
 
 	if !req.SubAccountExists {
-		return errors.New("SubAccount is populated")
+		return ErrAccountMatchSubAccountPopulated
 	}
 
 	// Optionally can require a certain subaccount address
 	if len(req.SubAccountAddress) > 0 && account.SubAccount.Address != req.SubAccountAddress {
 		return fmt.Errorf(
-			"SubAccountIdentifier.Address is %s not %s",
-			account.SubAccount.Address,
+			"%w: expected %s but got %s",
+			ErrAccountMatchUnexpectedSubAccountAddr,
 			req.SubAccountAddress,
+			account.SubAccount.Address,
 		)
 	}
 
@@ -223,18 +228,18 @@ func amountMatch(req *AmountDescription, amount *types.Amount) error {
 
 	if amount == nil {
 		if req.Exists {
-			return errors.New("amount is missing")
+			return ErrAmountMatchAmountMissing
 		}
 
 		return nil
 	}
 
 	if !req.Exists {
-		return errors.New("amount is populated")
+		return ErrAmountMatchAmountPopulated
 	}
 
 	if !req.Sign.Match(amount) {
-		return fmt.Errorf("amount sign was not %s", req.Sign.String())
+		return fmt.Errorf("%w: expected %s", ErrAmountMatchUnexpectedSign, req.Sign.String())
 	}
 
 	// If no currency is provided, anything is ok.
@@ -243,7 +248,12 @@ func amountMatch(req *AmountDescription, amount *types.Amount) error {
 	}
 
 	if amount.Currency == nil || types.Hash(amount.Currency) != types.Hash(req.Currency) {
-		return fmt.Errorf("currency %+v is not %+v", amount.Currency, req.Currency)
+		return fmt.Errorf(
+			"%w: expected %+v but got %+v",
+			ErrAmountMatchUnexpectedCurrency,
+			req.Currency,
+			amount.Currency,
+		)
 	}
 
 	return nil
@@ -255,14 +265,15 @@ func coinActionMatch(requiredAction types.CoinAction, coinChange *types.CoinChan
 	}
 
 	if coinChange == nil {
-		return fmt.Errorf("coin change is nil but expected %s", requiredAction)
+		return fmt.Errorf("%w: expected %s", ErrCoinActionMatchCoinChangeIsNil, requiredAction)
 	}
 
 	if coinChange.CoinAction != requiredAction {
 		return fmt.Errorf(
-			"coin change action is %s but expected %s",
-			coinChange.CoinAction,
+			"%w: expected %s but got %s",
+			ErrCoinActionMatchUnexpectedCoinAction,
 			requiredAction,
+			coinChange.CoinAction,
 		)
 	}
 
@@ -331,7 +342,7 @@ func operationMatch(
 // equal amounts.
 func equalAmounts(ops []*types.Operation) error {
 	if len(ops) == 0 {
-		return errors.New("cannot check equality of 0 operations")
+		return ErrEqualAmountsNoOperations
 	}
 
 	val, err := types.AmountValue(ops[0].Amount)
@@ -346,7 +357,12 @@ func equalAmounts(ops []*types.Operation) error {
 		}
 
 		if val.Cmp(otherVal) != 0 {
-			return fmt.Errorf("%s is not equal to %s", val.String(), otherVal.String())
+			return fmt.Errorf(
+				"%w: %s is not equal to %s",
+				ErrEqualAmountsNotEqual,
+				val.String(),
+				otherVal.String(),
+			)
 		}
 	}
 
@@ -367,11 +383,16 @@ func oppositeAmounts(a *types.Operation, b *types.Operation) error {
 	}
 
 	if aVal.Sign() == bVal.Sign() {
-		return fmt.Errorf("%s and %s have the same sign", aVal.String(), bVal.String())
+		return fmt.Errorf("%w: %s and %s", ErrOppositeAmountsSameSign, aVal.String(), bVal.String())
 	}
 
 	if aVal.CmpAbs(bVal) != 0 {
-		return fmt.Errorf("|%s| and |%s| are not equal", aVal.String(), bVal.String())
+		return fmt.Errorf(
+			"%w: %s and %s",
+			ErrOppositeAmountsAbsValMismatch,
+			aVal.String(),
+			bVal.String(),
+		)
 	}
 
 	return nil
@@ -381,14 +402,14 @@ func oppositeAmounts(a *types.Operation, b *types.Operation) error {
 // equal addresses.
 func equalAddresses(ops []*types.Operation) error {
 	if len(ops) <= 1 {
-		return fmt.Errorf("cannot check equality of %d operations", len(ops))
+		return fmt.Errorf("%w: got %d operations", ErrEqualAddressesTooFewOperations, len(ops))
 	}
 
 	base := ""
 
 	for _, op := range ops {
 		if op.Account == nil {
-			return fmt.Errorf("account is nil")
+			return ErrEqualAddressesAccountIsNil
 		}
 
 		if len(base) == 0 {
@@ -397,7 +418,12 @@ func equalAddresses(ops []*types.Operation) error {
 		}
 
 		if base != op.Account.Address {
-			return fmt.Errorf("%s is not equal to %s", base, op.Account.Address)
+			return fmt.Errorf(
+				"%w: %s is not equal to %s",
+				ErrEqualAddressesAddrMismatch,
+				base,
+				op.Account.Address,
+			)
 		}
 	}
 
@@ -407,13 +433,15 @@ func equalAddresses(ops []*types.Operation) error {
 func matchIndexValid(matches []*Match, index int) error {
 	if index >= len(matches) {
 		return fmt.Errorf(
-			"match index %d out of range",
+			"%w: at index %d",
+			ErrMatchIndexValidIndexOutOfRange,
 			index,
 		)
 	}
 	if matches[index] == nil {
 		return fmt.Errorf(
-			"match index %d is nil",
+			"%w: at index %d",
+			ErrMatchIndexValidIndexIsNil,
 			index,
 		)
 	}
@@ -532,11 +560,11 @@ func MatchOperations(
 	operations []*types.Operation,
 ) ([]*Match, error) {
 	if len(operations) == 0 {
-		return nil, errors.New("unable to match anything to 0 operations")
+		return nil, ErrMatchOperationsNoOperations
 	}
 
 	if len(descriptions.OperationDescriptions) == 0 {
-		return nil, errors.New("no descriptions to match")
+		return nil, ErrMatchOperationsDescriptionsMissing
 	}
 
 	operationDescriptions := descriptions.OperationDescriptions
@@ -547,7 +575,8 @@ func MatchOperations(
 		matchFound := operationMatch(op, operationDescriptions, matches)
 		if !matchFound && descriptions.ErrUnmatched {
 			return nil, fmt.Errorf(
-				"unable to find match for operation at index %d",
+				"%w: at index %d",
+				ErrMatchOperationsMatchNotFound,
 				i,
 			)
 		}
@@ -556,7 +585,7 @@ func MatchOperations(
 	// Error if any *OperationDescription is not matched
 	for i := 0; i < len(matches); i++ {
 		if matches[i] == nil && !descriptions.OperationDescriptions[i].Optional {
-			return nil, fmt.Errorf("could not find match for description %d", i)
+			return nil, fmt.Errorf("%w: %d", ErrMatchOperationsDescriptionNotMatched, i)
 		}
 	}
 
diff --git a/storage/badger_storage.go b/storage/badger_storage.go
index e6ca883..dc21058 100644
--- a/storage/badger_storage.go
+++ b/storage/badger_storage.go
@@ -70,41 +70,30 @@ type BadgerStorage struct {
 	writer sync.Mutex
 }
 
-func defaultBadgerOptions(dir string) badger.Options {
-	opts := badger.DefaultOptions(dir)
-	opts.Logger = nil
-
-	// LoadBloomsOnOpen=false will improve the db startup speed
-	opts.LoadBloomsOnOpen = false
-
-	// To allow writes at a faster speed, we create a new memtable as soon as
-	// an existing memtable is filled up. This option determines how many
-	// memtables should be kept in memory.
-	opts.NumMemtables = 1
-
-	// Don't keep multiple memtables in memory. With larger
-	// memtable size, this explodes memory usage.
-	opts.NumLevelZeroTables = 1
-	opts.NumLevelZeroTablesStall = 2
-
-	return opts
-}
-
 // lowMemoryOptions returns a set of BadgerDB configuration
 // options that significantly reduce memory usage.
 //
 // Inspired by: https://github.com/dgraph-io/badger/issues/1304
 func lowMemoryOptions(dir string) badger.Options {
-	opts := defaultBadgerOptions(dir)
+	opts := badger.DefaultOptions(dir)
+	opts.Logger = nil
 
 	// Don't load tables into memory.
 	opts.TableLoadingMode = options.FileIO
 	opts.ValueLogLoadingMode = options.FileIO
 
+	// To allow writes at a faster speed, we create a new memtable as soon as
+	// an existing memtable is filled up. This option determines how many
+	// memtables should be kept in memory.
+	opts.NumMemtables = 1
+
 	// This option will have a significant effect the memory. If the level is kept
 	// in-memory, read are faster but the tables will be kept in memory.
 	opts.KeepL0InMemory = false
 
+	// LoadBloomsOnOpen=false will improve the db startup speed
+	opts.LoadBloomsOnOpen = false
+
 	// Bloom filters will be kept in memory if the following option is not set. Each
 	// bloom filter takes up 5 MB of memory. A smaller bf cache would mean that
 	// bloom filters will be evicted quickly from the cache and they will be read from
@@ -114,6 +103,11 @@ func lowMemoryOptions(dir string) badger.Options {
 	// Don't cache blocks in memory. All reads should go to disk.
 	opts.MaxCacheSize = DefaultCacheSize
 
+	// Don't keep multiple memtables in memory.
+	opts.NumLevelZeroTables = 1
+	opts.NumLevelZeroTablesStall = 2
+	opts.NumMemtables = 1
+
 	// Limit ValueLogFileSize as log files
 	// must be read into memory during compaction.
 	opts.ValueLogFileSize = DefaultValueLogFileSize
@@ -125,7 +119,8 @@ func lowMemoryOptions(dir string) badger.Options {
 // options that don't attempt to reduce memory usage (can
 // improve performance).
 func performanceOptions(dir string) badger.Options {
-	opts := defaultBadgerOptions(dir)
+	opts := badger.DefaultOptions(dir)
+	opts.Logger = nil
 	opts.MaxTableSize = PerformanceMaxTableSize
 
 	return opts
diff --git a/storage/key_storage.go b/storage/key_storage.go
index 6b81508..a002028 100644
--- a/storage/key_storage.go
+++ b/storage/key_storage.go
@@ -153,13 +153,12 @@ func (k *KeyStorage) Store(
 	return nil
 }
 
-// GetTransactional returns a *keys.KeyPair for an address in a DatabaseTransaction, if it exists.
-func (k *KeyStorage) GetTransactional(
-	ctx context.Context,
-	dbTx DatabaseTransaction,
-	address string,
-) (*keys.KeyPair, error) {
-	exists, rawKey, err := dbTx.Get(ctx, getAddressKey(address))
+// Get returns a *keys.KeyPair for an address, if it exists.
+func (k *KeyStorage) Get(ctx context.Context, address string) (*keys.KeyPair, error) {
+	transaction := k.db.NewDatabaseTransaction(ctx, false)
+	defer transaction.Discard(ctx)
+
+	exists, rawKey, err := transaction.Get(ctx, getAddressKey(address))
 	if err != nil {
 		return nil, fmt.Errorf("%w: unable to get address %s", err, address)
 	}
@@ -176,14 +175,6 @@ func (k *KeyStorage) GetTransactional(
 	return key.KeyPair, nil
 }
 
-// Get returns a *keys.KeyPair for an address, if it exists.
-func (k *KeyStorage) Get(ctx context.Context, address string) (*keys.KeyPair, error) {
-	transaction := k.db.NewDatabaseTransaction(ctx, false)
-	defer transaction.Discard(ctx)
-
-	return k.GetTransactional(ctx, transaction, address)
-}
-
 // GetAllAddressesTransactional returns all addresses in key storage.
 func (k *KeyStorage) GetAllAddressesTransactional(
 	ctx context.Context,
diff --git a/syncer/syncer.go b/syncer/syncer.go
index cff209c..43b09ac 100644
--- a/syncer/syncer.go
+++ b/syncer/syncer.go
@@ -58,17 +58,6 @@ var (
 	// a block that is out of order. This typically
 	// means the Helper has a bug.
 	ErrOutOfOrder = errors.New("out of order")
-
-	// ErrOrphanHead is returned by the Helper when
-	// the current head should be orphaned. In some
-	// cases, it may not be possible to populate a block
-	// if the head of the canonical chain is not yet synced.
-	ErrOrphanHead = errors.New("orphan head")
-
-	// ErrBlockResultNil is returned by the syncer
-	// when attempting to process a block and the block
-	// result is nil.
-	ErrBlockResultNil = errors.New("block result is nil")
 )
 
 // Handler is called at various times during the sync cycle
@@ -207,30 +196,14 @@ func (s *Syncer) nextSyncableRange(
 	return endIndex, false, nil
 }
 
-func (s *Syncer) attemptOrphan(
-	lastBlock *types.BlockIdentifier,
-) (bool, *types.BlockIdentifier, error) {
-	if types.Hash(s.genesisBlock) == types.Hash(lastBlock) {
-		return false, nil, ErrCannotRemoveGenesisBlock
-	}
-
-	return true, lastBlock, nil
-}
-
 func (s *Syncer) checkRemove(
-	br *blockResult,
+	block *types.Block,
 ) (bool, *types.BlockIdentifier, error) {
 	if len(s.pastBlocks) == 0 {
 		return false, nil, nil
 	}
 
-	lastBlock := s.pastBlocks[len(s.pastBlocks)-1]
-	if br.orphanHead {
-		return s.attemptOrphan(lastBlock)
-	}
-
 	// Ensure processing correct index
-	block := br.block
 	if block.BlockIdentifier.Index != s.nextIndex {
 		return false, nil, fmt.Errorf(
 			"%w: got block %d instead of %d",
@@ -241,8 +214,13 @@ func (s *Syncer) checkRemove(
 	}
 
 	// Check if block parent is head
+	lastBlock := s.pastBlocks[len(s.pastBlocks)-1]
 	if types.Hash(block.ParentBlockIdentifier) != types.Hash(lastBlock) {
-		return s.attemptOrphan(lastBlock)
+		if types.Hash(s.genesisBlock) == types.Hash(lastBlock) {
+			return false, nil, ErrCannotRemoveGenesisBlock
+		}
+
+		return true, lastBlock, nil
 	}
 
 	return false, lastBlock, nil
@@ -250,19 +228,16 @@ func (s *Syncer) checkRemove(
 
 func (s *Syncer) processBlock(
 	ctx context.Context,
-	br *blockResult,
+	block *types.Block,
 ) error {
-	if br == nil {
-		return ErrBlockResultNil
-	}
 	// If the block is omitted, increase
 	// index and return.
-	if br.block == nil && !br.orphanHead {
+	if block == nil {
 		s.nextIndex++
 		return nil
 	}
 
-	shouldRemove, lastBlock, err := s.checkRemove(br)
+	shouldRemove, lastBlock, err := s.checkRemove(block)
 	if err != nil {
 		return err
 	}
@@ -277,7 +252,6 @@ func (s *Syncer) processBlock(
 		return nil
 	}
 
-	block := br.block
 	err = s.handler.BlockAdded(ctx, block)
 	if err != nil {
 		return err
@@ -320,32 +294,6 @@ func (s *Syncer) addBlockIndicies(
 	return nil
 }
 
-func (s *Syncer) fetchBlockResult(
-	ctx context.Context,
-	network *types.NetworkIdentifier,
-	index int64,
-) (*blockResult, error) {
-	block, err := s.helper.Block(
-		ctx,
-		network,
-		&types.PartialBlockIdentifier{
-			Index: &index,
-		},
-	)
-
-	br := &blockResult{index: index}
-	switch {
-	case errors.Is(err, ErrOrphanHead):
-		br.orphanHead = true
-	case err != nil:
-		return nil, err
-	default:
-		br.block = block
-	}
-
-	return br, nil
-}
-
 // fetchChannelBlocks fetches blocks from a
 // channel with retries until there are no
 // more blocks in the channel or there is an
@@ -357,17 +305,19 @@ func (s *Syncer) fetchChannelBlocks(
 	results chan *blockResult,
 ) error {
 	for b := range blockIndicies {
-		br, err := s.fetchBlockResult(
+		block, err := s.helper.Block(
 			ctx,
 			network,
-			b,
+			&types.PartialBlockIdentifier{
+				Index: &b,
+			},
 		)
 		if err != nil {
-			return fmt.Errorf("%w: unable to fetch block %d", err, b)
+			return err
 		}
 
 		select {
-		case results <- br:
+		case results <- &blockResult{index: b, block: block}:
 		case <-ctx.Done():
 			return ctx.Err()
 		}
@@ -380,7 +330,7 @@ func (s *Syncer) fetchChannelBlocks(
 // to process as many blocks as possible.
 func (s *Syncer) processBlocks(
 	ctx context.Context,
-	cache map[int64]*blockResult,
+	cache map[int64]*types.Block,
 	endIndex int64,
 ) error {
 	// We need to determine if we are in a reorg
@@ -389,7 +339,7 @@ func (s *Syncer) processBlocks(
 	reorgStart := int64(-1)
 
 	for s.nextIndex <= endIndex {
-		br, exists := cache[s.nextIndex]
+		block, exists := cache[s.nextIndex]
 		if !exists {
 			// Wait for more blocks if we aren't
 			// in a reorg.
@@ -399,15 +349,18 @@ func (s *Syncer) processBlocks(
 
 			// Fetch the nextIndex if we are
 			// in a re-org.
-			var err error
-			br, err = s.fetchBlockResult(
+			newBlock, err := s.helper.Block(
 				ctx,
 				s.network,
-				s.nextIndex,
+				&types.PartialBlockIdentifier{
+					Index: &s.nextIndex,
+				},
 			)
 			if err != nil {
-				return fmt.Errorf("%w: unable to fetch block during re-org", err)
+				return fmt.Errorf("%w: unable to fetch block %d", err, s.nextIndex)
 			}
+
+			block = newBlock
 		} else {
 			// Anytime we re-fetch an index, we
 			// will need to make another call to the node
@@ -416,7 +369,7 @@ func (s *Syncer) processBlocks(
 		}
 
 		lastProcessed := s.nextIndex
-		if err := s.processBlock(ctx, br); err != nil {
+		if err := s.processBlock(ctx, block); err != nil {
 			return fmt.Errorf("%w: unable to process block", err)
 		}
 
@@ -434,9 +387,8 @@ func (s *Syncer) processBlocks(
 // the block is omitted and we can't
 // determine the index of the request.
 type blockResult struct {
-	index      int64
-	block      *types.Block
-	orphanHead bool
+	index int64
+	block *types.Block
 }
 
 // syncRange fetches and processes a range of blocks
@@ -475,9 +427,9 @@ func (s *Syncer) syncRange(
 		close(results)
 	}()
 
-	cache := make(map[int64]*blockResult)
+	cache := make(map[int64]*types.Block)
 	for b := range results {
-		cache[b.index] = b
+		cache[b.index] = b.block
 
 		if err := s.processBlocks(ctx, cache, endIndex); err != nil {
 			return fmt.Errorf("%w: unable to process blocks", err)
diff --git a/types/construction_metadata_request.go b/types/construction_metadata_request.go
index ee0e36d..aa9126f 100644
--- a/types/construction_metadata_request.go
+++ b/types/construction_metadata_request.go
@@ -18,9 +18,7 @@ package types
 
 // ConstructionMetadataRequest A ConstructionMetadataRequest is utilized to get information required
 // to construct a transaction. The Options object used to specify which metadata to return is left
-// purposely unstructured to allow flexibility for implementers. Optionally, the request can also
-// include an array of PublicKeys associated with the AccountIdentifiers returned in
-// ConstructionPreprocessResponse.
+// purposely unstructured to allow flexibility for implementers.
 type ConstructionMetadataRequest struct {
 	NetworkIdentifier *NetworkIdentifier `json:"network_identifier"`
 	// Some blockchains require different metadata for different types of transaction construction
@@ -28,6 +26,5 @@ type ConstructionMetadataRequest struct {
 	// possible types of metadata for construction (which may require multiple node fetches), the
 	// client can populate an options object to limit the metadata returned to only the subset
 	// required.
-	Options    map[string]interface{} `json:"options"`
-	PublicKeys []*PublicKey           `json:"public_keys,omitempty"`
+	Options map[string]interface{} `json:"options"`
 }
diff --git a/types/construction_parse_response.go b/types/construction_parse_response.go
index a935f20..551f089 100644
--- a/types/construction_parse_response.go
+++ b/types/construction_parse_response.go
@@ -21,8 +21,7 @@ package types
 // `/construction/preprocess` and `/construction/payloads`.
 type ConstructionParseResponse struct {
 	Operations []*Operation `json:"operations"`
-	// All signers (addresses) of a particular transaction. If the transaction is unsigned, it
-	// should be empty.
+	// All signers of a particular transaction. If the transaction is unsigned, it should be empty.
 	Signers  []string               `json:"signers"`
 	Metadata map[string]interface{} `json:"metadata,omitempty"`
 }
diff --git a/types/construction_payloads_request.go b/types/construction_payloads_request.go
index 6d28869..e47118a 100644
--- a/types/construction_payloads_request.go
+++ b/types/construction_payloads_request.go
@@ -18,12 +18,9 @@ package types
 
 // ConstructionPayloadsRequest ConstructionPayloadsRequest is the request to
 // `/construction/payloads`. It contains the network, a slice of operations, and arbitrary metadata
-// that was returned by the call to `/construction/metadata`. Optionally, the request can also
-// include an array of PublicKeys associated with the AccountIdentifiers returned in
-// ConstructionPreprocessResponse.
+// that was returned by the call to `/construction/metadata`.
 type ConstructionPayloadsRequest struct {
 	NetworkIdentifier *NetworkIdentifier     `json:"network_identifier"`
 	Operations        []*Operation           `json:"operations"`
 	Metadata          map[string]interface{} `json:"metadata,omitempty"`
-	PublicKeys        []*PublicKey           `json:"public_keys,omitempty"`
 }
diff --git a/types/construction_preprocess_response.go b/types/construction_preprocess_response.go
index 42467ce..66c94f6 100644
--- a/types/construction_preprocess_response.go
+++ b/types/construction_preprocess_response.go
@@ -16,15 +16,10 @@
 
 package types
 
-// ConstructionPreprocessResponse ConstructionPreprocessResponse contains `options` that will be
-// sent unmodified to `/construction/metadata`. If it is not necessary to make a request to
-// `/construction/metadata`, `options` should be omitted.  Some blockchains require the PublicKey of
-// particular AccountIdentifiers to construct a valid transaction. To fetch these PublicKeys,
-// populate `required_public_keys` with the AccountIdentifiers associated with the desired
-// PublicKeys. If it is not necessary to retrieve any PublicKeys for construction,
-// `required_public_keys` should be omitted.
+// ConstructionPreprocessResponse ConstructionPreprocessResponse contains the request that will be
+// sent directly to `/construction/metadata`. If it is not necessary to make a request to
+// `/construction/metadata`, options should be null.
 type ConstructionPreprocessResponse struct {
 	// The options that will be sent directly to `/construction/metadata` by the caller.
-	Options            map[string]interface{} `json:"options,omitempty"`
-	RequiredPublicKeys []*AccountIdentifier   `json:"required_public_keys,omitempty"`
+	Options map[string]interface{} `json:"options,omitempty"`
 }
diff --git a/types/curve_type.go b/types/curve_type.go
index 8797aa1..22dd4e0 100644
--- a/types/curve_type.go
+++ b/types/curve_type.go
@@ -16,18 +16,13 @@
 
 package types
 
-// CurveType CurveType is the type of cryptographic curve associated with a PublicKey. * secp256k1:
-// SEC compressed - `33 bytes` (https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) * secp256r1: SEC
-// compressed - `33 bytes` (https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) * edwards25519: `y
-// (255-bits) || x-sign-bit (1-bit)` - `32 bytes` (https://ed25519.cr.yp.to/ed25519-20110926.pdf) *
-// tweedle: 1st pk : Fq.t (32 bytes) || 2nd pk : Fq.t (32 bytes)
-// (https://github.com/CodaProtocol/coda/blob/develop/rfcs/0038-rosetta-construction-api.md#marshal-keys)
+// CurveType CurveType is the type of cryptographic curve associated with a PublicKey.  * secp256k1:
+// SEC compressed - `33 bytes` (https://secg.org/sec1-v2.pdf#subsubsection.2.3.3) * edwards25519: `y
+// (255-bits) || x-sign-bit (1-bit)` - `32 bytes` (https://ed25519.cr.yp.to/ed25519-20110926.pdf)
 type CurveType string
 
 // List of CurveType
 const (
 	Secp256k1    CurveType = "secp256k1"
-	Secp256r1    CurveType = "secp256r1"
 	Edwards25519 CurveType = "edwards25519"
-	Tweedle      CurveType = "tweedle"
 )
diff --git a/types/signature_type.go b/types/signature_type.go
index 1c00238..81bec14 100644
--- a/types/signature_type.go
+++ b/types/signature_type.go
@@ -18,20 +18,12 @@ package types
 
 // SignatureType SignatureType is the type of a cryptographic signature. * ecdsa: `r (32-bytes) || s
 // (32-bytes)` - `64 bytes` * ecdsa_recovery: `r (32-bytes) || s (32-bytes) || v (1-byte)` - `65
-// bytes` * ed25519: `R (32-byte) || s (32-bytes)` - `64 bytes` * schnorr_1: `r (32-bytes) || s
-// (32-bytes)` - `64 bytes`  (schnorr signature implemented by Zilliqa where both `r` and `s` are
-// scalars encoded as `32-bytes` values, most significant byte first.) * schnorr_poseidon: `r
-// (32-bytes) || s (32-bytes)` where s = Hash(1st pk || 2nd pk || r) - `64 bytes`  (schnorr
-// signature w/ Poseidon hash function implemented by O(1) Labs where both `r` and `s` are scalars
-// encoded as `32-bytes` values, least significant byte first.
-// https://github.com/CodaProtocol/signer-reference/blob/master/schnorr.ml )
+// bytes` * ed25519: `R (32-byte) || s (32-bytes)` - `64 bytes`
 type SignatureType string
 
 // List of SignatureType
 const (
-	Ecdsa           SignatureType = "ecdsa"
-	EcdsaRecovery   SignatureType = "ecdsa_recovery"
-	Ed25519         SignatureType = "ed25519"
-	Schnorr1        SignatureType = "schnorr_1"
-	SchnorrPoseidon SignatureType = "schnorr_poseidon"
+	Ecdsa         SignatureType = "ecdsa"
+	EcdsaRecovery SignatureType = "ecdsa_recovery"
+	Ed25519       SignatureType = "ed25519"
 )


Test patch:
diff --git a/asserter/construction_test.go b/asserter/construction_test.go
index b18238b..0361dd6 100644
--- a/asserter/construction_test.go
+++ b/asserter/construction_test.go
@@ -24,56 +24,6 @@ import (
 	"github.com/stretchr/testify/assert"
 )
 
-func TestConstructionPreprocessResponse(t *testing.T) {
-	var tests = map[string]struct {
-		response *types.ConstructionPreprocessResponse
-		err      error
-	}{
-		"valid response": {
-			response: &types.ConstructionPreprocessResponse{
-				Options: map[string]interface{}{},
-			},
-			err: nil,
-		},
-		"valid response with accounts": {
-			response: &types.ConstructionPreprocessResponse{
-				Options: map[string]interface{}{},
-				RequiredPublicKeys: []*types.AccountIdentifier{
-					{
-						Address: "hello",
-					},
-				},
-			},
-			err: nil,
-		},
-		"invalid response with accounts": {
-			response: &types.ConstructionPreprocessResponse{
-				Options: map[string]interface{}{},
-				RequiredPublicKeys: []*types.AccountIdentifier{
-					{
-						Address: "",
-					},
-				},
-			},
-			err: ErrAccountAddrMissing,
-		},
-		"nil response": {
-			err: ErrConstructionPreprocessResponseIsNil,
-		},
-	}
-
-	for name, test := range tests {
-		t.Run(name, func(t *testing.T) {
-			err := ConstructionPreprocessResponse(test.response)
-			if test.err != nil {
-				assert.Contains(t, err.Error(), test.err.Error())
-			} else {
-				assert.NoError(t, err)
-			}
-		})
-	}
-}
-
 func TestConstructionMetadataResponse(t *testing.T) {
 	var tests = map[string]struct {
 		response *types.ConstructionMetadataResponse
diff --git a/asserter/server_test.go b/asserter/server_test.go
index 9ac43df..94963fa 100644
--- a/asserter/server_test.go
+++ b/asserter/server_test.go
@@ -471,19 +471,6 @@ func TestConstructionMetadataRequest(t *testing.T) {
 			},
 			err: nil,
 		},
-		"valid request with public keys": {
-			request: &types.ConstructionMetadataRequest{
-				NetworkIdentifier: validNetworkIdentifier,
-				Options:           map[string]interface{}{},
-				PublicKeys: []*types.PublicKey{
-					{
-						Bytes:     []byte("hello"),
-						CurveType: types.Secp256k1,
-					},
-				},
-			},
-			err: nil,
-		},
 		"invalid request wrong network": {
 			request: &types.ConstructionMetadataRequest{
 				NetworkIdentifier: wrongNetworkIdentifier,
@@ -511,18 +498,6 @@ func TestConstructionMetadataRequest(t *testing.T) {
 			},
 			err: ErrConstructionMetadataRequestOptionsIsNil,
 		},
-		"invalid request with public keys": {
-			request: &types.ConstructionMetadataRequest{
-				NetworkIdentifier: validNetworkIdentifier,
-				Options:           map[string]interface{}{},
-				PublicKeys: []*types.PublicKey{
-					{
-						CurveType: types.Secp256k1,
-					},
-				},
-			},
-			err: ErrPublicKeyBytesEmpty,
-		},
 	}
 
 	for name, test := range tests {
@@ -877,20 +852,6 @@ func TestConstructionPayloadsRequest(t *testing.T) {
 			},
 			err: nil,
 		},
-		"valid request with public keys": {
-			request: &types.ConstructionPayloadsRequest{
-				NetworkIdentifier: validNetworkIdentifier,
-				Operations:        validOps,
-				Metadata:          map[string]interface{}{"test": "hello"},
-				PublicKeys: []*types.PublicKey{
-					{
-						Bytes:     []byte("hello"),
-						CurveType: types.Secp256k1,
-					},
-				},
-			},
-			err: nil,
-		},
 		"invalid request wrong network": {
 			request: &types.ConstructionPayloadsRequest{
 				NetworkIdentifier: wrongNetworkIdentifier,
@@ -932,19 +893,6 @@ func TestConstructionPayloadsRequest(t *testing.T) {
 			},
 			err: ErrOperationStatusNotEmptyForConstruction,
 		},
-		"invalid request with public keys": {
-			request: &types.ConstructionPayloadsRequest{
-				NetworkIdentifier: validNetworkIdentifier,
-				Operations:        validOps,
-				Metadata:          map[string]interface{}{"test": "hello"},
-				PublicKeys: []*types.PublicKey{
-					{
-						CurveType: types.Secp256k1,
-					},
-				},
-			},
-			err: ErrPublicKeyBytesEmpty,
-		},
 	}
 
 	for name, test := range tests {
diff --git a/constructor/coordinator/coordinator_test.go b/constructor/coordinator/coordinator_test.go
index 2cc1493..52930ad 100644
--- a/constructor/coordinator/coordinator_test.go
+++ b/constructor/coordinator/coordinator_test.go
@@ -22,7 +22,6 @@ import (
 
 	"github.com/coinbase/rosetta-sdk-go/asserter"
 	"github.com/coinbase/rosetta-sdk-go/constructor/job"
-	"github.com/coinbase/rosetta-sdk-go/keys"
 	mocks "github.com/coinbase/rosetta-sdk-go/mocks/constructor/coordinator"
 	"github.com/coinbase/rosetta-sdk-go/parser"
 	"github.com/coinbase/rosetta-sdk-go/storage"
@@ -547,7 +546,7 @@ func TestProcess(t *testing.T) {
 		map[string]interface{}{
 			"test": "works",
 		},
-	).Return(metadataOptions, nil, nil).Once()
+	).Return(metadataOptions, nil).Once()
 	fetchedMetadata := map[string]interface{}{
 		"tx_meta": "help",
 	}
@@ -556,7 +555,6 @@ func TestProcess(t *testing.T) {
 		ctx,
 		network,
 		metadataOptions,
-		[]*types.PublicKey{},
 	).Return(fetchedMetadata, nil).Once()
 
 	unsignedTx := "unsigned transaction"
@@ -573,7 +571,6 @@ func TestProcess(t *testing.T) {
 		network,
 		ops,
 		fetchedMetadata,
-		[]*types.PublicKey{},
 	).Return(unsignedTx, signingPayloads, nil).Once()
 	helper.On(
 		"Parse",
@@ -953,43 +950,7 @@ func TestProcess_Failed(t *testing.T) {
 		map[string]interface{}{
 			"test": "works",
 		},
-	).Return(metadataOptions, []*types.AccountIdentifier{
-		{
-			Address: "hello",
-		},
-		{
-			Address: "hello2",
-		},
-	}, nil).Once()
-	helper.On(
-		"GetKey",
-		ctx,
-		dbTx,
-		"hello",
-	).Return(
-		&keys.KeyPair{
-			PublicKey: &types.PublicKey{
-				Bytes:     []byte("hello"),
-				CurveType: types.Secp256k1,
-			},
-		},
-		nil,
-	).Once()
-	helper.On(
-		"GetKey",
-		ctx,
-		dbTx,
-		"hello2",
-	).Return(
-		&keys.KeyPair{
-			PublicKey: &types.PublicKey{
-				Bytes:     []byte("hello2"),
-				CurveType: types.Edwards25519,
-			},
-		},
-		nil,
-	).Once()
-
+	).Return(metadataOptions, nil).Once()
 	fetchedMetadata := map[string]interface{}{
 		"tx_meta": "help",
 	}
@@ -998,16 +959,6 @@ func TestProcess_Failed(t *testing.T) {
 		ctx,
 		network,
 		metadataOptions,
-		[]*types.PublicKey{
-			{
-				Bytes:     []byte("hello"),
-				CurveType: types.Secp256k1,
-			},
-			{
-				Bytes:     []byte("hello2"),
-				CurveType: types.Edwards25519,
-			},
-		},
 	).Return(fetchedMetadata, nil).Once()
 
 	unsignedTx := "unsigned transaction"
@@ -1024,16 +975,6 @@ func TestProcess_Failed(t *testing.T) {
 		network,
 		ops,
 		fetchedMetadata,
-		[]*types.PublicKey{
-			{
-				Bytes:     []byte("hello"),
-				CurveType: types.Secp256k1,
-			},
-			{
-				Bytes:     []byte("hello2"),
-				CurveType: types.Edwards25519,
-			},
-		},
 	).Return(unsignedTx, signingPayloads, nil).Once()
 	helper.On(
 		"Parse",
diff --git a/errors/utils_test.go b/errors/utils_test.go
new file mode 100644
index 0000000..d9dae2e
--- /dev/null
+++ b/errors/utils_test.go
@@ -0,0 +1,52 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package errors
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestErrParser(t *testing.T) {
+	err1 := errors.New("error 1")
+	err2 := errors.New("error 2")
+	err3 := errors.New("error 3")
+
+	var tests = map[string]struct {
+		err     error
+		errList []error
+		found   bool
+	}{
+		"intent error": {
+			err:     err1,
+			errList: []error{err1, err2, err3},
+			found:   true,
+		},
+		"match operations error": {
+			err:     errors.New("this is not an error we expect to find"),
+			errList: []error{err1, err2},
+			found:   false,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			found := FindError(test.errList, test.err)
+			assert.Equal(t, test.found, found)
+		})
+	}
+}
diff --git a/keys/errors_test.go b/keys/errors_test.go
new file mode 100644
index 0000000..ace8d1d
--- /dev/null
+++ b/keys/errors_test.go
@@ -0,0 +1,45 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package keys
+
+import (
+	"errors"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestErrKeys(t *testing.T) {
+	var tests = map[string]struct {
+		err error
+		is  bool
+	}{
+		"is a keys error": {
+			err: ErrPrivKeyLengthInvalid,
+			is:  true,
+		},
+		"not a keys error": {
+			err: errors.New("blah"),
+			is:  false,
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			is := ErrKeys(test.err)
+			assert.Equal(t, test.is, is)
+		})
+	}
+}
diff --git a/keys/signer_secp256k1_test.go b/keys/signer_secp256k1_test.go
index b3898e0..3cc32c3 100644
--- a/keys/signer_secp256k1_test.go
+++ b/keys/signer_secp256k1_test.go
@@ -52,7 +52,7 @@ func TestSignSecp256k1(t *testing.T) {
 			types.Ed25519,
 			64,
 			true,
-			ErrSignUnsupportedSigType,
+			ErrSignUnsupportedSignatureType,
 		},
 	}
 
diff --git a/parser/errors_test.go b/parser/errors_test.go
new file mode 100644
index 0000000..98830bc
--- /dev/null
+++ b/parser/errors_test.go
@@ -0,0 +1,48 @@
+// Copyright 2020 Coinbase, Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package parser
+
+import (
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestErrParser(t *testing.T) {
+	var tests = map[string]struct {
+		err    error
+		is     bool
+		source string
+	}{
+		"intent error": {
+			err:    ErrExpectedOperationAccountMismatch,
+			is:     true,
+			source: "intent error",
+		},
+		"match operations error": {
+			err:    ErrAccountMatchAccountMissing,
+			is:     true,
+			source: "match operations error",
+		},
+	}
+
+	for name, test := range tests {
+		t.Run(name, func(t *testing.T) {
+			is, source := ErrParser(test.err)
+			assert.Equal(t, test.is, is)
+			assert.Equal(t, test.source, source)
+		})
+	}
+}
diff --git a/syncer/syncer_test.go b/syncer/syncer_test.go
index 1cf314b..d63a258 100644
--- a/syncer/syncer_test.go
+++ b/syncer/syncer_test.go
@@ -209,7 +209,7 @@ func TestProcessBlock(t *testing.T) {
 		)
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[0]},
+			blockSequence[0],
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(1), syncer.nextIndex)
@@ -224,7 +224,7 @@ func TestProcessBlock(t *testing.T) {
 	t.Run("Orphan genesis", func(t *testing.T) {
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{block: orphanGenesis},
+			orphanGenesis,
 		)
 
 		assert.EqualError(t, err, "cannot remove genesis block")
@@ -241,7 +241,7 @@ func TestProcessBlock(t *testing.T) {
 		mockHandler.On("BlockAdded", ctx, blockSequence[1]).Return(nil).Once()
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[1]},
+			blockSequence[1],
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(2), syncer.nextIndex)
@@ -260,7 +260,7 @@ func TestProcessBlock(t *testing.T) {
 		mockHandler.On("BlockRemoved", ctx, blockSequence[1].BlockIdentifier).Return(nil).Once()
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[2]},
+			blockSequence[2],
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(1), syncer.nextIndex)
@@ -274,7 +274,7 @@ func TestProcessBlock(t *testing.T) {
 		mockHandler.On("BlockAdded", ctx, blockSequence[3]).Return(nil).Once()
 		err = syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[3]},
+			blockSequence[3],
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(2), syncer.nextIndex)
@@ -291,7 +291,7 @@ func TestProcessBlock(t *testing.T) {
 		mockHandler.On("BlockAdded", ctx, blockSequence[2]).Return(nil).Once()
 		err = syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[2]},
+			blockSequence[2],
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(3), syncer.nextIndex)
@@ -310,7 +310,7 @@ func TestProcessBlock(t *testing.T) {
 	t.Run("Out of order block", func(t *testing.T) {
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{block: blockSequence[5]},
+			blockSequence[5],
 		)
 		assert.Contains(t, err.Error(), "got block 5 instead of 3")
 		assert.Equal(t, int64(3), syncer.nextIndex)
@@ -329,7 +329,7 @@ func TestProcessBlock(t *testing.T) {
 	t.Run("Process omitted block", func(t *testing.T) {
 		err := syncer.processBlock(
 			ctx,
-			&blockResult{},
+			nil,
 		)
 		assert.NoError(t, err)
 		assert.Equal(t, int64(4), syncer.nextIndex)
@@ -345,41 +345,6 @@ func TestProcessBlock(t *testing.T) {
 		)
 	})
 
-	t.Run("Process nil block result", func(t *testing.T) {
-		err := syncer.processBlock(
-			ctx,
-			nil,
-		)
-		assert.True(t, errors.Is(err, ErrBlockResultNil))
-	})
-
-	t.Run("Process orphan head block result", func(t *testing.T) {
-		mockHandler.On(
-			"BlockRemoved",
-			ctx,
-			blockSequence[2].BlockIdentifier,
-		).Return(
-			nil,
-		).Run(func(args mock.Arguments) {
-			assertNotCanceled(t, args)
-		}).Once()
-		err := syncer.processBlock(
-			ctx,
-			&blockResult{orphanHead: true},
-		)
-		assert.NoError(t, err)
-		assert.Equal(t, int64(2), syncer.nextIndex)
-		assert.Equal(t, blockSequence[3].BlockIdentifier, lastBlockIdentifier(syncer))
-		assert.Equal(
-			t,
-			[]*types.BlockIdentifier{
-				blockSequence[0].BlockIdentifier,
-				blockSequence[3].BlockIdentifier,
-			},
-			syncer.pastBlocks,
-		)
-	})
-
 	mockHelper.AssertExpectations(t)
 	mockHandler.AssertExpectations(t)
 }
@@ -726,108 +691,3 @@ func TestSync_Reorg(t *testing.T) {
 	mockHelper.AssertExpectations(t)
 	mockHandler.AssertExpectations(t)
 }
-
-func TestSync_ManualReorg(t *testing.T) {
-	ctx, cancel := context.WithCancel(context.Background())
-
-	mockHelper := &mocks.Helper{}
-	mockHandler := &mocks.Handler{}
-	syncer := New(networkIdentifier, mockHelper, mockHandler, cancel, WithConcurrency(16))
-
-	mockHelper.On("NetworkStatus", ctx, networkIdentifier).Return(&types.NetworkStatusResponse{
-		CurrentBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "block 1300",
-			Index: 1300,
-		},
-		GenesisBlockIdentifier: &types.BlockIdentifier{
-			Hash:  "block 0",
-			Index: 0,
-		},
-	}, nil).Run(func(args mock.Arguments) {
-		err := args.Get(0).(context.Context)
-		assert.NoError(t, err.Err())
-	})
-
-	blocks := createBlocks(0, 800, "")
-	for _, b := range blocks { // [0, 800]
-		mockHelper.On(
-			"Block",
-			mock.AnythingOfType("*context.cancelCtx"),
-			networkIdentifier,
-			&types.PartialBlockIdentifier{Index: &b.BlockIdentifier.Index},
-		).Return(
-			b,
-			nil,
-		).Run(func(args mock.Arguments) {
-			assertNotCanceled(t, args)
-		}).Once()
-		mockHandler.On(
-			"BlockAdded",
-			mock.AnythingOfType("*context.cancelCtx"),
-			b,
-		).Run(func(args mock.Arguments) {
-			assertNotCanceled(t, args)
-		}).Return(
-			nil,
-		).Once()
-	}
-
-	// Create reorg
-	index801 := int64(801)
-	mockHelper.On(
-		"Block",
-		mock.AnythingOfType("*context.cancelCtx"),
-		networkIdentifier,
-		&types.PartialBlockIdentifier{Index: &index801},
-	).Return(
-		nil,
-		ErrOrphanHead,
-	).Run(func(args mock.Arguments) {
-		err := args.Get(0).(context.Context)
-		assert.NoError(t, err.Err())
-	}).Once() // [801]
-	mockHandler.On(
-		"BlockRemoved",
-		mock.AnythingOfType("*context.cancelCtx"),
-		blocks[len(blocks)-1].BlockIdentifier,
-	).Return(
-		nil,
-	).Run(func(args mock.Arguments) {
-		assertNotCanceled(t, args)
-	}).Once()
-
-	newBlocks := createBlocks(800, 1200, "")
-	for _, b := range newBlocks { // [800, 1200]
-		mockHelper.On(
-			"Block",
-			mock.AnythingOfType("*context.cancelCtx"),
-			networkIdentifier,
-			&types.PartialBlockIdentifier{Index: &b.BlockIdentifier.Index},
-		).Return(
-			b,
-			nil,
-		).Run(func(args mock.Arguments) {
-			assertNotCanceled(t, args)
-		}).Once()
-		mockHandler.On(
-			"BlockAdded",
-			mock.AnythingOfType("*context.cancelCtx"),
-			b,
-		).Run(func(args mock.Arguments) {
-			assertNotCanceled(t, args)
-		}).Return(
-			nil,
-		).Once()
-	}
-
-	// Expected Calls to Block
-	// [0, 799] = 1
-	// [800, 801] = 2
-	// [802,1200] = 1
-
-	err := syncer.Sync(ctx, -1, 1200)
-	assert.NoError(t, err)
-	mockHelper.AssertNumberOfCalls(t, "NetworkStatus", 3)
-	mockHelper.AssertExpectations(t)
-	mockHandler.AssertExpectations(t)
-}