Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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