diff --git a/cmd/init.go b/cmd/init.go index b746ae6c..cd9ce411 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -33,7 +33,7 @@ import ( var initOptions stacks.InitOptions var databaseSelection string var blockchainProviderInput string -var tokensProviderSelection string +var tokenProvidersSelection []string var promptNames bool var ffNameValidator = regexp.MustCompile(`^[0-9a-zA-Z]([0-9a-zA-Z._-]{0,62}[0-9a-zA-Z])?$`) @@ -53,7 +53,7 @@ var initCmd = &cobra.Command{ if err := validateBlockchainProvider(blockchainProviderInput); err != nil { return err } - if err := validateTokensProvider(tokensProviderSelection); err != nil { + if err := validateTokensProvider(tokenProvidersSelection); err != nil { return err } @@ -100,7 +100,7 @@ var initCmd = &cobra.Command{ initOptions.Verbose = verbose initOptions.BlockchainProvider, _ = stacks.BlockchainProviderFromString(blockchainProviderInput) initOptions.DatabaseSelection, _ = stacks.DatabaseSelectionFromString(databaseSelection) - initOptions.TokensProvider, _ = stacks.TokensProviderFromString(tokensProviderSelection) + initOptions.TokenProviders, _ = stacks.TokenProvidersFromStrings(tokenProvidersSelection) if err := stackManager.InitStack(stackName, memberCount, &initOptions); err != nil { return err @@ -161,14 +161,14 @@ func validateBlockchainProvider(input string) error { // TODO: When we get tokens on Fabric this should change if blockchainSelection == stacks.HyperledgerFabric { - tokensProviderSelection = "none" + tokenProvidersSelection = []string{} } return nil } -func validateTokensProvider(input string) error { - _, err := stacks.TokensProviderFromString(input) +func validateTokensProvider(input []string) error { + _, err := stacks.TokenProvidersFromStrings(input) if err != nil { return err } @@ -180,13 +180,11 @@ func init() { initCmd.Flags().IntVarP(&initOptions.ServicesBasePort, "services-base-port", "s", 5100, "Mapped port base of services (100 added for each member)") initCmd.Flags().StringVarP(&databaseSelection, "database", "d", "sqlite3", fmt.Sprintf("Database type to use. Options are: %v", stacks.DBSelectionStrings)) initCmd.Flags().StringVarP(&blockchainProviderInput, "blockchain-provider", "b", "geth", fmt.Sprintf("Blockchain provider to use. Options are: %v", stacks.BlockchainProviderStrings)) - initCmd.Flags().StringVarP(&tokensProviderSelection, "tokens-provider", "t", "erc1155", fmt.Sprintf("Tokens provider to use. Options are: %v", stacks.TokensProviderStrings)) + initCmd.Flags().StringArrayVarP(&tokenProvidersSelection, "token-providers", "t", []string{"erc1155", "erc20_erc721"}, fmt.Sprintf("Token providers to use. Options are: %v", stacks.ValidTokenProviders)) initCmd.Flags().IntVarP(&initOptions.ExternalProcesses, "external", "e", 0, "Manage a number of FireFly core processes outside of the docker-compose stack - useful for development and debugging") initCmd.Flags().StringVarP(&initOptions.FireFlyVersion, "release", "r", "latest", "Select the FireFly release version to use") initCmd.Flags().StringVarP(&initOptions.ManifestPath, "manifest", "m", "", "Path to a manifest.json file containing the versions of each FireFly microservice to use. Overrides the --release flag.") initCmd.Flags().BoolVar(&promptNames, "prompt-names", false, "Prompt for org and node names instead of using the defaults") - initCmd.Flags().BoolVar(&initOptions.PrometheusEnabled, "prometheus-enabled", false, "Enables Prometheus metrics exposition and aggregation to a shared Prometheus server") - initCmd.Flags().IntVar(&initOptions.PrometheusPort, "prometheus-port", 9090, "Port for the shared Prometheus server") rootCmd.AddCommand(initCmd) } diff --git a/internal/core/firefly_config.go b/internal/core/firefly_config.go index f0590254..7033e368 100644 --- a/internal/core/firefly_config.go +++ b/internal/core/firefly_config.go @@ -160,7 +160,7 @@ type FireflyConfig struct { Database *DatabaseConfig `yaml:"database,omitempty"` P2PFS *PublicStorageConfig `yaml:"publicstorage,omitempty"` DataExchange *DataExchangeConfig `yaml:"dataexchange,omitempty"` - Tokens *TokensConfig `yaml:"tokens,omitempty"` + Tokens TokensConfig `yaml:"tokens,omitempty"` Event *EventConfig `yaml:"event,omitempty"` } diff --git a/internal/core/manifest_test.go b/internal/core/manifest_test.go index 9f3c31f0..a7d8c58a 100644 --- a/internal/core/manifest_test.go +++ b/internal/core/manifest_test.go @@ -37,7 +37,8 @@ func TestGetFireFlyManifest(T *testing.T) { assert.NotNil(T, manifest.Ethconnect) assert.NotNil(T, manifest.Fabconnect) assert.NotNil(T, manifest.DataExchange) - assert.NotNil(T, manifest.Tokens) + assert.NotNil(T, manifest.TokensERC1155) + assert.NotNil(T, manifest.TokensERC20ERC721) } func TestGetLatestReleaseManifest(T *testing.T) { @@ -48,5 +49,6 @@ func TestGetLatestReleaseManifest(T *testing.T) { assert.NotNil(T, manifest.Ethconnect) assert.NotNil(T, manifest.Fabconnect) assert.NotNil(T, manifest.DataExchange) - assert.NotNil(T, manifest.Tokens) + assert.NotNil(T, manifest.TokensERC1155) + assert.NotNil(T, manifest.TokensERC20ERC721) } diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index 5f64a355..8ed61468 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -41,7 +41,7 @@ import ( "github.com/hyperledger/firefly-cli/internal/docker" "github.com/hyperledger/firefly-cli/internal/tokens" "github.com/hyperledger/firefly-cli/internal/tokens/erc1155" - "github.com/hyperledger/firefly-cli/internal/tokens/niltokens" + "github.com/hyperledger/firefly-cli/internal/tokens/erc20erc721" "github.com/hyperledger/firefly-cli/pkg/types" "golang.org/x/crypto/sha3" @@ -54,7 +54,7 @@ type StackManager struct { Log log.Logger Stack *types.Stack blockchainProvider blockchain.IBlockchainProvider - tokensProvider tokens.ITokensProvider + tokenProviders []tokens.ITokensProvider } type PullOptions struct { @@ -74,7 +74,7 @@ type InitOptions struct { OrgNames []string NodeNames []string BlockchainProvider BlockchainProvider - TokensProvider TokensProvider + TokenProviders types.TokenProviders FireFlyVersion string ManifestPath string PrometheusEnabled bool @@ -114,7 +114,7 @@ func (s *StackManager) InitStack(stackName string, memberCount int, options *Ini ExposedBlockchainPort: options.ServicesBasePort, Database: options.DatabaseSelection.String(), BlockchainProvider: options.BlockchainProvider.String(), - TokensProvider: options.TokensProvider.String(), + TokenProviders: options.TokenProviders, } if options.PrometheusEnabled { @@ -147,7 +147,7 @@ func (s *StackManager) InitStack(stackName string, memberCount int, options *Ini s.Stack.VersionManifest = manifest s.blockchainProvider = s.getBlockchainProvider(false) - s.tokensProvider = s.getTokensProvider(false) + s.tokenProviders = s.getITokenProviders(false) for i := 0; i < memberCount; i++ { externalProcess := i < options.ExternalProcesses @@ -155,7 +155,9 @@ func (s *StackManager) InitStack(stackName string, memberCount int, options *Ini } compose := docker.CreateDockerCompose(s.Stack) extraServices := s.blockchainProvider.GetDockerServiceDefinitions() - extraServices = append(extraServices, s.tokensProvider.GetDockerServiceDefinitions()...) + for i, tp := range s.tokenProviders { + extraServices = append(extraServices, tp.GetDockerServiceDefinitions(i)...) + } for _, serviceDefinition := range extraServices { // Add each service definition to the docker compose file @@ -215,7 +217,7 @@ func (s *StackManager) LoadStack(stackName string, verbose bool) error { } s.Stack = stack s.blockchainProvider = s.getBlockchainProvider(verbose) - s.tokensProvider = s.getTokensProvider(verbose) + s.tokenProviders = s.getITokenProviders(verbose) } // For backwards compatability, add a "default" VersionManifest // in memory for stacks that were created with old CLI versions @@ -237,10 +239,14 @@ func (s *StackManager) LoadStack(stackName string, verbose bool) error { Image: "ghcr.io/hyperledger/firefly-dataexchange-https", Tag: "latest", }, - Tokens: &types.ManifestEntry{ + TokensERC1155: &types.ManifestEntry{ Image: "ghcr.io/hyperledger/firefly-tokens-erc1155", Tag: "latest", }, + TokensERC20ERC721: &types.ManifestEntry{ + Image: "ghcr.io/hyperledger/firefly-tokens-erc20-erc721", + Tag: "latest", + }, } } return nil @@ -284,7 +290,9 @@ func (s *StackManager) writeConfigs(verbose bool) error { for _, member := range s.Stack.Members { config := core.NewFireflyConfig(s.Stack, member) config.Blockchain, config.Org = s.blockchainProvider.GetFireflyConfig(member) - config.Tokens = s.tokensProvider.GetFireflyConfig(member) + for iTok, tp := range s.tokenProviders { + config.Tokens = append(config.Tokens, tp.GetFireflyConfig(member, iTok)) + } if err := core.WriteFireflyConfig(config, filepath.Join(stackDir, "configs", fmt.Sprintf("firefly_core_%s.yml", member.ID))); err != nil { return err } @@ -369,14 +377,18 @@ func createMember(id string, index int, options *InitOptions, external bool) *ty ExposedDataexchangePort: serviceBase + 5, ExposedIPFSApiPort: serviceBase + 6, ExposedIPFSGWPort: serviceBase + 7, - ExposedTokensPort: serviceBase + 8, External: external, OrgName: options.OrgNames[index], NodeName: options.NodeNames[index], } - + nextPort := serviceBase + 8 if options.PrometheusEnabled { - member.ExposedFireflyMetricsPort = serviceBase + 9 + member.ExposedFireflyMetricsPort = nextPort + nextPort++ + } + for range options.TokenProviders { + member.ExposedTokensPorts = append(member.ExposedTokensPorts, nextPort) + nextPort++ } return member } @@ -449,8 +461,10 @@ func (s *StackManager) PullStack(verbose bool, options *PullOptions) error { } // Iterate over all images used by the tokens provider - for _, service := range s.tokensProvider.GetDockerServiceDefinitions() { - images = append(images, service.Service.Image) + for iTok, tp := range s.tokenProviders { + for _, service := range tp.GetDockerServiceDefinitions(iTok) { + images = append(images, service.Service.Image) + } } // Use docker to pull every image - retry on failure @@ -468,8 +482,10 @@ func (s *StackManager) removeVolumes(verbose bool) { for _, service := range s.blockchainProvider.GetDockerServiceDefinitions() { volumes = append(volumes, service.VolumeNames...) } - for _, service := range s.tokensProvider.GetDockerServiceDefinitions() { - volumes = append(volumes, service.VolumeNames...) + for iTok, tp := range s.tokenProviders { + for _, service := range tp.GetDockerServiceDefinitions(iTok) { + volumes = append(volumes, service.VolumeNames...) + } } for volumeName := range docker.CreateDockerCompose(s.Stack).Volumes { volumes = append(volumes, volumeName) @@ -539,7 +555,7 @@ func (s *StackManager) checkPortsAvailable() error { ports = append(ports, member.ExposedIPFSGWPort) ports = append(ports, member.ExposedPostgresPort) ports = append(ports, member.ExposedUIPort) - ports = append(ports, member.ExposedTokensPort) + ports = append(ports, member.ExposedTokensPorts...) } if s.Stack.PrometheusEnabled { @@ -633,8 +649,10 @@ func (s *StackManager) runFirstTimeSetup(verbose bool, options *StartOptions) er if err := s.blockchainProvider.DeploySmartContracts(); err != nil { return err } - if err := s.tokensProvider.DeploySmartContracts(); err != nil { - return err + for i, tp := range s.tokenProviders { + if err := tp.DeploySmartContracts(i); err != nil { + return err + } } if err := s.patchConfigAndRestartFireflyNodes(verbose); err != nil { @@ -647,8 +665,10 @@ func (s *StackManager) runFirstTimeSetup(verbose bool, options *StartOptions) er } s.Log.Info("initializing token providers") - if err := s.tokensProvider.FirstTimeSetup(); err != nil { - return err + for iTok, tp := range s.tokenProviders { + if err := tp.FirstTimeSetup(iTok); err != nil { + return err + } } return nil } @@ -721,12 +741,9 @@ func (s *StackManager) PrintStackInfo(verbose bool) error { func (s *StackManager) patchConfigAndRestartFireflyNodes(verbose bool) error { for _, member := range s.Stack.Members { - configPayload := map[string]interface{}{ - "preInit": false, - } s.Log.Info(fmt.Sprintf("applying configuration changes to %s", member.ID)) - configRecordUrl := fmt.Sprintf("http://localhost:%d/admin/api/v1/config/records/admin", member.ExposedFireflyAdminPort) - if err := core.RequestWithRetry("PUT", configRecordUrl, configPayload, nil); err != nil && err != io.EOF { + configRecordUrl := fmt.Sprintf("http://localhost:%d/admin/api/v1/config/records/admin.preInit", member.ExposedFireflyAdminPort) + if err := core.RequestWithRetry("PUT", configRecordUrl, "false", nil); err != nil && err != io.EOF { return err } resetUrl := fmt.Sprintf("http://localhost:%d/admin/api/v1/config/reset", member.ExposedFireflyAdminPort) @@ -774,21 +791,25 @@ func (s *StackManager) getBlockchainProvider(verbose bool) blockchain.IBlockchai } } -func (s *StackManager) getTokensProvider(verbose bool) tokens.ITokensProvider { - switch s.Stack.TokensProvider { - case NilTokens.String(): - return &niltokens.NilTokensProvider{ - Verbose: verbose, - Log: s.Log, - Stack: s.Stack, - } - case ERC1155.String(): - return &erc1155.ERC1155Provider{ - Verbose: verbose, - Log: s.Log, - Stack: s.Stack, +func (s *StackManager) getITokenProviders(verbose bool) []tokens.ITokensProvider { + tps := make([]tokens.ITokensProvider, len(s.Stack.TokenProviders)) + for i, tp := range s.Stack.TokenProviders { + switch tp { + case ERC1155: + tps[i] = &erc1155.ERC1155Provider{ + Verbose: verbose, + Log: s.Log, + Stack: s.Stack, + } + case ERC20_ERC721: + tps[i] = &erc20erc721.ERC20ERC721Provider{ + Verbose: verbose, + Log: s.Log, + Stack: s.Stack, + } + default: + return nil } - default: - return nil } + return tps } diff --git a/internal/stacks/types.go b/internal/stacks/types.go index cc87fac6..7c9332ba 100644 --- a/internal/stacks/types.go +++ b/internal/stacks/types.go @@ -19,6 +19,8 @@ package stacks import ( "fmt" "strings" + + "github.com/hyperledger/firefly-cli/pkg/types" ) type DatabaseSelection int @@ -67,24 +69,29 @@ func BlockchainProviderFromString(s string) (BlockchainProvider, error) { return GoEthereum, fmt.Errorf("\"%s\" is not a valid blockchain provider selection. valid options are: %v", s, BlockchainProviderStrings) } -type TokensProvider int - const ( - NilTokens TokensProvider = iota - ERC1155 + NilTokens types.TokenProvider = "none" + ERC1155 types.TokenProvider = "erc1155" + ERC20_ERC721 types.TokenProvider = "erc20_erc721" ) -var TokensProviderStrings = []string{"none", "erc1155"} - -func (tokensProvider TokensProvider) String() string { - return TokensProviderStrings[tokensProvider] -} - -func TokensProviderFromString(s string) (TokensProvider, error) { - for i, tokensProviderSelection := range TokensProviderStrings { - if strings.ToLower(s) == tokensProviderSelection { - return TokensProvider(i), nil +var ValidTokenProviders = []types.TokenProvider{NilTokens, ERC1155, ERC20_ERC721} + +func TokenProvidersFromStrings(strTokens []string) (tps types.TokenProviders, err error) { + tps = make([]types.TokenProvider, 0, len(strTokens)) + for _, s := range strTokens { + found := false + for _, tokensProviderSelection := range ValidTokenProviders { + if strings.ToLower(s) == string(tokensProviderSelection) { + found = true + if tokensProviderSelection != NilTokens { + tps = append(tps, tokensProviderSelection) + } + } + } + if !found { + return nil, fmt.Errorf("\"%s\" is not a valid tokens provider selection. valid options are: %v", s, ValidTokenProviders) } } - return ERC1155, fmt.Errorf("\"%s\" is not a valid tokens provider selection. valid options are: %v", s, TokensProviderStrings) + return tps, nil } diff --git a/internal/tokens/erc1155/contracts.go b/internal/tokens/erc1155/contracts.go index da15ccb0..9bb3b703 100644 --- a/internal/tokens/erc1155/contracts.go +++ b/internal/tokens/erc1155/contracts.go @@ -29,11 +29,11 @@ import ( const TOKEN_URI_PATTERN = "firefly://token/{id}" -func DeployContracts(s *types.Stack, log log.Logger, verbose bool) error { +func DeployContracts(s *types.Stack, log log.Logger, verbose bool, tokenIndex int) error { var containerName string for _, member := range s.Members { if !member.External { - containerName = fmt.Sprintf("%s_tokens_%s", s.Name, member.ID) + containerName = fmt.Sprintf("%s_tokens_%s_%d", s.Name, member.ID, tokenIndex) break } } diff --git a/internal/tokens/erc1155/erc1155_provider.go b/internal/tokens/erc1155/erc1155_provider.go index f6413e60..7fe44e91 100644 --- a/internal/tokens/erc1155/erc1155_provider.go +++ b/internal/tokens/erc1155/erc1155_provider.go @@ -31,14 +31,14 @@ type ERC1155Provider struct { Stack *types.Stack } -func (p *ERC1155Provider) DeploySmartContracts() error { - return DeployContracts(p.Stack, p.Log, p.Verbose) +func (p *ERC1155Provider) DeploySmartContracts(tokenIndex int) error { + return DeployContracts(p.Stack, p.Log, p.Verbose, tokenIndex) } -func (p *ERC1155Provider) FirstTimeSetup() error { +func (p *ERC1155Provider) FirstTimeSetup(tokenIdx int) error { for _, member := range p.Stack.Members { p.Log.Info(fmt.Sprintf("initializing tokens on member %s", member.ID)) - tokenInitUrl := fmt.Sprintf("http://localhost:%d/api/v1/init", member.ExposedTokensPort) + tokenInitUrl := fmt.Sprintf("http://localhost:%d/api/v1/init", member.ExposedTokensPorts[tokenIdx]) if err := core.RequestWithRetry("POST", tokenInitUrl, nil, nil); err != nil { return err } @@ -46,19 +46,18 @@ func (p *ERC1155Provider) FirstTimeSetup() error { return nil } -func (p *ERC1155Provider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { +func (p *ERC1155Provider) GetDockerServiceDefinitions(tokenIdx int) []*docker.ServiceDefinition { serviceDefinitions := make([]*docker.ServiceDefinition, 0, len(p.Stack.Members)) for i, member := range p.Stack.Members { serviceDefinitions = append(serviceDefinitions, &docker.ServiceDefinition{ - ServiceName: "tokens_" + member.ID, + ServiceName: fmt.Sprintf("tokens_%v_%v", member.ID, tokenIdx), Service: &docker.Service{ - Image: p.Stack.VersionManifest.Tokens.GetDockerImageString(), - ContainerName: fmt.Sprintf("%s_tokens_%v", p.Stack.Name, i), - Ports: []string{fmt.Sprintf("%d:3000", member.ExposedTokensPort)}, + Image: p.Stack.VersionManifest.TokensERC1155.GetDockerImageString(), + ContainerName: fmt.Sprintf("%s_tokens_%v_%v", p.Stack.Name, i, tokenIdx), + Ports: []string{fmt.Sprintf("%d:3000", member.ExposedTokensPorts[tokenIdx])}, Environment: map[string]string{ - "ETHCONNECT_URL": p.getEthconnectURL(member), - "ETHCONNECT_INSTANCE": "/contracts/erc1155", - "AUTO_INIT": "false", + "ETHCONNECT_URL": p.getEthconnectURL(member, member.ExposedTokensPorts[tokenIdx]), + "AUTO_INIT": "false", }, DependsOn: map[string]map[string]string{ "ethconnect_" + member.ID: {"condition": "service_started"}, @@ -73,24 +72,22 @@ func (p *ERC1155Provider) GetDockerServiceDefinitions() []*docker.ServiceDefinit return serviceDefinitions } -func (p *ERC1155Provider) GetFireflyConfig(m *types.Member) *core.TokensConfig { - return &core.TokensConfig{ - &core.TokenConnector{ - Plugin: "fftokens", - Name: "erc1155", - URL: p.getTokensURL(m), - }, +func (p *ERC1155Provider) GetFireflyConfig(m *types.Member, tokenIdx int) *core.TokenConnector { + return &core.TokenConnector{ + Plugin: "fftokens", + Name: "erc1155", + URL: p.getTokensURL(m, tokenIdx), } } -func (p *ERC1155Provider) getEthconnectURL(member *types.Member) string { +func (p *ERC1155Provider) getEthconnectURL(member *types.Member, tokenIdx int) string { return fmt.Sprintf("http://ethconnect_%s:8080", member.ID) } -func (p *ERC1155Provider) getTokensURL(member *types.Member) string { +func (p *ERC1155Provider) getTokensURL(member *types.Member, tokenIdx int) string { if !member.External { - return fmt.Sprintf("http://tokens_%s:3000", member.ID) + return fmt.Sprintf("http://tokens_%s_%d:3000", member.ID, tokenIdx) } else { - return fmt.Sprintf("http://127.0.0.1:%v", member.ExposedTokensPort) + return fmt.Sprintf("http://127.0.0.1:%v", member.ExposedTokensPorts[tokenIdx]) } } diff --git a/internal/tokens/erc20erc721/contracts.go b/internal/tokens/erc20erc721/contracts.go new file mode 100644 index 00000000..8ac06ada --- /dev/null +++ b/internal/tokens/erc20erc721/contracts.go @@ -0,0 +1,39 @@ +// 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 erc20erc721 + +import ( + "github.com/hyperledger/firefly-cli/internal/log" + "github.com/hyperledger/firefly-cli/pkg/types" +) + +func DeployContracts(s *types.Stack, log log.Logger, verbose bool, tokenIndex int) error { + + // Currently the act of creating and deploying a suitable ERC20 or ERC721 compliant + // contract, or contract factory, is an exercise left to the user. + // + // For users simply experimenting with how tokens work, the ERC1155 standard is recommended + // as a flexbile and fully formed sample implementation of fungible and non-fungible tokens + // with a set of features you would expect. + // + // For users looking to take the next step and create a "proper" coin or NFT collection, + // you really can't bypass the step of investigating the right OpenZeppelin (or other) + // base class and determining the tokenomics (around supply / minting / burning / governance) + // on top of that base class using the examples and standards out there. + + return nil +} diff --git a/internal/tokens/erc20erc721/erc20_erc721_provider.go b/internal/tokens/erc20erc721/erc20_erc721_provider.go new file mode 100644 index 00000000..1cec32d0 --- /dev/null +++ b/internal/tokens/erc20erc721/erc20_erc721_provider.go @@ -0,0 +1,93 @@ +// 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 erc20erc721 + +import ( + "fmt" + + "github.com/hyperledger/firefly-cli/internal/core" + "github.com/hyperledger/firefly-cli/internal/docker" + "github.com/hyperledger/firefly-cli/internal/log" + "github.com/hyperledger/firefly-cli/pkg/types" +) + +type ERC20ERC721Provider struct { + Log log.Logger + Verbose bool + Stack *types.Stack +} + +func (p *ERC20ERC721Provider) DeploySmartContracts(tokenIndex int) error { + return DeployContracts(p.Stack, p.Log, p.Verbose, tokenIndex) +} + +func (p *ERC20ERC721Provider) FirstTimeSetup(tokenIdx int) error { + for _, member := range p.Stack.Members { + p.Log.Info(fmt.Sprintf("initializing tokens on member %s", member.ID)) + tokenInitUrl := fmt.Sprintf("http://localhost:%d/api/v1/init", member.ExposedTokensPorts[tokenIdx]) + if err := core.RequestWithRetry("POST", tokenInitUrl, nil, nil); err != nil { + return err + } + } + return nil +} + +func (p *ERC20ERC721Provider) GetDockerServiceDefinitions(tokenIdx int) []*docker.ServiceDefinition { + serviceDefinitions := make([]*docker.ServiceDefinition, 0, len(p.Stack.Members)) + for i, member := range p.Stack.Members { + serviceDefinitions = append(serviceDefinitions, &docker.ServiceDefinition{ + ServiceName: fmt.Sprintf("tokens_%v_%v", member.ID, tokenIdx), + Service: &docker.Service{ + Image: p.Stack.VersionManifest.TokensERC20ERC721.GetDockerImageString(), + ContainerName: fmt.Sprintf("%s_tokens_%v_%v", p.Stack.Name, i, tokenIdx), + Ports: []string{fmt.Sprintf("%d:3000", member.ExposedTokensPorts[tokenIdx])}, + Environment: map[string]string{ + "ETHCONNECT_URL": p.getEthconnectURL(member, member.ExposedTokensPorts[tokenIdx]), + "AUTO_INIT": "false", + }, + DependsOn: map[string]map[string]string{ + "ethconnect_" + member.ID: {"condition": "service_started"}, + }, + HealthCheck: &docker.HealthCheck{ + Test: []string{"CMD", "curl", "http://localhost:3000/api"}, + }, + Logging: docker.StandardLogOptions, + }, + }) + } + return serviceDefinitions +} + +func (p *ERC20ERC721Provider) GetFireflyConfig(m *types.Member, tokenIdx int) *core.TokenConnector { + return &core.TokenConnector{ + Plugin: "fftokens", + Name: "erc20_erc721", + URL: p.getTokensURL(m, tokenIdx), + } +} + +func (p *ERC20ERC721Provider) getEthconnectURL(member *types.Member, tokenIdx int) string { + return fmt.Sprintf("http://ethconnect_%s:8080", member.ID) +} + +func (p *ERC20ERC721Provider) getTokensURL(member *types.Member, tokenIdx int) string { + if !member.External { + return fmt.Sprintf("http://tokens_%s_%d:3000", member.ID, tokenIdx) + } else { + return fmt.Sprintf("http://127.0.0.1:%v", member.ExposedTokensPorts[tokenIdx]) + } +} diff --git a/internal/tokens/niltokens/niltokens_provider.go b/internal/tokens/niltokens/niltokens_provider.go deleted file mode 100644 index 900ec81d..00000000 --- a/internal/tokens/niltokens/niltokens_provider.go +++ /dev/null @@ -1,46 +0,0 @@ -// 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 niltokens - -import ( - "github.com/hyperledger/firefly-cli/internal/core" - "github.com/hyperledger/firefly-cli/internal/docker" - "github.com/hyperledger/firefly-cli/internal/log" - "github.com/hyperledger/firefly-cli/pkg/types" -) - -type NilTokensProvider struct { - Log log.Logger - Verbose bool - Stack *types.Stack -} - -func (p *NilTokensProvider) DeploySmartContracts() error { - return nil -} - -func (p *NilTokensProvider) FirstTimeSetup() error { - return nil -} - -func (p *NilTokensProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { - return nil -} - -func (p *NilTokensProvider) GetFireflyConfig(m *types.Member) *core.TokensConfig { - return nil -} diff --git a/internal/tokens/tokens_provider.go b/internal/tokens/tokens_provider.go index 342a9a2c..09f835f2 100644 --- a/internal/tokens/tokens_provider.go +++ b/internal/tokens/tokens_provider.go @@ -23,8 +23,8 @@ import ( ) type ITokensProvider interface { - DeploySmartContracts() error - FirstTimeSetup() error - GetDockerServiceDefinitions() []*docker.ServiceDefinition - GetFireflyConfig(m *types.Member) *core.TokensConfig + DeploySmartContracts(tokenIndex int) error + FirstTimeSetup(tokenIdx int) error + GetDockerServiceDefinitions(tokenIdx int) []*docker.ServiceDefinition + GetFireflyConfig(m *types.Member, tokenIdx int) *core.TokenConnector } diff --git a/pkg/types/manifest.go b/pkg/types/manifest.go index 0a8d91d5..1320c2d3 100644 --- a/pkg/types/manifest.go +++ b/pkg/types/manifest.go @@ -23,11 +23,12 @@ type GitHubRelease struct { } type VersionManifest struct { - FireFly *ManifestEntry `json:"firefly,omitempty"` - Ethconnect *ManifestEntry `json:"ethconnect"` - Fabconnect *ManifestEntry `json:"fabconnect"` - DataExchange *ManifestEntry `json:"dataexchange-https"` - Tokens *ManifestEntry `json:"tokens-erc1155"` + FireFly *ManifestEntry `json:"firefly,omitempty"` + Ethconnect *ManifestEntry `json:"ethconnect"` + Fabconnect *ManifestEntry `json:"fabconnect"` + DataExchange *ManifestEntry `json:"dataexchange-https"` + TokensERC1155 *ManifestEntry `json:"tokens-erc1155"` + TokensERC20ERC721 *ManifestEntry `json:"tokens-erc20-erc721"` } func (m *VersionManifest) Entries() []*ManifestEntry { @@ -39,7 +40,8 @@ func (m *VersionManifest) Entries() []*ManifestEntry { m.Ethconnect, m.Fabconnect, m.DataExchange, - m.Tokens, + m.TokensERC1155, + m.TokensERC20ERC721, } } diff --git a/pkg/types/stack.go b/pkg/types/stack.go index 3c500ce2..d706fae6 100644 --- a/pkg/types/stack.go +++ b/pkg/types/stack.go @@ -23,7 +23,7 @@ type Stack struct { ExposedBlockchainPort int `json:"exposedGethPort,omitempty"` Database string `json:"database"` BlockchainProvider string `json:"blockchainProvider"` - TokensProvider string `json:"tokensProvider"` + TokenProviders TokenProviders `json:"tokenProviders"` VersionManifest *VersionManifest `json:"versionManifest,omitempty"` PrometheusEnabled bool `json:"prometheusEnabled,omitempty"` ExposedPrometheusPort int `json:"exposedPrometheusPort,omitempty"` @@ -43,8 +43,20 @@ type Member struct { ExposedIPFSApiPort int `json:"exposedIPFSApiPort,omitempty"` ExposedIPFSGWPort int `json:"exposedIPFSGWPort,omitempty"` ExposedUIPort int `json:"exposedUiPort,omitempty"` - ExposedTokensPort int `json:"exposedTokensPort,omitempty"` + ExposedTokensPorts []int `json:"exposedTokensPorts,omitempty"` External bool `json:"external,omitempty"` OrgName string `json:"orgName,omitempty"` NodeName string `json:"nodeName,omitempty"` } + +type TokenProvider string + +type TokenProviders []TokenProvider + +func (tps TokenProviders) Strings() []string { + ret := make([]string, len(tps)) + for i, t := range tps { + ret[i] = string(t) + } + return ret +}