Repository: kaspanet/kaspad
Pull request #2111 (2022-07-02 09:44:02)

Mempool: Retrive stable state of the mempool. optimze get mempool entries by addresses
1) Adds a `clone` parameter to relevent mempool functions.
2) retrive only cloned mempool transactions (no pointers!) through relevent interfaces (i.e. outside of the mempool internal functioning)

3) when accessinging the mempool, query orphan and transactionpool together with one lock.

4) add retrival of mempool transactions by addresses through the interface
5) use point number 3 in order to not double loop transactions in get mempool entries by addresses

6) check if requested transaction is a known orphan before requesting. 

7) since both mempool and utxoindex now need accesses to a stringification and destringification of SciptPublicKeys for map building, I decided to add these as methods to externalapi.ScriptPublicKey

- `1)` and `2)`  ensures that accessing the mempool does not incur multiple threads accessing the same memory, it also ensures that retrived transactionpool transactions must be in a stable state with populated utxos.

- `3)` side-steps unintuitive mempool state as per rpc responses, where a transaction could be both an orphan and not an orphan, due to state changes between transactionpool and orphanpool queries. 

- `4)` and `5`) is an optimization and rewrite of the GetMempoolEntriesByAddresses RPC command. it ensures mempool transactions are only looped once instead of twice. 

- `6`) is a flow improvement for transaction requesting. i.e. it filters transactions that are already known transactions in the orphan pool.

- `7`) is for code maintenance.  

it should fix mempool syncing and missing utxos in retrived mempool transactions and perhaps a whole range of other mempool related issues, which may be caused by this issue, or flows which access mempool data.

