Repository: hyperledger/firefly
Pull request #1226 (2023-03-20 17:59:54)

Add "intent" to address resolver plugin interface and API
The address resolver interface is extremely useful as a checking/enforcement point for signing actions being accepted into the system. However, currently there is no way to distinguish between simply a call to `/api/v1/resolve` to request a resolution of an input string to a signing key from the plugin, vs. the other actions (messages, custom invoke, token ops) that actually accept a signing request into the system.

This PR adds a simple addition field of `.Intent` that is `lookup` for `/api/v1/resolve` and `sign` for all other actions, and is passed into the existing Go API to the blockchain connector, and Go templates on the remote API hook (ethereum plugin only).


Patch:
diff --git a/docs/reference/config.md b/docs/reference/config.md
index 4544ba454..95225103e 100644
--- a/docs/reference/config.md
+++ b/docs/reference/config.md
@@ -95,7 +95,7 @@ nav_order: 2
 |retainOriginal|When true the original pre-resolved string is retained after the lookup, and passed down to Ethconnect as the from address|`boolean`|`<nil>`
 |tlsHandshakeTimeout|The maximum amount of time to wait for a successful TLS handshake|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
 |url|The URL of the Address Resolver|`string`|`<nil>`
-|urlTemplate|The URL Go template string to use when calling the Address Resolver|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
+|urlTemplate|The URL Go template string to use when calling the Address Resolver. The template input contains '.Key' and '.Intent' string variables|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
 
 ## blockchain.ethereum.addressResolver.auth
 
@@ -820,7 +820,7 @@ nav_order: 2
 |Key|Description|Type|Default Value|
 |---|-----------|----|-------------|
 |alwaysResolve|Causes the address resolver to be invoked on every API call that submits a signing key, regardless of whether the input string conforms to an 0x address. Also disables any result caching|`boolean`|`<nil>`
