diff --git a/cmd/init.go b/cmd/init.go index e6c0e2f4..63f3bebd 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -62,7 +62,7 @@ var initCmd = &cobra.Command{ if err := validateBlockchainProvider(blockchainProviderInput, blockchainNodeProviderInput); err != nil { return err } - if err := validateTokensProvider(tokenProvidersSelection); err != nil { + if err := validateTokensProvider(tokenProvidersSelection, blockchainNodeProviderInput); err != nil { return err } if err := validateReleaseChannel(releaseChannelInput); err != nil { @@ -186,11 +186,18 @@ func validateBlockchainProvider(providerString, nodeString string) error { return nil } -func validateTokensProvider(input []string) error { - _, err := types.TokenProvidersFromStrings(input) +func validateTokensProvider(input []string, blockchainNodeProviderInput string) error { + tokenProviders, err := types.TokenProvidersFromStrings(input) if err != nil { return err } + if blockchainNodeProviderInput == types.RemoteRPC.String() { + for _, t := range tokenProviders { + if t == types.ERC1155 { + return errors.New("erc1155 is currently not supported with a remote-rpc node") + } + } + } return nil } diff --git a/internal/blockchain/ethereum/connector/ethconnect/client.go b/internal/blockchain/ethereum/connector/ethconnect/client.go index 78f5b2ec..dea3adeb 100644 --- a/internal/blockchain/ethereum/connector/ethconnect/client.go +++ b/internal/blockchain/ethereum/connector/ethconnect/client.go @@ -17,16 +17,10 @@ package ethconnect import ( - "bytes" "context" "encoding/base64" "encoding/hex" - "encoding/json" "fmt" - "io" - "io/ioutil" - "mime/multipart" - "net/http" "net/url" "path" "strings" @@ -64,6 +58,7 @@ type EthconnectMessageRequest struct { From string `json:"from,omitempty"` ABI interface{} `json:"abi,omitempty"` Bytecode string `json:"compiled"` + Params []interface{} `json:"params"` } type EthconnectMessageHeaders struct { @@ -107,137 +102,6 @@ func (e *Ethconnect) Port() int { return 8080 } -func publishABI(ethconnectUrl string, contract *ethtypes.CompiledContract) (*PublishAbiResponseBody, error) { - u, err := url.Parse(ethconnectUrl) - if err != nil { - return nil, err - } - u, err = u.Parse("abis") - if err != nil { - return nil, err - } - requestUrl := u.String() - abi, err := json.Marshal(contract.ABI) - if err != nil { - return nil, err - } - body := &bytes.Buffer{} - writer := multipart.NewWriter(body) - fw, err := writer.CreateFormField("abi") - if err != nil { - return nil, err - } - if _, err := io.Copy(fw, bytes.NewReader(abi)); err != nil { - return nil, err - } - fw, err = writer.CreateFormField("bytecode") - if err != nil { - return nil, err - } - if _, err = io.Copy(fw, strings.NewReader(contract.Bytecode)); err != nil { - return nil, err - } - writer.Close() - req, err := http.NewRequest("POST", requestUrl, bytes.NewReader(body.Bytes())) - if err != nil { - return nil, err - } - req.Header.Add("Content-Type", writer.FormDataContentType()) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != 200 { - return nil, fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) - } - var publishAbiResponse *PublishAbiResponseBody - json.Unmarshal(responseBody, &publishAbiResponse) - return publishAbiResponse, nil -} - -func deprecatedDeployContract(ethconnectUrl string, abiId string, fromAddress string, params map[string]string, registeredName string) (*DeployContractResponseBody, error) { - u, err := url.Parse(ethconnectUrl) - if err != nil { - return nil, err - } - u, err = u.Parse(path.Join("abis", abiId)) - if err != nil { - return nil, err - } - requestUrl := u.String() - requestBody, err := json.Marshal(params) - if err != nil { - return nil, err - } - req, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(requestBody)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("x-firefly-from", fromAddress) - req.Header.Set("x-firefly-sync", "true") - if registeredName != "" { - req.Header.Set("x-firefly-register", registeredName) - } - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != 200 { - return nil, fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) - } - var deployContractResponse *DeployContractResponseBody - json.Unmarshal(responseBody, &deployContractResponse) - return deployContractResponse, nil -} - -func deprecatedRegisterContract(ethconnectUrl string, abiId string, contractAddress string, fromAddress string, registeredName string, params map[string]string) (*RegisterResponseBody, error) { - u, err := url.Parse(ethconnectUrl) - if err != nil { - return nil, err - } - u, err = u.Parse(path.Join("abis", abiId, contractAddress)) - if err != nil { - return nil, err - } - requestUrl := u.String() - req, err := http.NewRequest("POST", requestUrl, bytes.NewBuffer(nil)) - if err != nil { - return nil, err - } - req.Header.Set("Content-Type", "application/json") - req.Header.Set("x-firefly-sync", "true") - req.Header.Set("x-firefly-register", registeredName) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - responseBody, err := ioutil.ReadAll(resp.Body) - if err != nil { - return nil, err - } - if resp.StatusCode != 201 { - return nil, fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) - } - var registerResponseBody *RegisterResponseBody - json.Unmarshal(responseBody, ®isterResponseBody) - return registerResponseBody, nil -} - func (e *Ethconnect) DeployContract(contract *ethtypes.CompiledContract, contractName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) { ethconnectUrl := fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) address := member.Account.(*ethereum.Account).Address @@ -247,6 +111,11 @@ func (e *Ethconnect) DeployContract(contract *ethtypes.CompiledContract, contrac } base64Bytecode := base64.StdEncoding.EncodeToString(hexBytecode) + params := make([]interface{}, len(extraArgs)) + for i, arg := range extraArgs { + params[i] = arg + } + requestBody := &EthconnectMessageRequest{ Headers: EthconnectMessageHeaders{ Type: "DeployContract", @@ -254,6 +123,7 @@ func (e *Ethconnect) DeployContract(contract *ethtypes.CompiledContract, contrac From: address, ABI: contract.ABI, Bytecode: base64Bytecode, + Params: params, } ethconnectResponse := &EthconnectMessageResponse{} @@ -277,45 +147,6 @@ func (e *Ethconnect) DeployContract(contract *ethtypes.CompiledContract, contrac return result, nil } -func DeprecatedDeployContract(member *types.Organization, contract *ethtypes.CompiledContract, name string, args map[string]string) (string, error) { - ethconnectUrl := fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) - abiResponse, err := publishABI(ethconnectUrl, contract) - address := member.Account.(*ethereum.Account).Address - if err != nil { - return "", err - } - deployResponse, err := deprecatedDeployContract(ethconnectUrl, abiResponse.ID, address, args, name) - if err != nil { - return "", err - } - return deployResponse.ContractAddress, nil -} - -func DeprecatedRegisterContract(member *types.Organization, contract *ethtypes.CompiledContract, contractAddress string, name string, args map[string]string) error { - ethconnectUrl := fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) - abiResponse, err := publishABI(ethconnectUrl, contract) - address := member.Account.(*ethereum.Account).Address - if err != nil { - return err - } - _, err = deprecatedRegisterContract(ethconnectUrl, abiResponse.ID, contractAddress, address, name, args) - if err != nil { - return err - } - return nil -} - -// func DeployCustomContract(member *types.Organization, filename, contractName string) (string, error) { -// contracts, err := ethereum.ReadContractJSON(filename) -// if err != nil { -// return "", nil -// } - -// contract := contracts.Contracts[contractName] - -// return deployContract(member, contract, map[string]string{}) -// } - func getReply(ctx context.Context, ethconnectUrl, id string) (*EthconnectReply, error) { u, err := url.Parse(ethconnectUrl) if err != nil { diff --git a/internal/blockchain/ethereum/connector/evmconnect/client.go b/internal/blockchain/ethereum/connector/evmconnect/client.go index f0fc69eb..bb642beb 100644 --- a/internal/blockchain/ethereum/connector/evmconnect/client.go +++ b/internal/blockchain/ethereum/connector/evmconnect/client.go @@ -35,6 +35,7 @@ type EvmconnectRequest struct { From string `json:"from,omitempty"` Definition interface{} `json:"definition,omitempty"` Contract string `json:"contract,omitempty"` + Params []interface{} `json:"params,omitempty"` } type EvmconnectHeaders struct { @@ -77,6 +78,11 @@ func (e *Evmconnect) DeployContract(contract *ethtypes.CompiledContract, contrac evmconnectURL := fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) fromAddress := member.Account.(*ethereum.Account).Address + params := make([]interface{}, len(extraArgs)) + for i, arg := range extraArgs { + params[i] = arg + } + requestBody := &EvmconnectRequest{ Headers: EvmconnectHeaders{ Type: "DeployContract", @@ -84,6 +90,7 @@ func (e *Evmconnect) DeployContract(contract *ethtypes.CompiledContract, contrac From: fromAddress, Definition: contract.ABI, Contract: contract.Bytecode, + Params: params, } txResponse := &EvmconnectTransactionResponse{} diff --git a/internal/blockchain/ethereum/remoterpc/remoterpc_provider.go b/internal/blockchain/ethereum/remoterpc/remoterpc_provider.go index 399b7987..fbf64014 100644 --- a/internal/blockchain/ethereum/remoterpc/remoterpc_provider.go +++ b/internal/blockchain/ethereum/remoterpc/remoterpc_provider.go @@ -132,11 +132,25 @@ func (p *RemoteRPCProvider) Reset() error { } func (p *RemoteRPCProvider) GetContracts(filename string, extraArgs []string) ([]string, error) { - return []string{}, nil + contracts, err := ethereum.ReadContractJSON(filename) + if err != nil { + return []string{}, err + } + contractNames := make([]string, len(contracts.Contracts)) + i := 0 + for contractName := range contracts.Contracts { + contractNames[i] = contractName + i++ + } + return contractNames, err } func (p *RemoteRPCProvider) DeployContract(filename, contractName, instanceName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) { - return nil, fmt.Errorf("contract deployment not supported for Remote RPC URL connections") + contracts, err := ethereum.ReadContractJSON(filename) + if err != nil { + return nil, err + } + return p.connector.DeployContract(contracts.Contracts[contractName], instanceName, member, extraArgs) } func (p *RemoteRPCProvider) CreateAccount(args []string) (interface{}, error) { diff --git a/internal/docker/docker_checks.go b/internal/docker/docker_checks.go index 75cccbe3..4db4c054 100644 --- a/internal/docker/docker_checks.go +++ b/internal/docker/docker_checks.go @@ -1,3 +1,19 @@ +// Copyright © 2022 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 docker import ( @@ -11,20 +27,20 @@ func CheckDockerConfig() error { dockerCmd := exec.Command("docker", "-v") _, err := dockerCmd.Output() if err != nil { - return fmt.Errorf("An error occurred while running docker. Is docker installed on your computer?") + return fmt.Errorf("an error occurred while running docker. Is docker installed on your computer?") } dockerComposeCmd := exec.Command("docker-compose", "-v") _, err = dockerComposeCmd.Output() if err != nil { - return fmt.Errorf("An error occurred while running docker-compose. Is docker-compose installed on your computer?") + return fmt.Errorf("an error occurred while running docker-compose. Is docker-compose installed on your computer?") } dockerDeamonCheck := exec.Command("docker", "ps") _, err = dockerDeamonCheck.Output() if err != nil { - return fmt.Errorf("An error occurred while running docker. Is docker running on your computer?") + return fmt.Errorf("an error occurred while running docker. Is docker running on your computer?") } return nil diff --git a/internal/docker/docker_config.go b/internal/docker/docker_config.go index 2c7f8dcb..f659f306 100644 --- a/internal/docker/docker_config.go +++ b/internal/docker/docker_config.go @@ -1,4 +1,4 @@ -// Copyright © 2021 Kaleido, Inc. +// Copyright © 2022 Kaleido, Inc. // // SPDX-License-Identifier: Apache-2.0 // @@ -95,12 +95,13 @@ func CreateDockerCompose(s *types.Stack) *DockerComposeConfig { fmt.Sprintf("%d:%d", member.ExposedFireflyPort, member.ExposedFireflyPort), fmt.Sprintf("%d:%d", member.ExposedFireflyAdminSPIPort, member.ExposedFireflyAdminSPIPort), }, - Volumes: []string{fmt.Sprintf("%s:/etc/firefly/firefly.core.yml:ro", configFile)}, - DependsOn: map[string]map[string]string{ - "dataexchange_" + member.ID: {"condition": "service_started"}, - "ipfs_" + member.ID: {"condition": "service_healthy"}, - }, - Logging: StandardLogOptions, + Volumes: []string{fmt.Sprintf("%s:/etc/firefly/firefly.core.yml:ro", configFile)}, + DependsOn: map[string]map[string]string{}, + Logging: StandardLogOptions, + } + if s.MultipartyEnabled { + compose.Services["firefly_core_"+member.ID].DependsOn["dataexchange_"+member.ID] = map[string]string{"condition": "service_started"} + compose.Services["firefly_core_"+member.ID].DependsOn["ipfs_"+member.ID] = map[string]string{"condition": "service_healthy"} } } if s.Database == "postgres" { @@ -126,37 +127,39 @@ func CreateDockerCompose(s *types.Stack) *DockerComposeConfig { service.DependsOn["postgres_"+member.ID] = map[string]string{"condition": "service_healthy"} } } - compose.Services["ipfs_"+member.ID] = &Service{ - Image: constants.IPFSImageName, - ContainerName: fmt.Sprintf("%s_ipfs_%s", s.Name, member.ID), - Ports: []string{ - fmt.Sprintf("%d:5001", member.ExposedIPFSApiPort), - fmt.Sprintf("%d:8080", member.ExposedIPFSGWPort), - }, - Environment: map[string]interface{}{ - "IPFS_SWARM_KEY": s.SwarmKey, - "LIBP2P_FORCE_PNET": "1", - }, - Volumes: []string{ - fmt.Sprintf("ipfs_staging_%s:/export", member.ID), - fmt.Sprintf("ipfs_data_%s:/data/ipfs", member.ID), - }, - Logging: StandardLogOptions, - HealthCheck: &HealthCheck{ - Test: []string{"CMD-SHELL", `wget --post-data= http://127.0.0.1:5001/api/v0/id -O - -q`}, - Interval: "5s", - Timeout: "3s", - Retries: 12, - }, - } - compose.Volumes[fmt.Sprintf("ipfs_staging_%s", member.ID)] = struct{}{} - compose.Volumes[fmt.Sprintf("ipfs_data_%s", member.ID)] = struct{}{} - compose.Services["dataexchange_"+member.ID] = &Service{ - Image: s.VersionManifest.DataExchange.GetDockerImageString(), - ContainerName: fmt.Sprintf("%s_dataexchange_%s", s.Name, member.ID), - Ports: []string{fmt.Sprintf("%d:3000", member.ExposedDataexchangePort)}, - Volumes: []string{fmt.Sprintf("dataexchange_%s:/data", member.ID)}, - Logging: StandardLogOptions, + if s.MultipartyEnabled { + compose.Services["ipfs_"+member.ID] = &Service{ + Image: constants.IPFSImageName, + ContainerName: fmt.Sprintf("%s_ipfs_%s", s.Name, member.ID), + Ports: []string{ + fmt.Sprintf("%d:5001", member.ExposedIPFSApiPort), + fmt.Sprintf("%d:8080", member.ExposedIPFSGWPort), + }, + Environment: map[string]interface{}{ + "IPFS_SWARM_KEY": s.SwarmKey, + "LIBP2P_FORCE_PNET": "1", + }, + Volumes: []string{ + fmt.Sprintf("ipfs_staging_%s:/export", member.ID), + fmt.Sprintf("ipfs_data_%s:/data/ipfs", member.ID), + }, + Logging: StandardLogOptions, + HealthCheck: &HealthCheck{ + Test: []string{"CMD-SHELL", `wget --post-data= http://127.0.0.1:5001/api/v0/id -O - -q`}, + Interval: "5s", + Timeout: "3s", + Retries: 12, + }, + } + compose.Volumes[fmt.Sprintf("ipfs_staging_%s", member.ID)] = struct{}{} + compose.Volumes[fmt.Sprintf("ipfs_data_%s", member.ID)] = struct{}{} + compose.Services["dataexchange_"+member.ID] = &Service{ + Image: s.VersionManifest.DataExchange.GetDockerImageString(), + ContainerName: fmt.Sprintf("%s_dataexchange_%s", s.Name, member.ID), + Ports: []string{fmt.Sprintf("%d:3000", member.ExposedDataexchangePort)}, + Volumes: []string{fmt.Sprintf("dataexchange_%s:/data", member.ID)}, + Logging: StandardLogOptions, + } } compose.Volumes[fmt.Sprintf("dataexchange_%s", member.ID)] = struct{}{} if s.SandboxEnabled { diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index fe9607a1..8e849e2f 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -355,9 +355,11 @@ func (s *StackManager) ensureInitDirectories() error { return err } - for _, member := range s.Stack.Members { - if err := os.MkdirAll(filepath.Join(configDir, "dataexchange_"+member.ID, "peer-certs"), 0755); err != nil { - return err + if s.Stack.MultipartyEnabled { + for _, member := range s.Stack.Members { + if err := os.MkdirAll(filepath.Join(configDir, "dataexchange_"+member.ID, "peer-certs"), 0755); err != nil { + return err + } } } @@ -398,8 +400,10 @@ func (s *StackManager) writeStackConfig() error { } func (s *StackManager) writeConfig(options *types.InitOptions) error { - if err := s.writeDataExchangeCerts(options.Verbose); err != nil { - return err + if s.Stack.MultipartyEnabled { + if err := s.writeDataExchangeCerts(options.Verbose); err != nil { + return err + } } for _, member := range s.Stack.Members { @@ -506,14 +510,21 @@ func (s *StackManager) createMember(id string, index int, options *types.InitOpt ExposedConnectorPort: serviceBase + 2, ExposedUIPort: serviceBase + 3, ExposedDatabasePort: serviceBase + 4, - ExposedDataexchangePort: serviceBase + 5, - ExposedIPFSApiPort: serviceBase + 6, - ExposedIPFSGWPort: serviceBase + 7, External: external, OrgName: options.OrgNames[index], NodeName: options.NodeNames[index], } - nextPort := serviceBase + 8 + + nextPort := serviceBase + 5 + if options.MultipartyEnabled { + member.ExposedDataexchangePort = serviceBase + nextPort + nextPort++ + member.ExposedIPFSApiPort = serviceBase + nextPort + nextPort++ + member.ExposedIPFSGWPort = serviceBase + nextPort + nextPort++ + } + if options.PrometheusEnabled { member.ExposedFireflyMetricsPort = nextPort nextPort++ @@ -596,8 +607,9 @@ func (s *StackManager) PullStack(options *types.PullOptions) error { } } - // IPFS is the only one that we always use in every stack - images = append(images, constants.IPFSImageName) + if s.Stack.MultipartyEnabled { + images = append(images, constants.IPFSImageName) + } // Also pull postgres if we're using it if s.Stack.Database == types.PostgreSQL.String() { @@ -700,19 +712,22 @@ func (s *StackManager) checkPortsAvailable() error { ports := make([]int, 1) ports[0] = s.Stack.ExposedBlockchainPort for _, member := range s.Stack.Members { - ports = append(ports, member.ExposedDataexchangePort) + ports = append(ports, member.ExposedConnectorPort) + ports = append(ports, member.ExposedDatabasePort) + ports = append(ports, member.ExposedUIPort) + ports = append(ports, member.ExposedTokensPorts...) + if !member.External { ports = append(ports, member.ExposedFireflyAdminSPIPort) ports = append(ports, member.ExposedFireflyPort) ports = append(ports, member.ExposedFireflyMetricsPort) } - ports = append(ports, member.ExposedIPFSApiPort) - ports = append(ports, member.ExposedIPFSGWPort) - ports = append(ports, member.ExposedDatabasePort) - ports = append(ports, member.ExposedUIPort) - ports = append(ports, member.ExposedTokensPorts...) - + if s.Stack.MultipartyEnabled { + ports = append(ports, member.ExposedDataexchangePort) + ports = append(ports, member.ExposedIPFSApiPort) + ports = append(ports, member.ExposedIPFSGWPort) + } if s.Stack.SandboxEnabled { ports = append(ports, member.ExposedSandboxPort) } @@ -801,8 +816,10 @@ func (s *StackManager) runFirstTimeSetup(options *types.StartOptions) (messages } } - if err := s.copyDataExchangeConfigToVolumes(); err != nil { - return messages, err + if s.Stack.MultipartyEnabled { + if err := s.copyDataExchangeConfigToVolumes(); err != nil { + return messages, err + } } pullOptions := &types.PullOptions{ diff --git a/internal/tokens/erc1155/contracts.go b/internal/tokens/erc1155/contracts.go deleted file mode 100644 index 95bb7143..00000000 --- a/internal/tokens/erc1155/contracts.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright © 2022 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 erc1155 - -import ( - "context" - "errors" - "fmt" - "path/filepath" - - "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum" - "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/connector/ethconnect" - "github.com/hyperledger/firefly-cli/internal/log" - "github.com/hyperledger/firefly-cli/pkg/types" -) - -// TODO: -// -// REMOVE THIS FILE ONCE ERC-1155 SUPPORTS DEPLOYING WITH -// ETHCONNECT AND EVMCONNECT THE SAME WAY -// - -const TOKEN_URI_PATTERN = "firefly://token/{id}" - -func DeployContracts(ctx context.Context, s *types.Stack, tokenIndex int) (*types.ContractDeploymentResult, error) { - l := log.LoggerFromContext(ctx) - var containerName string - for _, member := range s.Members { - if !member.External { - containerName = fmt.Sprintf("%s_tokens_%s_%d", s.Name, member.ID, tokenIndex) - break - } - } - if containerName == "" { - return nil, errors.New("unable to extract contracts from container - no valid tokens containers found in stack") - } - l.Info("extracting smart contracts") - - if err := ethereum.ExtractContracts(ctx, containerName, "/root/contracts", s.RuntimeDir); err != nil { - return nil, err - } - - contracts, err := ethereum.ReadTruffleCompiledContract(filepath.Join(s.RuntimeDir, "contracts", "ERC1155MixedFungible.json")) - if err != nil { - return nil, err - } - tokenContract, ok := contracts.Contracts["ERC1155MixedFungible"] - if !ok { - return nil, fmt.Errorf("unable to find ERC1155MixedFungible in compiled contracts") - } - - var tokenContractAddress string - for _, member := range s.Members { - // TODO: move to address based contract deployment, once ERC-1155 connector is updated to not require an EthConnect REST API registration - if tokenContractAddress == "" { - l.Info(fmt.Sprintf("deploying ERC1155 contract on '%s'", member.ID)) - tokenContractAddress, err = ethconnect.DeprecatedDeployContract(member, tokenContract, "erc1155", map[string]string{"uri": TOKEN_URI_PATTERN}) - if err != nil { - return nil, err - } - } else { - l.Info(fmt.Sprintf("registering ERC1155 contract on '%s'", member.ID)) - err = ethconnect.DeprecatedRegisterContract(member, tokenContract, tokenContractAddress, "erc1155", map[string]string{"uri": TOKEN_URI_PATTERN}) - if err != nil { - return nil, err - } - } - } - - result := &types.ContractDeploymentResult{ - Message: fmt.Sprintf("Deployed ERC-1155 contract to: %s", tokenContractAddress), - DeployedContract: &types.DeployedContract{ - Name: "erc1155", - Location: map[string]string{"address": tokenContractAddress}, - }, - } - - return result, nil -} diff --git a/internal/tokens/erc1155/erc1155_provider.go b/internal/tokens/erc1155/erc1155_provider.go index 5d2cf5d2..fb49fb8f 100644 --- a/internal/tokens/erc1155/erc1155_provider.go +++ b/internal/tokens/erc1155/erc1155_provider.go @@ -18,9 +18,12 @@ package erc1155 import ( "context" + "errors" "fmt" + "path/filepath" "github.com/hyperledger/firefly-cli/internal/blockchain" + "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum" "github.com/hyperledger/firefly-cli/internal/core" "github.com/hyperledger/firefly-cli/internal/docker" "github.com/hyperledger/firefly-cli/internal/log" @@ -28,6 +31,7 @@ import ( ) const tokenProviderName = "erc1155" +const contractName = "ERC1155MixedFungible" type ERC1155Provider struct { ctx context.Context @@ -44,30 +48,24 @@ func NewERC1155Provider(ctx context.Context, stack *types.Stack, blockchainProvi } func (p *ERC1155Provider) DeploySmartContracts(tokenIndex int) (*types.ContractDeploymentResult, error) { - return DeployContracts(p.ctx, p.stack, tokenIndex) - - // TODO: - // - // USE THE CODE BELOW ONCE THE ERC-1155 SUPPORTS DEPLOYING WITH - // ETHCONNECT AND EVMCONNECT THE SAME WAY - // - // l := log.LoggerFromContext(p.ctx) - // var containerName string - // for _, member := range p.stack.Members { - // if !member.External { - // containerName = fmt.Sprintf("%s_tokens_%s_%d", p.stack.Name, member.ID, tokenIndex) - // break - // } - // } - // if containerName == "" { - // return nil, errors.New("unable to extract contracts from container - no valid tokens containers found in stack") - // } - // l.Info("extracting smart contracts") + l := log.LoggerFromContext(p.ctx) + var containerName string + for _, member := range p.stack.Members { + if !member.External { + containerName = fmt.Sprintf("%s_tokens_%s_%d", p.stack.Name, member.ID, tokenIndex) + break + } + } + if containerName == "" { + return nil, errors.New("unable to extract contracts from container - no valid tokens containers found in stack") + } + l.Info("extracting smart contracts") - // if err := ethereum.ExtractContracts(p.ctx, containerName, "/root/contracts", p.stack.RuntimeDir); err != nil { - // return nil, err - // } - // return p.blockchainProvider.DeployContract(filepath.Join(p.stack.RuntimeDir, "contracts", "ERC1155MixedFungible.json"), "ERC1155MixedFungible", "ERC1155MixedFungible", p.stack.Members[0], nil) + if err := ethereum.ExtractContracts(p.ctx, containerName, "/root/contracts", p.stack.RuntimeDir); err != nil { + return nil, err + } + constructorArgs := []string{"firefly://"} + return p.blockchainProvider.DeployContract(filepath.Join(p.stack.RuntimeDir, "contracts", "ERC1155MixedFungible.json"), contractName, contractName, p.stack.Members[0], constructorArgs) } func (p *ERC1155Provider) FirstTimeSetup(tokenIdx int) error { @@ -86,10 +84,22 @@ func (p *ERC1155Provider) GetDockerServiceDefinitions(tokenIdx int) []*docker.Se serviceDefinitions := make([]*docker.ServiceDefinition, 0, len(p.stack.Members)) for i, member := range p.stack.Members { connectorName := fmt.Sprintf("tokens_%v_%v", member.ID, tokenIdx) + + var contractAddress types.HexAddress + for _, contract := range p.stack.State.DeployedContracts { + if contract.Name == contractName { + switch loc := contract.Location.(type) { + case map[string]string: + contractAddress = types.HexAddress(loc["address"]) + } + } + } + env := map[string]interface{}{ "ETHCONNECT_URL": p.blockchainProvider.GetConnectorURL(member), "ETHCONNECT_TOPIC": connectorName, "AUTO_INIT": "false", + "CONTRACT_ADDRESS": contractAddress, } serviceDefinitions = append(serviceDefinitions, &docker.ServiceDefinition{ ServiceName: connectorName, diff --git a/internal/tokens/erc20erc721/erc20_erc721_provider.go b/internal/tokens/erc20erc721/erc20_erc721_provider.go index 93119a6d..5839e21b 100644 --- a/internal/tokens/erc20erc721/erc20_erc721_provider.go +++ b/internal/tokens/erc20erc721/erc20_erc721_provider.go @@ -28,7 +28,6 @@ import ( "github.com/hyperledger/firefly-cli/internal/docker" "github.com/hyperledger/firefly-cli/internal/log" "github.com/hyperledger/firefly-cli/pkg/types" - "gopkg.in/yaml.v3" ) const tokenProviderName = "erc20_erc721" @@ -39,17 +38,6 @@ type ERC20ERC721Provider struct { blockchainProvider blockchain.IBlockchainProvider } -type HexAddress string - -// Explicitly quote hex addresses so that they are interpreted as string (not int) -func (h HexAddress) MarshalYAML() (interface{}, error) { - return yaml.Node{ - Value: string(h), - Kind: yaml.ScalarNode, - Style: yaml.DoubleQuotedStyle, - }, nil -} - func NewERC20ERC721Provider(ctx context.Context, stack *types.Stack, blockchainProvider blockchain.IBlockchainProvider) *ERC20ERC721Provider { return &ERC20ERC721Provider{ ctx: ctx, @@ -96,12 +84,12 @@ func (p *ERC20ERC721Provider) GetDockerServiceDefinitions(tokenIdx int) []*docke for i, member := range p.stack.Members { connectorName := fmt.Sprintf("tokens_%v_%v", member.ID, tokenIdx) - var factoryAddress HexAddress + var factoryAddress types.HexAddress for _, contract := range p.stack.State.DeployedContracts { if contract.Name == contractName(tokenIdx) { switch loc := contract.Location.(type) { case map[string]string: - factoryAddress = HexAddress(loc["address"]) + factoryAddress = types.HexAddress(loc["address"]) } } } diff --git a/pkg/types/hex_address.go b/pkg/types/hex_address.go new file mode 100644 index 00000000..95ed86db --- /dev/null +++ b/pkg/types/hex_address.go @@ -0,0 +1,30 @@ +// Copyright © 2022 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 types + +import "gopkg.in/yaml.v3" + +type HexAddress string + +// Explicitly quote hex addresses so that they are interpreted as string (not int) +func (h HexAddress) MarshalYAML() (interface{}, error) { + return yaml.Node{ + Value: string(h), + Kind: yaml.ScalarNode, + Style: yaml.DoubleQuotedStyle, + }, nil +}