codecov[bot]: # [Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet) Report
> Merging [#2111](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet) (e1f66e9) into [dev](https://codecov.io/gh/kaspanet/kaspad/commit/b9093d59eb83d8f97c34962d6ab9f0720f5364db?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet) (b9093d5) will **decrease** coverage by `0.32%`.
> The diff coverage is `38.28%`.

```diff
@@            Coverage Diff             @@
##              dev    #2111      +/-   ##
==========================================
- Coverage   59.19%   58.86%   -0.33%     
==========================================
  Files         678      678              
  Lines       32831    32882      +51     
==========================================
- Hits        19434    19357      -77     
- Misses      10596    10714     +118     
- Partials     2801     2811      +10     
```


| [Impacted Files](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet) | Coverage Δ | |
|---|---|---|
| [domain/miningmanager/mempool/orphan\_pool.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC9vcnBoYW5fcG9vbC5nbw==) | `48.01% <5.00%> (-9.80%)` | :arrow_down: |
| [...pc/rpchandlers/get\_mempool\_entries\_by\_addresses.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-YXBwL3JwYy9ycGNoYW5kbGVycy9nZXRfbWVtcG9vbF9lbnRyaWVzX2J5X2FkZHJlc3Nlcy5nbw==) | `22.64% <19.14%> (-50.28%)` | :arrow_down: |
| [...r/mempool/revalidate\_high\_priority\_transactions.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC9yZXZhbGlkYXRlX2hpZ2hfcHJpb3JpdHlfdHJhbnNhY3Rpb25zLmdv) | `61.11% <41.17%> (-16.67%)` | :arrow_down: |
| [domain/miningmanager/mempool/transactions\_pool.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC90cmFuc2FjdGlvbnNfcG9vbC5nbw==) | `70.51% <65.00%> (-3.26%)` | :arrow_down: |
| [domain/miningmanager/mempool/mempool.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC9tZW1wb29sLmdv) | `67.21% <66.66%> (-9.71%)` | :arrow_down: |
| [...manager/mempool/validate\_and\_insert\_transaction.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC92YWxpZGF0ZV9hbmRfaW5zZXJ0X3RyYW5zYWN0aW9uLmdv) | `58.82% <66.66%> (-2.47%)` | :arrow_down: |
| [domain/miningmanager/factory.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvZmFjdG9yeS5nbw==) | `100.00% <100.00%> (ø)` | |
| [...ngmanager/mempool/handle\_new\_block\_transactions.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC9oYW5kbGVfbmV3X2Jsb2NrX3RyYW5zYWN0aW9ucy5nbw==) | `50.00% <100.00%> (ø)` | |
| [domain/miningmanager/mempool/mempool\_utxo\_set.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWVtcG9vbC9tZW1wb29sX3V0eG9fc2V0Lmdv) | `100.00% <100.00%> (ø)` | |
| [domain/miningmanager/miningmanager.go](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet#diff-ZG9tYWluL21pbmluZ21hbmFnZXIvbWluaW5nbWFuYWdlci5nbw==) | `80.39% <100.00%> (-2.59%)` | :arrow_down: |
| ... and [10 more](https://codecov.io/gh/kaspanet/kaspad/pull/2111/diff?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=footer&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet). Last update [b9093d5...e1f66e9](https://codecov.io/gh/kaspanet/kaspad/pull/2111?src=pr&el=lastupdated&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=kaspanet).

D-Stacks: something failing tests, need to debug. 

Patch:
diff --git a/app/protocol/flows/v5/transactionrelay/handle_relayed_transactions.go b/app/protocol/flows/v5/transactionrelay/handle_relayed_transactions.go
index 21702986..401ac46f 100644
--- a/app/protocol/flows/v5/transactionrelay/handle_relayed_transactions.go
+++ b/app/protocol/flows/v5/transactionrelay/handle_relayed_transactions.go
@@ -102,7 +102,7 @@ func (flow *handleRelayedTransactionsFlow) requestInvTransactions(
 func (flow *handleRelayedTransactionsFlow) isKnownTransaction(txID *externalapi.DomainTransactionID) bool {
 	// Ask the transaction memory pool if the transaction is known
 	// to it in any form (main pool or orphan).
-	if _, ok := flow.Domain().MiningManager().GetTransaction(txID); ok {
+	if _, _, ok := flow.Domain().MiningManager().GetTransaction(txID, true, true); ok {
 		return true
 	}
 
diff --git a/app/protocol/flows/v5/transactionrelay/handle_requested_transactions.go b/app/protocol/flows/v5/transactionrelay/handle_requested_transactions.go
index 3dd8b294..00fd23ae 100644
--- a/app/protocol/flows/v5/transactionrelay/handle_requested_transactions.go
+++ b/app/protocol/flows/v5/transactionrelay/handle_requested_transactions.go
@@ -30,7 +30,7 @@ func (flow *handleRequestedTransactionsFlow) start() error {
 		}
 
 		for _, transactionID := range msgRequestTransactions.IDs {
-			tx, ok := flow.Domain().MiningManager().GetTransaction(transactionID)
+			tx, _, ok := flow.Domain().MiningManager().GetTransaction(transactionID, true, false)
 
 			if !ok {
 				msgTransactionNotFound := appmessage.NewMsgTransactionNotFound(transactionID)
@@ -40,7 +40,6 @@ func (flow *handleRequestedTransactionsFlow) start() error {
 				}
 				continue
 			}
-
 			err := flow.outgoingRoute.Enqueue(appmessage.DomainTransactionToMsgTx(tx))
 			if err != nil {
 				return err
diff --git a/app/rpc/rpccontext/notificationmanager.go b/app/rpc/rpccontext/notificationmanager.go
index e9b3ef98..0ba132f1 100644
--- a/app/rpc/rpccontext/notificationmanager.go
+++ b/app/rpc/rpccontext/notificationmanager.go
@@ -5,6 +5,7 @@ import (
 
 	"github.com/kaspanet/kaspad/domain/dagconfig"
 
+	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
 
 	"github.com/kaspanet/kaspad/app/appmessage"
@@ -421,7 +422,7 @@ func (nl *NotificationListener) convertUTXOChangesToUTXOsChangedNotification(
 }
 
 func (nl *NotificationListener) scriptPubKeyStringToAddressString(scriptPublicKeyString utxoindex.ScriptPublicKeyString) (string, error) {
-	scriptPubKey := utxoindex.ConvertStringToScriptPublicKey(scriptPublicKeyString)
+	scriptPubKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
 
 	// ignore error because it is often returned when the script is of unknown type
 	scriptType, address, err := txscript.ExtractScriptPubKeyAddress(scriptPubKey, nl.params)
diff --git a/app/rpc/rpccontext/utxos_by_addresses.go b/app/rpc/rpccontext/utxos_by_addresses.go
index d9ca04ae..00251589 100644
--- a/app/rpc/rpccontext/utxos_by_addresses.go
+++ b/app/rpc/rpccontext/utxos_by_addresses.go
@@ -32,22 +32,6 @@ func ConvertUTXOOutpointEntryPairsToUTXOsByAddressesEntries(address string, pair
 	return utxosByAddressesEntries
 }
 
-// convertUTXOOutpointsToUTXOsByAddressesEntries converts
-// UTXOOutpoints to a slice of UTXOsByAddressesEntry
-func convertUTXOOutpointsToUTXOsByAddressesEntries(address string, outpoints utxoindex.UTXOOutpoints) []*appmessage.UTXOsByAddressesEntry {
-	utxosByAddressesEntries := make([]*appmessage.UTXOsByAddressesEntry, 0, len(outpoints))
-	for outpoint := range outpoints {
-		utxosByAddressesEntries = append(utxosByAddressesEntries, &appmessage.UTXOsByAddressesEntry{
-			Address: address,
-			Outpoint: &appmessage.RPCOutpoint{
-				TransactionID: outpoint.TransactionID.String(),
-				Index:         outpoint.Index,
-			},
-		})
-	}
-	return utxosByAddressesEntries
-}
-
 // ConvertAddressStringsToUTXOsChangedNotificationAddresses converts address strings
 // to UTXOsChangedNotificationAddresses
 func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
@@ -63,7 +47,7 @@ func (ctx *Context) ConvertAddressStringsToUTXOsChangedNotificationAddresses(
 		if err != nil {
 			return nil, errors.Errorf("Could not create a scriptPublicKey for address '%s': %s", addressString, err)
 		}
-		scriptPublicKeyString := utxoindex.ConvertScriptPublicKeyToString(scriptPublicKey)
+		scriptPublicKeyString := utxoindex.ScriptPublicKeyString(scriptPublicKey.String())
 		addresses[i] = &UTXOsChangedNotificationAddress{
 			Address:               addressString,
 			ScriptPublicKeyString: scriptPublicKeyString,
diff --git a/app/rpc/rpchandlers/get_info.go b/app/rpc/rpchandlers/get_info.go
index 2b72d742..308e4761 100644
--- a/app/rpc/rpchandlers/get_info.go
+++ b/app/rpc/rpchandlers/get_info.go
@@ -16,7 +16,7 @@ func HandleGetInfo(context *rpccontext.Context, _ *router.Router, _ appmessage.M
 
 	response := appmessage.NewGetInfoResponseMessage(
 		context.NetAdapter.ID().String(),
-		uint64(context.Domain.MiningManager().TransactionCount()),
+		uint64(context.Domain.MiningManager().TransactionCount(true, false)),
 		version.Version(),
 		context.Config.UTXOIndex,
 		context.ProtocolManager.Context().HasPeers() && isNearlySynced,
diff --git a/app/rpc/rpchandlers/get_mempool_entries.go b/app/rpc/rpchandlers/get_mempool_entries.go
index e37d8492..4f5ec8c8 100644
--- a/app/rpc/rpchandlers/get_mempool_entries.go
+++ b/app/rpc/rpchandlers/get_mempool_entries.go
@@ -12,58 +12,36 @@ func HandleGetMempoolEntries(context *rpccontext.Context, _ *router.Router, requ
 
 	entries := make([]*appmessage.MempoolEntry, 0)
 
+	transactionPoolTransactions, orphanPoolTransactions := context.Domain.MiningManager().AllTransactions(!getMempoolEntriesRequest.FilterTransactionPool, getMempoolEntriesRequest.IncludeOrphanPool)
+
 	if !getMempoolEntriesRequest.FilterTransactionPool {
-		transactionPoolEntries, err := getTransactionPoolMempoolEntries(context)
-		if err != nil {
-			return nil, err
+		for _, transaction := range transactionPoolTransactions {
+			rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+			err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
+			if err != nil {
+				return nil, err
+			}
+			entries = append(entries, &appmessage.MempoolEntry{
+				Fee:         transaction.Fee,
+				Transaction: rpcTransaction,
+				IsOrphan:    false,
+			})
 		}
-
-		entries = append(entries, transactionPoolEntries...)
 	}
-
 	if getMempoolEntriesRequest.IncludeOrphanPool {
-		orphanPoolEntries, err := getOrphanPoolMempoolEntries(context)
-		if err != nil {
-			return nil, err
+		for _, transaction := range orphanPoolTransactions {
+			rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+			err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
+			if err != nil {
+				return nil, err
+			}
+			entries = append(entries, &appmessage.MempoolEntry{
+				Fee:         transaction.Fee,
+				Transaction: rpcTransaction,
+				IsOrphan:    true,
+			})
 		}
-		entries = append(entries, orphanPoolEntries...)
 	}
 
 	return appmessage.NewGetMempoolEntriesResponseMessage(entries), nil
 }
-
-func getTransactionPoolMempoolEntries(context *rpccontext.Context) ([]*appmessage.MempoolEntry, error) {
-	transactions := context.Domain.MiningManager().AllTransactions()
-	entries := make([]*appmessage.MempoolEntry, 0, len(transactions))
-	for _, transaction := range transactions {
-		rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
-		err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
-		if err != nil {
-			return nil, err
-		}
-		entries = append(entries, &appmessage.MempoolEntry{
-			Fee:         transaction.Fee,
-			Transaction: rpcTransaction,
-			IsOrphan:    false,
-		})
-	}
-	return entries, nil
-}
-
-func getOrphanPoolMempoolEntries(context *rpccontext.Context) ([]*appmessage.MempoolEntry, error) {
-	orphanTransactions := context.Domain.MiningManager().AllOrphanTransactions()
-	entries := make([]*appmessage.MempoolEntry, 0, len(orphanTransactions))
-	for _, orphanTransaction := range orphanTransactions {
-		rpcTransaction := appmessage.DomainTransactionToRPCTransaction(orphanTransaction)
-		err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
-		if err != nil {
-			return nil, err
-		}
-		entries = append(entries, &appmessage.MempoolEntry{
-			Fee:         orphanTransaction.Fee,
-			Transaction: rpcTransaction,
-			IsOrphan:    true,
-		})
-	}
-	return entries, nil
-}
diff --git a/app/rpc/rpchandlers/get_mempool_entries_by_addresses.go b/app/rpc/rpchandlers/get_mempool_entries_by_addresses.go
index 04e8ac30..0e9bf65f 100644
--- a/app/rpc/rpchandlers/get_mempool_entries_by_addresses.go
+++ b/app/rpc/rpchandlers/get_mempool_entries_by_addresses.go
@@ -1,14 +1,10 @@
 package rpchandlers
 
 import (
-	"errors"
-
 	"github.com/kaspanet/kaspad/app/appmessage"
 	"github.com/kaspanet/kaspad/app/rpc/rpccontext"
-
-	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
-	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
 	"github.com/kaspanet/kaspad/domain/consensus/utils/txscript"
+
 	"github.com/kaspanet/kaspad/infrastructure/network/netadapter/router"
 	"github.com/kaspanet/kaspad/util"
 )
@@ -20,132 +16,107 @@ func HandleGetMempoolEntriesByAddresses(context *rpccontext.Context, _ *router.R
 
 	mempoolEntriesByAddresses := make([]*appmessage.MempoolEntryByAddress, 0)
 
-	if !getMempoolEntriesByAddressesRequest.FilterTransactionPool {
-		transactionPoolTransactions := context.Domain.MiningManager().AllTransactions()
-		transactionPoolEntriesByAddresses, err := extractMempoolEntriesByAddressesFromTransactions(
-			context,
-			getMempoolEntriesByAddressesRequest.Addresses,
-			transactionPoolTransactions,
-			false,
-		)
-		if err != nil {
-			rpcError := &appmessage.RPCError{}
-			if !errors.As(err, &rpcError) {
-				return nil, err
-			}
-			errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
-			errorMessage.Error = rpcError
-			return errorMessage, nil
-		}
-		mempoolEntriesByAddresses = append(mempoolEntriesByAddresses, transactionPoolEntriesByAddresses...)
+	sendingInTransactionPool, receivingInTransactionPool, sendingInOrphanPool, receivingInOrphanPool, err := context.Domain.MiningManager().GetTransactionsByAddresses(!getMempoolEntriesByAddressesRequest.FilterTransactionPool, getMempoolEntriesByAddressesRequest.IncludeOrphanPool)
+	if err != nil {
+		return nil, err
 	}
 
-	if getMempoolEntriesByAddressesRequest.IncludeOrphanPool {
+	for _, addressString := range getMempoolEntriesByAddressesRequest.Addresses {
 
-		orphanPoolTransactions := context.Domain.MiningManager().AllOrphanTransactions()
-		orphanPoolEntriesByAddress, err := extractMempoolEntriesByAddressesFromTransactions(
-			context,
-			getMempoolEntriesByAddressesRequest.Addresses,
-			orphanPoolTransactions,
-			true,
-		)
+		address, err := util.DecodeAddress(addressString, context.Config.NetParams().Prefix)
 		if err != nil {
-			rpcError := &appmessage.RPCError{}
-			if !errors.As(err, &rpcError) {
-				return nil, err
-			}
-			errorMessage := &appmessage.GetUTXOsByAddressesResponseMessage{}
-			errorMessage.Error = rpcError
+			errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
+			errorMessage.Error = appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
 			return errorMessage, nil
 		}
 
-		mempoolEntriesByAddresses = append(mempoolEntriesByAddresses, orphanPoolEntriesByAddress...)
-	}
-
-	return appmessage.NewGetMempoolEntriesByAddressesResponseMessage(mempoolEntriesByAddresses), nil
-}
+		sending := make([]*appmessage.MempoolEntry, 0)
+		receiving := make([]*appmessage.MempoolEntry, 0)
 
-//TO DO: optimize extractMempoolEntriesByAddressesFromTransactions
-func extractMempoolEntriesByAddressesFromTransactions(context *rpccontext.Context, addresses []string, transactions []*externalapi.DomainTransaction, areOrphans bool) ([]*appmessage.MempoolEntryByAddress, error) {
-	mempoolEntriesByAddresses := make([]*appmessage.MempoolEntryByAddress, 0)
-	for _, addressString := range addresses {
-		_, err := util.DecodeAddress(addressString, context.Config.ActiveNetParams.Prefix)
+		scriptPublicKey, err := txscript.PayToAddrScript(address)
 		if err != nil {
-			return nil, appmessage.RPCErrorf("Could not decode address '%s': %s", addressString, err)
+			errorMessage := &appmessage.GetMempoolEntriesByAddressesResponseMessage{}
+			errorMessage.Error = appmessage.RPCErrorf("Could not extract scriptPublicKey from address '%s': %s", addressString, err)
+			return errorMessage, nil
 		}
 
-		sending := make([]*appmessage.MempoolEntry, 0)
-		receiving := make([]*appmessage.MempoolEntry, 0)
+		if !getMempoolEntriesByAddressesRequest.FilterTransactionPool {
 
-		for _, transaction := range transactions {
-
-			for i, input := range transaction.Inputs {
-				if input.UTXOEntry == nil {
-					if !areOrphans { // Orphans can legitimately have `input.UTXOEntry == nil`
-						// TODO: Fix the underlying cause of the bug for non-orphan entries
-						log.Debugf(
-							"Couldn't find UTXO entry for input %d in mempool transaction %s. This is a bug and should be fixed.",
-							i, consensushashing.TransactionID(transaction))
-					}
-					continue
+			if transaction, found := sendingInTransactionPool[scriptPublicKey.String()]; found {
+				rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+				err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
+				if err != nil {
+					return nil, err
 				}
 
-				_, transactionSendingAddress, err := txscript.ExtractScriptPubKeyAddress(
-					input.UTXOEntry.ScriptPublicKey(),
-					context.Config.ActiveNetParams)
+				sending = append(sending, &appmessage.MempoolEntry{
+					Fee:         transaction.Fee,
+					Transaction: rpcTransaction,
+					IsOrphan:    false,
+				},
+				)
+			}
+
+			if transaction, found := receivingInTransactionPool[scriptPublicKey.String()]; found {
+				rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+				err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
 				if err != nil {
 					return nil, err
 				}
-				if addressString == transactionSendingAddress.String() {
-					rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
-					sending = append(
-						sending,
-						&appmessage.MempoolEntry{
-							Fee:         transaction.Fee,
-							Transaction: rpcTransaction,
-							IsOrphan:    areOrphans,
-						},
-					)
-					break //one input is enough
-				}
-			}
 
-			for _, output := range transaction.Outputs {
-				_, transactionReceivingAddress, err := txscript.ExtractScriptPubKeyAddress(
-					output.ScriptPublicKey,
-					context.Config.ActiveNetParams,
+				receiving = append(receiving, &appmessage.MempoolEntry{
+					Fee:         transaction.Fee,
+					Transaction: rpcTransaction,
+					IsOrphan:    false,
+				},
 				)
+			}
+		}
+		if getMempoolEntriesByAddressesRequest.IncludeOrphanPool {
+
+			if transaction, found := sendingInOrphanPool[scriptPublicKey.String()]; found {
+				rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+				err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
 				if err != nil {
 					return nil, err
 				}
-				if addressString == transactionReceivingAddress.String() {
-					rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
-					receiving = append(
-						receiving,
-						&appmessage.MempoolEntry{
-							Fee:         transaction.Fee,
-							Transaction: rpcTransaction,
-							IsOrphan:    areOrphans,
-						},
-					)
-					break //one output is enough
-				}
+
+				sending = append(sending, &appmessage.MempoolEntry{
+					Fee:         transaction.Fee,
+					Transaction: rpcTransaction,
+					IsOrphan:    true,
+				},
+				)
 			}
 
-			//Only append mempoolEntriesByAddress, if at least 1 mempoolEntry for the address is found.
-			//This mimics the behaviour of GetUtxosByAddresses RPC call.
-			if len(sending) > 0 || len(receiving) > 0 {
-				mempoolEntriesByAddresses = append(
-					mempoolEntriesByAddresses,
-					&appmessage.MempoolEntryByAddress{
-						Address:   addressString,
-						Sending:   sending,
-						Receiving: receiving,
-					},
+			if transaction, found := receivingInOrphanPool[scriptPublicKey.String()]; found {
+				rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+				err := context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
+				if err != nil {
+					return nil, err
+				}
+
+				receiving = append(receiving, &appmessage.MempoolEntry{
+					Fee:         transaction.Fee,
+					Transaction: rpcTransaction,
+					IsOrphan:    true,
+				},
 				)
 			}
+
 		}
 
+		if len(sending) > 0 || len(receiving) > 0 {
+			mempoolEntriesByAddresses = append(
+				mempoolEntriesByAddresses,
+				&appmessage.MempoolEntryByAddress{
+					Address:   address.String(),
+					Sending:   sending,
+					Receiving: receiving,
+				},
+			)
+		}
 	}
-	return mempoolEntriesByAddresses, nil
+
+	return appmessage.NewGetMempoolEntriesByAddressesResponseMessage(mempoolEntriesByAddresses), nil
 }
diff --git a/app/rpc/rpchandlers/get_mempool_entry.go b/app/rpc/rpchandlers/get_mempool_entry.go
index 5bc8938e..e2200ed5 100644
--- a/app/rpc/rpchandlers/get_mempool_entry.go
+++ b/app/rpc/rpchandlers/get_mempool_entry.go
@@ -24,14 +24,7 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
 		return errorMessage, nil
 	}
 
-	if !getMempoolEntryRequest.FilterTransactionPool {
-		transaction, found = context.Domain.MiningManager().GetTransaction(transactionID)
-	}
-
-	if getMempoolEntryRequest.IncludeOrphanPool && !found {
-		transaction, found = context.Domain.MiningManager().GetOrphanTransaction(transactionID)
-		isOrphan = true
-	}
+	mempoolTransaction, isOrphan, found := context.Domain.MiningManager().GetTransaction(transactionID, !getMempoolEntryRequest.FilterTransactionPool, getMempoolEntryRequest.IncludeOrphanPool)
 
 	if !found {
 		errorMessage := &appmessage.GetMempoolEntryResponseMessage{}
@@ -39,7 +32,7 @@ func HandleGetMempoolEntry(context *rpccontext.Context, _ *router.Router, reques
 		return errorMessage, nil
 	}
 
-	rpcTransaction := appmessage.DomainTransactionToRPCTransaction(transaction)
+	rpcTransaction := appmessage.DomainTransactionToRPCTransaction(mempoolTransaction)
 	err = context.PopulateTransactionWithVerboseData(rpcTransaction, nil)
 	if err != nil {
 		return nil, err
diff --git a/domain/consensus/model/externalapi/transaction.go b/domain/consensus/model/externalapi/transaction.go
index a64d3181..b12baec4 100644
--- a/domain/consensus/model/externalapi/transaction.go
+++ b/domain/consensus/model/externalapi/transaction.go
@@ -2,6 +2,7 @@ package externalapi
 
 import (
 	"bytes"
+	"encoding/binary"
 	"fmt"
 
 	"github.com/pkg/errors"
@@ -242,6 +243,23 @@ func (spk *ScriptPublicKey) Equal(other *ScriptPublicKey) bool {
 	return bytes.Equal(spk.Script, other.Script)
 }
 
+// String stringifies a ScriptPublicKey.
+func (spk *ScriptPublicKey) String() string {
+	var versionBytes = make([]byte, 2) // uint16
+	binary.LittleEndian.PutUint16(versionBytes, spk.Version)
+	versionString := string(versionBytes)
+	scriptString := string(spk.Script)
+	return versionString + scriptString
+}
+
+// NewScriptPublicKeyFromString converts the given string to a scriptPublicKey
+func NewScriptPublicKeyFromString(ScriptPublicKeyString string) *ScriptPublicKey {
+	bytes := []byte(ScriptPublicKeyString)
+	version := binary.LittleEndian.Uint16(bytes[:2])
+	script := bytes[2:]
+	return &ScriptPublicKey{Script: script, Version: version}
+}
+
 // DomainTransactionOutput represents a Kaspad transaction output
 type DomainTransactionOutput struct {
 	Value           uint64
diff --git a/domain/miningmanager/mempool/mempool.go b/domain/miningmanager/mempool/mempool.go
index c6eb41fe..f4d6ef7b 100644
--- a/domain/miningmanager/mempool/mempool.go
+++ b/domain/miningmanager/mempool/mempool.go
@@ -1,9 +1,10 @@
 package mempool
 
 import (
-	"github.com/kaspanet/kaspad/domain/consensusreference"
 	"sync"
 
+	"github.com/kaspanet/kaspad/domain/consensusreference"
+
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 	miningmanagermodel "github.com/kaspanet/kaspad/domain/miningmanager/model"
 )
@@ -42,39 +43,89 @@ func (mp *mempool) ValidateAndInsertTransaction(transaction *externalapi.DomainT
 	return mp.validateAndInsertTransaction(transaction, isHighPriority, allowOrphan)
 }
 
-func (mp *mempool) GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
+func (mp *mempool) GetTransaction(transactionID *externalapi.DomainTransactionID,
+	includeTransactionPool bool,
+	includeOrphanPool bool) (
+	transaction *externalapi.DomainTransaction,
+	isOrphan bool,
+	found bool) {
+
 	mp.mtx.RLock()
 	defer mp.mtx.RUnlock()
 
-	return mp.transactionsPool.getTransaction(transactionID)
-}
+	var transactionfound bool
+	isOrphan = false
 
-func (mp *mempool) AllTransactions() []*externalapi.DomainTransaction {
-	mp.mtx.RLock()
-	defer mp.mtx.RUnlock()
+	if includeTransactionPool {
+		transaction, transactionfound = mp.transactionsPool.getTransaction(transactionID, true)
+		isOrphan = false
+	}
+	if !transactionfound && includeOrphanPool {
+		transaction, transactionfound = mp.orphansPool.getOrphanTransaction(transactionID)
+		isOrphan = true
+	}
 
-	return mp.transactionsPool.getAllTransactions()
+	return transaction, isOrphan, transactionfound
 }
 
-func (mp *mempool) GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
+func (mp *mempool) GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
+	sendingInTransactionPool map[string]*externalapi.DomainTransaction,
+	receivingInTransactionPool map[string]*externalapi.DomainTransaction,
+	sendingInOrphanPool map[string]*externalapi.DomainTransaction,
+	receivingInOrphanPool map[string]*externalapi.DomainTransaction,
+	err error) {
 	mp.mtx.RLock()
 	defer mp.mtx.RUnlock()
 
-	return mp.orphansPool.getOrphanTransaction(transactionID)
+	if includeTransactionPool {
+		sendingInTransactionPool, receivingInTransactionPool, err = mp.transactionsPool.getTransactionsByAddresses()
+		if err != nil {
+			return nil, nil, nil, nil, err
+		}
+	}
+
+	if includeOrphanPool {
+		sendingInTransactionPool, receivingInOrphanPool, err = mp.orphansPool.getOrphanTransactionsByAddresses()
+		if err != nil {
+			return nil, nil, nil, nil, err
+		}
+	}
+
+	return sendingInTransactionPool, receivingInTransactionPool, sendingInTransactionPool, receivingInOrphanPool, nil
 }
 
-func (mp *mempool) AllOrphanTransactions() []*externalapi.DomainTransaction {
+func (mp *mempool) AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
+	transactionPoolTransactions []*externalapi.DomainTransaction,
+	orphanPoolTransactions []*externalapi.DomainTransaction) {
+
 	mp.mtx.RLock()
 	defer mp.mtx.RUnlock()
 
-	return mp.orphansPool.getAllOrphanTransactions()
+	if includeTransactionPool {
+		transactionPoolTransactions = mp.transactionsPool.getAllTransactions()
+	}
+
+	if includeOrphanPool {
+		orphanPoolTransactions = mp.orphansPool.getAllOrphanTransactions()
+	}
+
+	return transactionPoolTransactions, orphanPoolTransactions
 }
 
-func (mp *mempool) TransactionCount() int {
+func (mp *mempool) TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int {
 	mp.mtx.RLock()
 	defer mp.mtx.RUnlock()
 
-	return mp.transactionsPool.transactionCount()
+	transactionCount := 0
+
+	if includeOrphanPool {
+		transactionCount += mp.orphansPool.orphanTransactionCount()
+	}
+	if includeTransactionPool {
+		transactionCount += mp.transactionsPool.transactionCount()
+	}
+
+	return transactionCount
 }
 
 func (mp *mempool) HandleNewBlockTransactions(transactions []*externalapi.DomainTransaction) (
diff --git a/domain/miningmanager/mempool/mempool_utxo_set.go b/domain/miningmanager/mempool/mempool_utxo_set.go
index aa20d05d..bbe55525 100644
--- a/domain/miningmanager/mempool/mempool_utxo_set.go
+++ b/domain/miningmanager/mempool/mempool_utxo_set.go
@@ -51,7 +51,7 @@ func (mpus *mempoolUTXOSet) addTransaction(transaction *model.MempoolTransaction
 func (mpus *mempoolUTXOSet) removeTransaction(transaction *model.MempoolTransaction) {
 	for _, input := range transaction.Transaction().Inputs {
 		// If the transaction creating the output spent by this input is in the mempool - restore it's UTXO
-		if _, ok := mpus.mempool.transactionsPool.getTransaction(&input.PreviousOutpoint.TransactionID); ok {
+		if _, ok := mpus.mempool.transactionsPool.getTransaction(&input.PreviousOutpoint.TransactionID, false); ok {
 			mpus.poolUnspentOutputs[input.PreviousOutpoint] = input.UTXOEntry
 		}
 		delete(mpus.transactionByPreviousOutpoint, input.PreviousOutpoint)
diff --git a/domain/miningmanager/mempool/model/map_types.go b/domain/miningmanager/mempool/model/map_types.go
index d6ca685d..ca364287 100644
--- a/domain/miningmanager/mempool/model/map_types.go
+++ b/domain/miningmanager/mempool/model/map_types.go
@@ -15,3 +15,6 @@ type OutpointToUTXOEntryMap map[externalapi.DomainOutpoint]externalapi.UTXOEntry
 
 // OutpointToTransactionMap maps an outpoint to a MempoolTransaction
 type OutpointToTransactionMap map[externalapi.DomainOutpoint]*MempoolTransaction
+
+// ScriptPublicKeyStringToDomainTransaction maps an outpoint to a DomainTransaction
+type ScriptPublicKeyStringToDomainTransaction map[string]*externalapi.DomainTransaction
diff --git a/domain/miningmanager/mempool/orphan_pool.go b/domain/miningmanager/mempool/orphan_pool.go
index d053ecd2..59767d50 100644
--- a/domain/miningmanager/mempool/orphan_pool.go
+++ b/domain/miningmanager/mempool/orphan_pool.go
@@ -169,7 +169,7 @@ func (op *orphansPool) processOrphansAfterAcceptedTransaction(acceptedTransactio
 					}
 					return nil, err
 				}
-				acceptedOrphans = append(acceptedOrphans, orphan.Transaction())
+				acceptedOrphans = append(acceptedOrphans, orphan.Transaction().Clone()) //these pointers leave the mempool, hence the clone
 			}
 		}
 	}
@@ -331,15 +331,45 @@ func (op *orphansPool) randomNonHighPriorityOrphan() *model.OrphanTransaction {
 
 func (op *orphansPool) getOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
 	if orphanTransaction, ok := op.allOrphans[*transactionID]; ok {
-		return orphanTransaction.Transaction(), true
+		return orphanTransaction.Transaction().Clone(), true //this pointer leaves the mempool, hence we clone.
 	}
 	return nil, false
 }
 
+func (op *orphansPool) getOrphanTransactionsByAddresses() (
+	sending model.ScriptPublicKeyStringToDomainTransaction,
+	receiving model.ScriptPublicKeyStringToDomainTransaction,
+	err error) {
+	sending = make(model.ScriptPublicKeyStringToDomainTransaction)
+	receiving = make(model.ScriptPublicKeyStringToDomainTransaction, op.orphanTransactionCount())
+	var transaction *externalapi.DomainTransaction
+	for _, mempoolTransaction := range op.allOrphans {
+		transaction = mempoolTransaction.Transaction().Clone() //these pointers leave the mempool, hence we clone.
+		for _, input := range transaction.Inputs {
+			if input.UTXOEntry == nil { //this is not a bug, but a valid state of orphan transactions with missing outpoints.
+				continue
+			}
+
+			sending[input.UTXOEntry.ScriptPublicKey().String()] = transaction
+		}
+		for _, output := range transaction.Outputs {
+			receiving[output.ScriptPublicKey.String()] = transaction
+
+		}
+	}
+	return sending, receiving, nil
+}
+
 func (op *orphansPool) getAllOrphanTransactions() []*externalapi.DomainTransaction {
-	allOrphanTransactions := make([]*externalapi.DomainTransaction, 0, len(op.allOrphans))
+	allOrphanTransactions := make([]*externalapi.DomainTransaction, len(op.allOrphans))
+	i := 0
 	for _, mempoolTransaction := range op.allOrphans {
-		allOrphanTransactions = append(allOrphanTransactions, mempoolTransaction.Transaction())
+		allOrphanTransactions[i] = mempoolTransaction.Transaction().Clone() //these pointers leave the mempool, hence we clone.
+		i++
 	}
 	return allOrphanTransactions
 }
+
+func (op *orphansPool) orphanTransactionCount() int {
+	return len(op.allOrphans)
+}
diff --git a/domain/miningmanager/mempool/revalidate_high_priority_transactions.go b/domain/miningmanager/mempool/revalidate_high_priority_transactions.go
index 6cb87687..12f76403 100644
--- a/domain/miningmanager/mempool/revalidate_high_priority_transactions.go
+++ b/domain/miningmanager/mempool/revalidate_high_priority_transactions.go
@@ -11,7 +11,6 @@ func (mp *mempool) revalidateHighPriorityTransactions() ([]*externalapi.DomainTr
 	defer onEnd()
 
 	validTransactions := []*externalapi.DomainTransaction{}
-
 	for _, transaction := range mp.transactionsPool.highPriorityTransactions {
 		isValid, err := mp.revalidateTransaction(transaction)
 		if err != nil {
@@ -21,7 +20,7 @@ func (mp *mempool) revalidateHighPriorityTransactions() ([]*externalapi.DomainTr
 			continue
 		}
 
-		validTransactions = append(validTransactions, transaction.Transaction())
+		validTransactions = append(validTransactions, transaction.Transaction().Clone())
 	}
 
 	return validTransactions, nil
diff --git a/domain/miningmanager/mempool/transactions_pool.go b/domain/miningmanager/mempool/transactions_pool.go
index bad4e7f7..63908ee7 100644
--- a/domain/miningmanager/mempool/transactions_pool.go
+++ b/domain/miningmanager/mempool/transactions_pool.go
@@ -6,6 +6,7 @@ import (
 	"github.com/pkg/errors"
 
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
+	"github.com/kaspanet/kaspad/domain/consensus/utils/consensushashing"
 	"github.com/kaspanet/kaspad/domain/miningmanager/mempool/model"
 )
 
@@ -135,7 +136,7 @@ func (tp *transactionsPool) allReadyTransactions() []*externalapi.DomainTransact
 
 	for _, mempoolTransaction := range tp.allTransactions {
 		if len(mempoolTransaction.ParentTransactionsInPool()) == 0 {
-			result = append(result, mempoolTransaction.Transaction())
+			result = append(result, mempoolTransaction.Transaction().Clone()) //this pointer leaves the mempool, and gets its utxo set to nil, hence we clone.
 		}
 	}
 
@@ -204,17 +205,44 @@ func (tp *transactionsPool) limitTransactionCount() error {
 	return nil
 }
 
-func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
+func (tp *transactionsPool) getTransaction(transactionID *externalapi.DomainTransactionID, clone bool) (*externalapi.DomainTransaction, bool) {
 	if mempoolTransaction, ok := tp.allTransactions[*transactionID]; ok {
+		if clone {
+			return mempoolTransaction.Transaction().Clone(), true //this pointer leaves the mempool, hence we clone.
+		}
 		return mempoolTransaction.Transaction(), true
 	}
 	return nil, false
 }
 
+func (tp *transactionsPool) getTransactionsByAddresses() (
+	sending model.ScriptPublicKeyStringToDomainTransaction,
+	receiving model.ScriptPublicKeyStringToDomainTransaction,
+	err error) {
+	sending = make(model.ScriptPublicKeyStringToDomainTransaction, tp.transactionCount())
+	receiving = make(model.ScriptPublicKeyStringToDomainTransaction, tp.transactionCount())
+	var transaction *externalapi.DomainTransaction
+	for _, mempoolTransaction := range tp.allTransactions {
+		transaction = mempoolTransaction.Transaction().Clone() //this pointer leaves the mempool, hence we clone.
+		for _, input := range transaction.Inputs {
+			if input.UTXOEntry == nil {
+				return nil, nil, errors.Errorf("Mempool transaction %s is missing an UTXOEntry. This should be fixed, and not happen", consensushashing.TransactionID(transaction).String())
+			}
+			sending[input.UTXOEntry.ScriptPublicKey().String()] = transaction
+		}
+		for _, output := range transaction.Outputs {
+			receiving[output.ScriptPublicKey.String()] = transaction
+		}
+	}
+	return sending, receiving, nil
+}
+
 func (tp *transactionsPool) getAllTransactions() []*externalapi.DomainTransaction {
-	allTransactions := make([]*externalapi.DomainTransaction, 0, len(tp.allTransactions))
+	allTransactions := make([]*externalapi.DomainTransaction, len(tp.allTransactions))
+	i := 0
 	for _, mempoolTransaction := range tp.allTransactions {
-		allTransactions = append(allTransactions, mempoolTransaction.Transaction())
+		allTransactions[i] = mempoolTransaction.Transaction().Clone() //this pointer leaves the mempool, hence we clone.
+		i++
 	}
 	return allTransactions
 }
diff --git a/domain/miningmanager/mempool/validate_and_insert_transaction.go b/domain/miningmanager/mempool/validate_and_insert_transaction.go
index 3ccedc9b..d06a19ce 100644
--- a/domain/miningmanager/mempool/validate_and_insert_transaction.go
+++ b/domain/miningmanager/mempool/validate_and_insert_transaction.go
@@ -54,7 +54,7 @@ func (mp *mempool) validateAndInsertTransaction(transaction *externalapi.DomainT
 		return nil, err
 	}
 
-	acceptedTransactions = append([]*externalapi.DomainTransaction{transaction}, acceptedOrphans...)
+	acceptedTransactions = append([]*externalapi.DomainTransaction{transaction.Clone()}, acceptedOrphans...) //these pointer leave the mempool, hence we clone.
 
 	err = mp.transactionsPool.limitTransactionCount()
 	if err != nil {
diff --git a/domain/miningmanager/miningmanager.go b/domain/miningmanager/miningmanager.go
index 08e576f1..10bf6d0d 100644
--- a/domain/miningmanager/miningmanager.go
+++ b/domain/miningmanager/miningmanager.go
@@ -15,11 +15,20 @@ type MiningManager interface {
 	GetBlockTemplate(coinbaseData *externalapi.DomainCoinbaseData) (block *externalapi.DomainBlock, isNearlySynced bool, err error)
 	ClearBlockTemplate()
 	GetBlockTemplateBuilder() miningmanagermodel.BlockTemplateBuilder
-	GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
-	AllTransactions() []*externalapi.DomainTransaction
-	GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
-	AllOrphanTransactions() []*externalapi.DomainTransaction
-	TransactionCount() int
+	GetTransaction(transactionID *externalapi.DomainTransactionID, includeTransactionPool bool, includeOrphanPool bool) (
+		transactionPoolTransaction *externalapi.DomainTransaction,
+		isOrphan bool,
+		found bool)
+	GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
+		sendingInTransactionPool map[string]*externalapi.DomainTransaction,
+		receivingInTransactionPool map[string]*externalapi.DomainTransaction,
+		sendingInOrphanPool map[string]*externalapi.DomainTransaction,
+		receivingInOrphanPool map[string]*externalapi.DomainTransaction,
+		err error)
+	AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
+		transactionPoolTransactions []*externalapi.DomainTransaction,
+		orphanPoolTransactions []*externalapi.DomainTransaction)
+	TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int
 	HandleNewBlockTransactions(txs []*externalapi.DomainTransaction) ([]*externalapi.DomainTransaction, error)
 	ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) (
 		acceptedTransactions []*externalapi.DomainTransaction, err error)
@@ -109,28 +118,35 @@ func (mm *miningManager) ValidateAndInsertTransaction(transaction *externalapi.D
 }
 
 func (mm *miningManager) GetTransaction(
-	transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
-
-	return mm.mempool.GetTransaction(transactionID)
-}
-
-func (mm *miningManager) AllTransactions() []*externalapi.DomainTransaction {
-	return mm.mempool.AllTransactions()
+	transactionID *externalapi.DomainTransactionID,
+	includeTransactionPool bool,
+	includeOrphanPool bool) (
+	transactionPoolTransaction *externalapi.DomainTransaction,
+	isOrphan bool,
+	found bool) {
+
+	return mm.mempool.GetTransaction(transactionID, includeTransactionPool, includeOrphanPool)
 }
 
-func (mm *miningManager) GetOrphanTransaction(
-	transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool) {
+func (mm *miningManager) AllTransactions(includeTransactionPool bool, includeOrphanPool bool) (
+	transactionPoolTransactions []*externalapi.DomainTransaction,
+	orphanPoolTransactions []*externalapi.DomainTransaction) {
 
-	return mm.mempool.GetOrphanTransaction(transactionID)
+	return mm.mempool.AllTransactions(includeTransactionPool, includeOrphanPool)
 }
 
-func (mm *miningManager) AllOrphanTransactions() []*externalapi.DomainTransaction {
+func (mm *miningManager) GetTransactionsByAddresses(includeTransactionPool bool, includeOrphanPool bool) (
+	sendingInTransactionPool map[string]*externalapi.DomainTransaction,
+	receivingInTransactionPool map[string]*externalapi.DomainTransaction,
+	sendingInOrphanPool map[string]*externalapi.DomainTransaction,
+	receivingInOrphanPool map[string]*externalapi.DomainTransaction,
+	err error) {
 
-	return mm.mempool.AllOrphanTransactions()
+	return mm.mempool.GetTransactionsByAddresses(includeTransactionPool, includeOrphanPool)
 }
 
-func (mm *miningManager) TransactionCount() int {
-	return mm.mempool.TransactionCount()
+func (mm *miningManager) TransactionCount(includeTransactionPool bool, includeOrphanPool bool) int {
+	return mm.mempool.TransactionCount(includeTransactionPool, includeOrphanPool)
 }
 
 func (mm *miningManager) RevalidateHighPriorityTransactions() (
diff --git a/domain/miningmanager/model/interface_mempool.go b/domain/miningmanager/model/interface_mempool.go
index caf6b596..fe9be9b5 100644
--- a/domain/miningmanager/model/interface_mempool.go
+++ b/domain/miningmanager/model/interface_mempool.go
@@ -12,11 +12,31 @@ type Mempool interface {
 	ValidateAndInsertTransaction(transaction *externalapi.DomainTransaction, isHighPriority bool, allowOrphan bool) (
 		acceptedTransactions []*externalapi.DomainTransaction, err error)
 	RemoveTransactions(txs []*externalapi.DomainTransaction, removeRedeemers bool) error
-	GetTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
-	AllTransactions() []*externalapi.DomainTransaction
-	GetOrphanTransaction(transactionID *externalapi.DomainTransactionID) (*externalapi.DomainTransaction, bool)
-	AllOrphanTransactions() []*externalapi.DomainTransaction
-	TransactionCount() int
+	GetTransaction(
+		transactionID *externalapi.DomainTransactionID,
+		includeTransactionPool bool,
+		includeOrphanPool bool,
+	) (
+		transactionPoolTransaction *externalapi.DomainTransaction,
+		isOrphan bool,
+		found bool)
+	GetTransactionsByAddresses(
+		includeTransactionPool bool,
+		includeOrphanPool bool) (
+		sendingInTransactionPool map[string]*externalapi.DomainTransaction,
+		receivingInTransactionPool map[string]*externalapi.DomainTransaction,
+		sendingInOrphanPool map[string]*externalapi.DomainTransaction,
+		receivingInOrphanPool map[string]*externalapi.DomainTransaction,
+		err error)
+	AllTransactions(
+		includeTransactionPool bool,
+		includeOrphanPool bool,
+	) (
+		transactionPoolTransactions []*externalapi.DomainTransaction,
+		orphanPoolTransactions []*externalapi.DomainTransaction)
+	TransactionCount(
+		includeTransactionPool bool,
+		includeOrphanPool bool) int
 	RevalidateHighPriorityTransactions() (validTransactions []*externalapi.DomainTransaction, err error)
 	IsTransactionOutputDust(output *externalapi.DomainTransactionOutput) bool
 }
diff --git a/domain/utxoindex/model.go b/domain/utxoindex/model.go
index 2ceb6560..a6ffefc1 100644
--- a/domain/utxoindex/model.go
+++ b/domain/utxoindex/model.go
@@ -1,7 +1,6 @@
 package utxoindex
 
 import (
-	"encoding/binary"
 	"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
 )
 
@@ -22,22 +21,3 @@ type UTXOChanges struct {
 	Added   map[ScriptPublicKeyString]UTXOOutpointEntryPairs
 	Removed map[ScriptPublicKeyString]UTXOOutpointEntryPairs
 }
-
-// ConvertScriptPublicKeyToString converts the given scriptPublicKey to a string
-func ConvertScriptPublicKeyToString(scriptPublicKey *externalapi.ScriptPublicKey) ScriptPublicKeyString {
-	var versionBytes = make([]byte, 2) // uint16
-	binary.LittleEndian.PutUint16(versionBytes, scriptPublicKey.Version)
-	versionString := ScriptPublicKeyString(versionBytes)
-	scriptString := ScriptPublicKeyString(scriptPublicKey.Script)
-	return versionString + scriptString
-
-}
-
-// ConvertStringToScriptPublicKey converts the given string to a scriptPublicKey
-func ConvertStringToScriptPublicKey(string ScriptPublicKeyString) *externalapi.ScriptPublicKey {
-	bytes := []byte(string)
-	version := binary.LittleEndian.Uint16(bytes[:2])
-	script := bytes[2:]
-	return &externalapi.ScriptPublicKey{Script: script, Version: version}
-
-}
diff --git a/domain/utxoindex/store.go b/domain/utxoindex/store.go
index dbca42b7..d42f4ac5 100644
--- a/domain/utxoindex/store.go
+++ b/domain/utxoindex/store.go
@@ -32,7 +32,7 @@ func newUTXOIndexStore(database database.Database) *utxoIndexStore {
 
 func (uis *utxoIndexStore) add(scriptPublicKey *externalapi.ScriptPublicKey, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error {
 
-	key := ConvertScriptPublicKeyToString(scriptPublicKey)
+	key := ScriptPublicKeyString(scriptPublicKey.String())
 	log.Tracef("Adding outpoint %s:%d to scriptPublicKey %s",
 		outpoint.TransactionID, outpoint.Index, key)
 
@@ -66,7 +66,7 @@ func (uis *utxoIndexStore) add(scriptPublicKey *externalapi.ScriptPublicKey, out
 }
 
 func (uis *utxoIndexStore) remove(scriptPublicKey *externalapi.ScriptPublicKey, outpoint *externalapi.DomainOutpoint, utxoEntry externalapi.UTXOEntry) error {
-	key := ConvertScriptPublicKeyToString(scriptPublicKey)
+	key := ScriptPublicKeyString(scriptPublicKey.String())
 	log.Tracef("Removing outpoint %s:%d from scriptPublicKey %s",
 		outpoint.TransactionID, outpoint.Index, key)
 
@@ -122,7 +122,7 @@ func (uis *utxoIndexStore) commit() error {
 	toRemoveSompiSupply := uint64(0)
 
 	for scriptPublicKeyString, toRemoveUTXOOutpointEntryPairs := range uis.toRemove {
-		scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
+		scriptPublicKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
 		bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
 		for outpointToRemove, utxoEntryToRemove := range toRemoveUTXOOutpointEntryPairs {
 			key, err := uis.convertOutpointToKey(bucket, &outpointToRemove)
@@ -140,7 +140,7 @@ func (uis *utxoIndexStore) commit() error {
 	toAddSompiSupply := uint64(0)
 
 	for scriptPublicKeyString, toAddUTXOOutpointEntryPairs := range uis.toAdd {
-		scriptPublicKey := ConvertStringToScriptPublicKey(scriptPublicKeyString)
+		scriptPublicKey := externalapi.NewScriptPublicKeyFromString(string(scriptPublicKeyString))
 		bucket := uis.bucketForScriptPublicKey(scriptPublicKey)
 		for outpointToAdd, utxoEntryToAdd := range toAddUTXOOutpointEntryPairs {
 			key, err := uis.convertOutpointToKey(bucket, &outpointToAdd)


Test patch:
diff --git a/domain/miningmanager/miningmanager_test.go b/domain/miningmanager/miningmanager_test.go
index d8a31aed..d98950b1 100644
--- a/domain/miningmanager/miningmanager_test.go
+++ b/domain/miningmanager/miningmanager_test.go
@@ -52,7 +52,7 @@ func TestValidateAndInsertTransaction(t *testing.T) {
 		}
 		// The UTXOEntry was filled manually for those transactions, so the transactions won't be considered orphans.
 		// Therefore, all the transactions expected to be contained in the mempool.
-		transactionsFromMempool := miningManager.AllTransactions()
+		transactionsFromMempool, _ := miningManager.AllTransactions(true, false)
 		if len(transactionsToInsert) != len(transactionsFromMempool) {
 			t.Fatalf("Wrong number of transactions in mempool: expected: %d, got: %d", len(transactionsToInsert), len(transactionsFromMempool))
 		}
@@ -72,7 +72,7 @@ func TestValidateAndInsertTransaction(t *testing.T) {
 		if err != nil {
 			t.Fatalf("ValidateAndInsertTransaction: %v", err)
 		}
-		transactionsFromMempool = miningManager.AllTransactions()
+		transactionsFromMempool, _ = miningManager.AllTransactions(true, false)
 		if !contains(transactionNotAnOrphan, transactionsFromMempool) {
 			t.Fatalf("Missing transaction %s in the mempool", consensushashing.TransactionID(transactionNotAnOrphan))
 		}
@@ -99,7 +99,7 @@ func TestImmatureSpend(t *testing.T) {
 		if !errors.As(err, txRuleError) || txRuleError.RejectCode != mempool.RejectImmatureSpend {
 			t.Fatalf("Unexpected error %+v", err)
 		}
-		transactionsFromMempool := miningManager.AllTransactions()
+		transactionsFromMempool, _ := miningManager.AllTransactions(true, false)
 		if contains(tx, transactionsFromMempool) {
 			t.Fatalf("Mempool contains a transaction with immature coinbase")
 		}
@@ -205,7 +205,7 @@ func TestHandleNewBlockTransactions(t *testing.T) {
 		if err != nil {
 			t.Fatalf("HandleNewBlockTransactions: %v", err)
 		}
-		mempoolTransactions := miningManager.AllTransactions()
+		mempoolTransactions, _ := miningManager.AllTransactions(true, false)
 		for _, removedTransaction := range blockWithFirstPartOfTheTransactions {
 			if contains(removedTransaction, mempoolTransactions) {
 				t.Fatalf("This transaction shouldnt be in mempool: %s", consensushashing.TransactionID(removedTransaction))
@@ -214,7 +214,7 @@ func TestHandleNewBlockTransactions(t *testing.T) {
 
 		// There are no chained/double-spends transactions, and hence it is expected that all the other
 		// transactions, will still be included in the mempool.
-		mempoolTransactions = miningManager.AllTransactions()
+		mempoolTransactions, _ = miningManager.AllTransactions(true, false)
 		for _, transaction := range blockWithRestOfTheTransactions[transactionhelper.CoinbaseTransactionIndex+1:] {
 			if !contains(transaction, mempoolTransactions) {
 				t.Fatalf("This transaction %s should be in mempool.", consensushashing.TransactionID(transaction))
@@ -225,8 +225,9 @@ func TestHandleNewBlockTransactions(t *testing.T) {
 		if err != nil {
 			t.Fatalf("HandleNewBlockTransactions: %v", err)
 		}
-		if len(miningManager.AllTransactions()) != 0 {
-			blockIDs := domainBlocksToBlockIds(miningManager.AllTransactions())
+		mempoolTransactions, _ = miningManager.AllTransactions(true, false)
+		if len(mempoolTransactions) != 0 {
+			blockIDs := domainBlocksToBlockIds(mempoolTransactions)
 			t.Fatalf("The mempool contains unexpected transactions: %s", blockIDs)
 		}
 	})
@@ -269,7 +270,8 @@ func TestDoubleSpendWithBlock(t *testing.T) {
 		if err != nil {
 			t.Fatalf("HandleNewBlockTransactions: %v", err)
 		}
-		if contains(transactionInTheMempool, miningManager.AllTransactions()) {
+		mempoolTransactions, _ := miningManager.AllTransactions(true, false)
+		if contains(transactionInTheMempool, mempoolTransactions) {
 			t.Fatalf("The transaction %s, shouldn't be in the mempool, since at least one "+
 				"output was already spent.", consensushashing.TransactionID(transactionInTheMempool))
 		}
@@ -303,7 +305,7 @@ func TestOrphanTransactions(t *testing.T) {
 				t.Fatalf("ValidateAndInsertTransaction: %v", err)
 			}
 		}
-		transactionsMempool := miningManager.AllTransactions()
+		transactionsMempool, _ := miningManager.AllTransactions(true, false)
 		for _, transaction := range transactionsMempool {
 			if contains(transaction, childTransactions) {
 				t.Fatalf("Error: an orphan transaction is exist in the mempool")
@@ -345,7 +347,7 @@ func TestOrphanTransactions(t *testing.T) {
 		if err != nil {
 			t.Fatalf("HandleNewBlockTransactions: %+v", err)
 		}
-		transactionsMempool = miningManager.AllTransactions()
+		transactionsMempool, _ = miningManager.AllTransactions(true, false)
 		if len(transactionsMempool) != len(childTransactions) {
 			t.Fatalf("Expected %d transactions in the mempool but got %d", len(childTransactions), len(transactionsMempool))
 		}
@@ -553,8 +555,8 @@ func TestRevalidateHighPriorityTransactions(t *testing.T) {
 		}
 
 		// Make sure spendingTransaction is still in mempool
-		allTransactions := miningManager.AllTransactions()
-		if len(allTransactions) != 1 || !allTransactions[0].Equal(spendingTransaction) {
+		mempoolTransactions, _ := miningManager.AllTransactions(true, false)
+		if len(mempoolTransactions) != 1 || !mempoolTransactions[0].Equal(spendingTransaction) {
 			t.Fatalf("Expected to have spendingTransaction as only validTransaction returned from "+
 				"RevalidateHighPriorityTransactions, but got %v instead", validTransactions)
 		}
@@ -568,9 +570,9 @@ func TestRevalidateHighPriorityTransactions(t *testing.T) {
 			t.Fatalf("Expected to have empty validTransactions, but got %v instead", validTransactions)
 		}
 		// And also AllTransactions should be empty as well
-		allTransactions = miningManager.AllTransactions()
-		if len(allTransactions) != 0 {
-			t.Fatalf("Expected to have empty allTransactions, but got %v instead", allTransactions)
+		mempoolTransactions, _ = miningManager.AllTransactions(true, false)
+		if len(mempoolTransactions) != 0 {
+			t.Fatalf("Expected to have empty allTransactions, but got %v instead", mempoolTransactions)
 		}
 	})
 }
@@ -605,7 +607,7 @@ func TestModifyBlockTemplate(t *testing.T) {
 				t.Fatalf("ValidateAndInsertTransaction: %v", err)
 			}
 		}
-		transactionsMempool := miningManager.AllTransactions()
+		transactionsMempool, _ := miningManager.AllTransactions(true, false)
 		for _, transaction := range transactionsMempool {
 			if contains(transaction, childTransactions) {
 				t.Fatalf("Error: an orphan transaction is exist in the mempool")
@@ -654,7 +656,7 @@ func TestModifyBlockTemplate(t *testing.T) {
 		if err != nil {
 			t.Fatalf("HandleNewBlockTransactions: %+v", err)
 		}
-		transactionsMempool = miningManager.AllTransactions()
+		transactionsMempool, _ = miningManager.AllTransactions(true, false)
 		if len(transactionsMempool) != len(childTransactions) {
 			t.Fatalf("Expected %d transactions in the mempool but got %d", len(childTransactions), len(transactionsMempool))
 		}