diff --git a/docs/swagger/swagger.yaml b/docs/swagger/swagger.yaml index 88726790ca..b0a323d243 100644 --- a/docs/swagger/swagger.yaml +++ b/docs/swagger/swagger.yaml @@ -4282,7 +4282,7 @@ paths: description: "" post: description: 'TODO: Description' - operationId: postTokenPool + operationId: postTokenPoolByType parameters: - description: 'TODO: Description' in: path @@ -4598,7 +4598,7 @@ paths: /namespaces/{ns}/tokens/{type}/pools/{name}/burn: post: description: 'TODO: Description' - operationId: postTokenBurn + operationId: postTokenBurnByType parameters: - description: 'TODO: Description' in: path @@ -4719,6 +4719,8 @@ paths: messageHash: {} namespace: type: string + pool: + type: string poolProtocolId: type: string protocolId: @@ -4816,7 +4818,7 @@ paths: /namespaces/{ns}/tokens/{type}/pools/{name}/mint: post: description: 'TODO: Description' - operationId: postTokenMint + operationId: postTokenMintByType parameters: - description: 'TODO: Description' in: path @@ -4937,6 +4939,8 @@ paths: messageHash: {} namespace: type: string + pool: + type: string poolProtocolId: type: string protocolId: @@ -5198,7 +5202,7 @@ paths: description: "" post: description: 'TODO: Description' - operationId: postTokenTransfer + operationId: postTokenTransferByType parameters: - description: 'TODO: Description' in: path @@ -5319,6 +5323,8 @@ paths: messageHash: {} namespace: type: string + pool: + type: string poolProtocolId: type: string protocolId: @@ -5525,6 +5531,214 @@ paths: description: Success default: description: "" + /namespaces/{ns}/tokens/burn: + post: + description: 'TODO: Description' + operationId: postTokenBurn + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + message: + properties: + batch: {} + confirmed: {} + data: + items: + properties: + hash: {} + id: {} + type: object + type: array + group: + properties: + ledger: {} + members: + items: + properties: + identity: + type: string + node: + type: string + type: object + type: array + name: + type: string + type: object + hash: {} + header: + properties: + author: + type: string + cid: {} + created: {} + datahash: {} + group: {} + id: {} + key: + type: string + namespace: + type: string + tag: + type: string + topics: + items: + type: string + type: array + txtype: + type: string + type: + enum: + - definition + - broadcast + - private + - groupinit + - transfer_broadcast + - transfer_private + type: string + type: object + pins: + items: + type: string + type: array + state: + enum: + - staged + - ready + - pending + - confirmed + - rejected + type: string + type: object + messageHash: {} + namespace: + type: string + pool: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + enum: + - mint + - burn + - transfer + type: string + type: object + responses: + "200": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + default: + description: "" /namespaces/{ns}/tokens/connectors: get: description: 'TODO: Description' @@ -5558,10 +5772,10 @@ paths: description: Success default: description: "" - /namespaces/{ns}/tokens/pools: - get: + /namespaces/{ns}/tokens/mint: + post: description: 'TODO: Description' - operationId: getTokenPools + operationId: postTokenMint parameters: - description: 'TODO: Description' in: path @@ -5570,6 +5784,11 @@ paths: schema: example: default type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + type: string - description: Server-side request timeout (millseconds, or set a custom suffix like 10s) in: header @@ -5577,39 +5796,242 @@ paths: schema: default: 120s type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: connector - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: created - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: id - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: key - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: message - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: name - schema: - type: string - - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' - in: query - name: namespace + requestBody: + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + message: + properties: + batch: {} + confirmed: {} + data: + items: + properties: + hash: {} + id: {} + type: object + type: array + group: + properties: + ledger: {} + members: + items: + properties: + identity: + type: string + node: + type: string + type: object + type: array + name: + type: string + type: object + hash: {} + header: + properties: + author: + type: string + cid: {} + created: {} + datahash: {} + group: {} + id: {} + key: + type: string + namespace: + type: string + tag: + type: string + topics: + items: + type: string + type: array + txtype: + type: string + type: + enum: + - definition + - broadcast + - private + - groupinit + - transfer_broadcast + - transfer_private + type: string + type: object + pins: + items: + type: string + type: array + state: + enum: + - staged + - ready + - pending + - confirmed + - rejected + type: string + type: object + messageHash: {} + namespace: + type: string + pool: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + enum: + - mint + - burn + - transfer + type: string + type: object + responses: + "200": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + default: + description: "" + /namespaces/{ns}/tokens/pools: + get: + description: 'TODO: Description' + operationId: getTokenPools + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: connector + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: created + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: id + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: key + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: message + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: name + schema: + type: string + - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' + in: query + name: namespace schema: type: string - description: 'Data filter field. Prefixes supported: > >= < <= @ ^ ! !@ !^' @@ -5706,6 +6128,124 @@ paths: description: Success default: description: "" + post: + description: 'TODO: Description' + operationId: postTokenPool + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: + properties: + config: + additionalProperties: {} + type: object + connector: + type: string + key: + type: string + name: + type: string + symbol: + type: string + type: + enum: + - fungible + - nonfungible + type: string + type: object + responses: + "200": + content: + application/json: + schema: + properties: + config: + additionalProperties: {} + type: object + connector: + type: string + created: {} + id: {} + key: + type: string + message: {} + name: + type: string + namespace: + type: string + protocolId: + type: string + standard: + type: string + symbol: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + config: + additionalProperties: {} + type: object + connector: + type: string + created: {} + id: {} + key: + type: string + message: {} + name: + type: string + namespace: + type: string + protocolId: + type: string + standard: + type: string + symbol: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + default: + description: "" /namespaces/{ns}/tokens/pools/{nameOrID}: get: description: 'TODO: Description' @@ -5922,6 +6462,213 @@ paths: description: Success default: description: "" + post: + description: 'TODO: Description' + operationId: postTokenTransfer + parameters: + - description: 'TODO: Description' + in: path + name: ns + required: true + schema: + example: default + type: string + - description: When true the HTTP request blocks until the message is confirmed + in: query + name: confirm + schema: + type: string + - description: Server-side request timeout (millseconds, or set a custom suffix + like 10s) + in: header + name: Request-Timeout + schema: + default: 120s + type: string + requestBody: + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + message: + properties: + batch: {} + confirmed: {} + data: + items: + properties: + hash: {} + id: {} + type: object + type: array + group: + properties: + ledger: {} + members: + items: + properties: + identity: + type: string + node: + type: string + type: object + type: array + name: + type: string + type: object + hash: {} + header: + properties: + author: + type: string + cid: {} + created: {} + datahash: {} + group: {} + id: {} + key: + type: string + namespace: + type: string + tag: + type: string + topics: + items: + type: string + type: array + txtype: + type: string + type: + enum: + - definition + - broadcast + - private + - groupinit + - transfer_broadcast + - transfer_private + type: string + type: object + pins: + items: + type: string + type: array + state: + enum: + - staged + - ready + - pending + - confirmed + - rejected + type: string + type: object + messageHash: {} + namespace: + type: string + pool: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + enum: + - mint + - burn + - transfer + type: string + type: object + responses: + "200": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + "202": + content: + application/json: + schema: + properties: + amount: {} + connector: + type: string + created: {} + from: + type: string + key: + type: string + localId: {} + messageHash: {} + namespace: + type: string + poolProtocolId: + type: string + protocolId: + type: string + to: + type: string + tokenIndex: + type: string + tx: + properties: + id: {} + type: + type: string + type: object + type: + type: string + type: object + description: Success + default: + description: "" /namespaces/{ns}/tokens/transfers/{transferID}: get: description: 'TODO: Description' diff --git a/internal/apiserver/route_post_token_burn.go b/internal/apiserver/route_post_token_burn.go index 232e080f52..074a0d743e 100644 --- a/internal/apiserver/route_post_token_burn.go +++ b/internal/apiserver/route_post_token_burn.go @@ -28,12 +28,10 @@ import ( var postTokenBurn = &oapispec.Route{ Name: "postTokenBurn", - Path: "namespaces/{ns}/tokens/{type}/pools/{name}/burn", + Path: "namespaces/{ns}/tokens/burn", Method: http.MethodPost, PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, - {Name: "type", Description: i18n.MsgTBD}, - {Name: "name", Description: i18n.MsgTBD}, }, QueryParams: []*oapispec.QueryParam{ {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, @@ -41,12 +39,12 @@ var postTokenBurn = &oapispec.Route{ FilterFactory: nil, Description: i18n.MsgTBD, JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, - JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "To", "ProtocolID", "MessageHash", "Connector", "TX", "Created"}, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "To", "ProtocolID", "MessageHash", "TX", "Created"}, JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - return r.Or.Assets().BurnTokens(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + return r.Or.Assets().BurnTokens(r.Ctx, r.PP["ns"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, } diff --git a/internal/apiserver/route_post_token_burn_by_type.go b/internal/apiserver/route_post_token_burn_by_type.go new file mode 100644 index 0000000000..36de79899a --- /dev/null +++ b/internal/apiserver/route_post_token_burn_by_type.go @@ -0,0 +1,52 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + "strings" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var postTokenBurnByType = &oapispec.Route{ + Name: "postTokenBurnByType", + Path: "namespaces/{ns}/tokens/{type}/pools/{name}/burn", + Method: http.MethodPost, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + {Name: "type", Description: i18n.MsgTBD}, + {Name: "name", Description: i18n.MsgTBD}, + }, + QueryParams: []*oapispec.QueryParam{ + {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, + }, + FilterFactory: nil, + Description: i18n.MsgTBD, + JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "To", "ProtocolID", "MessageHash", "Connector", "TX", "Created"}, + JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, + JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + waitConfirm := strings.EqualFold(r.QP["confirm"], "true") + r.SuccessStatus = syncRetcode(waitConfirm) + return r.Or.Assets().BurnTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + }, +} diff --git a/internal/apiserver/route_post_token_burn_by_type_test.go b/internal/apiserver/route_post_token_burn_by_type_test.go new file mode 100644 index 0000000000..82d258da70 --- /dev/null +++ b/internal/apiserver/route_post_token_burn_by_type_test.go @@ -0,0 +1,47 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "bytes" + "encoding/json" + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/assetmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestPostTokenBurnByType(t *testing.T) { + o, r := newTestAPIServer() + mam := &assetmocks.Manager{} + o.On("Assets").Return(mam) + input := fftypes.TokenTransferInput{} + var buf bytes.Buffer + json.NewEncoder(&buf).Encode(&input) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/burn", &buf) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mam.On("BurnTokensByType", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + Return(&fftypes.TokenTransfer{}, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 202, res.Result().StatusCode) +} diff --git a/internal/apiserver/route_post_token_burn_test.go b/internal/apiserver/route_post_token_burn_test.go index 6b4de5ee98..549520f19c 100644 --- a/internal/apiserver/route_post_token_burn_test.go +++ b/internal/apiserver/route_post_token_burn_test.go @@ -35,11 +35,11 @@ func TestPostTokenBurn(t *testing.T) { input := fftypes.TokenTransferInput{} var buf bytes.Buffer json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/burn", &buf) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/burn", &buf) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("BurnTokens", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + mam.On("BurnTokens", mock.Anything, "ns1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). Return(&fftypes.TokenTransfer{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_token_mint.go b/internal/apiserver/route_post_token_mint.go index 608f12d7a9..4d8802c5d7 100644 --- a/internal/apiserver/route_post_token_mint.go +++ b/internal/apiserver/route_post_token_mint.go @@ -28,12 +28,10 @@ import ( var postTokenMint = &oapispec.Route{ Name: "postTokenMint", - Path: "namespaces/{ns}/tokens/{type}/pools/{name}/mint", + Path: "namespaces/{ns}/tokens/mint", Method: http.MethodPost, PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, - {Name: "type", Description: i18n.MsgTBD}, - {Name: "name", Description: i18n.MsgTBD}, }, QueryParams: []*oapispec.QueryParam{ {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, @@ -41,12 +39,12 @@ var postTokenMint = &oapispec.Route{ FilterFactory: nil, Description: i18n.MsgTBD, JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, - JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "TokenIndex", "From", "ProtocolID", "MessageHash", "Connector", "TX", "Created"}, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "TokenIndex", "From", "ProtocolID", "MessageHash", "TX", "Created"}, JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - return r.Or.Assets().MintTokens(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + return r.Or.Assets().MintTokens(r.Ctx, r.PP["ns"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, } diff --git a/internal/apiserver/route_post_token_mint_by_type.go b/internal/apiserver/route_post_token_mint_by_type.go new file mode 100644 index 0000000000..440366d3cc --- /dev/null +++ b/internal/apiserver/route_post_token_mint_by_type.go @@ -0,0 +1,52 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + "strings" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var postTokenMintByType = &oapispec.Route{ + Name: "postTokenMintByType", + Path: "namespaces/{ns}/tokens/{type}/pools/{name}/mint", + Method: http.MethodPost, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + {Name: "type", Description: i18n.MsgTBD}, + {Name: "name", Description: i18n.MsgTBD}, + }, + QueryParams: []*oapispec.QueryParam{ + {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, + }, + FilterFactory: nil, + Description: i18n.MsgTBD, + JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "TokenIndex", "From", "ProtocolID", "MessageHash", "Connector", "TX", "Created"}, + JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, + JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + waitConfirm := strings.EqualFold(r.QP["confirm"], "true") + r.SuccessStatus = syncRetcode(waitConfirm) + return r.Or.Assets().MintTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + }, +} diff --git a/internal/apiserver/route_post_token_mint_by_type_test.go b/internal/apiserver/route_post_token_mint_by_type_test.go new file mode 100644 index 0000000000..32f615276d --- /dev/null +++ b/internal/apiserver/route_post_token_mint_by_type_test.go @@ -0,0 +1,47 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "bytes" + "encoding/json" + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/assetmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestPostTokenMintByType(t *testing.T) { + o, r := newTestAPIServer() + mam := &assetmocks.Manager{} + o.On("Assets").Return(mam) + input := fftypes.TokenTransferInput{} + var buf bytes.Buffer + json.NewEncoder(&buf).Encode(&input) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/mint", &buf) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mam.On("MintTokensByType", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + Return(&fftypes.TokenTransfer{}, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 202, res.Result().StatusCode) +} diff --git a/internal/apiserver/route_post_token_mint_test.go b/internal/apiserver/route_post_token_mint_test.go index 1b4eca0c64..2ab2412363 100644 --- a/internal/apiserver/route_post_token_mint_test.go +++ b/internal/apiserver/route_post_token_mint_test.go @@ -35,11 +35,11 @@ func TestPostTokenMint(t *testing.T) { input := fftypes.TokenTransferInput{} var buf bytes.Buffer json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/mint", &buf) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/mint", &buf) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("MintTokens", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + mam.On("MintTokens", mock.Anything, "ns1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). Return(&fftypes.TokenTransfer{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_token_pool.go b/internal/apiserver/route_post_token_pool.go index e92c87eb65..5892ab8d4c 100644 --- a/internal/apiserver/route_post_token_pool.go +++ b/internal/apiserver/route_post_token_pool.go @@ -28,11 +28,10 @@ import ( var postTokenPool = &oapispec.Route{ Name: "postTokenPool", - Path: "namespaces/{ns}/tokens/{type}/pools", + Path: "namespaces/{ns}/tokens/pools", Method: http.MethodPost, PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, - {Name: "type", Description: i18n.MsgTBD}, }, QueryParams: []*oapispec.QueryParam{ {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, @@ -40,12 +39,12 @@ var postTokenPool = &oapispec.Route{ FilterFactory: nil, Description: i18n.MsgTBD, JSONInputValue: func() interface{} { return &fftypes.TokenPool{} }, - JSONInputMask: []string{"ID", "Namespace", "Standard", "ProtocolID", "TX", "Connector", "Message", "Created"}, + JSONInputMask: []string{"ID", "Namespace", "Standard", "ProtocolID", "TX", "Message", "Created"}, JSONOutputValue: func() interface{} { return &fftypes.TokenPool{} }, JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - return r.Or.Assets().CreateTokenPool(r.Ctx, r.PP["ns"], r.PP["type"], r.Input.(*fftypes.TokenPool), waitConfirm) + return r.Or.Assets().CreateTokenPool(r.Ctx, r.PP["ns"], r.Input.(*fftypes.TokenPool), waitConfirm) }, } diff --git a/internal/apiserver/route_post_token_pool_by_type.go b/internal/apiserver/route_post_token_pool_by_type.go new file mode 100644 index 0000000000..21cdd84a55 --- /dev/null +++ b/internal/apiserver/route_post_token_pool_by_type.go @@ -0,0 +1,51 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + "strings" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var postTokenPoolByType = &oapispec.Route{ + Name: "postTokenPoolByType", + Path: "namespaces/{ns}/tokens/{type}/pools", + Method: http.MethodPost, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + {Name: "type", Description: i18n.MsgTBD}, + }, + QueryParams: []*oapispec.QueryParam{ + {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, + }, + FilterFactory: nil, + Description: i18n.MsgTBD, + JSONInputValue: func() interface{} { return &fftypes.TokenPool{} }, + JSONInputMask: []string{"ID", "Namespace", "Standard", "ProtocolID", "TX", "Connector", "Message", "Created"}, + JSONOutputValue: func() interface{} { return &fftypes.TokenPool{} }, + JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + waitConfirm := strings.EqualFold(r.QP["confirm"], "true") + r.SuccessStatus = syncRetcode(waitConfirm) + return r.Or.Assets().CreateTokenPoolByType(r.Ctx, r.PP["ns"], r.PP["type"], r.Input.(*fftypes.TokenPool), waitConfirm) + }, +} diff --git a/internal/apiserver/route_post_token_pool_by_type_test.go b/internal/apiserver/route_post_token_pool_by_type_test.go new file mode 100644 index 0000000000..96eed311b3 --- /dev/null +++ b/internal/apiserver/route_post_token_pool_by_type_test.go @@ -0,0 +1,47 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "bytes" + "encoding/json" + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/assetmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestPostTokenPoolByType(t *testing.T) { + o, r := newTestAPIServer() + mam := &assetmocks.Manager{} + o.On("Assets").Return(mam) + input := fftypes.TokenPool{} + var buf bytes.Buffer + json.NewEncoder(&buf).Encode(&input) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools", &buf) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mam.On("CreateTokenPoolByType", mock.Anything, "ns1", "tok1", mock.AnythingOfType("*fftypes.TokenPool"), false). + Return(&fftypes.TokenPool{}, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 202, res.Result().StatusCode) +} diff --git a/internal/apiserver/route_post_token_pool_test.go b/internal/apiserver/route_post_token_pool_test.go index bf7c99b9f0..86262505fb 100644 --- a/internal/apiserver/route_post_token_pool_test.go +++ b/internal/apiserver/route_post_token_pool_test.go @@ -35,11 +35,11 @@ func TestPostTokenPool(t *testing.T) { input := fftypes.TokenPool{} var buf bytes.Buffer json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools", &buf) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/pools", &buf) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("CreateTokenPool", mock.Anything, "ns1", "tok1", mock.AnythingOfType("*fftypes.TokenPool"), false). + mam.On("CreateTokenPool", mock.Anything, "ns1", mock.AnythingOfType("*fftypes.TokenPool"), false). Return(&fftypes.TokenPool{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/route_post_token_transfer.go b/internal/apiserver/route_post_token_transfer.go index 0d70356818..1fe71e50ac 100644 --- a/internal/apiserver/route_post_token_transfer.go +++ b/internal/apiserver/route_post_token_transfer.go @@ -28,12 +28,10 @@ import ( var postTokenTransfer = &oapispec.Route{ Name: "postTokenTransfer", - Path: "namespaces/{ns}/tokens/{type}/pools/{name}/transfers", + Path: "namespaces/{ns}/tokens/transfers", Method: http.MethodPost, PathParams: []*oapispec.PathParam{ {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, - {Name: "type", Description: i18n.MsgTBD}, - {Name: "name", Description: i18n.MsgTBD}, }, QueryParams: []*oapispec.QueryParam{ {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, @@ -41,12 +39,12 @@ var postTokenTransfer = &oapispec.Route{ FilterFactory: nil, Description: i18n.MsgTBD, JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, - JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "ProtocolID", "MessageHash", "Connector", "Namespace", "TX", "Created"}, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "ProtocolID", "MessageHash", "Namespace", "TX", "Created"}, JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { waitConfirm := strings.EqualFold(r.QP["confirm"], "true") r.SuccessStatus = syncRetcode(waitConfirm) - return r.Or.Assets().TransferTokens(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + return r.Or.Assets().TransferTokens(r.Ctx, r.PP["ns"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) }, } diff --git a/internal/apiserver/route_post_token_transfer_by_type.go b/internal/apiserver/route_post_token_transfer_by_type.go new file mode 100644 index 0000000000..c838f0b91f --- /dev/null +++ b/internal/apiserver/route_post_token_transfer_by_type.go @@ -0,0 +1,52 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "net/http" + "strings" + + "github.com/hyperledger/firefly/internal/config" + "github.com/hyperledger/firefly/internal/i18n" + "github.com/hyperledger/firefly/internal/oapispec" + "github.com/hyperledger/firefly/pkg/fftypes" +) + +var postTokenTransferByType = &oapispec.Route{ + Name: "postTokenTransferByType", + Path: "namespaces/{ns}/tokens/{type}/pools/{name}/transfers", + Method: http.MethodPost, + PathParams: []*oapispec.PathParam{ + {Name: "ns", ExampleFromConf: config.NamespacesDefault, Description: i18n.MsgTBD}, + {Name: "type", Description: i18n.MsgTBD}, + {Name: "name", Description: i18n.MsgTBD}, + }, + QueryParams: []*oapispec.QueryParam{ + {Name: "confirm", Description: i18n.MsgConfirmQueryParam, IsBool: true}, + }, + FilterFactory: nil, + Description: i18n.MsgTBD, + JSONInputValue: func() interface{} { return &fftypes.TokenTransferInput{} }, + JSONInputMask: []string{"Type", "LocalID", "PoolProtocolID", "ProtocolID", "MessageHash", "Connector", "Namespace", "TX", "Created"}, + JSONOutputValue: func() interface{} { return &fftypes.TokenTransfer{} }, + JSONOutputCodes: []int{http.StatusAccepted, http.StatusOK}, + JSONHandler: func(r *oapispec.APIRequest) (output interface{}, err error) { + waitConfirm := strings.EqualFold(r.QP["confirm"], "true") + r.SuccessStatus = syncRetcode(waitConfirm) + return r.Or.Assets().TransferTokensByType(r.Ctx, r.PP["ns"], r.PP["type"], r.PP["name"], r.Input.(*fftypes.TokenTransferInput), waitConfirm) + }, +} diff --git a/internal/apiserver/route_post_token_transfer_by_type_test.go b/internal/apiserver/route_post_token_transfer_by_type_test.go new file mode 100644 index 0000000000..0658466ef2 --- /dev/null +++ b/internal/apiserver/route_post_token_transfer_by_type_test.go @@ -0,0 +1,47 @@ +// Copyright © 2021 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package apiserver + +import ( + "bytes" + "encoding/json" + "net/http/httptest" + "testing" + + "github.com/hyperledger/firefly/mocks/assetmocks" + "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestPostTokenTransferByType(t *testing.T) { + o, r := newTestAPIServer() + mam := &assetmocks.Manager{} + o.On("Assets").Return(mam) + input := fftypes.TokenTransferInput{} + var buf bytes.Buffer + json.NewEncoder(&buf).Encode(&input) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/transfers", &buf) + req.Header.Set("Content-Type", "application/json; charset=utf-8") + res := httptest.NewRecorder() + + mam.On("TransferTokensByType", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + Return(&fftypes.TokenTransfer{}, nil) + r.ServeHTTP(res, req) + + assert.Equal(t, 202, res.Result().StatusCode) +} diff --git a/internal/apiserver/route_post_token_transfer_test.go b/internal/apiserver/route_post_token_transfer_test.go index a9b2e195c0..3ac1548c58 100644 --- a/internal/apiserver/route_post_token_transfer_test.go +++ b/internal/apiserver/route_post_token_transfer_test.go @@ -35,11 +35,11 @@ func TestPostTokenTransfer(t *testing.T) { input := fftypes.TokenTransferInput{} var buf bytes.Buffer json.NewEncoder(&buf).Encode(&input) - req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/tok1/pools/pool1/transfers", &buf) + req := httptest.NewRequest("POST", "/api/v1/namespaces/ns1/tokens/transfers", &buf) req.Header.Set("Content-Type", "application/json; charset=utf-8") res := httptest.NewRecorder() - mam.On("TransferTokens", mock.Anything, "ns1", "tok1", "pool1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). + mam.On("TransferTokens", mock.Anything, "ns1", mock.AnythingOfType("*fftypes.TokenTransferInput"), false). Return(&fftypes.TokenTransfer{}, nil) r.ServeHTTP(res, req) diff --git a/internal/apiserver/routes.go b/internal/apiserver/routes.go index f1d981a0ab..1fc28b3f2e 100644 --- a/internal/apiserver/routes.go +++ b/internal/apiserver/routes.go @@ -78,6 +78,7 @@ var routes = []*oapispec.Route{ getTxns, postTokenPool, + postTokenPoolByType, getTokenPools, getTokenPoolsByType, getTokenPoolByNameOrID, @@ -88,7 +89,10 @@ var routes = []*oapispec.Route{ getTokenTransfersByPool, getTokenTransferByID, postTokenMint, + postTokenMintByType, postTokenBurn, + postTokenBurnByType, postTokenTransfer, + postTokenTransferByType, getTokenConnectors, } diff --git a/internal/assets/manager.go b/internal/assets/manager.go index c2970ea28e..5cd1c85639 100644 --- a/internal/assets/manager.go +++ b/internal/assets/manager.go @@ -35,7 +35,8 @@ import ( ) type Manager interface { - CreateTokenPool(ctx context.Context, ns, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) + CreateTokenPool(ctx context.Context, ns string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) + CreateTokenPoolByType(ctx context.Context, ns, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) GetTokenPools(ctx context.Context, ns string, filter database.AndFilter) ([]*fftypes.TokenPool, *database.FilterResult, error) GetTokenPoolsByType(ctx context.Context, ns, connector string, filter database.AndFilter) ([]*fftypes.TokenPool, *database.FilterResult, error) GetTokenPool(ctx context.Context, ns, connector, poolName string) (*fftypes.TokenPool, error) @@ -47,9 +48,12 @@ type Manager interface { GetTokenTransferByID(ctx context.Context, ns, id string) (*fftypes.TokenTransfer, error) GetTokenTransfersByPool(ctx context.Context, ns, connector, poolName string, filter database.AndFilter) ([]*fftypes.TokenTransfer, *database.FilterResult, error) NewTransfer(ns, connector, poolName string, transfer *fftypes.TokenTransferInput) sysmessaging.MessageSender - MintTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) - BurnTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) - TransferTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + MintTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + MintTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + BurnTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + BurnTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + TransferTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) + TransferTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) GetTokenConnectors(ctx context.Context, ns string) ([]*fftypes.TokenConnector, error) // Bound token callbacks @@ -145,3 +149,27 @@ func (am *assetManager) Start() error { func (am *assetManager) WaitStop() { // No go routines } + +func (am *assetManager) getTokenConnectorName(ctx context.Context, ns string) (string, error) { + tokenConnectors, err := am.GetTokenConnectors(ctx, ns) + if err != nil { + return "", err + } + if len(tokenConnectors) != 1 { + return "", i18n.NewError(ctx, i18n.MsgFieldNotSpecified, "connector") + } + return tokenConnectors[0].Name, nil +} + +func (am *assetManager) getTokenPoolName(ctx context.Context, ns string) (string, error) { + f := database.TokenPoolQueryFactory.NewFilter(ctx).And() + f.Limit(1).Count(true) + tokenPools, fr, err := am.GetTokenPools(ctx, ns, f) + if err != nil { + return "", err + } + if *fr.TotalCount != 1 { + return "", i18n.NewError(ctx, i18n.MsgFieldNotSpecified, "pool") + } + return tokenPools[0].Name, nil +} diff --git a/internal/assets/token_pool.go b/internal/assets/token_pool.go index 1b535e7d17..c972df99c3 100644 --- a/internal/assets/token_pool.go +++ b/internal/assets/token_pool.go @@ -49,7 +49,32 @@ func retrieveTokenPoolCreateInputs(ctx context.Context, op *fftypes.Operation, p return nil } -func (am *assetManager) CreateTokenPool(ctx context.Context, ns string, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { +func (am *assetManager) CreateTokenPool(ctx context.Context, ns string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { + if err := am.data.VerifyNamespaceExists(ctx, ns); err != nil { + return nil, err + } + pool.ID = fftypes.NewUUID() + pool.Namespace = ns + + if pool.Connector == "" { + connector, err := am.getTokenConnectorName(ctx, ns) + if err != nil { + return nil, err + } + pool.Connector = connector + } + + if pool.Key == "" { + org, err := am.identity.GetLocalOrganization(ctx) + if err != nil { + return nil, err + } + pool.Key = org.Identity + } + return am.createTokenPoolInternal(ctx, pool, waitConfirm) +} + +func (am *assetManager) CreateTokenPoolByType(ctx context.Context, ns string, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { if err := am.data.VerifyNamespaceExists(ctx, ns); err != nil { return nil, err } diff --git a/internal/assets/token_pool_test.go b/internal/assets/token_pool_test.go index 8eea63f1e5..a396956520 100644 --- a/internal/assets/token_pool_test.go +++ b/internal/assets/token_pool_test.go @@ -27,6 +27,7 @@ import ( "github.com/hyperledger/firefly/mocks/tokenmocks" "github.com/hyperledger/firefly/pkg/database" "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/hyperledger/firefly/pkg/tokens" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -38,7 +39,7 @@ func TestCreateTokenPoolBadNamespace(t *testing.T) { mdm := am.data.(*datamocks.Manager) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "test", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) assert.EqualError(t, err, "pop") } @@ -51,24 +52,198 @@ func TestCreateTokenPoolIdentityFail(t *testing.T) { mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "test", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) assert.EqualError(t, err, "pop") } -func TestCreateTokenPoolBadConnector(t *testing.T) { +func TestCreateTokenPoolFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenPool + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + mti.On("CreateTokenPool", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.Regexp(t, "pop", err) +} + +func TestCreateTokenPoolTransactionFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.Regexp(t, "pop", err) +} + +func TestCreateTokenPoolOperationFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdi := am.database.(*databasemocks.Plugin) mdm := am.data.(*datamocks.Manager) mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenPool + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "bad", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.Regexp(t, "pop", err) +} + +func TestCreateTokenPoolSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + pool := &fftypes.TokenPool{} + pool.Connector = "test" + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mti.On("CreateTokenPool", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenPool + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.NoError(t, err) +} + +func TestCreateTokenPoolUnknownConnectorSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mti.On("CreateTokenPool", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenPool + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.NoError(t, err) +} + +func TestCreateTokenPoolUnknownConnectorNoConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + am.tokens = make(map[string]tokens.Plugin) + + mdm := am.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.Regexp(t, "FF10292", err) +} + +func TestCreateTokenPoolUnknownConnectorMultipleConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + am.tokens["magic-tokens"] = nil + am.tokens["magic-tokens2"] = nil + + mdm := am.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, false) + assert.Regexp(t, "FF10292", err) +} + +func TestCreateTokenPoolConfirm(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + msa := am.syncasync.(*syncasyncmocks.Bridge) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil).Times(2) + mti.On("CreateTokenPool", context.Background(), mock.Anything, mock.Anything).Return(nil).Times(1) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenPool + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil).Times(1) + msa.On("WaitForTokenPool", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(nil, nil) + + _, err := am.CreateTokenPool(context.Background(), "ns1", &fftypes.TokenPool{}, true) + assert.NoError(t, err) +} + +func TestCreateTokenPoolByTypeBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdm := am.data.(*datamocks.Manager) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(fmt.Errorf("pop")) + + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "test", &fftypes.TokenPool{}, false) + assert.EqualError(t, err, "pop") +} + +func TestCreateTokenPoolByTypeIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdm := am.data.(*datamocks.Manager) + mim := am.identity.(*identitymanagermocks.Manager) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "test", &fftypes.TokenPool{}, false) + assert.EqualError(t, err, "pop") +} + +func TestCreateTokenPoolByTypeBadConnector(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mdm := am.data.(*datamocks.Manager) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) + + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "bad", &fftypes.TokenPool{}, false) assert.Regexp(t, "FF10272", err) } -func TestCreateTokenPoolFail(t *testing.T) { +func TestCreateTokenPoolByTypeFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -84,11 +259,11 @@ func TestCreateTokenPoolFail(t *testing.T) { mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) mti.On("CreateTokenPool", context.Background(), mock.Anything, mock.Anything, mock.Anything).Return(fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) assert.Regexp(t, "pop", err) } -func TestCreateTokenPoolTransactionFail(t *testing.T) { +func TestCreateTokenPoolByTypeTransactionFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -99,11 +274,11 @@ func TestCreateTokenPoolTransactionFail(t *testing.T) { mdm.On("VerifyNamespaceExists", context.Background(), "ns1").Return(nil) mdi.On("UpsertTransaction", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) assert.Regexp(t, "pop", err) } -func TestCreateTokenPoolOperationFail(t *testing.T) { +func TestCreateTokenPoolByTypeOperationFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -117,11 +292,11 @@ func TestCreateTokenPoolOperationFail(t *testing.T) { }), false).Return(nil) mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) - _, err := am.CreateTokenPool(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) assert.Regexp(t, "pop", err) } -func TestCreateTokenPoolSuccess(t *testing.T) { +func TestCreateTokenPoolByTypeSuccess(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -137,11 +312,11 @@ func TestCreateTokenPoolSuccess(t *testing.T) { }), false).Return(nil) mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) - _, err := am.CreateTokenPool(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, false) assert.NoError(t, err) } -func TestCreateTokenPoolConfirm(t *testing.T) { +func TestCreateTokenPoolByTypeConfirm(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -164,7 +339,7 @@ func TestCreateTokenPoolConfirm(t *testing.T) { }). Return(nil, nil) - _, err := am.CreateTokenPool(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, true) + _, err := am.CreateTokenPoolByType(context.Background(), "ns1", "magic-tokens", &fftypes.TokenPool{}, true) assert.NoError(t, err) } diff --git a/internal/assets/token_transfer.go b/internal/assets/token_transfer.go index 3a5133a873..d0fdaa0cca 100644 --- a/internal/assets/token_transfer.go +++ b/internal/assets/token_transfer.go @@ -99,7 +99,22 @@ func (s *transferSender) setDefaults() { s.transfer.Connector = s.connector } -func (am *assetManager) MintTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { +func (am *assetManager) MintTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { + connector := transfer.Connector + if connector == "" { + connector, err = am.getTokenConnectorName(ctx, ns) + if err != nil { + return nil, err + } + } + poolName := transfer.Pool + if poolName == "" { + poolName, err = am.getTokenPoolName(ctx, ns) + if err != nil { + return nil, err + } + } + transfer.Type = fftypes.TokenTransferTypeMint if transfer.Key == "" { org, err := am.identity.GetLocalOrganization(ctx) @@ -122,7 +137,68 @@ func (am *assetManager) MintTokens(ctx context.Context, ns, connector, poolName return &transfer.TokenTransfer, err } -func (am *assetManager) BurnTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { +func (am *assetManager) MintTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { + transfer.Type = fftypes.TokenTransferTypeMint + if transfer.Key == "" { + org, err := am.identity.GetLocalOrganization(ctx) + if err != nil { + return nil, err + } + transfer.Key = org.Identity + } + transfer.From = "" + if transfer.To == "" { + transfer.To = transfer.Key + } + + sender := am.NewTransfer(ns, connector, poolName, transfer) + if waitConfirm { + err = sender.SendAndWait(ctx) + } else { + err = sender.Send(ctx) + } + return &transfer.TokenTransfer, err +} + +func (am *assetManager) BurnTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { + connector := transfer.Connector + if connector == "" { + connector, err = am.getTokenConnectorName(ctx, ns) + if err != nil { + return nil, err + } + } + poolName := transfer.Pool + if poolName == "" { + poolName, err = am.getTokenPoolName(ctx, ns) + if err != nil { + return nil, err + } + } + + transfer.Type = fftypes.TokenTransferTypeBurn + if transfer.Key == "" { + org, err := am.identity.GetLocalOrganization(ctx) + if err != nil { + return nil, err + } + transfer.Key = org.Identity + } + if transfer.From == "" { + transfer.From = transfer.Key + } + transfer.To = "" + + sender := am.NewTransfer(ns, connector, poolName, transfer) + if waitConfirm { + err = sender.SendAndWait(ctx) + } else { + err = sender.Send(ctx) + } + return &transfer.TokenTransfer, err +} + +func (am *assetManager) BurnTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { transfer.Type = fftypes.TokenTransferTypeBurn if transfer.Key == "" { org, err := am.identity.GetLocalOrganization(ctx) @@ -145,7 +221,50 @@ func (am *assetManager) BurnTokens(ctx context.Context, ns, connector, poolName return &transfer.TokenTransfer, err } -func (am *assetManager) TransferTokens(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { +func (am *assetManager) TransferTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { + connector := transfer.Connector + if connector == "" { + connector, err = am.getTokenConnectorName(ctx, ns) + if err != nil { + return nil, err + } + } + poolName := transfer.Pool + if poolName == "" { + poolName, err = am.getTokenPoolName(ctx, ns) + if err != nil { + return nil, err + } + } + + transfer.Type = fftypes.TokenTransferTypeTransfer + if transfer.Key == "" { + org, err := am.identity.GetLocalOrganization(ctx) + if err != nil { + return nil, err + } + transfer.Key = org.Identity + } + if transfer.From == "" { + transfer.From = transfer.Key + } + if transfer.To == "" { + transfer.To = transfer.Key + } + if transfer.From == transfer.To { + return nil, i18n.NewError(ctx, i18n.MsgCannotTransferToSelf) + } + + sender := am.NewTransfer(ns, connector, poolName, transfer) + if waitConfirm { + err = sender.SendAndWait(ctx) + } else { + err = sender.Send(ctx) + } + return &transfer.TokenTransfer, err +} + +func (am *assetManager) TransferTokensByType(ctx context.Context, ns, connector, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (out *fftypes.TokenTransfer, err error) { transfer.Type = fftypes.TokenTransferTypeTransfer if transfer.Key == "" { org, err := am.identity.GetLocalOrganization(ctx) diff --git a/internal/assets/token_transfer_test.go b/internal/assets/token_transfer_test.go index 2cc9e631e8..025b6393d7 100644 --- a/internal/assets/token_transfer_test.go +++ b/internal/assets/token_transfer_test.go @@ -30,6 +30,7 @@ import ( "github.com/hyperledger/firefly/mocks/tokenmocks" "github.com/hyperledger/firefly/pkg/database" "github.com/hyperledger/firefly/pkg/fftypes" + "github.com/hyperledger/firefly/pkg/tokens" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" ) @@ -111,6 +112,8 @@ func TestMintTokensSuccess(t *testing.T) { mint := &fftypes.TokenTransferInput{} mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" mdi := am.database.(*databasemocks.Plugin) mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) @@ -123,19 +126,226 @@ func TestMintTokensSuccess(t *testing.T) { }), false).Return(nil) mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + _, err := am.MintTokens(context.Background(), "ns1", mint, false) assert.NoError(t, err) } -func TestMintTokensBadPlugin(t *testing.T) { +func TestMintTokenUnknownConnectorSuccess(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) - _, err := am.MintTokens(context.Background(), "", "", "", &fftypes.TokenTransferInput{}, false) - assert.Regexp(t, "FF10272", err) + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.NoError(t, err) +} + +func TestMintTokenUnknownConnectorNoConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "" + mint.Pool = "pool1" + + am.tokens = make(map[string]tokens.Plugin) + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.Regexp(t, "FF10292", err) +} + +func TestMintTokenUnknownConnectorMultipleConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "" + mint.Pool = "pool1" + + am.tokens["magic-tokens"] = nil + am.tokens["magic-tokens2"] = nil + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.Regexp(t, "FF10292", err) +} + +func TestMintTokenUnknownConnectorBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "" + mint.Pool = "pool1" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "", mint, false) + assert.Regexp(t, "FF10131", err) +} + +func TestMintTokenUnknownPoolSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + } + totalCount := int64(1) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.NoError(t, err) +} + +func TestMintTokenUnknownPoolNoPools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{} + totalCount := int64(0) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.Regexp(t, "FF10292", err) +} + +func TestMintTokenUnknownPoolMultiplePools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + { + Name: "pool2", + }, + } + totalCount := int64(2) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.Regexp(t, "FF10292", err) +} + +func TestMintTokenUnknownPoolBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokens(context.Background(), "", mint, false) + assert.Regexp(t, "FF10131", err) +} + +func TestMintTokensNoPool(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(nil, fmt.Errorf("pop")) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.EqualError(t, err, "pop") } func TestMintTokensBadPool(t *testing.T) { @@ -144,80 +354,1199 @@ func TestMintTokensBadPool(t *testing.T) { mint := &fftypes.TokenTransferInput{} mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(nil, fmt.Errorf("pop")) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(fmt.Errorf("pop")) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensOperationFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) + + _, err := am.MintTokens(context.Background(), "ns1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensConfirm(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + mint.Connector = "magic-tokens" + mint.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + msa := am.syncasync.(*syncasyncmocks.Bridge) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + msa.On("WaitForTokenTransfer", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(&fftypes.TokenTransfer{}, nil) + + _, err := am.MintTokens(context.Background(), "ns1", mint, true) + assert.NoError(t, err) + + mdi.AssertExpectations(t) + mdm.AssertExpectations(t) + msa.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestMintTokensByTypeSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + assert.NoError(t, err) +} + +func TestMintTokensByTypeBadPlugin(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.MintTokensByType(context.Background(), "", "", "", &fftypes.TokenTransferInput{}, false) + assert.Regexp(t, "FF10272", err) +} + +func TestMintTokensByTypeBadPool(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(nil, fmt.Errorf("pop")) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensByTypeIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensByTypeFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(fmt.Errorf("pop")) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensByTypeOperationFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + assert.EqualError(t, err, "pop") +} + +func TestMintTokensByTypeConfirm(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + mint := &fftypes.TokenTransferInput{} + mint.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + msa := am.syncasync.(*syncasyncmocks.Bridge) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + msa.On("WaitForTokenTransfer", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(&fftypes.TokenTransfer{}, nil) + + _, err := am.MintTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", mint, true) + assert.NoError(t, err) + + mdi.AssertExpectations(t) + mdm.AssertExpectations(t) + msa.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestBurnTokensSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.NoError(t, err) + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestBurnTokensUnknownConnectorSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "" + burn.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.NoError(t, err) + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestBurnTokenUnknownConnectorNoConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "" + burn.Pool = "pool1" + + am.tokens = make(map[string]tokens.Plugin) + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.Regexp(t, "FF10292", err) +} + +func TestBurnTokenUnknownConnectorMultipleConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "" + burn.Pool = "pool1" + + am.tokens["magic-tokens"] = nil + am.tokens["magic-tokens2"] = nil + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.Regexp(t, "FF10292", err) +} + +func TestBurnTokenUnknownConnectorBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "" + burn.Pool = "pool1" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "", burn, false) + assert.Regexp(t, "FF10131", err) +} + +func TestBurnTokensIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.EqualError(t, err, "pop") +} + +func TestBurnTokensConfirm(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + msa := am.syncasync.(*syncasyncmocks.Bridge) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + msa.On("WaitForTokenTransfer", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(&fftypes.TokenTransfer{}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, true) + assert.NoError(t, err) + + mdi.AssertExpectations(t) + mdm.AssertExpectations(t) + msa.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestBurnTokenUnknownPoolSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + } + totalCount := int64(1) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.NoError(t, err) +} + +func TestBurnTokenUnknownPoolNoPools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{} + totalCount := int64(0) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.Regexp(t, "FF10292", err) +} + +func TestBurnTokenUnknownPoolMultiplePools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + { + Name: "pool2", + }, + } + totalCount := int64(2) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "ns1", burn, false) + assert.Regexp(t, "FF10292", err) +} + +func TestBurnTokenUnknownPoolBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + burn.Connector = "magic-tokens" + burn.Pool = "" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.BurnTokens(context.Background(), "", burn, false) + assert.Regexp(t, "FF10131", err) +} + +func TestBurnTokensByTypeSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.BurnTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", burn, false) + assert.NoError(t, err) + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestBurnTokensByTypeIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + + _, err := am.BurnTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", burn, false) + assert.EqualError(t, err, "pop") +} + +func TestBurnTokensByTypeConfirm(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + burn := &fftypes.TokenTransferInput{} + burn.Amount.Int().SetInt64(5) + + mdi := am.database.(*databasemocks.Plugin) + mdm := am.data.(*datamocks.Manager) + msa := am.syncasync.(*syncasyncmocks.Bridge) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + msa.On("WaitForTokenTransfer", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(&fftypes.TokenTransfer{}, nil) + + _, err := am.BurnTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", burn, true) + assert.NoError(t, err) + + mdi.AssertExpectations(t) + mdm.AssertExpectations(t) + msa.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestTransferTokensSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.NoError(t, err) + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestTransferTokensUnknownConnectorSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "" + transfer.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.NoError(t, err) + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) +} + +func TestTransferTokenUnknownConnectorNoConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "" + transfer.Pool = "pool1" + + am.tokens = make(map[string]tokens.Plugin) + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10292", err) +} + +func TestTransferTokenUnknownConnectorMultipleConnectors(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "" + transfer.Pool = "pool1" + + am.tokens["magic-tokens"] = nil + am.tokens["magic-tokens2"] = nil + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10292", err) +} + +func TestTransferTokenUnknownConnectorBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "" + transfer.Pool = "pool1" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "", transfer, false) + assert.Regexp(t, "FF10131", err) +} + +func TestTransferTokenUnknownPoolSuccess(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + } + totalCount := int64(1) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.NoError(t, err) +} + +func TestTransferTokenUnknownPoolNoPools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{} + totalCount := int64(0) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10292", err) +} + +func TestTransferTokenUnknownPoolMultiplePools(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + fb := database.TokenPoolQueryFactory.NewFilter(context.Background()) + f := fb.And() + f.Limit(1).Count(true) + tokenPools := []*fftypes.TokenPool{ + { + Name: "pool1", + }, + { + Name: "pool2", + }, + } + totalCount := int64(2) + filterResult := &database.FilterResult{ + TotalCount: &totalCount, + } + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPools", context.Background(), mock.MatchedBy((func(f database.AndFilter) bool { + info, _ := f.Finalize() + return info.Count && info.Limit == 1 + }))).Return(tokenPools, filterResult, nil) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10292", err) +} + +func TestTransferTokenUnknownPoolBadNamespace(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "", transfer, false) + assert.Regexp(t, "FF10131", err) +} + +func TestTransferTokensIdentityFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.EqualError(t, err, "pop") +} + +func TestTransferTokensNoFromOrTo(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{} + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" + + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10280", err) + + mim.AssertExpectations(t) +} + +func TestTransferTokensInvalidType(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mdi.On("GetTokenPool", am.ctx, "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mdi.On("UpsertTransaction", am.ctx, mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", am.ctx, mock.Anything, false).Return(nil) + + sender := &transferSender{ + mgr: am, + namespace: "ns1", + connector: "magic-tokens", + poolName: "pool1", + transfer: transfer, + } + assert.PanicsWithValue(t, "unknown transfer type: ", func() { + sender.Send(am.ctx) + }) +} + +func TestTransferTokensTransactionFail(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" + + mdi := am.database.(*databasemocks.Plugin) + mim := am.identity.(*identitymanagermocks.Manager) + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(fmt.Errorf("pop")) + + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.EqualError(t, err, "pop") + + mim.AssertExpectations(t) + mdi.AssertExpectations(t) +} + +func TestTransferTokensWithBroadcastMessage(t *testing.T) { + am, cancel := newTestAssets(t) + defer cancel() + + hash := fftypes.NewRandB32() + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + Message: &fftypes.MessageInOut{ + Message: fftypes.Message{ + Hash: hash, + }, + InlineData: fftypes.InlineData{ + { + Value: []byte("test data"), + }, + }, + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" mdi := am.database.(*databasemocks.Plugin) + mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) + mbm := am.broadcast.(*broadcastmocks.Manager) + mms := &sysmessagingmocks.MessageSender{} mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) - mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(nil, fmt.Errorf("pop")) + mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) + mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { + return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer + }), false).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + mbm.On("NewBroadcast", "ns1", transfer.Message).Return(mms) + mms.On("Prepare", context.Background()).Return(nil) + mdi.On("UpsertMessage", context.Background(), mock.MatchedBy(func(msg *fftypes.Message) bool { + return msg.State == fftypes.MessageStateStaged + }), false, false).Return(nil) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) - assert.EqualError(t, err, "pop") + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.NoError(t, err) + assert.Equal(t, *hash, *transfer.MessageHash) + + mbm.AssertExpectations(t) + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) + mms.AssertExpectations(t) } -func TestMintTokensIdentityFail(t *testing.T) { +func TestTransferTokensWithBroadcastPrepareFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() - mint := &fftypes.TokenTransferInput{} - mint.Amount.Int().SetInt64(5) + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + Message: &fftypes.MessageInOut{ + InlineData: fftypes.InlineData{ + { + Value: []byte("test data"), + }, + }, + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" - mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) - mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) + mbm := am.broadcast.(*broadcastmocks.Manager) + mms := &sysmessagingmocks.MessageSender{} + mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) + mbm.On("NewBroadcast", "ns1", transfer.Message).Return(mms) + mms.On("Prepare", context.Background()).Return(fmt.Errorf("pop")) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) assert.EqualError(t, err, "pop") + + mbm.AssertExpectations(t) + mim.AssertExpectations(t) + mms.AssertExpectations(t) } -func TestMintTokensFail(t *testing.T) { +func TestTransferTokensWithPrivateMessage(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() - mint := &fftypes.TokenTransferInput{} - mint.Amount.Int().SetInt64(5) + hash := fftypes.NewRandB32() + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + Message: &fftypes.MessageInOut{ + Message: fftypes.Message{ + Header: fftypes.MessageHeader{ + Type: fftypes.MessageTypeTransferPrivate, + }, + Hash: hash, + }, + InlineData: fftypes.InlineData{ + { + Value: []byte("test data"), + }, + }, + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" mdi := am.database.(*databasemocks.Plugin) mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) + mpm := am.messaging.(*privatemessagingmocks.Manager) + mms := &sysmessagingmocks.MessageSender{} mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(fmt.Errorf("pop")) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer }), false).Return(nil) mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + mpm.On("NewMessage", "ns1", transfer.Message).Return(mms) + mms.On("Prepare", context.Background()).Return(nil) + mdi.On("UpsertMessage", context.Background(), mock.MatchedBy(func(msg *fftypes.Message) bool { + return msg.State == fftypes.MessageStateStaged + }), false, false).Return(nil) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) - assert.EqualError(t, err, "pop") + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.NoError(t, err) + assert.Equal(t, *hash, *transfer.MessageHash) + + mpm.AssertExpectations(t) + mim.AssertExpectations(t) + mdi.AssertExpectations(t) + mti.AssertExpectations(t) + mms.AssertExpectations(t) } -func TestMintTokensOperationFail(t *testing.T) { +func TestTransferTokensWithInvalidMessage(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() - mint := &fftypes.TokenTransferInput{} - mint.Amount.Int().SetInt64(5) + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + Message: &fftypes.MessageInOut{ + Message: fftypes.Message{ + Header: fftypes.MessageHeader{ + Type: fftypes.MessageTypeDefinition, + }, + }, + InlineData: fftypes.InlineData{ + { + Value: []byte("test data"), + }, + }, + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" - mdi := am.database.(*databasemocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) - mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { - return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer - }), false).Return(nil) - mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(fmt.Errorf("pop")) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, false) - assert.EqualError(t, err, "pop") + _, err := am.TransferTokens(context.Background(), "ns1", transfer, false) + assert.Regexp(t, "FF10287", err) + + mim.AssertExpectations(t) } -func TestMintTokensConfirm(t *testing.T) { +func TestTransferTokensConfirm(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() - mint := &fftypes.TokenTransferInput{} - mint.Amount.Int().SetInt64(5) + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" mdi := am.database.(*databasemocks.Plugin) mdm := am.data.(*datamocks.Manager) @@ -226,7 +1555,7 @@ func TestMintTokensConfirm(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - mti.On("MintTokens", context.Background(), mock.Anything, &mint.TokenTransfer).Return(nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer }), false).Return(nil) @@ -238,7 +1567,7 @@ func TestMintTokensConfirm(t *testing.T) { }). Return(&fftypes.TokenTransfer{}, nil) - _, err := am.MintTokens(context.Background(), "ns1", "magic-tokens", "pool1", mint, true) + _, err := am.TransferTokens(context.Background(), "ns1", transfer, true) assert.NoError(t, err) mdi.AssertExpectations(t) @@ -247,84 +1576,75 @@ func TestMintTokensConfirm(t *testing.T) { mti.AssertExpectations(t) } -func TestBurnTokensSuccess(t *testing.T) { +func TestTransferTokensWithBroadcastConfirm(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() - burn := &fftypes.TokenTransferInput{} - burn.Amount.Int().SetInt64(5) + hash := fftypes.NewRandB32() + transfer := &fftypes.TokenTransferInput{ + TokenTransfer: fftypes.TokenTransfer{ + From: "A", + To: "B", + }, + Message: &fftypes.MessageInOut{ + Message: fftypes.Message{ + Hash: hash, + }, + InlineData: fftypes.InlineData{ + { + Value: []byte("test data"), + }, + }, + }, + } + transfer.Amount.Int().SetInt64(5) + transfer.Connector = "magic-tokens" + transfer.Pool = "pool1" mdi := am.database.(*databasemocks.Plugin) mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) mim := am.identity.(*identitymanagermocks.Manager) - mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) - mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) - mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { - return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer - }), false).Return(nil) - mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) - - _, err := am.BurnTokens(context.Background(), "ns1", "magic-tokens", "pool1", burn, false) - assert.NoError(t, err) - - mim.AssertExpectations(t) - mdi.AssertExpectations(t) - mti.AssertExpectations(t) -} - -func TestBurnTokensIdentityFail(t *testing.T) { - am, cancel := newTestAssets(t) - defer cancel() - - burn := &fftypes.TokenTransferInput{} - burn.Amount.Int().SetInt64(5) - - mdi := am.database.(*databasemocks.Plugin) - mim := am.identity.(*identitymanagermocks.Manager) - mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) - mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - - _, err := am.BurnTokens(context.Background(), "ns1", "magic-tokens", "pool1", burn, false) - assert.EqualError(t, err, "pop") -} - -func TestBurnTokensConfirm(t *testing.T) { - am, cancel := newTestAssets(t) - defer cancel() - - burn := &fftypes.TokenTransferInput{} - burn.Amount.Int().SetInt64(5) - - mdi := am.database.(*databasemocks.Plugin) - mdm := am.data.(*datamocks.Manager) + mbm := am.broadcast.(*broadcastmocks.Manager) + mms := &sysmessagingmocks.MessageSender{} msa := am.syncasync.(*syncasyncmocks.Bridge) - mti := am.tokens["magic-tokens"].(*tokenmocks.Plugin) - mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - mti.On("BurnTokens", context.Background(), mock.Anything, &burn.TokenTransfer).Return(nil) + mti.On("TransferTokens", context.Background(), mock.Anything, &transfer.TokenTransfer).Return(nil) + mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) mdi.On("UpsertTransaction", context.Background(), mock.MatchedBy(func(tx *fftypes.Transaction) bool { return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer }), false).Return(nil) - mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) + mbm.On("NewBroadcast", "ns1", transfer.Message).Return(mms) + mms.On("Prepare", context.Background()).Return(nil) + mdi.On("UpsertMessage", context.Background(), mock.MatchedBy(func(msg *fftypes.Message) bool { + return msg.State == fftypes.MessageStateStaged + }), false, false).Return(nil) + msa.On("WaitForMessage", context.Background(), "ns1", mock.Anything, mock.Anything). + Run(func(args mock.Arguments) { + send := args[3].(syncasync.RequestSender) + send(context.Background()) + }). + Return(&fftypes.Message{}, nil) msa.On("WaitForTokenTransfer", context.Background(), "ns1", mock.Anything, mock.Anything). Run(func(args mock.Arguments) { send := args[3].(syncasync.RequestSender) send(context.Background()) }). - Return(&fftypes.TokenTransfer{}, nil) + Return(&transfer.TokenTransfer, nil) - _, err := am.BurnTokens(context.Background(), "ns1", "magic-tokens", "pool1", burn, true) + _, err := am.TransferTokens(context.Background(), "ns1", transfer, true) assert.NoError(t, err) + assert.Equal(t, *hash, *transfer.MessageHash) + mbm.AssertExpectations(t) + mim.AssertExpectations(t) mdi.AssertExpectations(t) - mdm.AssertExpectations(t) - msa.AssertExpectations(t) mti.AssertExpectations(t) + mms.AssertExpectations(t) + msa.AssertExpectations(t) } -func TestTransferTokensSuccess(t *testing.T) { +func TestTransferTokensByTypeSuccess(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -347,7 +1667,7 @@ func TestTransferTokensSuccess(t *testing.T) { }), false).Return(nil) mdi.On("UpsertOperation", context.Background(), mock.Anything, false).Return(nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.NoError(t, err) mim.AssertExpectations(t) @@ -355,7 +1675,7 @@ func TestTransferTokensSuccess(t *testing.T) { mti.AssertExpectations(t) } -func TestTransferTokensIdentityFail(t *testing.T) { +func TestTransferTokensByTypeIdentityFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -372,11 +1692,11 @@ func TestTransferTokensIdentityFail(t *testing.T) { mim.On("GetLocalOrganization", context.Background()).Return(nil, fmt.Errorf("pop")) mdi.On("GetTokenPool", context.Background(), "ns1", "pool1").Return(&fftypes.TokenPool{}, nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.EqualError(t, err, "pop") } -func TestTransferTokensNoFromOrTo(t *testing.T) { +func TestTransferTokensByTypeNoFromOrTo(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -385,13 +1705,13 @@ func TestTransferTokensNoFromOrTo(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.Regexp(t, "FF10280", err) mim.AssertExpectations(t) } -func TestTransferTokensInvalidType(t *testing.T) { +func TestTransferTokensByTypeInvalidType(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -422,7 +1742,7 @@ func TestTransferTokensInvalidType(t *testing.T) { }) } -func TestTransferTokensTransactionFail(t *testing.T) { +func TestTransferTokensByTypeTransactionFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -442,14 +1762,14 @@ func TestTransferTokensTransactionFail(t *testing.T) { return tx.Subject.Type == fftypes.TransactionTypeTokenTransfer }), false).Return(fmt.Errorf("pop")) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.EqualError(t, err, "pop") mim.AssertExpectations(t) mdi.AssertExpectations(t) } -func TestTransferTokensWithBroadcastMessage(t *testing.T) { +func TestTransferTokensByTypeWithBroadcastMessage(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -490,7 +1810,7 @@ func TestTransferTokensWithBroadcastMessage(t *testing.T) { return msg.State == fftypes.MessageStateStaged }), false, false).Return(nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.NoError(t, err) assert.Equal(t, *hash, *transfer.MessageHash) @@ -501,7 +1821,7 @@ func TestTransferTokensWithBroadcastMessage(t *testing.T) { mms.AssertExpectations(t) } -func TestTransferTokensWithBroadcastPrepareFail(t *testing.T) { +func TestTransferTokensByTypeWithBroadcastPrepareFail(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -527,7 +1847,7 @@ func TestTransferTokensWithBroadcastPrepareFail(t *testing.T) { mbm.On("NewBroadcast", "ns1", transfer.Message).Return(mms) mms.On("Prepare", context.Background()).Return(fmt.Errorf("pop")) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.EqualError(t, err, "pop") mbm.AssertExpectations(t) @@ -535,7 +1855,7 @@ func TestTransferTokensWithBroadcastPrepareFail(t *testing.T) { mms.AssertExpectations(t) } -func TestTransferTokensWithPrivateMessage(t *testing.T) { +func TestTransferTokensByTypeWithPrivateMessage(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -579,7 +1899,7 @@ func TestTransferTokensWithPrivateMessage(t *testing.T) { return msg.State == fftypes.MessageStateStaged }), false, false).Return(nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.NoError(t, err) assert.Equal(t, *hash, *transfer.MessageHash) @@ -590,7 +1910,7 @@ func TestTransferTokensWithPrivateMessage(t *testing.T) { mms.AssertExpectations(t) } -func TestTransferTokensWithInvalidMessage(t *testing.T) { +func TestTransferTokensByTypeWithInvalidMessage(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -617,13 +1937,13 @@ func TestTransferTokensWithInvalidMessage(t *testing.T) { mim := am.identity.(*identitymanagermocks.Manager) mim.On("GetLocalOrganization", context.Background()).Return(&fftypes.Organization{Identity: "0x12345"}, nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, false) assert.Regexp(t, "FF10287", err) mim.AssertExpectations(t) } -func TestTransferTokensConfirm(t *testing.T) { +func TestTransferTokensByTypeConfirm(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -654,7 +1974,7 @@ func TestTransferTokensConfirm(t *testing.T) { }). Return(&fftypes.TokenTransfer{}, nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, true) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, true) assert.NoError(t, err) mdi.AssertExpectations(t) @@ -682,7 +2002,7 @@ func TestTransferPrepare(t *testing.T) { assert.NoError(t, err) } -func TestTransferTokensWithBroadcastConfirm(t *testing.T) { +func TestTransferTokensByTypeWithBroadcastConfirm(t *testing.T) { am, cancel := newTestAssets(t) defer cancel() @@ -736,7 +2056,7 @@ func TestTransferTokensWithBroadcastConfirm(t *testing.T) { }). Return(&transfer.TokenTransfer, nil) - _, err := am.TransferTokens(context.Background(), "ns1", "magic-tokens", "pool1", transfer, true) + _, err := am.TransferTokensByType(context.Background(), "ns1", "magic-tokens", "pool1", transfer, true) assert.NoError(t, err) assert.Equal(t, *hash, *transfer.MessageHash) diff --git a/internal/i18n/en_translations.go b/internal/i18n/en_translations.go index 71ecf2a6af..2c2cc99db0 100644 --- a/internal/i18n/en_translations.go +++ b/internal/i18n/en_translations.go @@ -209,4 +209,5 @@ var ( MsgFetchDataDesc = ffm("FF10289", "Fetch the data and include it in the messages returned", 400) MsgWSClosed = ffm("FF10290", "Websocket closed") MsgTokenTransferFailed = ffm("FF10291", "Token transfer with ID '%s' failed. Please check the FireFly logs for more information") + MsgFieldNotSpecified = ffm("FF10292", "Field '%s' must be specified", 400) ) diff --git a/mocks/assetmocks/manager.go b/mocks/assetmocks/manager.go index 7837aa8bd6..3480a6407e 100644 --- a/mocks/assetmocks/manager.go +++ b/mocks/assetmocks/manager.go @@ -20,8 +20,31 @@ type Manager struct { mock.Mock } -// BurnTokens provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm -func (_m *Manager) BurnTokens(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { +// BurnTokens provides a mock function with given fields: ctx, ns, transfer, waitConfirm +func (_m *Manager) BurnTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { + ret := _m.Called(ctx, ns, transfer, waitConfirm) + + var r0 *fftypes.TokenTransfer + if rf, ok := ret.Get(0).(func(context.Context, string, *fftypes.TokenTransferInput, bool) *fftypes.TokenTransfer); ok { + r0 = rf(ctx, ns, transfer, waitConfirm) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*fftypes.TokenTransfer) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, *fftypes.TokenTransferInput, bool) error); ok { + r1 = rf(ctx, ns, transfer, waitConfirm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// BurnTokensByType provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm +func (_m *Manager) BurnTokensByType(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { ret := _m.Called(ctx, ns, connector, poolName, transfer, waitConfirm) var r0 *fftypes.TokenTransfer @@ -43,8 +66,31 @@ func (_m *Manager) BurnTokens(ctx context.Context, ns string, connector string, return r0, r1 } -// CreateTokenPool provides a mock function with given fields: ctx, ns, connector, pool, waitConfirm -func (_m *Manager) CreateTokenPool(ctx context.Context, ns string, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { +// CreateTokenPool provides a mock function with given fields: ctx, ns, pool, waitConfirm +func (_m *Manager) CreateTokenPool(ctx context.Context, ns string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { + ret := _m.Called(ctx, ns, pool, waitConfirm) + + var r0 *fftypes.TokenPool + if rf, ok := ret.Get(0).(func(context.Context, string, *fftypes.TokenPool, bool) *fftypes.TokenPool); ok { + r0 = rf(ctx, ns, pool, waitConfirm) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*fftypes.TokenPool) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, *fftypes.TokenPool, bool) error); ok { + r1 = rf(ctx, ns, pool, waitConfirm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// CreateTokenPoolByType provides a mock function with given fields: ctx, ns, connector, pool, waitConfirm +func (_m *Manager) CreateTokenPoolByType(ctx context.Context, ns string, connector string, pool *fftypes.TokenPool, waitConfirm bool) (*fftypes.TokenPool, error) { ret := _m.Called(ctx, ns, connector, pool, waitConfirm) var r0 *fftypes.TokenPool @@ -350,8 +396,31 @@ func (_m *Manager) GetTokenTransfersByPool(ctx context.Context, ns string, conne return r0, r1, r2 } -// MintTokens provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm -func (_m *Manager) MintTokens(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { +// MintTokens provides a mock function with given fields: ctx, ns, transfer, waitConfirm +func (_m *Manager) MintTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { + ret := _m.Called(ctx, ns, transfer, waitConfirm) + + var r0 *fftypes.TokenTransfer + if rf, ok := ret.Get(0).(func(context.Context, string, *fftypes.TokenTransferInput, bool) *fftypes.TokenTransfer); ok { + r0 = rf(ctx, ns, transfer, waitConfirm) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*fftypes.TokenTransfer) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, *fftypes.TokenTransferInput, bool) error); ok { + r1 = rf(ctx, ns, transfer, waitConfirm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MintTokensByType provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm +func (_m *Manager) MintTokensByType(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { ret := _m.Called(ctx, ns, connector, poolName, transfer, waitConfirm) var r0 *fftypes.TokenTransfer @@ -417,8 +486,31 @@ func (_m *Manager) TokenPoolCreated(ti tokens.Plugin, pool *fftypes.TokenPool, p return r0 } -// TransferTokens provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm -func (_m *Manager) TransferTokens(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { +// TransferTokens provides a mock function with given fields: ctx, ns, transfer, waitConfirm +func (_m *Manager) TransferTokens(ctx context.Context, ns string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { + ret := _m.Called(ctx, ns, transfer, waitConfirm) + + var r0 *fftypes.TokenTransfer + if rf, ok := ret.Get(0).(func(context.Context, string, *fftypes.TokenTransferInput, bool) *fftypes.TokenTransfer); ok { + r0 = rf(ctx, ns, transfer, waitConfirm) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*fftypes.TokenTransfer) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(context.Context, string, *fftypes.TokenTransferInput, bool) error); ok { + r1 = rf(ctx, ns, transfer, waitConfirm) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// TransferTokensByType provides a mock function with given fields: ctx, ns, connector, poolName, transfer, waitConfirm +func (_m *Manager) TransferTokensByType(ctx context.Context, ns string, connector string, poolName string, transfer *fftypes.TokenTransferInput, waitConfirm bool) (*fftypes.TokenTransfer, error) { ret := _m.Called(ctx, ns, connector, poolName, transfer, waitConfirm) var r0 *fftypes.TokenTransfer diff --git a/pkg/fftypes/tokentransfer.go b/pkg/fftypes/tokentransfer.go index d23829b12f..41276f8fca 100644 --- a/pkg/fftypes/tokentransfer.go +++ b/pkg/fftypes/tokentransfer.go @@ -44,4 +44,5 @@ type TokenTransfer struct { type TokenTransferInput struct { TokenTransfer Message *MessageInOut `json:"message,omitempty"` + Pool string `json:"pool,omitempty"` }