Skip to content

Commit

Permalink
Merge pull request #900 from kaleido-io/remote-name-tokens
Browse files Browse the repository at this point in the history
Tokens plugins now support a remote name
  • Loading branch information
shorsher authored Jul 19, 2022
2 parents 157a0e0 + 17dfa78 commit 8e17c9a
Show file tree
Hide file tree
Showing 16 changed files with 239 additions and 45 deletions.
1 change: 1 addition & 0 deletions docs/reference/config.md
Original file line number Diff line number Diff line change
Expand Up @@ -1084,6 +1084,7 @@ nav_order: 2
|Key|Description|Type|Default Value|
|---|-----------|----|-------------|
|name|The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector|`string`|`<nil>`
|remotename|The remote name of the Tokens Connector. This will be used in messages and events to refer to this specific Token Connector, if it differs from the name|`string`|`<nil>`
|type|The type of the Tokens Connector plugin to use|`string`|`<nil>`

## plugins.tokens[].fftokens
Expand Down
2 changes: 2 additions & 0 deletions internal/coreconfig/coreconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ const (
PluginConfigName = "name"
// PluginConfigType is the type of the plugin to be loaded
PluginConfigType = "type"
// PluginRemoteName is the plugin name to be sent in plugin calls
PluginRemoteName = "remotename"
// NamespaceName is the short name for a pre-defined namespace
NamespaceName = "name"
// NamespaceName is the long description for a pre-defined namespace
Expand Down
11 changes: 6 additions & 5 deletions internal/coremsgs/en_config_descriptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,12 @@ var (
ConfigTokensPlugin = ffc("config.tokens[].plugin", "The name of the Tokens Connector plugin to use", i18n.StringType)
ConfigTokensURL = ffc("config.tokens[].url", "The URL of the Token Connector", "URL "+i18n.StringType)

ConfigPluginTokens = ffc("config.plugins.tokens", "The tokens plugin configurations. This will be used to configure tokens connectors", i18n.StringType)
ConfigPluginTokensName = ffc("config.plugins.tokens[].name", "The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector", i18n.StringType)
ConfigPluginTokensType = ffc("config.plugins.tokens[].type", "The type of the Tokens Connector plugin to use", i18n.StringType)
ConfigPluginTokensURL = ffc("config.plugins.tokens[].fftokens.url", "The URL of the Token Connector", "URL "+i18n.StringType)
ConfigPluginTokensProxyURL = ffc("config.plugins.tokens[].fftokens.proxy.url", "Optional HTTP proxy server to use when connecting to the Token Connector", "URL "+i18n.StringType)
ConfigPluginTokens = ffc("config.plugins.tokens", "The tokens plugin configurations. This will be used to configure tokens connectors", i18n.StringType)
ConfigPluginTokensName = ffc("config.plugins.tokens[].name", "The name of the Tokens Connector. This will be used in the FireFly API path to refer to this specific Token Connector", i18n.StringType)
ConfigPluginTokensRemoteName = ffc("config.plugins.tokens[].remotename", "The remote name of the Tokens Connector. This will be used in messages and events to refer to this specific Token Connector, if it differs from the name", i18n.StringType)
ConfigPluginTokensType = ffc("config.plugins.tokens[].type", "The type of the Tokens Connector plugin to use", i18n.StringType)
ConfigPluginTokensURL = ffc("config.plugins.tokens[].fftokens.url", "The URL of the Token Connector", "URL "+i18n.StringType)
ConfigPluginTokensProxyURL = ffc("config.plugins.tokens[].fftokens.proxy.url", "Optional HTTP proxy server to use when connecting to the Token Connector", "URL "+i18n.StringType)

ConfigTokensProxyURL = ffc("config.tokens[].proxy.url", "Optional HTTP proxy server to use when connecting to the Token Connector", "URL "+i18n.StringType)

Expand Down
2 changes: 2 additions & 0 deletions internal/coremsgs/en_error_messages.go
Original file line number Diff line number Diff line change
Expand Up @@ -258,4 +258,6 @@ var (
MsgInvalidSubscriptionForNetwork = ffe("FF10416", "Subscription name '%s' is invalid according to multiparty network rules in effect (network version=%d)")
MsgBlockchainNotConfigured = ffe("FF10417", "No blockchain plugin configured")
MsgInvalidBatchPinEvent = ffe("FF10418", "BatchPin event is not valid - %s (%s): %s")
MsgDuplicatePluginRemoteName = ffe("FF10419", "Invalid %s plugin remote name: %s - remote names must be unique")
MsgInvalidConnectorName = ffe("FF10420", "Could not find name %s for %s connector")
)
4 changes: 3 additions & 1 deletion internal/definitions/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,9 +86,10 @@ type definitionHandler struct {
identity identity.Manager
assets assets.Manager
contracts contracts.Manager // optional
tokenNames map[string]string // mapping of token connector remote name => name
}

func newDefinitionHandler(ctx context.Context, ns string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager) (*definitionHandler, error) {
func newDefinitionHandler(ctx context.Context, ns string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, dm data.Manager, im identity.Manager, am assets.Manager, cm contracts.Manager, tokenNames map[string]string) (*definitionHandler, error) {
if di == nil || dm == nil || im == nil || am == nil {
return nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DefinitionHandler")
}
Expand All @@ -102,6 +103,7 @@ func newDefinitionHandler(ctx context.Context, ns string, multiparty bool, di da
identity: im,
assets: am,
contracts: cm,
tokenNames: tokenNames,
}, nil
}

Expand Down
6 changes: 4 additions & 2 deletions internal/definitions/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ func newTestDefinitionHandler(t *testing.T) (*definitionHandler, *testDefinition
mim := &identitymanagermocks.Manager{}
mam := &assetmocks.Manager{}
mcm := &contractmocks.Manager{}
tokenNames := make(map[string]string)
tokenNames["remote1"] = "connector1"
mbi.On("VerifierType").Return(core.VerifierTypeEthAddress).Maybe()
dh, _ := newDefinitionHandler(context.Background(), "ns1", false, mdi, mbi, mdx, mdm, mim, mam, mcm)
dh, _ := newDefinitionHandler(context.Background(), "ns1", false, mdi, mbi, mdx, mdm, mim, mam, mcm, tokenNames)
return dh, newTestDefinitionBatchState(t)
}

Expand All @@ -68,7 +70,7 @@ func (bs *testDefinitionBatchState) assertNoFinalizers() {
}

func TestInitFail(t *testing.T) {
_, err := newDefinitionHandler(context.Background(), "", false, nil, nil, nil, nil, nil, nil, nil)
_, err := newDefinitionHandler(context.Background(), "", false, nil, nil, nil, nil, nil, nil, nil, nil)
assert.Regexp(t, "FF10128", err)
}

Expand Down
8 changes: 8 additions & 0 deletions internal/definitions/handler_tokenpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ func (dh *definitionHandler) handleTokenPoolBroadcast(ctx context.Context, state
}

pool := announce.Pool
// Map remote connector name -> local name
if localName, ok := dh.tokenNames[pool.Connector]; ok {
pool.Connector = localName
} else {
log.L(ctx).Infof("Could not find local name for token connector: %s", pool.Connector)
return HandlerResult{Action: ActionReject}, i18n.NewError(ctx, coremsgs.MsgInvalidConnectorName, pool.Connector, "token")
}

pool.Message = msg.Header.ID
return dh.handleTokenPoolDefinition(ctx, state, pool)
}
Expand Down
23 changes: 22 additions & 1 deletion internal/definitions/handler_tokenpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func newPoolAnnouncement() *core.TokenPoolAnnouncement {
Type: core.TransactionTypeTokenPool,
ID: fftypes.NewUUID(),
},
Connector: "remote1",
}
return &core.TokenPoolAnnouncement{
Pool: pool,
Expand Down Expand Up @@ -78,7 +79,7 @@ func TestHandleDefinitionBroadcastTokenPoolActivateOK(t *testing.T) {
mam := sh.assets.(*assetmocks.Manager)
mdi.On("GetTokenPoolByID", context.Background(), "ns1", pool.ID).Return(nil, nil)
mdi.On("UpsertTokenPool", context.Background(), mock.MatchedBy(func(p *core.TokenPool) bool {
return *p.ID == *pool.ID && p.Message == msg.Header.ID
return *p.ID == *pool.ID && p.Message == msg.Header.ID && p.Connector == "connector1"
})).Return(nil)
mam.On("ActivateTokenPool", context.Background(), mock.AnythingOfType("*core.TokenPool")).Return(nil)

Expand All @@ -92,6 +93,26 @@ func TestHandleDefinitionBroadcastTokenPoolActivateOK(t *testing.T) {
mdi.AssertExpectations(t)
}

func TestHandleDefinitionBroadcastTokenPoolBadConnector(t *testing.T) {
sh, bs := newTestDefinitionHandler(t)

announce := newPoolAnnouncement()
pool := announce.Pool
pool.Name = "//bad"
msg, data, err := buildPoolDefinitionMessage(announce)
assert.NoError(t, err)

mam := sh.assets.(*assetmocks.Manager)
mam.On("ActivateTokenPool", context.Background(), mock.AnythingOfType("*core.TokenPool")).Return(nil)

action, err := sh.HandleDefinitionBroadcast(context.Background(), &bs.BatchState, msg, data, fftypes.NewUUID())
assert.Equal(t, HandlerResult{Action: ActionReject, CustomCorrelator: pool.ID}, action)
assert.Regexp(t, "FF10403", err)

err = bs.RunPreFinalize(context.Background())
assert.NoError(t, err)
}

func TestHandleDefinitionBroadcastTokenPoolGetPoolFail(t *testing.T) {
sh, bs := newTestDefinitionHandler(t)

Expand Down
49 changes: 30 additions & 19 deletions internal/definitions/sender.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,15 +46,16 @@ type Sender interface {
}

type definitionSender struct {
ctx context.Context
namespace string
multiparty bool
database database.Plugin
broadcast broadcast.Manager // optional
identity identity.Manager
data data.Manager
contracts contracts.Manager // optional
handler *definitionHandler
ctx context.Context
namespace string
multiparty bool
database database.Plugin
broadcast broadcast.Manager // optional
identity identity.Manager
data data.Manager
contracts contracts.Manager // optional
handler *definitionHandler
tokenRemoteNames map[string]string // mapping of token connector name => remote name
}

// Definitions that get processed immediately will create a temporary batch state and then finalize it inline
Expand All @@ -70,25 +71,35 @@ func fakeBatch(ctx context.Context, handler func(context.Context, *core.BatchSta
return err
}

func NewDefinitionSender(ctx context.Context, ns string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, bm broadcast.Manager, im identity.Manager, dm data.Manager, am assets.Manager, cm contracts.Manager) (Sender, Handler, error) {
func NewDefinitionSender(ctx context.Context, ns string, multiparty bool, di database.Plugin, bi blockchain.Plugin, dx dataexchange.Plugin, bm broadcast.Manager, im identity.Manager, dm data.Manager, am assets.Manager, cm contracts.Manager, tokenRemoteNames map[string]string) (Sender, Handler, error) {
if di == nil || im == nil || dm == nil {
return nil, nil, i18n.NewError(ctx, coremsgs.MsgInitializationNilDepError, "DefinitionSender")
}
ds := &definitionSender{
ctx: ctx,
namespace: ns,
multiparty: multiparty,
database: di,
broadcast: bm,
identity: im,
data: dm,
contracts: cm,
ctx: ctx,
namespace: ns,
multiparty: multiparty,
database: di,
broadcast: bm,
identity: im,
data: dm,
contracts: cm,
tokenRemoteNames: tokenRemoteNames,
}
dh, err := newDefinitionHandler(ctx, ns, multiparty, di, bi, dx, dm, im, am, cm)
dh, err := newDefinitionHandler(ctx, ns, multiparty, di, bi, dx, dm, im, am, cm, reverseMap(tokenRemoteNames))
ds.handler = dh
return ds, dh, err
}

// reverseMap reverses the key/values of a given map
func reverseMap(orderedMap map[string]string) map[string]string {
reverseMap := make(map[string]string, len(orderedMap))
for k, v := range orderedMap {
reverseMap[v] = k
}
return reverseMap
}

func (bm *definitionSender) Name() string {
return "DefinitionSender"
}
Expand Down
6 changes: 4 additions & 2 deletions internal/definitions/sender_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,17 @@ func newTestDefinitionSender(t *testing.T) (*definitionSender, func()) {
mdm := &datamocks.Manager{}
mam := &assetmocks.Manager{}
mcm := &contractmocks.Manager{}
tokenRemoteNames := make(map[string]string)
tokenRemoteNames["connector1"] = "remote1"

ctx, cancel := context.WithCancel(context.Background())
ds, _, err := NewDefinitionSender(ctx, "ns1", false, mdi, mbi, mdx, mbm, mim, mdm, mam, mcm)
ds, _, err := NewDefinitionSender(ctx, "ns1", false, mdi, mbi, mdx, mbm, mim, mdm, mam, mcm, tokenRemoteNames)
assert.NoError(t, err)
return ds.(*definitionSender), cancel
}

func TestInitSenderFail(t *testing.T) {
_, _, err := NewDefinitionSender(context.Background(), "", false, nil, nil, nil, nil, nil, nil, nil, nil)
_, _, err := NewDefinitionSender(context.Background(), "", false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
assert.Regexp(t, "FF10128", err)
}

Expand Down
11 changes: 11 additions & 0 deletions internal/definitions/sender_tokenpool.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,21 @@ package definitions
import (
"context"

"github.com/hyperledger/firefly-common/pkg/i18n"
"github.com/hyperledger/firefly-common/pkg/log"
"github.com/hyperledger/firefly/internal/coremsgs"
"github.com/hyperledger/firefly/pkg/core"
)

func (bm *definitionSender) DefineTokenPool(ctx context.Context, pool *core.TokenPoolAnnouncement, waitConfirm bool) error {
// Map token connector name -> remote name
if remoteName, exists := bm.tokenRemoteNames[pool.Pool.Connector]; exists {
pool.Pool.Connector = remoteName
} else {
log.L(ctx).Infof("Could not find remote name for token connector: %s", pool.Pool.Connector)
return i18n.NewError(ctx, coremsgs.MsgInvalidConnectorName, remoteName, "token")
}

if bm.multiparty {
if err := pool.Pool.Validate(ctx); err != nil {
return err
Expand Down
65 changes: 63 additions & 2 deletions internal/definitions/sender_tokenpool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ package definitions

import (
"context"
"fmt"
"testing"

"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly/mocks/broadcastmocks"
"github.com/hyperledger/firefly/mocks/databasemocks"
"github.com/hyperledger/firefly/mocks/datamocks"
"github.com/hyperledger/firefly/mocks/identitymanagermocks"
"github.com/hyperledger/firefly/mocks/sysmessagingmocks"
Expand Down Expand Up @@ -49,7 +51,7 @@ func TestBroadcastTokenPoolInvalid(t *testing.T) {
}

err := ds.DefineTokenPool(context.Background(), pool, false)
assert.Regexp(t, "FF00140", err)
assert.Regexp(t, "FF10420", err)

mdm.AssertExpectations(t)
}
Expand All @@ -73,7 +75,7 @@ func TestBroadcastTokenPoolInvalidNonMultiparty(t *testing.T) {
}

err := ds.DefineTokenPool(context.Background(), pool, false)
assert.Regexp(t, "FF00140", err)
assert.Regexp(t, "FF10420", err)

mdm.AssertExpectations(t)
}
Expand All @@ -96,6 +98,7 @@ func TestDefineTokenPoolOk(t *testing.T) {
Type: core.TokenTypeNonFungible,
Locator: "N1",
Symbol: "COIN",
Connector: "connector1",
},
}

Expand All @@ -111,3 +114,61 @@ func TestDefineTokenPoolOk(t *testing.T) {
mbm.AssertExpectations(t)
mms.AssertExpectations(t)
}

func TestDefineTokenPoolNonMultipartyTokenPoolFail(t *testing.T) {
ds, cancel := newTestDefinitionSender(t)
defer cancel()

mdm := ds.data.(*datamocks.Manager)
mbm := ds.broadcast.(*broadcastmocks.Manager)
mdi := ds.database.(*databasemocks.Plugin)

pool := &core.TokenPoolAnnouncement{
Pool: &core.TokenPool{
ID: fftypes.NewUUID(),
Namespace: "ns1",
Name: "mypool",
Type: core.TokenTypeNonFungible,
Locator: "N1",
Symbol: "COIN",
Connector: "connector1",
},
}

mdi.On("GetTokenPoolByID", context.Background(), "ns1", pool.Pool.ID).Return(nil, fmt.Errorf("pop"))

err := ds.DefineTokenPool(context.Background(), pool, false)
assert.Regexp(t, "pop", err)

mdm.AssertExpectations(t)
mbm.AssertExpectations(t)
}

func TestDefineTokenPoolBadName(t *testing.T) {
ds, cancel := newTestDefinitionSender(t)
defer cancel()
ds.multiparty = true

mim := ds.identity.(*identitymanagermocks.Manager)
mbm := ds.broadcast.(*broadcastmocks.Manager)
mms := &sysmessagingmocks.MessageSender{}

pool := &core.TokenPoolAnnouncement{
Pool: &core.TokenPool{
ID: fftypes.NewUUID(),
Namespace: "ns1",
Name: "///bad/////",
Type: core.TokenTypeNonFungible,
Locator: "N1",
Symbol: "COIN",
Connector: "connector1",
},
}

mim.On("ResolveInputSigningIdentity", mock.Anything, mock.Anything).Return(nil)
mbm.On("NewBroadcast", mock.Anything).Return(mms)
mms.On("Send", context.Background()).Return(nil)

err := ds.DefineTokenPool(context.Background(), pool, false)
assert.Regexp(t, "FF00140", err)
}
Loading

0 comments on commit 8e17c9a

Please sign in to comment.