-|bodyTemplate|The body go template string to use when making HTTP requests|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
+|bodyTemplate|The body go template string to use when making HTTP requests. The template input contains '.Key' and '.Intent' string variables.|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
 |connectionTimeout|The maximum amount of time that a connection is allowed to remain with no data transmitted|[`time.Duration`](https://pkg.go.dev/time#Duration)|`30s`
 |expectContinueTimeout|See [ExpectContinueTimeout in the Go docs](https://pkg.go.dev/net/http#Transport)|[`time.Duration`](https://pkg.go.dev/time#Duration)|`1s`
 |headers|Adds custom headers to HTTP requests|`string`|`<nil>`
@@ -833,7 +833,7 @@ nav_order: 2
 |retainOriginal|When true the original pre-resolved string is retained after the lookup, and passed down to Ethconnect as the from address|`boolean`|`<nil>`
 |tlsHandshakeTimeout|The maximum amount of time to wait for a successful TLS handshake|[`time.Duration`](https://pkg.go.dev/time#Duration)|`10s`
 |url|The URL of the Address Resolver|`string`|`<nil>`
-|urlTemplate|The URL Go template string to use when calling the Address Resolver|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
+|urlTemplate|The URL Go template string to use when calling the Address Resolver. The template input contains '.Key' and '.Intent' string variables.|[Go Template](https://pkg.go.dev/text/template) `string`|`<nil>`
 
 ## plugins.blockchain[].ethereum.addressResolver.auth
 
diff --git a/internal/apiserver/route_post_verifiers_resolve.go b/internal/apiserver/route_post_verifiers_resolve.go
index a7c107847..06d67623f 100644
--- a/internal/apiserver/route_post_verifiers_resolve.go
+++ b/internal/apiserver/route_post_verifiers_resolve.go
@@ -21,6 +21,7 @@ import (
 
 	"github.com/hyperledger/firefly-common/pkg/ffapi"
 	"github.com/hyperledger/firefly/internal/coremsgs"
+	"github.com/hyperledger/firefly/pkg/blockchain"
 	"github.com/hyperledger/firefly/pkg/core"
 )
 
@@ -36,7 +37,9 @@ var postVerifiersResolve = &ffapi.Route{
 	JSONOutputCodes: []int{http.StatusOK},
 	Extensions: &coreExtensions{
 		CoreJSONHandler: func(r *ffapi.APIRequest, cr *coreRequest) (output interface{}, err error) {
-			return cr.or.Identity().ResolveInputVerifierRef(cr.ctx, r.Input.(*core.VerifierRef))
+			return cr.or.Identity().ResolveInputVerifierRef(cr.ctx, r.Input.(*core.VerifierRef),
+				blockchain.ResolveKeyIntentLookup, /* This is special - as we are not actually submitting a signing request */
+			)
 		},
 	},
 }
diff --git a/internal/blockchain/ethereum/address_resolver.go b/internal/blockchain/ethereum/address_resolver.go
index abe61b4e5..a82d7042c 100644
--- a/internal/blockchain/ethereum/address_resolver.go
+++ b/internal/blockchain/ethereum/address_resolver.go
@@ -29,6 +29,7 @@ import (
 	"github.com/hyperledger/firefly/internal/cache"
 	"github.com/hyperledger/firefly/internal/coreconfig"
 	"github.com/hyperledger/firefly/internal/coremsgs"
+	"github.com/hyperledger/firefly/pkg/blockchain"
 )
 
 // addressResolver is a REST-pluggable interface to allow arbitrary strings that reference
@@ -49,7 +50,8 @@ type addressResolver struct {
 }
 
 type addressResolverInserts struct {
-	Key string
+	Key    string
+	Intent blockchain.ResolveKeyIntent
 }
 
 func newAddressResolver(ctx context.Context, localConfig config.Section, cacheManager cache.Manager, enableCache bool) (ar *addressResolver, err error) {
@@ -90,7 +92,7 @@ func newAddressResolver(ctx context.Context, localConfig config.Section, cacheMa
 	return ar, nil
 }
 
-func (ar *addressResolver) ResolveInputSigningKey(ctx context.Context, keyDescriptor string) (string, error) {
+func (ar *addressResolver) ResolveSigningKey(ctx context.Context, keyDescriptor string, intent blockchain.ResolveKeyIntent) (string, error) {
 
 	if ar.cache != nil {
 		if cached := ar.cache.GetString(keyDescriptor); cached != "" {
@@ -99,7 +101,8 @@ func (ar *addressResolver) ResolveInputSigningKey(ctx context.Context, keyDescri
 	}
 
 	inserts := &addressResolverInserts{
-		Key: keyDescriptor,
+		Key:    keyDescriptor,
+		Intent: intent,
 	}
 
 	urlStr := &strings.Builder{}
diff --git a/internal/blockchain/ethereum/config.go b/internal/blockchain/ethereum/config.go
index 875dc9bb5..3ede4e494 100644
--- a/internal/blockchain/ethereum/config.go
+++ b/internal/blockchain/ethereum/config.go
@@ -60,9 +60,9 @@ const (
 	AddressResolverRetainOriginal = "retainOriginal"
 	// AddressResolverMethod the HTTP method to use to call the address resolver (default GET)
 	AddressResolverMethod = "method"
-	// AddressResolverURLTemplate the URL go template string to use when calling the address resolver
+	// AddressResolverURLTemplate the URL go template string to use when calling the address resolver - a ".intent" string can be used in the go template
 	AddressResolverURLTemplate = "urlTemplate"
-	// AddressResolverBodyTemplate the body go template string to use when calling the address resolver
+	// AddressResolverBodyTemplate the body go template string to use when calling the address resolver - a ".intent" string can be used in the go template
 	AddressResolverBodyTemplate = "bodyTemplate"
 	// AddressResolverResponseField the name of a JSON field that is provided in the response, that contains the ethereum address (default "address")
 	AddressResolverResponseField = "responseField"
diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go
index e0fcde5d8..5e8ead9c0 100644
--- a/internal/blockchain/ethereum/ethereum.go
+++ b/internal/blockchain/ethereum/ethereum.go
@@ -498,7 +498,7 @@ func formatEthAddress(ctx context.Context, key string) (string, error) {
 	return "", i18n.NewError(ctx, coremsgs.MsgInvalidEthAddress)
 }
 
-func (e *Ethereum) ResolveInputSigningKey(ctx context.Context, key string) (resolved string, err error) {
+func (e *Ethereum) ResolveSigningKey(ctx context.Context, key string, intent blockchain.ResolveKeyIntent) (resolved string, err error) {
 	if !e.addressResolveAlways {
 		// If there's no address resolver plugin, or addressResolveAlways is false,
 		// we check if it's already an ethereum address - in which case we can just return it.
@@ -507,7 +507,7 @@ func (e *Ethereum) ResolveInputSigningKey(ctx context.Context, key string) (reso
 	if e.addressResolveAlways || (err != nil && e.addressResolver != nil) {
 		// Either it's not a valid ethereum address,
 		// or we've been configured to invoke the address resolver on every call
-		resolved, err = e.addressResolver.ResolveInputSigningKey(ctx, key)
+		resolved, err = e.addressResolver.ResolveSigningKey(ctx, key, intent)
 		if err == nil {
 			log.L(ctx).Infof("Key '%s' resolved to '%s'", key, resolved)
 			return resolved, nil
diff --git a/internal/blockchain/fabric/fabric.go b/internal/blockchain/fabric/fabric.go
index 9ec154946..aaf9c0b5f 100644
--- a/internal/blockchain/fabric/fabric.go
+++ b/internal/blockchain/fabric/fabric.go
@@ -499,7 +499,11 @@ func (f *Fabric) eventLoop() {
 	}
 }
 
-func (f *Fabric) ResolveInputSigningKey(ctx context.Context, signingKeyInput string) (string, error) {
+func (f *Fabric) ResolveSigningKey(ctx context.Context, signingKeyInput string, intent blockchain.ResolveKeyIntent) (string, error) {
+	// Note: "intent" is not currently used for Fabric, as the identity resolution is not
+	//       currently pluggable to external identity resolution systems (as is the case for
+	//       ethereum blockchain connectors).
+
 	// we expand the short user name into the fully qualified onchain identity:
 	// mspid::x509::{ecert DN}::{CA DN}	return signingKeyInput, nil
 	if !fullIdentityPattern.MatchString(signingKeyInput) {
diff --git a/internal/coremsgs/en_config_descriptions.go b/internal/coremsgs/en_config_descriptions.go
index fb032c96a..128295701 100644
--- a/internal/coremsgs/en_config_descriptions.go
+++ b/internal/coremsgs/en_config_descriptions.go
@@ -70,7 +70,7 @@ var (
 	ConfigBlockchainEthereumAddressResolverResponseField  = ffc("config.blockchain.ethereum.addressResolver.responseField", "The name of a JSON field that is provided in the response, that contains the ethereum address (default `address`)", i18n.StringType)
 	ConfigBlockchainEthereumAddressResolverRetainOriginal = ffc("config.blockchain.ethereum.addressResolver.retainOriginal", "When true the original pre-resolved string is retained after the lookup, and passed down to Ethconnect as the from address", i18n.BooleanType)
 	ConfigBlockchainEthereumAddressResolverURL            = ffc("config.blockchain.ethereum.addressResolver.url", "The URL of the Address Resolver", i18n.StringType)
-	ConfigBlockchainEthereumAddressResolverURLTemplate    = ffc("config.blockchain.ethereum.addressResolver.urlTemplate", "The URL Go template string to use when calling the Address Resolver", i18n.GoTemplateType)
+	ConfigBlockchainEthereumAddressResolverURLTemplate    = ffc("config.blockchain.ethereum.addressResolver.urlTemplate", "The URL Go template string to use when calling the Address Resolver. The template input contains '.Key' and '.Intent' string variables", i18n.GoTemplateType)
 	ConfigBlockchainEthereumAddressResolverProxyURL       = ffc("config.blockchain.ethereum.addressResolver.proxy.url", "Optional HTTP proxy server to use when connecting to the Address Resolver", "URL "+i18n.StringType)
 
 	ConfigBlockchainEthereumEthconnectBatchSize    = ffc("config.blockchain.ethereum.ethconnect.batchSize", "The number of events Ethconnect should batch together for delivery to FireFly core. Only applies when automatically creating a new event stream", i18n.IntType)
@@ -145,7 +145,7 @@ var (
 	ConfigPluginBlockchainType = ffc("config.plugins.blockchain[].type", "The type of the configured Blockchain Connector plugin", i18n.StringType)
 
 	ConfigPluginBlockchainEthereumAddressResolverAlwaysResolve         = ffc("config.plugins.blockchain[].ethereum.addressResolver.alwaysResolve", "Causes the address resolver to be invoked on every API call that submits a signing key, regardless of whether the input string conforms to an 0x address. Also disables any result caching", i18n.BooleanType)
-	ConfigPluginBlockchainEthereumAddressResolverBodyTemplate          = ffc("config.plugins.blockchain[].ethereum.addressResolver.bodyTemplate", "The body go template string to use when making HTTP requests", i18n.GoTemplateType)
+	ConfigPluginBlockchainEthereumAddressResolverBodyTemplate          = ffc("config.plugins.blockchain[].ethereum.addressResolver.bodyTemplate", "The body go template string to use when making HTTP requests. The template input contains '.Key' and '.Intent' string variables.", i18n.GoTemplateType)
 	ConfigPluginBlockchainEthereumAddressResolverCustomClient          = ffc("config.plugins.blockchain[].ethereum.addressResolver.customClient", "Used for testing purposes only", i18n.IgnoredType)
 	ConfigPluginBlockchainEthereumAddressResolverExpectContinueTimeout = ffc("config.plugins.blockchain[].ethereum.addressResolver.expectContinueTimeout", "See [ExpectContinueTimeout in the Go docs](https://pkg.go.dev/net/http#Transport)", i18n.TimeDurationType)
 	ConfigPluginBlockchainEthereumAddressResolverHeaders               = ffc("config.plugins.blockchain[].ethereum.addressResolver.headers", "Adds custom headers to HTTP requests", i18n.StringType)
@@ -156,7 +156,7 @@ var (
 	ConfigPluginBlockchainEthereumAddressResolverResponseField  = ffc("config.plugins.blockchain[].ethereum.addressResolver.responseField", "The name of a JSON field that is provided in the response, that contains the ethereum address (default `address`)", i18n.StringType)
 	ConfigPluginBlockchainEthereumAddressResolverRetainOriginal = ffc("config.plugins.blockchain[].ethereum.addressResolver.retainOriginal", "When true the original pre-resolved string is retained after the lookup, and passed down to Ethconnect as the from address", i18n.BooleanType)
 	ConfigPluginBlockchainEthereumAddressResolverURL            = ffc("config.plugins.blockchain[].ethereum.addressResolver.url", "The URL of the Address Resolver", i18n.StringType)
-	ConfigPluginBlockchainEthereumAddressResolverURLTemplate    = ffc("config.plugins.blockchain[].ethereum.addressResolver.urlTemplate", "The URL Go template string to use when calling the Address Resolver", i18n.GoTemplateType)
+	ConfigPluginBlockchainEthereumAddressResolverURLTemplate    = ffc("config.plugins.blockchain[].ethereum.addressResolver.urlTemplate", "The URL Go template string to use when calling the Address Resolver. The template input contains '.Key' and '.Intent' string variables.", i18n.GoTemplateType)
 
 	ConfigPluginBlockchainEthereumAddressResolverProxyURL = ffc("config.plugins.blockchain[].ethereum.addressResolver.proxy.url", "Optional HTTP proxy server to use when connecting to the Address Resolver", "URL "+i18n.StringType)
 
diff --git a/internal/identity/identitymanager.go b/internal/identity/identitymanager.go
index 79eb8f676..a9682431a 100644
--- a/internal/identity/identitymanager.go
+++ b/internal/identity/identitymanager.go
@@ -40,7 +40,7 @@ const (
 
 type Manager interface {
 	ResolveInputSigningIdentity(ctx context.Context, signerRef *core.SignerRef) (err error)
-	ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef) (*core.VerifierRef, error)
+	ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef, intent blockchain.ResolveKeyIntent) (*core.VerifierRef, error)
 	ResolveInputSigningKey(ctx context.Context, inputKey string, keyNormalizationMode int) (signingKey string, err error)
 	ResolveIdentitySigner(ctx context.Context, identity *core.Identity) (parentSigner *core.SignerRef, err error)
 
@@ -140,7 +140,7 @@ func (im *identityManager) ResolveInputSigningKey(ctx context.Context, inputKey
 	return signer.Value, nil
 }
 
-func (im *identityManager) ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef) (*core.VerifierRef, error) {
+func (im *identityManager) ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef, intent blockchain.ResolveKeyIntent) (*core.VerifierRef, error) {
 	log.L(ctx).Debugf("Resolving input signing key: type='%s' value='%s'", inputKey.Type, inputKey.Value)
 
 	if im.blockchain == nil {
@@ -157,7 +157,7 @@ func (im *identityManager) ResolveInputVerifierRef(ctx context.Context, inputKey
 		return nil, i18n.NewError(ctx, coremsgs.MsgUnknownVerifierType)
 	}
 
-	signingKey, err := im.blockchain.ResolveInputSigningKey(ctx, inputKey.Value)
+	signingKey, err := im.blockchain.ResolveSigningKey(ctx, inputKey.Value, intent)
 	if err != nil {
 		return nil, err
 	}
@@ -304,7 +304,7 @@ func (im *identityManager) resolveInputKeyViaBlockchainPlugin(ctx context.Contex
 		return nil, i18n.NewError(ctx, coremsgs.MsgBlockchainNotConfigured)
 	}
 
-	keyString, err := im.blockchain.ResolveInputSigningKey(ctx, inputKey)
+	keyString, err := im.blockchain.ResolveSigningKey(ctx, inputKey, blockchain.ResolveKeyIntentSign)
 	if err != nil {
 		return nil, err
 	}
diff --git a/mocks/blockchainmocks/plugin.go b/mocks/blockchainmocks/plugin.go
index eb5633f48..ceb1d1a4f 100644
--- a/mocks/blockchainmocks/plugin.go
+++ b/mocks/blockchainmocks/plugin.go
@@ -399,23 +399,23 @@ func (_m *Plugin) RemoveFireflySubscription(ctx context.Context, subID string) {
 	_m.Called(ctx, subID)
 }
 
-// ResolveInputSigningKey provides a mock function with given fields: ctx, keyRef
-func (_m *Plugin) ResolveInputSigningKey(ctx context.Context, keyRef string) (string, error) {
-	ret := _m.Called(ctx, keyRef)
+// ResolveSigningKey provides a mock function with given fields: ctx, keyRef, intent
+func (_m *Plugin) ResolveSigningKey(ctx context.Context, keyRef string, intent blockchain.ResolveKeyIntent) (string, error) {
+	ret := _m.Called(ctx, keyRef, intent)
 
 	var r0 string
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, string) (string, error)); ok {
-		return rf(ctx, keyRef)
+	if rf, ok := ret.Get(0).(func(context.Context, string, blockchain.ResolveKeyIntent) (string, error)); ok {
+		return rf(ctx, keyRef, intent)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, string) string); ok {
-		r0 = rf(ctx, keyRef)
+	if rf, ok := ret.Get(0).(func(context.Context, string, blockchain.ResolveKeyIntent) string); ok {
+		r0 = rf(ctx, keyRef, intent)
 	} else {
 		r0 = ret.Get(0).(string)
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
-		r1 = rf(ctx, keyRef)
+	if rf, ok := ret.Get(1).(func(context.Context, string, blockchain.ResolveKeyIntent) error); ok {
+		r1 = rf(ctx, keyRef, intent)
 	} else {
 		r1 = ret.Error(1)
 	}
diff --git a/mocks/identitymanagermocks/manager.go b/mocks/identitymanagermocks/manager.go
index ec2133afc..e844560f3 100644
--- a/mocks/identitymanagermocks/manager.go
+++ b/mocks/identitymanagermocks/manager.go
@@ -5,9 +5,12 @@ package identitymanagermocks
 import (
 	context "context"
 
-	fftypes "github.com/hyperledger/firefly-common/pkg/fftypes"
+	blockchain "github.com/hyperledger/firefly/pkg/blockchain"
+
 	core "github.com/hyperledger/firefly/pkg/core"
 
+	fftypes "github.com/hyperledger/firefly-common/pkg/fftypes"
+
 	mock "github.com/stretchr/testify/mock"
 )
 
@@ -276,25 +279,25 @@ func (_m *Manager) ResolveInputSigningKey(ctx context.Context, inputKey string,
 	return r0, r1
 }
 
-// ResolveInputVerifierRef provides a mock function with given fields: ctx, inputKey
-func (_m *Manager) ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef) (*core.VerifierRef, error) {
-	ret := _m.Called(ctx, inputKey)
+// ResolveInputVerifierRef provides a mock function with given fields: ctx, inputKey, intent
+func (_m *Manager) ResolveInputVerifierRef(ctx context.Context, inputKey *core.VerifierRef, intent blockchain.ResolveKeyIntent) (*core.VerifierRef, error) {
+	ret := _m.Called(ctx, inputKey, intent)
 
 	var r0 *core.VerifierRef
 	var r1 error
-	if rf, ok := ret.Get(0).(func(context.Context, *core.VerifierRef) (*core.VerifierRef, error)); ok {
-		return rf(ctx, inputKey)
+	if rf, ok := ret.Get(0).(func(context.Context, *core.VerifierRef, blockchain.ResolveKeyIntent) (*core.VerifierRef, error)); ok {
+		return rf(ctx, inputKey, intent)
 	}
-	if rf, ok := ret.Get(0).(func(context.Context, *core.VerifierRef) *core.VerifierRef); ok {
-		r0 = rf(ctx, inputKey)
+	if rf, ok := ret.Get(0).(func(context.Context, *core.VerifierRef, blockchain.ResolveKeyIntent) *core.VerifierRef); ok {
+		r0 = rf(ctx, inputKey, intent)
 	} else {
 		if ret.Get(0) != nil {
 			r0 = ret.Get(0).(*core.VerifierRef)
 		}
 	}
 
-	if rf, ok := ret.Get(1).(func(context.Context, *core.VerifierRef) error); ok {
-		r1 = rf(ctx, inputKey)
+	if rf, ok := ret.Get(1).(func(context.Context, *core.VerifierRef, blockchain.ResolveKeyIntent) error); ok {
+		r1 = rf(ctx, inputKey, intent)
 	} else {
 		r1 = ret.Error(1)
 	}
diff --git a/pkg/blockchain/plugin.go b/pkg/blockchain/plugin.go
index 4d9beb22e..c3c9f293d 100644
--- a/pkg/blockchain/plugin.go
+++ b/pkg/blockchain/plugin.go
@@ -26,6 +26,14 @@ import (
 	"github.com/hyperledger/firefly/pkg/core"
 )
 
+// ResolveKeyIntent allows us to distinguish between resolving a key just for a lookup, vs. accepting in an action to sign
+type ResolveKeyIntent string
+
+const (
+	ResolveKeyIntentSign   ResolveKeyIntent = "sign"   // used everywhere we accept an signing action (messages, tokens, custom invoke)
+	ResolveKeyIntentLookup ResolveKeyIntent = "lookup" // used only on the /api/v1/resolve API
+)
+
 // Plugin is the interface implemented by each blockchain plugin
 type Plugin interface {
 	core.Named
@@ -53,14 +61,14 @@ type Plugin interface {
 	// VerifierType returns the verifier (key) type that is used by this blockchain
 	VerifierType() core.VerifierType
 
-	// ResolveInputSigningKey allows blockchain specific processing of keys supplied by users
+	// ResolveSigningKey allows blockchain specific processing of keys supplied by users
 	// of this FireFly core API before a transaction is accepted using that signing key.
 	// May perform sophisticated checks and resolution as determined by the blockchain connector,
 	// and associated resolution plugins:
 	// - Such as resolving a Fabric shortname to a MSP ID
 	// - Such using an external REST API plugin to resolve a HD wallet address, or other key alias
 	// - Results in a string that can be stored/compared consistently with the key emitted on events signed by this key
-	ResolveInputSigningKey(ctx context.Context, keyRef string) (string, error)
+	ResolveSigningKey(ctx context.Context, keyRef string, intent ResolveKeyIntent) (string, error)
 
 	// SubmitBatchPin sequences a batch of message globally to all viewers of a given ledger
 	SubmitBatchPin(ctx context.Context, nsOpID, networkNamespace, signingKey string, batch *BatchPin, location *fftypes.JSONAny) error


Test patch:
diff --git a/internal/apiserver/route_post_verifiers_resolve_test.go b/internal/apiserver/route_post_verifiers_resolve_test.go
index e080f1c4f..c9faffa5b 100644
--- a/internal/apiserver/route_post_verifiers_resolve_test.go
+++ b/internal/apiserver/route_post_verifiers_resolve_test.go
@@ -23,6 +23,7 @@ import (
 	"testing"
 
 	"github.com/hyperledger/firefly/mocks/identitymanagermocks"
+	"github.com/hyperledger/firefly/pkg/blockchain"
 	"github.com/hyperledger/firefly/pkg/core"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -40,7 +41,7 @@ func TestPostVerifiersResolve(t *testing.T) {
 	req.Header.Set("Content-Type", "application/json; charset=utf-8")
 	res := httptest.NewRecorder()
 
-	im.On("ResolveInputVerifierRef", mock.Anything, mock.AnythingOfType("*core.VerifierRef")).
+	im.On("ResolveInputVerifierRef", mock.Anything, mock.AnythingOfType("*core.VerifierRef"), blockchain.ResolveKeyIntentLookup).
 		Return(&core.VerifierRef{}, nil)
 	r.ServeHTTP(res, req)
 
diff --git a/internal/blockchain/ethereum/address_resolver_test.go b/internal/blockchain/ethereum/address_resolver_test.go
index 117363ee5..475d3530f 100644
--- a/internal/blockchain/ethereum/address_resolver_test.go
+++ b/internal/blockchain/ethereum/address_resolver_test.go
@@ -31,6 +31,7 @@ import (
 	"github.com/hyperledger/firefly/internal/cache"
 	"github.com/hyperledger/firefly/internal/coreconfig"
 	"github.com/hyperledger/firefly/mocks/cachemocks"
+	"github.com/hyperledger/firefly/pkg/blockchain"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
 	"golang.org/x/net/context"
@@ -86,11 +87,11 @@ func TestAddressResolverInEthereumOKCached(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	resolved, err := e.ResolveInputSigningKey(ctx, "testkeystring")
+	resolved, err := e.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, strings.ToLower(addr), resolved)
 
-	resolved, err = e.ResolveInputSigningKey(ctx, "testkeystring") // cached
+	resolved, err = e.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign) // cached
 	assert.NoError(t, err)
 	assert.Equal(t, strings.ToLower(addr), resolved)
 	assert.Equal(t, 1, count)
@@ -113,7 +114,7 @@ func TestAddressResolverURLEncode(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	resolved, err := e.ResolveInputSigningKey(ctx, "uri://testkeystring")
+	resolved, err := e.ResolveSigningKey(ctx, "uri://testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, strings.ToLower(addr), resolved)
 }
@@ -148,11 +149,11 @@ func TestAddressResolverForceNoCacheAlwaysInvoke(t *testing.T) {
 	e.addressResolver, err = newAddressResolver(ctx, config, nil, false)
 	assert.NoError(t, err)
 
-	resolved, err := e.ResolveInputSigningKey(ctx, addr1)
+	resolved, err := e.ResolveSigningKey(ctx, addr1, blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, strings.ToLower(addr2), resolved)
 
-	resolved, err = e.ResolveInputSigningKey(ctx, addr1)
+	resolved, err = e.ResolveSigningKey(ctx, addr1, blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, strings.ToLower(addr2), resolved)
 
@@ -166,6 +167,7 @@ func TestAddressResolverPOSTOk(t *testing.T) {
 		var jo fftypes.JSONObject
 		json.NewDecoder(r.Body).Decode(&jo)
 		assert.Equal(t, "testkeystring", jo.GetString("key"))
+		assert.Equal(t, "lookup", jo.GetString("intent"))
 		rw.Header().Set("Content-Type", "application/json")
 		rw.WriteHeader(200)
 		rw.Write([]byte(fmt.Sprintf(`{"Addr":"%s"}`, addr)))
@@ -176,13 +178,13 @@ func TestAddressResolverPOSTOk(t *testing.T) {
 	config.Set(AddressResolverRetainOriginal, true)
 	config.Set(AddressResolverMethod, "POST")
 	config.Set(AddressResolverURLTemplate, fmt.Sprintf("%s/resolve", server.URL))
-	config.Set(AddressResolverBodyTemplate, `{"key":"{{.Key}}"}`)
+	config.Set(AddressResolverBodyTemplate, `{"key":"{{.Key}}","intent":"{{.Intent}}"}`)
 	config.Set(AddressResolverResponseField, "Addr")
 
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	resolved, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	resolved, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentLookup)
 	assert.NoError(t, err)
 
 	assert.Equal(t, strings.ToLower(addr), resolved)
@@ -194,19 +196,19 @@ func TestAddressResolverPOSTBadKey(t *testing.T) {
 	server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
 		rw.Header().Set("Content-Type", "application/json")
 		rw.WriteHeader(200)
-		rw.Write([]byte(`{"address":"badness"}`))
+		rw.Write([]byte(`{"address":"badness","intent":"sign"}`))
 	}))
 	defer server.Close()
 
 	config := utAddresResolverConfig()
 	config.Set(AddressResolverMethod, "POST")
 	config.Set(AddressResolverURLTemplate, fmt.Sprintf("%s/resolve", server.URL))
-	config.Set(AddressResolverBodyTemplate, `{"key":"{{.Key}}"}`)
+	config.Set(AddressResolverBodyTemplate, `{"key":"{{.Key}}","intent":"{{.Intent}}"}`)
 
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	_, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10341", err)
 
 }
@@ -226,7 +228,7 @@ func TestAddressResolverPOSTResponse(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	_, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10341", err)
 
 }
@@ -244,7 +246,7 @@ func TestAddressResolverFailureResponse(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	_, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10340", err)
 
 }
@@ -262,7 +264,7 @@ func TestAddressResolverErrorResponse(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	_, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10339", err)
 
 }
@@ -290,7 +292,7 @@ func TestAddressResolverErrorURLTemplate(t *testing.T) {
 	ctx, e, cancel := newAddressResolverTestEth(t, config)
 	defer cancel()
 
-	_, err := e.addressResolver.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err := e.addressResolver.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10338.*urlTemplate", err)
 
 }
@@ -314,7 +316,7 @@ func TestAddressResolverErrorBodyTemplate(t *testing.T) {
 	))
 	assert.NoError(t, err)
 
-	_, err = ar.ResolveInputSigningKey(ctx, "testkeystring")
+	_, err = ar.ResolveSigningKey(ctx, "testkeystring", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10338.*bodyTemplate", err)
 
 }
diff --git a/internal/blockchain/ethereum/ethereum_test.go b/internal/blockchain/ethereum/ethereum_test.go
index d98ebb053..bb1e20902 100644
--- a/internal/blockchain/ethereum/ethereum_test.go
+++ b/internal/blockchain/ethereum/ethereum_test.go
@@ -1113,10 +1113,10 @@ func TestVerifyEthAddress(t *testing.T) {
 	e, cancel := newTestEthereum()
 	defer cancel()
 
-	_, err := e.ResolveInputSigningKey(context.Background(), "0x12345")
+	_, err := e.ResolveSigningKey(context.Background(), "0x12345", blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10141", err)
 
-	key, err := e.ResolveInputSigningKey(context.Background(), "0x2a7c9D5248681CE6c393117E641aD037F5C079F6")
+	key, err := e.ResolveSigningKey(context.Background(), "0x2a7c9D5248681CE6c393117E641aD037F5C079F6", blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, "0x2a7c9d5248681ce6c393117e641ad037f5c079f6", key)
 
diff --git a/internal/blockchain/fabric/fabric_test.go b/internal/blockchain/fabric/fabric_test.go
index 783fdc344..75cebb049 100644
--- a/internal/blockchain/fabric/fabric_test.go
+++ b/internal/blockchain/fabric/fabric_test.go
@@ -959,7 +959,7 @@ func TestResolveFullIDSigner(t *testing.T) {
 	defer cancel()
 
 	id := "org1MSP::x509::CN=admin,OU=client::CN=fabric-ca-server"
-	signKey, err := e.ResolveInputSigningKey(context.Background(), id)
+	signKey, err := e.ResolveSigningKey(context.Background(), id, blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, "org1MSP::x509::CN=admin,OU=client::CN=fabric-ca-server", signKey)
 
@@ -979,7 +979,7 @@ func TestResolveSigner(t *testing.T) {
 
 	responder, _ := httpmock.NewJsonResponder(200, res)
 	httpmock.RegisterResponder("GET", `http://localhost:12345/identities/signer001`, responder)
-	resolved, err := e.ResolveInputSigningKey(context.Background(), "signer001")
+	resolved, err := e.ResolveSigningKey(context.Background(), "signer001", blockchain.ResolveKeyIntentSign)
 	assert.NoError(t, err)
 	assert.Equal(t, "org1MSP::x509::CN=admin,OU=client::CN=fabric-ca-server", resolved)
 }
@@ -994,7 +994,7 @@ func TestResolveSignerFailedFabricCARequest(t *testing.T) {
 
 	responder, _ := httpmock.NewJsonResponder(503, res)
 	httpmock.RegisterResponder("GET", `http://localhost:12345/identities/signer001`, responder)
-	_, err := e.ResolveInputSigningKey(context.Background(), "signer001")
+	_, err := e.ResolveSigningKey(context.Background(), "signer001", blockchain.ResolveKeyIntentSign)
 	assert.EqualError(t, err, "FF10284: Error from fabconnect: %!!(MISSING)s(<nil>)")
 }
 
@@ -1012,7 +1012,7 @@ func TestResolveSignerBadECertReturned(t *testing.T) {
 
 	responder, _ := httpmock.NewJsonResponder(200, res)
 	httpmock.RegisterResponder("GET", `http://localhost:12345/identities/signer001`, responder)
-	_, err := e.ResolveInputSigningKey(context.Background(), "signer001")
+	_, err := e.ResolveSigningKey(context.Background(), "signer001", blockchain.ResolveKeyIntentSign)
 	assert.Contains(t, err.Error(), "FF10286: Failed to decode certificate:")
 }
 
@@ -1030,7 +1030,7 @@ func TestResolveSignerBadCACertReturned(t *testing.T) {
 
 	responder, _ := httpmock.NewJsonResponder(200, res)
 	httpmock.RegisterResponder("GET", `http://localhost:12345/identities/signer001`, responder)
-	_, err := e.ResolveInputSigningKey(context.Background(), "signer001")
+	_, err := e.ResolveSigningKey(context.Background(), "signer001", blockchain.ResolveKeyIntentSign)
 	assert.Contains(t, err.Error(), "FF10286: Failed to decode certificate:")
 }
 
diff --git a/internal/identity/identitymanager_test.go b/internal/identity/identitymanager_test.go
index 2ee2f452c..b2e2bd6eb 100644
--- a/internal/identity/identitymanager_test.go
+++ b/internal/identity/identitymanager_test.go
@@ -31,6 +31,7 @@ import (
 	"github.com/hyperledger/firefly/mocks/cachemocks"
 	"github.com/hyperledger/firefly/mocks/databasemocks"
 	"github.com/hyperledger/firefly/mocks/multipartymocks"
+	"github.com/hyperledger/firefly/pkg/blockchain"
 	"github.com/hyperledger/firefly/pkg/core"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/mock"
@@ -98,7 +99,7 @@ func TestResolveInputVerifierRefMissingBlockchain(t *testing.T) {
 		Value: "testValue",
 	}
 
-	_, err := im.ResolveInputVerifierRef(ctx, inputKey)
+	_, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentSign)
 	assert.Regexp(t, "FF10417", err)
 }
 
@@ -113,7 +114,7 @@ func TestResolveInputVerifierRefUnknownVerifier(t *testing.T) {
 		Value: "testValue",
 	}
 
-	_, err := im.ResolveInputVerifierRef(ctx, inputKey)
+	_, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentSign)
 
 	assert.Regexp(t, "FF10428", err)
 	mbi.AssertExpectations(t)
@@ -124,13 +125,32 @@ func TestResolveInputVerifierRefNoVerifierOK(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress, nil)
-	mbi.On("ResolveInputSigningKey", ctx, "testValue").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "testValue", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	inputKey := &core.VerifierRef{
 		Value: "testValue",
 	}
 
-	res, err := im.ResolveInputVerifierRef(ctx, inputKey)
+	res, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentSign)
+
+	assert.NoError(t, err)
+	assert.Equal(t, core.VerifierTypeEthAddress, res.Type)
+	assert.Equal(t, "fullkey123", res.Value)
+	mbi.AssertExpectations(t)
+}
+
+func TestResolveInputVerifierRefNoVerifierLookupOnly(t *testing.T) {
+	ctx, im := newTestIdentityManager(t)
+
+	mbi := im.blockchain.(*blockchainmocks.Plugin)
+	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress, nil)
+	mbi.On("ResolveSigningKey", ctx, "testValue", blockchain.ResolveKeyIntentLookup).Return("fullkey123", nil)
+
+	inputKey := &core.VerifierRef{
+		Value: "testValue",
+	}
+
+	res, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentLookup)
 
 	assert.NoError(t, err)
 	assert.Equal(t, core.VerifierTypeEthAddress, res.Type)
@@ -143,14 +163,14 @@ func TestResolveInputVerifierRefErrorNormalizingKey(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress, nil)
-	mbi.On("ResolveInputSigningKey", ctx, "testValue").Return("", fmt.Errorf("pop"))
+	mbi.On("ResolveSigningKey", ctx, "testValue", blockchain.ResolveKeyIntentSign).Return("", fmt.Errorf("pop"))
 
 	inputKey := &core.VerifierRef{
 		Type:  core.VerifierTypeEthAddress,
 		Value: "testValue",
 	}
 
-	_, err := im.ResolveInputVerifierRef(ctx, inputKey)
+	_, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentSign)
 
 	assert.Regexp(t, "pop", err)
 	mbi.AssertExpectations(t)
@@ -161,14 +181,14 @@ func TestResolveInputVerifierRefSuccess(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mbi.On("VerifierType").Return(core.VerifierTypeEthAddress, nil)
-	mbi.On("ResolveInputSigningKey", ctx, "testValue").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "testValue", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	inputKey := &core.VerifierRef{
 		Type:  core.VerifierTypeEthAddress,
 		Value: "testValue",
 	}
 
-	res, err := im.ResolveInputVerifierRef(ctx, inputKey)
+	res, err := im.ResolveInputVerifierRef(ctx, inputKey, blockchain.ResolveKeyIntentSign)
 
 	assert.NoError(t, err)
 	assert.Equal(t, core.VerifierTypeEthAddress, res.Type)
@@ -199,7 +219,7 @@ func TestResolveInputSigningIdentityOrgFallbackOk(t *testing.T) {
 	mmp.On("RootOrg").Return(multiparty.RootOrg{Name: "org1", Key: "key123"})
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	orgID := fftypes.NewUUID()
 
@@ -252,7 +272,7 @@ func TestResolveInputSigningIdentityByKeyOk(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	idID := fftypes.NewUUID()
 
@@ -296,7 +316,7 @@ func TestResolveInputSigningIdentityAnonymousKeyWithAuthorOk(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mmp := im.multiparty.(*multipartymocks.Manager)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 	mmp.On("GetNetworkVersion").Return(1)
 
 	idID := fftypes.NewUUID()
@@ -337,7 +357,7 @@ func TestResolveInputSigningIdentityKeyWithNoAuthorFail(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mmp := im.multiparty.(*multipartymocks.Manager)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 	mmp.On("GetNetworkVersion").Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
@@ -361,7 +381,7 @@ func TestResolveInputSigningIdentityByKeyDIDMismatch(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	idID := fftypes.NewUUID()
 
@@ -404,7 +424,7 @@ func TestResolveInputSigningIdentityByKeyNotFound(t *testing.T) {
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
 	mmp := im.multiparty.(*multipartymocks.Manager)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 	mmp.On("GetNetworkVersion").Return(1)
 
 	mdi := im.database.(*databasemocks.Plugin)
@@ -437,7 +457,7 @@ func TestResolveInputSigningIdentityByKeyFail(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "fullkey123").
@@ -459,7 +479,7 @@ func TestResolveInputSigningIdentityByKeyResolveFail(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "mykey123").Return("", fmt.Errorf("pop"))
+	mbi.On("ResolveSigningKey", ctx, "mykey123", blockchain.ResolveKeyIntentSign).Return("", fmt.Errorf("pop"))
 
 	msgIdentity := &core.SignerRef{
 		Key: "mykey123",
@@ -626,7 +646,7 @@ func TestResolveInputSigningKeyDefaultNoBlockchainDefaultKeyFallback(t *testing.
 	im.defaultKey = "key123"
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	resolvedKey, err := im.ResolveInputSigningKey(ctx, "", KeyNormalizationBlockchainPlugin)
 	assert.NoError(t, err)
@@ -641,7 +661,7 @@ func TestResolveInputSigningKeyOrgFallbackOk(t *testing.T) {
 	mmp.On("RootOrg").Return(multiparty.RootOrg{Key: "key123"})
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("fullkey123", nil) // just a format, not a lookup as we get it from the root org that's registered
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil) // just a format, not a lookup as we get it from the root org that's registered
 
 	resolvedKey, err := im.ResolveInputSigningKey(ctx, "", KeyNormalizationBlockchainPlugin)
 	assert.NoError(t, err)
@@ -660,7 +680,7 @@ func TestResolveInputSigningKeyOrgFallbackErr(t *testing.T) {
 	mmp.On("RootOrg").Return(multiparty.RootOrg{Key: "key123"})
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("fullkey123", fmt.Errorf("pop"))
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("fullkey123", fmt.Errorf("pop"))
 
 	_, err := im.ResolveInputSigningKey(ctx, "", KeyNormalizationBlockchainPlugin)
 	assert.Regexp(t, "pop", err)
@@ -685,7 +705,7 @@ func TestResolveInputSigningKeyOk(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	resolvedKey, err := im.ResolveInputSigningKey(ctx, "key123", KeyNormalizationBlockchainPlugin)
 	assert.NoError(t, err)
@@ -699,7 +719,7 @@ func TestResolveInputSigningKeyFail(t *testing.T) {
 	ctx, im := newTestIdentityManager(t)
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key123").Return("", fmt.Errorf("pop"))
+	mbi.On("ResolveSigningKey", ctx, "key123", blockchain.ResolveKeyIntentSign).Return("", fmt.Errorf("pop"))
 
 	_, err := im.ResolveInputSigningKey(ctx, "key123", KeyNormalizationBlockchainPlugin)
 	assert.Regexp(t, "pop", err)
@@ -751,7 +771,7 @@ func TestResolveDefaultSigningIdentityNotFound(t *testing.T) {
 	mmp.On("RootOrg").Return(multiparty.RootOrg{
 		Key: "key12345",
 	})
-	mbi.On("ResolveInputSigningKey", ctx, "key12345").Return("key12345", nil)
+	mbi.On("ResolveSigningKey", ctx, "key12345", blockchain.ResolveKeyIntentSign).Return("key12345", nil)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil)
@@ -790,7 +810,7 @@ func TestResolveDefaultSigningIdentitySystemFallback(t *testing.T) {
 	mmp := im.multiparty.(*multipartymocks.Manager)
 	mmp.On("GetNetworkVersion").Return(1)
 	mmp.On("RootOrg").Return(multiparty.RootOrg{Name: "org1", Key: "key12345"})
-	mbi.On("ResolveInputSigningKey", ctx, "key12345").Return("key12345", nil)
+	mbi.On("ResolveSigningKey", ctx, "key12345", blockchain.ResolveKeyIntentSign).Return("key12345", nil)
 
 	mdi := im.database.(*databasemocks.Plugin)
 	mdi.On("GetVerifierByValue", ctx, core.VerifierTypeEthAddress, "ns1", "key12345").Return(nil, nil)
@@ -817,7 +837,7 @@ func TestGetMultipartyRootVerifierResolveFailed(t *testing.T) {
 	mmp.On("RootOrg").Return(multiparty.RootOrg{Key: "0x12345"})
 
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "0x12345").Return("", fmt.Errorf("pop"))
+	mbi.On("ResolveSigningKey", ctx, "0x12345", blockchain.ResolveKeyIntentSign).Return("", fmt.Errorf("pop"))
 
 	_, err := im.GetMultipartyRootVerifier(ctx)
 	assert.Regexp(t, "pop", err)
@@ -850,7 +870,7 @@ func TestGetMultipartyRootOrgMismatch(t *testing.T) {
 		Key: "key12345",
 	})
 	mbi := im.blockchain.(*blockchainmocks.Plugin)
-	mbi.On("ResolveInputSigningKey", ctx, "key12345").Return("fullkey123", nil)
+	mbi.On("ResolveSigningKey", ctx, "key12345", blockchain.ResolveKeyIntentSign).Return("fullkey123", nil)
 
 	orgID := fftypes.NewUUID()
 	mdi := im.database.(*databasemocks.Plugin)