Repository: hyperledger/firefly
Pull request #849 (2022-06-02 17:36:26)

Enhancements to migration/network version behavior
Network version is loaded during blockchain plugin init, instead of lazily. This is (soon) going to be needed at manager initialization time, so might as well load it once upfront.

Since we currently have a single blockchain plugin that services all namespaces, any termination/migration event is recorded against ALL namespaces (rather than arbitrarily recording it against ff_system only).


Patch:
diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go
index 2b7791e9c..e9c280231 100644
--- a/internal/blockchain/ethereum/ethereum.go
+++ b/internal/blockchain/ethereum/ethereum.go
@@ -273,16 +273,20 @@ func (e *Ethereum) ConfigureContract(ctx context.Context, contracts *core.FireFl
 
 	sub, err := e.streams.ensureFireFlySubscription(ctx, address, fromBlock, e.streamID, batchPinEventABI)
 	if err == nil {
-		e.fireflyContract.mux.Lock()
-		e.fireflyContract.address = address
-		e.fireflyContract.fromBlock = fromBlock
-		e.fireflyContract.networkVersion = 0
-		e.fireflyContract.subscription = sub.ID
-		e.fireflyContract.mux.Unlock()
-		contracts.Active.Info = fftypes.JSONObject{
-			"address":      address,
-			"fromBlock":    fromBlock,
-			"subscription": sub.ID,
+		var version int
+		version, err = e.getNetworkVersion(ctx, address)
+		if err == nil {
+			e.fireflyContract.mux.Lock()
+			e.fireflyContract.address = address
+			e.fireflyContract.fromBlock = fromBlock
+			e.fireflyContract.networkVersion = version
+			e.fireflyContract.subscription = sub.ID
+			e.fireflyContract.mux.Unlock()
+			contracts.Active.Info = fftypes.JSONObject{
+				"address":      address,
+				"fromBlock":    fromBlock,
+				"subscription": sub.ID,
+			}
 		}
 	}
 	return err
@@ -295,12 +299,12 @@ func (e *Ethereum) TerminateContract(ctx context.Context, contracts *core.FireFl
 		return err
 	}
 	e.fireflyContract.mux.Lock()
-	if address != e.fireflyContract.address {
-		log.L(ctx).Warnf("Ignoring termination request from address %s, which differs from active address %s", address, e.fireflyContract.address)
-		e.fireflyContract.mux.Unlock()
+	fireflyAddress := e.fireflyContract.address
+	e.fireflyContract.mux.Unlock()
+	if address != fireflyAddress {
+		log.L(ctx).Warnf("Ignoring termination request from address %s, which differs from active address %s", address, fireflyAddress)
 		return nil
 	}
-	e.fireflyContract.mux.Unlock()
 
 	log.L(ctx).Infof("Processing termination request from address %s", address)
 	contracts.Active.FinalEvent = termination.ProtocolID
@@ -1093,16 +1097,8 @@ func (e *Ethereum) getNetworkVersion(ctx context.Context, address string) (int,
 	return strconv.Atoi(output.Output.(string))
 }
 
-func (e *Ethereum) NetworkVersion(ctx context.Context) (int, error) {
+func (e *Ethereum) NetworkVersion(ctx context.Context) int {
 	e.fireflyContract.mux.Lock()
 	defer e.fireflyContract.mux.Unlock()
-	if e.fireflyContract.networkVersion > 0 {
-		return e.fireflyContract.networkVersion, nil
-	}
-	address := e.fireflyContract.address
-	version, err := e.getNetworkVersion(ctx, address)
-	if err == nil {
-		e.fireflyContract.networkVersion = version
-	}
-	return version, err
+	return e.fireflyContract.networkVersion
 }
diff --git a/internal/blockchain/fabric/fabric.go b/internal/blockchain/fabric/fabric.go
index c83c93684..7dbd85725 100644
--- a/internal/blockchain/fabric/fabric.go
+++ b/internal/blockchain/fabric/fabric.go
@@ -249,16 +249,20 @@ func (f *Fabric) ConfigureContract(ctx context.Context, contracts *core.FireFlyC
 	location := &Location{Channel: f.defaultChannel, Chaincode: chaincode}
 	sub, err := f.streams.ensureFireFlySubscription(ctx, location, fromBlock, f.streamID, batchPinEvent)
 	if err == nil {
-		f.fireflyContract.mux.Lock()
-		f.fireflyContract.chaincode = chaincode
-		f.fireflyContract.fromBlock = fromBlock
-		f.fireflyContract.networkVersion = 0
-		f.fireflyContract.subscription = sub.ID
-		f.fireflyContract.mux.Unlock()
-		contracts.Active.Info = fftypes.JSONObject{
-			"chaincode":    chaincode,
-			"fromBlock":    fromBlock,
-			"subscription": sub.ID,
+		var version int
+		version, err = f.getNetworkVersion(ctx, chaincode)
+		if err == nil {
+			f.fireflyContract.mux.Lock()
+			f.fireflyContract.chaincode = chaincode
+			f.fireflyContract.fromBlock = fromBlock
+			f.fireflyContract.networkVersion = version
+			f.fireflyContract.subscription = sub.ID
+			f.fireflyContract.mux.Unlock()
+			contracts.Active.Info = fftypes.JSONObject{
+				"chaincode":    chaincode,
+				"fromBlock":    fromBlock,
+				"subscription": sub.ID,
+			}
 		}
 	}
 	return err
@@ -268,12 +272,12 @@ func (f *Fabric) TerminateContract(ctx context.Context, contracts *core.FireFlyC
 
 	chaincode := termination.Info.GetString("chaincodeId")
 	f.fireflyContract.mux.Lock()
-	if chaincode != f.fireflyContract.chaincode {
-		log.L(ctx).Warnf("Ignoring termination request from chaincode %s, which differs from active chaincode %s", chaincode, f.fireflyContract.chaincode)
-		f.fireflyContract.mux.Unlock()
+	fireflyChaincode := f.fireflyContract.chaincode
+	f.fireflyContract.mux.Unlock()
+	if chaincode != fireflyChaincode {
+		log.L(ctx).Warnf("Ignoring termination request from chaincode %s, which differs from active chaincode %s", chaincode, fireflyChaincode)
 		return nil
 	}
-	f.fireflyContract.mux.Unlock()
 
 	log.L(ctx).Infof("Processing termination request from chaincode %s", chaincode)
 	contracts.Active.FinalEvent = termination.ProtocolID
@@ -844,16 +848,8 @@ func (f *Fabric) getNetworkVersion(ctx context.Context, chaincode string) (int,
 	return int(output.Result.(float64)), nil
 }
 
-func (f *Fabric) NetworkVersion(ctx context.Context) (int, error) {
+func (f *Fabric) NetworkVersion(ctx context.Context) int {
 	f.fireflyContract.mux.Lock()
 	defer f.fireflyContract.mux.Unlock()
-	if f.fireflyContract.networkVersion > 0 {
-		return f.fireflyContract.networkVersion, nil
-	}
-	chaincode := f.fireflyContract.chaincode
-	version, err := f.getNetworkVersion(ctx, chaincode)
-	if err == nil {
-		f.fireflyContract.networkVersion = version
-	}
-	return version, err
+	return f.fireflyContract.networkVersion
 }
diff --git a/internal/events/network_action.go b/internal/events/network_action.go
index 957314580..60f7a70f4 100644
--- a/internal/events/network_action.go
+++ b/internal/events/network_action.go
@@ -22,18 +22,28 @@ import (
 	"github.com/hyperledger/firefly-common/pkg/log"
 	"github.com/hyperledger/firefly/pkg/blockchain"
 	"github.com/hyperledger/firefly/pkg/core"
+	"github.com/hyperledger/firefly/pkg/database"
 )
 
 func (em *eventManager) actionTerminate(bi blockchain.Plugin, event *blockchain.Event) error {
+	f := database.NamespaceQueryFactory.NewFilter(em.ctx)
+	namespaces, _, err := em.database.GetNamespaces(em.ctx, f.And())
+	if err != nil {
+		return err
+	}
+	contracts := &namespaces[0].Contracts
+	if err := bi.TerminateContract(em.ctx, contracts, event); err != nil {
+		return err
+	}
+	// Currently, a termination event is implied to apply to ALL namespaces
 	return em.database.RunAsGroup(em.ctx, func(ctx context.Context) error {
-		ns, err := em.database.GetNamespace(ctx, core.LegacySystemNamespace)
-		if err != nil {
-			return err
-		}
-		if err := bi.TerminateContract(ctx, &ns.Contracts, event); err != nil {
-			return err
+		for _, ns := range namespaces {
+			ns.Contracts = *contracts
+			if err := em.database.UpsertNamespace(em.ctx, ns, true); err != nil {
+				return err
+			}
 		}
-		return em.database.UpsertNamespace(ctx, ns, true)
+		return nil
 	})
 }
 
diff --git a/internal/identity/identitymanager.go b/internal/identity/identitymanager.go
index d481c60ad..0aa9c61f9 100644
--- a/internal/identity/identitymanager.go
+++ b/internal/identity/identitymanager.go
@@ -391,13 +391,9 @@ func (im *identityManager) cachedIdentityLookupByVerifierRef(ctx context.Context
 	if err != nil {
 		return nil, err
 	} else if verifier == nil {
-		if namespace != core.LegacySystemNamespace {
-			if version, err := im.blockchain.NetworkVersion(ctx); err != nil {
-				return nil, err
-			} else if version == 1 {
-				// For V1 networks, fall back to SystemNamespace for looking up identities
-				return im.cachedIdentityLookupByVerifierRef(ctx, core.LegacySystemNamespace, verifierRef)
-			}
+		if namespace != core.LegacySystemNamespace && im.blockchain.NetworkVersion(ctx) == 1 {
+			// For V1 networks, fall back to SystemNamespace for looking up identities
+			return im.cachedIdentityLookupByVerifierRef(ctx, core.LegacySystemNamespace, verifierRef)
 		}
 		return nil, err
 	}
diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go
index 6aa7a6480..ad3b654dc 100644
--- a/internal/metrics/metrics.go
+++ b/internal/metrics/metrics.go
@@ -51,6 +51,7 @@ type metricsManager struct {
 }
 
 func (mm *metricsManager) Start() error {
+	Registry() // ensure registry is initialized
 	return nil
 }
 
diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go
index f265e8570..945629fad 100644
--- a/internal/orchestrator/orchestrator.go
+++ b/internal/orchestrator/orchestrator.go
@@ -223,6 +223,9 @@ func (or *orchestrator) Init(ctx context.Context, cancelCtx context.CancelFunc)
 }
 
 func (or *orchestrator) Start() (err error) {
+	if err == nil {
+		err = or.metrics.Start()
+	}
 	if err == nil {
 		err = or.batch.Start()
 	}
@@ -265,9 +268,6 @@ func (or *orchestrator) Start() (err error) {
 			}
 		}
 	}
-	if err == nil {
-		err = or.metrics.Start()
-	}
 	or.started = true
 	return err
 }
diff --git a/mocks/blockchainmocks/plugin.go b/mocks/blockchainmocks/plugin.go
index 557e9702c..aee0abae3 100644
--- a/mocks/blockchainmocks/plugin.go
+++ b/mocks/blockchainmocks/plugin.go
@@ -188,7 +188,7 @@ func (_m *Plugin) Name() string {
 }
 
 // NetworkVersion provides a mock function with given fields: ctx
-func (_m *Plugin) NetworkVersion(ctx context.Context) (int, error) {
+func (_m *Plugin) NetworkVersion(ctx context.Context) int {
 	ret := _m.Called(ctx)
 
 	var r0 int
@@ -198,14 +198,7 @@ func (_m *Plugin) NetworkVersion(ctx context.Context) (int, error) {
 		r0 = ret.Get(0).(int)
 	}
 
-	var r1 error
-	if rf, ok := ret.Get(1).(func(context.Context) error); ok {
-		r1 = rf(ctx)
-	} else {
-		r1 = ret.Error(1)
-	}
-
-	return r0, r1
+	return r0
 }
 
 // NormalizeContractLocation provides a mock function with given fields: ctx, location
diff --git a/pkg/blockchain/plugin.go b/pkg/blockchain/plugin.go
index fc65a9603..a91f0abac 100644
--- a/pkg/blockchain/plugin.go
+++ b/pkg/blockchain/plugin.go
@@ -91,7 +91,7 @@ type Plugin interface {
 	GenerateEventSignature(ctx context.Context, event *core.FFIEventDefinition) string
 
 	// NetworkVersion returns the version of the network rules being used by this plugin
-	NetworkVersion(ctx context.Context) (int, error)
+	NetworkVersion(ctx context.Context) int
 }
 
 const FireFlyActionPrefix = "firefly:"


Test patch:
diff --git a/internal/blockchain/ethereum/ethereum_test.go b/internal/blockchain/ethereum/ethereum_test.go
index ada6a21eb..804b18cd9 100644
--- a/internal/blockchain/ethereum/ethereum_test.go
+++ b/internal/blockchain/ethereum/ethereum_test.go
@@ -101,11 +101,26 @@ func newTestEthereum() (*Ethereum, func()) {
 	}
 }
 
+func mockNetworkVersion(t *testing.T, version int) func(req *http.Request) (*http.Response, error) {
+	return func(req *http.Request) (*http.Response, error) {
+		var body map[string]interface{}
+		json.NewDecoder(req.Body).Decode(&body)
+		headers := body["headers"].(map[string]interface{})
+		method := body["method"].(map[string]interface{})
+		if headers["type"] == "Query" && method["name"] == "networkVersion" {
+			return httpmock.NewJsonResponderOrPanic(200, queryOutput{
+				Output: fmt.Sprintf("%d", version),
+			})(req)
+		}
+		return nil, nil
+	}
+}
+
 func TestInitMissingURL(t *testing.T) {
 	e, cancel := newTestEthereum()
 	defer cancel()
 	resetConf(e)
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10138.*url", err)
 }
 
@@ -114,7 +129,7 @@ func TestInitBadAddressResolver(t *testing.T) {
 	defer cancel()
 	resetConf(e)
 	utAddressResolverConf.Set(AddressResolverURLTemplate, "{{unclosed}")
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10337.*urlTemplate", err)
 }
 
@@ -125,7 +140,7 @@ func TestInitMissingTopic(t *testing.T) {
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x12345")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10138.*topic", err)
 }
 
@@ -159,6 +174,7 @@ func TestInitAndStartWithFFTM(t *testing.T) {
 			assert.Equal(t, "es12345", body["stream"])
 			return httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sub12345"})(req)
 		})
+	httpmock.RegisterResponder("POST", fmt.Sprintf("%s/", httpURL), mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, httpURL)
@@ -167,7 +183,7 @@ func TestInitAndStartWithFFTM(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utFFTMConf.Set(ffresty.HTTPConfigURL, "http://fftm.example.com:12345")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	assert.NotNil(t, e.fftmClient)
 
@@ -177,7 +193,7 @@ func TestInitAndStartWithFFTM(t *testing.T) {
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
 
-	assert.Equal(t, 4, httpmock.GetTotalCallCount())
+	assert.Equal(t, 5, httpmock.GetTotalCallCount())
 	assert.Equal(t, "es12345", e.streamID)
 	assert.Equal(t, "sub12345", e.fireflyContract.subscription)
 	assert.NotNil(t, e.Capabilities())
@@ -210,7 +226,7 @@ func TestWSInitFail(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF00149", err)
 
 }
@@ -234,7 +250,7 @@ func TestInitMissingInstance(t *testing.T) {
 	utEthconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.Regexp(t, "FF10138.*instance", err)
@@ -258,6 +274,7 @@ func TestInitAllExistingStreams(t *testing.T) {
 		}))
 	httpmock.RegisterResponder("PATCH", "http://localhost:12345/eventstreams/es12345",
 		httpmock.NewJsonResponderOrPanic(200, &eventStream{ID: "es12345", WebSocket: eventStreamWebsocket{Topic: "topic1"}}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -265,12 +282,12 @@ func TestInitAllExistingStreams(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
 
-	assert.Equal(t, 3, httpmock.GetTotalCallCount())
+	assert.Equal(t, 4, httpmock.GetTotalCallCount())
 	assert.Equal(t, "es12345", e.streamID)
 	assert.Equal(t, "sub12345", e.fireflyContract.subscription)
 
@@ -308,6 +325,7 @@ func TestInitOldInstancePathContracts(t *testing.T) {
 			"registeredAs": "firefly",
 		}),
 	)
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -315,7 +333,7 @@ func TestInitOldInstancePathContracts(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/contracts/firefly")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
@@ -345,6 +363,7 @@ func TestInitOldInstancePathInstances(t *testing.T) {
 			assert.Equal(t, "es12345", body["stream"])
 			return httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sub12345"})(req)
 		})
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -352,7 +371,7 @@ func TestInitOldInstancePathInstances(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
@@ -392,7 +411,7 @@ func TestInitOldInstancePathError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/contracts/firefly")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.Regexp(t, "FF10111.*pop", err)
@@ -415,6 +434,7 @@ func TestInitNewConfig(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 2))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -422,12 +442,13 @@ func TestInitNewConfig(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
 
-	assert.Equal(t, e.fireflyContract.address, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f")
+	assert.Equal(t, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f", e.fireflyContract.address)
+	assert.Equal(t, 2, e.fireflyContract.networkVersion)
 }
 
 func TestInitNewConfigError(t *testing.T) {
@@ -450,7 +471,7 @@ func TestInitNewConfigError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.Regexp(t, "FF10138", err)
@@ -476,7 +497,7 @@ func TestInitNewConfigBadIndex(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{
 		Active: core.FireFlyContractInfo{Index: 1},
@@ -484,6 +505,105 @@ func TestInitNewConfigBadIndex(t *testing.T) {
 	assert.Regexp(t, "FF10396", err)
 }
 
+func TestInitNetworkVersionNotFound(t *testing.T) {
+	e, cancel := newTestEthereum()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/",
+		httpmock.NewJsonResponderOrPanic(500, ethError{Error: "FFEC100148"}))
+
+	resetConf(e)
+	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utEthconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.NoError(t, err)
+
+	assert.Equal(t, "0x71c7656ec7ab88b098defb751b7401b5f6d8976f", e.fireflyContract.address)
+	assert.Equal(t, 1, e.fireflyContract.networkVersion)
+}
+
+func TestInitNetworkVersionError(t *testing.T) {
+	e, cancel := newTestEthereum()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/",
+		httpmock.NewJsonResponderOrPanic(500, ethError{Error: "Unknown"}))
+
+	resetConf(e)
+	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utEthconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.Regexp(t, "FF10111", err)
+}
+
+func TestInitNetworkVersionBadResponse(t *testing.T) {
+	e, cancel := newTestEthereum()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/",
+		httpmock.NewJsonResponderOrPanic(200, ""))
+
+	resetConf(e)
+	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utEthconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.Regexp(t, "json: cannot unmarshal", err)
+}
+
 func TestInitTerminateContract(t *testing.T) {
 	e, _ := newTestEthereum()
 
@@ -507,6 +627,7 @@ func TestInitTerminateContract(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sb-1"}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -515,7 +636,7 @@ func TestInitTerminateContract(t *testing.T) {
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x1C197604587F046FD40684A8f21f4609FB811A7b")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".1."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, contracts)
 	assert.NoError(t, err)
@@ -565,6 +686,7 @@ func TestInitTerminateContractIgnore(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -572,7 +694,7 @@ func TestInitTerminateContractIgnore(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, contracts)
 	assert.NoError(t, err)
@@ -604,6 +726,7 @@ func TestInitTerminateContractBadEvent(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/", mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utEthconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -611,7 +734,7 @@ func TestInitTerminateContractBadEvent(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractAddress, "0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, contracts)
 	assert.NoError(t, err)
@@ -639,7 +762,7 @@ func TestStreamQueryError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10111.*pop", err)
 
 }
@@ -665,7 +788,7 @@ func TestStreamCreateError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10111.*pop", err)
 
 }
@@ -691,7 +814,7 @@ func TestStreamUpdateError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.Regexp(t, "FF10111.*pop", err)
 
 }
@@ -719,7 +842,7 @@ func TestSubQueryError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.Regexp(t, "FF10111.*pop", err)
@@ -751,7 +874,7 @@ func TestSubQueryCreateError(t *testing.T) {
 	utEthconnectConf.Set(EthconnectConfigInstanceDeprecated, "/instances/0x71C7656EC7ab88b098defB751B7401B5f6d8976F")
 	utEthconnectConf.Set(EthconnectConfigTopic, "topic1")
 
-	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, &metricsmocks.Manager{})
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
 	assert.NoError(t, err)
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.Regexp(t, "FF10111.*pop", err)
@@ -975,6 +1098,7 @@ func TestHandleMessageBatchPinOK(t *testing.T) {
 		callbacks: em,
 	}
 	e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5"
+	e.fireflyContract.networkVersion = 1
 
 	expectedSigningKeyRef := &core.VerifierRef{
 		Type:  core.VerifierTypeEthAddress,
@@ -989,6 +1113,8 @@ func TestHandleMessageBatchPinOK(t *testing.T) {
 	err = e.handleMessageBatch(context.Background(), events)
 	assert.NoError(t, err)
 
+	em.AssertExpectations(t)
+
 	b := em.Calls[0].Arguments[0].(*blockchain.BatchPin)
 	assert.Equal(t, "ns1", b.Namespace)
 	assert.Equal(t, "e19af8b3-9060-4051-812d-7597d19adfb9", b.TransactionID.String())
@@ -1025,8 +1151,6 @@ func TestHandleMessageBatchPinOK(t *testing.T) {
 	}
 	assert.Equal(t, info2, b2.Event.Info)
 
-	em.AssertExpectations(t)
-
 }
 
 func TestHandleMessageBatchPinMissingAuthor(t *testing.T) {
@@ -1102,6 +1226,7 @@ func TestHandleMessageEmptyPayloadRef(t *testing.T) {
 		callbacks: em,
 	}
 	e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5"
+	e.fireflyContract.networkVersion = 1
 
 	expectedSigningKeyRef := &core.VerifierRef{
 		Type:  core.VerifierTypeEthAddress,
@@ -1116,6 +1241,8 @@ func TestHandleMessageEmptyPayloadRef(t *testing.T) {
 	err = e.handleMessageBatch(context.Background(), events)
 	assert.NoError(t, err)
 
+	em.AssertExpectations(t)
+
 	b := em.Calls[0].Arguments[0].(*blockchain.BatchPin)
 	assert.Equal(t, "ns1", b.Namespace)
 	assert.Equal(t, "e19af8b3-9060-4051-812d-7597d19adfb9", b.TransactionID.String())
@@ -1127,8 +1254,6 @@ func TestHandleMessageEmptyPayloadRef(t *testing.T) {
 	assert.Equal(t, "68e4da79f805bca5b912bcda9c63d03e6e867108dabb9b944109aea541ef522a", b.Contexts[0].String())
 	assert.Equal(t, "19b82093de5ce92a01e333048e877e2374354bf846dd034864ef6ffbd6438771", b.Contexts[1].String())
 
-	em.AssertExpectations(t)
-
 }
 
 func TestHandleMessageBatchPinExit(t *testing.T) {
@@ -1163,6 +1288,7 @@ func TestHandleMessageBatchPinExit(t *testing.T) {
 		callbacks: em,
 	}
 	e.fireflyContract.subscription = "sb-b5b97a4e-a317-4053-6400-1474650efcb5"
+	e.fireflyContract.networkVersion = 1
 
 	em.On("BatchPinComplete", mock.Anything, expectedSigningKeyRef, mock.Anything).Return(fmt.Errorf("pop"))
 
@@ -2893,87 +3019,7 @@ func TestHandleNetworkAction(t *testing.T) {
 }
 
 func TestNetworkVersion(t *testing.T) {
-	e, _ := newTestEthereum()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			headers := body["headers"].(map[string]interface{})
-			assert.Equal(t, "Query", headers["type"])
-			method := body["method"].(map[string]interface{})
-			assert.Equal(t, "networkVersion", method["name"])
-			return httpmock.NewJsonResponderOrPanic(200, queryOutput{Output: "2"})(req)
-		})
-
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 2, version)
-}
-
-func TestNetworkVersionCached(t *testing.T) {
 	e, _ := newTestEthereum()
 	e.fireflyContract.networkVersion = 2
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 2, version)
-}
-
-func TestNetworkVersionNotFound(t *testing.T) {
-	e, _ := newTestEthereum()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			headers := body["headers"].(map[string]interface{})
-			assert.Equal(t, "Query", headers["type"])
-			method := body["method"].(map[string]interface{})
-			assert.Equal(t, "networkVersion", method["name"])
-			return httpmock.NewJsonResponderOrPanic(500, ethError{Error: "FFEC100148"})(req)
-		})
-
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 1, version)
-}
-
-func TestNetworkVersionError(t *testing.T) {
-	e, _ := newTestEthereum()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			headers := body["headers"].(map[string]interface{})
-			assert.Equal(t, "Query", headers["type"])
-			method := body["method"].(map[string]interface{})
-			assert.Equal(t, "networkVersion", method["name"])
-			return httpmock.NewJsonResponderOrPanic(500, ethError{Error: "Unknown"})(req)
-		})
-
-	_, err := e.NetworkVersion(context.Background())
-	assert.Regexp(t, "FF10111", err)
-}
-
-func TestNetworkVersionBadResponse(t *testing.T) {
-	e, _ := newTestEthereum()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			headers := body["headers"].(map[string]interface{})
-			assert.Equal(t, "Query", headers["type"])
-			method := body["method"].(map[string]interface{})
-			assert.Equal(t, "networkVersion", method["name"])
-			return httpmock.NewJsonResponderOrPanic(200, "")(req)
-		})
-
-	_, err := e.NetworkVersion(context.Background())
-	assert.Regexp(t, "json: cannot unmarshal", err)
+	assert.Equal(t, 2, e.NetworkVersion(context.Background()))
 }
diff --git a/internal/blockchain/fabric/fabric_test.go b/internal/blockchain/fabric/fabric_test.go
index ddfc303fc..53fc16b64 100644
--- a/internal/blockchain/fabric/fabric_test.go
+++ b/internal/blockchain/fabric/fabric_test.go
@@ -101,6 +101,19 @@ func testFFIMethod() *core.FFIMethod {
 	}
 }
 
+func mockNetworkVersion(t *testing.T, version float64) func(req *http.Request) (*http.Response, error) {
+	return func(req *http.Request) (*http.Response, error) {
+		var body map[string]interface{}
+		json.NewDecoder(req.Body).Decode(&body)
+		if body["func"] == "NetworkVersion" {
+			return httpmock.NewJsonResponderOrPanic(200, fabQueryNamedOutput{
+				Result: version,
+			})(req)
+		}
+		return nil, nil
+	}
+}
+
 func TestInitMissingURL(t *testing.T) {
 	e, cancel := newTestFabric()
 	defer cancel()
@@ -152,6 +165,7 @@ func TestInitAllNewStreamsAndWSEvent(t *testing.T) {
 			assert.Equal(t, "0", body["fromBlock"])
 			return httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sub12345"})(req)
 		})
+	httpmock.RegisterResponder("POST", fmt.Sprintf("%s/query", httpURL), mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utFabconnectConf.Set(ffresty.HTTPConfigURL, httpURL)
@@ -171,7 +185,7 @@ func TestInitAllNewStreamsAndWSEvent(t *testing.T) {
 	err = e.Start()
 	assert.NoError(t, err)
 
-	assert.Equal(t, 4, httpmock.GetTotalCallCount())
+	assert.Equal(t, 5, httpmock.GetTotalCallCount())
 	assert.Equal(t, "es12345", e.streamID)
 	assert.Equal(t, "sub12345", e.fireflyContract.subscription)
 	assert.NotNil(t, e.Capabilities())
@@ -248,6 +262,7 @@ func TestInitAllExistingStreams(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{
 			{ID: "sub12345", Stream: "es12345", Name: "BatchPin"},
 		}))
+	httpmock.RegisterResponder("POST", fmt.Sprintf("http://localhost:12345/query"), mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -261,7 +276,7 @@ func TestInitAllExistingStreams(t *testing.T) {
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
 
-	assert.Equal(t, 2, httpmock.GetTotalCallCount())
+	assert.Equal(t, 3, httpmock.GetTotalCallCount())
 	assert.Equal(t, "es12345", e.streamID)
 	assert.Equal(t, "sub12345", e.fireflyContract.subscription)
 
@@ -282,6 +297,8 @@ func TestInitNewConfig(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{
 			{ID: "sub12345", Stream: "es12345", Name: "BatchPin"},
 		}))
+	httpmock.RegisterResponder("POST", fmt.Sprintf("http://localhost:12345/query"),
+		mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -295,7 +312,7 @@ func TestInitNewConfig(t *testing.T) {
 	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
 	assert.NoError(t, err)
 
-	assert.Equal(t, 2, httpmock.GetTotalCallCount())
+	assert.Equal(t, 3, httpmock.GetTotalCallCount())
 	assert.Equal(t, "es12345", e.streamID)
 	assert.Equal(t, "sub12345", e.fireflyContract.subscription)
 
@@ -355,6 +372,108 @@ func TestInitNewConfigBadIndex(t *testing.T) {
 
 }
 
+func TestInitNetworkVersionNotFound(t *testing.T) {
+	e, cancel := newTestFabric()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/query",
+		httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Function NetworkVersion not found"}))
+
+	resetConf(e)
+	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utFabconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utFabconnectConf.Set(FabconnectConfigSigner, "signer001")
+	utFabconnectConf.Set(FabconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractChaincode, "firefly")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.NoError(t, err)
+
+	assert.Equal(t, "firefly", e.fireflyContract.chaincode)
+	assert.Equal(t, 1, e.fireflyContract.networkVersion)
+}
+
+func TestInitNetworkVersionError(t *testing.T) {
+	e, cancel := newTestFabric()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/query",
+		httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Unknown"}))
+
+	resetConf(e)
+	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utFabconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utFabconnectConf.Set(FabconnectConfigSigner, "signer001")
+	utFabconnectConf.Set(FabconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractChaincode, "firefly")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.Regexp(t, "FF10284", err)
+}
+
+func TestInitNetworkVersionBadResponse(t *testing.T) {
+	e, cancel := newTestFabric()
+	defer cancel()
+	resetConf(e)
+
+	mockedClient := &http.Client{}
+	httpmock.ActivateNonDefault(mockedClient)
+	defer httpmock.DeactivateAndReset()
+
+	httpmock.RegisterResponder("GET", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, []eventStream{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/eventstreams",
+		httpmock.NewJsonResponderOrPanic(200, eventStream{ID: "es12345"}))
+	httpmock.RegisterResponder("GET", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
+		httpmock.NewJsonResponderOrPanic(200, subscription{}))
+	httpmock.RegisterResponder("POST", "http://localhost:12345/query",
+		httpmock.NewJsonResponderOrPanic(200, ""))
+
+	resetConf(e)
+	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
+	utFabconnectConf.Set(ffresty.HTTPCustomClient, mockedClient)
+	utFabconnectConf.Set(FabconnectConfigSigner, "signer001")
+	utFabconnectConf.Set(FabconnectConfigTopic, "topic1")
+	utConfig.AddKnownKey(FireFlyContractConfigKey+".0."+FireFlyContractChaincode, "firefly")
+
+	err := e.Init(e.ctx, utConfig, &blockchainmocks.Callbacks{}, e.metrics)
+	assert.NoError(t, err)
+	err = e.ConfigureContract(e.ctx, &core.FireFlyContracts{})
+	assert.Regexp(t, "json: cannot unmarshal", err)
+}
+
 func TestInitTerminateContract(t *testing.T) {
 	e, _ := newTestFabric()
 
@@ -378,6 +497,8 @@ func TestInitTerminateContract(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sb-1"}))
+	httpmock.RegisterResponder("POST", fmt.Sprintf("http://localhost:12345/query"),
+		mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -436,6 +557,8 @@ func TestInitTerminateContractIgnore(t *testing.T) {
 		httpmock.NewJsonResponderOrPanic(200, []subscription{}))
 	httpmock.RegisterResponder("POST", "http://localhost:12345/subscriptions",
 		httpmock.NewJsonResponderOrPanic(200, subscription{ID: "sb-1"}))
+	httpmock.RegisterResponder("POST", fmt.Sprintf("http://localhost:12345/query"),
+		mockNetworkVersion(t, 1))
 
 	resetConf(e)
 	utFabconnectConf.Set(ffresty.HTTPConfigURL, "http://localhost:12345")
@@ -1945,75 +2068,7 @@ func TestHandleNetworkAction(t *testing.T) {
 }
 
 func TestNetworkVersion(t *testing.T) {
-	e, _ := newTestFabric()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/query`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			assert.Equal(t, "NetworkVersion", body["func"])
-			return httpmock.NewJsonResponderOrPanic(200, fabQueryNamedOutput{Result: 2})(req)
-		})
-
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 2, version)
-}
-
-func TestNetworkVersionCached(t *testing.T) {
 	e, _ := newTestFabric()
 	e.fireflyContract.networkVersion = 2
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 2, version)
-}
-
-func TestNetworkVersionNotFound(t *testing.T) {
-	e, _ := newTestFabric()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/query`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			assert.Equal(t, "NetworkVersion", body["func"])
-			return httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Function NetworkVersion not found"})(req)
-		})
-
-	version, err := e.NetworkVersion(context.Background())
-	assert.NoError(t, err)
-	assert.Equal(t, 1, version)
-}
-
-func TestNetworkVersionError(t *testing.T) {
-	e, _ := newTestFabric()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/query`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			assert.Equal(t, "NetworkVersion", body["func"])
-			return httpmock.NewJsonResponderOrPanic(500, fabError{Error: "Unknown"})(req)
-		})
-
-	_, err := e.NetworkVersion(context.Background())
-	assert.Regexp(t, "FF10284", err)
-}
-
-func TestNetworkVersionBadResponse(t *testing.T) {
-	e, _ := newTestFabric()
-	httpmock.ActivateNonDefault(e.client.GetClient())
-	defer httpmock.DeactivateAndReset()
-	httpmock.RegisterResponder("POST", `http://localhost:12345/query`,
-		func(req *http.Request) (*http.Response, error) {
-			var body map[string]interface{}
-			json.NewDecoder(req.Body).Decode(&body)
-			assert.Equal(t, "NetworkVersion", body["func"])
-			return httpmock.NewJsonResponderOrPanic(200, "")(req)
-		})
-
-	_, err := e.NetworkVersion(context.Background())
-	assert.Regexp(t, "json: cannot unmarshal", err)
+	assert.Equal(t, 2, e.NetworkVersion(context.Background()))
 }
diff --git a/internal/events/network_action_test.go b/internal/events/network_action_test.go
index b27bd673d..28d86cbae 100644
--- a/internal/events/network_action_test.go
+++ b/internal/events/network_action_test.go
@@ -52,7 +52,7 @@ func TestNetworkAction(t *testing.T) {
 		return be.ProtocolID == "0001"
 	})).Return(nil)
 	mdi.On("InsertEvent", em.ctx, mock.Anything).Return(nil)
-	mdi.On("GetNamespace", em.ctx, "ff_system").Return(&core.Namespace{}, nil)
+	mdi.On("GetNamespaces", em.ctx, mock.Anything).Return([]*core.Namespace{{}}, nil, nil)
 	mdi.On("UpsertNamespace", em.ctx, mock.AnythingOfType("*core.Namespace"), true).Return(nil)
 	mbi.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), mock.AnythingOfType("*blockchain.Event")).Return(nil)
 
@@ -140,7 +140,7 @@ func TestActionTerminateQueryFail(t *testing.T) {
 	mbi := &blockchainmocks.Plugin{}
 	mdi := em.database.(*databasemocks.Plugin)
 
-	mdi.On("GetNamespace", em.ctx, "ff_system").Return(nil, fmt.Errorf("pop"))
+	mdi.On("GetNamespaces", em.ctx, mock.Anything).Return(nil, nil, fmt.Errorf("pop"))
 
 	err := em.actionTerminate(mbi, &blockchain.Event{})
 	assert.EqualError(t, err, "pop")
@@ -156,7 +156,7 @@ func TestActionTerminateFail(t *testing.T) {
 	mbi := &blockchainmocks.Plugin{}
 	mdi := em.database.(*databasemocks.Plugin)
 
-	mdi.On("GetNamespace", em.ctx, "ff_system").Return(&core.Namespace{}, nil)
+	mdi.On("GetNamespaces", em.ctx, mock.Anything).Return([]*core.Namespace{{}}, nil, nil)
 	mbi.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), mock.AnythingOfType("*blockchain.Event")).Return(fmt.Errorf("pop"))
 
 	err := em.actionTerminate(mbi, &blockchain.Event{})
@@ -173,7 +173,7 @@ func TestActionTerminateUpsertFail(t *testing.T) {
 	mbi := &blockchainmocks.Plugin{}
 	mdi := em.database.(*databasemocks.Plugin)
 
-	mdi.On("GetNamespace", em.ctx, "ff_system").Return(&core.Namespace{}, nil)
+	mdi.On("GetNamespaces", em.ctx, mock.Anything).Return([]*core.Namespace{{}}, nil, nil)
 	mdi.On("UpsertNamespace", em.ctx, mock.AnythingOfType("*core.Namespace"), true).Return(fmt.Errorf("pop"))
 	mbi.On("TerminateContract", em.ctx, mock.AnythingOfType("*core.FireFlyContracts"), mock.AnythingOfType("*blockchain.Event")).Return(nil)
 
diff --git a/internal/identity/identitymanager_test.go b/internal/identity/identitymanager_test.go
index 942ee862e..a013eb34c 100644
--- a/internal/identity/identitymanager_test.go
+++ b/internal/identity/identitymanager_test.go
@@ -170,7 +170,7 @@ func TestResolveInputSigningIdentityAnonymousKeyWithAuthorOk(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil)
-	mbi.On("NetworkVersion", ctx).Return(1, nil)
+	mbi.On("NetworkVersion", ctx).Return(1)
 
 	idID := fftypes.NewUUID()
 
@@ -209,7 +209,7 @@ func TestResolveInputSigningIdentityKeyWithNoAuthorFail(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil)
-	mbi.On("NetworkVersion", ctx).Return(1, nil)
+	mbi.On("NetworkVersion", ctx).Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").Return(nil, nil)
@@ -275,7 +275,7 @@ func TestResolveInputSigningIdentityByKeyNotFound(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("NormalizeSigningKey", ctx, "mykey123").Return("fullkey123", nil)
-	mbi.On("NetworkVersion", ctx).Return(1, nil)
+	mbi.On("NetworkVersion", ctx).Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").
@@ -571,7 +571,7 @@ func TestResolveDefaultSigningIdentityNotFound(t *testing.T) {
 	}
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("NetworkVersion", ctx).Return(1, nil)
+	mbi.On("NetworkVersion", ctx).Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil)
@@ -590,33 +590,6 @@ func TestResolveDefaultSigningIdentityNotFound(t *testing.T) {
 
 }
 
-func TestResolveDefaultSigningIdentityVersionError(t *testing.T) {
-
-	ctx, im := newTestIdentityManager(t)
-	im.multipartyRootVerifier["ns1"] = &core.VerifierRef{
-		Type:  core.VerifierTypeEthAddress,
-		Value: "key12345",
-	}
-
-	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("NetworkVersion", ctx).Return(1, fmt.Errorf("pop"))
-
-	mdi := im.database.(*databasemocks.Plugin)
-	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil)
-
-	mns := im.namespace.(*namespacemocks.Manager)
-	mns.On("GetDefaultKey", "ns1").Return("")
-	mns.On("GetMultipartyConfig", "ns1", coreconfig.OrgName).Return("org1")
-
-	err := im.resolveDefaultSigningIdentity(ctx, "ns1", &core.SignerRef{})
-	assert.Regexp(t, "FF10281", err)
-
-	mbi.AssertExpectations(t)
-	mdi.AssertExpectations(t)
-	mns.AssertExpectations(t)
-
-}
-
 func TestResolveDefaultSigningIdentitySystemFallback(t *testing.T) {
 
 	ctx, im := newTestIdentityManager(t)
@@ -642,7 +615,7 @@ func TestResolveDefaultSigningIdentitySystemFallback(t *testing.T) {
 	}
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("NetworkVersion", ctx).Return(1, nil)
+	mbi.On("NetworkVersion", ctx).Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil)
diff --git a/internal/orchestrator/orchestrator_test.go b/internal/orchestrator/orchestrator_test.go
index cf3c98ae3..bedba4934 100644
--- a/internal/orchestrator/orchestrator_test.go
+++ b/internal/orchestrator/orchestrator_test.go
@@ -983,6 +983,7 @@ func TestStartBatchFail(t *testing.T) {
 	coreconfig.Reset()
 	or := newTestOrchestrator()
 	defer or.cleanup(t)
+	or.mmi.On("Start").Return(nil)
 	or.mba.On("Start").Return(fmt.Errorf("pop"))
 	err := or.Start()
 	assert.EqualError(t, err, "pop")
@@ -1002,6 +1003,7 @@ func TestStartTokensFail(t *testing.T) {
 	or.mpm.On("Start").Return(nil)
 	or.msd.On("Start").Return(nil)
 	or.mom.On("Start").Return(nil)
+	or.mmi.On("Start").Return(nil)
 	or.mti.On("Start").Return(fmt.Errorf("pop"))
 	or.mdi.On("UpsertNamespace", mock.Anything, mock.Anything, true).Return(nil)
 	err := or.Start()
@@ -1017,6 +1019,7 @@ func TestStartBlockchainsFail(t *testing.T) {
 	or.mbi.On("ConfigureContract", mock.Anything, &core.FireFlyContracts{}).Return(nil)
 	or.mbi.On("Start").Return(fmt.Errorf("pop"))
 	or.mba.On("Start").Return(nil)
+	or.mmi.On("Start").Return(nil)
 	err := or.Start()
 	assert.EqualError(t, err, "pop")
 }
@@ -1029,6 +1032,7 @@ func TestStartBlockchainsConfigureFail(t *testing.T) {
 	or.mdi.On("GetNamespace", mock.Anything, "ff_system").Return(&core.Namespace{}, nil)
 	or.mbi.On("ConfigureContract", mock.Anything, &core.FireFlyContracts{}).Return(fmt.Errorf("pop"))
 	or.mba.On("Start").Return(nil)
+	or.mmi.On("Start").Return(nil)
 	err := or.Start()
 	assert.EqualError(t, err, "pop")
 }