From 0bebae83f4cf30b4842a4840b0b9a82fa17691cc Mon Sep 17 00:00:00 2001 From: Nicko Guyer Date: Thu, 7 Apr 2022 13:50:23 -0400 Subject: [PATCH] Add ability to create new Fabric accounts Signed-off-by: Nicko Guyer --- Makefile | 2 +- cmd/accounts_create.go | 22 +++++-- cmd/accounts_list.go | 18 ++++- cmd/deploy_ethereum.go | 18 ++++- cmd/deploy_fabric.go | 18 ++++- internal/blockchain/blockchain_provider.go | 3 +- .../blockchain/ethereum/besu/besu_provider.go | 28 +++++--- .../blockchain/ethereum/ethconnect/client.go | 10 +-- internal/blockchain/ethereum/ethereum.go | 5 ++ .../blockchain/ethereum/geth/geth_provider.go | 44 +++++++------ internal/blockchain/fabric/fabric_provider.go | 66 ++++++++++++++----- internal/core/firefly_config.go | 2 +- internal/docker/docker_config.go | 2 +- internal/stacks/stack_manager.go | 59 ++++++++++++----- pkg/types/stack.go | 37 ++++++----- 15 files changed, 234 insertions(+), 100 deletions(-) diff --git a/Makefile b/Makefile index 3f2e43dc..b2d9bf73 100644 --- a/Makefile +++ b/Makefile @@ -24,7 +24,7 @@ all: build build: ## Builds all go code cd ff && go build -ldflags="-X 'github.com/hyperledger/firefly-cli/cmd.BuildDate=$(DATE)' -X 'github.com/hyperledger/firefly-cli/cmd.BuildCommit=$(GITREF)'" install: ## Installs the package - cd ff && go install + cd ff && go install -ldflags="-X 'github.com/hyperledger/firefly-cli/cmd.BuildDate=$(DATE)' -X 'github.com/hyperledger/firefly-cli/cmd.BuildCommit=$(GITREF)'" lint: ${LINT} ## Checks and reports lint errors GOGC=20 $(LINT) run -v --timeout 5m diff --git a/cmd/accounts_create.go b/cmd/accounts_create.go index 6cc2f791..ed418ead 100644 --- a/cmd/accounts_create.go +++ b/cmd/accounts_create.go @@ -1,7 +1,19 @@ -/* -Copyright © 2022 NAME HERE +// 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 cmd import ( @@ -17,7 +29,7 @@ var accountsCreateCmd = &cobra.Command{ Use: "create ", Short: "Create a new account in the FireFly stack", Long: `Create a new account in the FireFly stack`, - Args: cobra.ExactArgs(1), + Args: cobra.MinimumNArgs(1), PreRunE: func(cmd *cobra.Command, args []string) error { return docker.CheckDockerConfig() }, @@ -27,7 +39,7 @@ var accountsCreateCmd = &cobra.Command{ if err := stackManager.LoadStack(stackName, verbose); err != nil { return err } - account, err := stackManager.CreateAccount() + account, err := stackManager.CreateAccount(args[1:]) if err != nil { return err } diff --git a/cmd/accounts_list.go b/cmd/accounts_list.go index 6f53e388..502b9328 100644 --- a/cmd/accounts_list.go +++ b/cmd/accounts_list.go @@ -1,7 +1,19 @@ -/* -Copyright © 2022 NAME HERE +// 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 cmd import ( diff --git a/cmd/deploy_ethereum.go b/cmd/deploy_ethereum.go index 49262354..4d28b890 100644 --- a/cmd/deploy_ethereum.go +++ b/cmd/deploy_ethereum.go @@ -1,7 +1,19 @@ -/* -Copyright © 2022 NAME HERE +// 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 cmd import ( diff --git a/cmd/deploy_fabric.go b/cmd/deploy_fabric.go index 9a6a264a..389c4fd9 100644 --- a/cmd/deploy_fabric.go +++ b/cmd/deploy_fabric.go @@ -1,7 +1,19 @@ -/* -Copyright © 2022 NAME HERE +// 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 cmd import ( diff --git a/internal/blockchain/blockchain_provider.go b/internal/blockchain/blockchain_provider.go index 51602bce..dc1d2206 100644 --- a/internal/blockchain/blockchain_provider.go +++ b/internal/blockchain/blockchain_provider.go @@ -33,5 +33,6 @@ type IBlockchainProvider interface { Reset() error GetContracts(filename string, extraArgs []string) ([]string, error) DeployContract(filename, contractName string, member *types.Member, extraArgs []string) (interface{}, error) - CreateAccount() (interface{}, error) + CreateAccount(args []string) (interface{}, error) + ParseAccount(interface{}) interface{} } diff --git a/internal/blockchain/ethereum/besu/besu_provider.go b/internal/blockchain/ethereum/besu/besu_provider.go index 3b73179b..5607259c 100644 --- a/internal/blockchain/ethereum/besu/besu_provider.go +++ b/internal/blockchain/ethereum/besu/besu_provider.go @@ -45,8 +45,9 @@ type BesuProvider struct { func (p *BesuProvider) WriteConfig(options *types.InitOptions) error { initDir := filepath.Join(constants.StacksDir, p.Stack.Name, "init") for i, member := range p.Stack.Members { + account := member.Account.(*ethereum.Account) // Write the private key to disk for each member - if err := p.writeAccountToDisk(p.Stack.InitDir, member.Address, member.PrivateKey); err != nil { + if err := p.writeAccountToDisk(p.Stack.InitDir, account.Address, account.PrivateKey); err != nil { return err } @@ -56,7 +57,7 @@ func (p *BesuProvider) WriteConfig(options *types.InitOptions) error { return nil } - if err := p.writeTomlKeyFile(p.Stack.InitDir, member.Address); err != nil { + if err := p.writeTomlKeyFile(p.Stack.InitDir, account.Address); err != nil { return err } @@ -111,7 +112,8 @@ func (p *BesuProvider) FirstTimeSetup() error { // Mount the directory containing all members' private keys and password, and import the accounts using the geth CLI // Note: This is needed because of licensing issues with the Go Ethereum library that could do this step for _, member := range p.Stack.Members { - if err := p.importAccountToEthsigner(member.Address); err != nil { + account := member.Account.(*ethereum.Account) + if err := p.importAccountToEthsigner(account.Address); err != nil { return err } } @@ -149,7 +151,8 @@ func (p *BesuProvider) DeployFireFlyContract() (*core.BlockchainConfig, error) { func (p *BesuProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { addresses := "" for i, member := range p.Stack.Members { - addresses = addresses + member.Address + account := member.Account.(*ethereum.Account) + addresses = addresses + account.Address if i+1 < len(p.Stack.Members) { addresses = addresses + "," } @@ -196,9 +199,10 @@ func (p *BesuProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition } func (p *BesuProvider) GetFireflyConfig(stack *types.Stack, m *types.Member) (blockchainConfig *core.BlockchainConfig, orgConfig *core.OrgConfig) { + account := m.Account.(*ethereum.Account) orgConfig = &core.OrgConfig{ Name: m.OrgName, - Key: m.Address, + Key: account.Address, } blockchainConfig = &core.BlockchainConfig{ @@ -214,10 +218,6 @@ func (p *BesuProvider) GetFireflyConfig(stack *types.Stack, m *types.Member) (bl return } -func (p *BesuProvider) getSmartContractAddressPatchJSON(contractAddress string) []byte { - return []byte(fmt.Sprintf(`{"blockchain":{"ethereum":{"ethconnect":{"instance":"%s"}}}}`, contractAddress)) -} - func (p *BesuProvider) Reset() error { return nil } @@ -246,7 +246,7 @@ func (p *BesuProvider) DeployContract(filename, contractName string, member *typ }, nil } -func (p *BesuProvider) CreateAccount() (interface{}, error) { +func (p *BesuProvider) CreateAccount(args []string) (interface{}, error) { address, privateKey := ethereum.GenerateAddressAndPrivateKey() if err := p.writeAccountToDisk(p.Stack.RuntimeDir, address, privateKey); err != nil { @@ -274,3 +274,11 @@ func (p *BesuProvider) getEthconnectURL(member *types.Member) string { return fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) } } + +func (p *BesuProvider) ParseAccount(account interface{}) interface{} { + accountMap := account.(map[string]interface{}) + return ðereum.Account{ + Address: accountMap["address"].(string), + PrivateKey: accountMap["privateKey"].(string), + } +} diff --git a/internal/blockchain/ethereum/ethconnect/client.go b/internal/blockchain/ethereum/ethconnect/client.go index 7a7f81b1..5cf9b293 100644 --- a/internal/blockchain/ethereum/ethconnect/client.go +++ b/internal/blockchain/ethereum/ethconnect/client.go @@ -223,7 +223,7 @@ func deprecatedRegisterContract(ethconnectUrl string, abiId string, contractAddr func deployContract(member *types.Member, contract *ethereum.CompiledContract, args map[string]string) (string, error) { ethconnectUrl := fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) - + address := member.Account.(*ethereum.Account).Address hexBytecode, err := hex.DecodeString(strings.TrimPrefix(contract.Bytecode, "0x")) if err != nil { return "", err @@ -234,7 +234,7 @@ func deployContract(member *types.Member, contract *ethereum.CompiledContract, a Headers: EthconnectMessageHeaders{ Type: "DeployContract", }, - From: member.Address, + From: address, ABI: contract.ABI, Bytecode: base64Bytecode, } @@ -257,10 +257,11 @@ func deployContract(member *types.Member, contract *ethereum.CompiledContract, a func DeprecatedDeployContract(member *types.Member, contract *ethereum.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, member.Address, args, name) + deployResponse, err := deprecatedDeployContract(ethconnectUrl, abiResponse.ID, address, args, name) if err != nil { return "", err } @@ -270,10 +271,11 @@ func DeprecatedDeployContract(member *types.Member, contract *ethereum.CompiledC func DeprecatedRegisterContract(member *types.Member, contract *ethereum.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, member.Address, name, args) + _, err = deprecatedRegisterContract(ethconnectUrl, abiResponse.ID, contractAddress, address, name, args) if err != nil { return err } diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go index ca8d2252..01d654ce 100644 --- a/internal/blockchain/ethereum/ethereum.go +++ b/internal/blockchain/ethereum/ethereum.go @@ -23,6 +23,11 @@ import ( "golang.org/x/crypto/sha3" ) +type Account struct { + Address string `json:"address"` + PrivateKey string `json:"privateKey"` +} + func GenerateAddressAndPrivateKey() (address string, privateKey string) { newPrivateKey, _ := secp256k1.NewPrivateKey(secp256k1.S256()) privateKeyBytes := newPrivateKey.Serialize() diff --git a/internal/blockchain/ethereum/geth/geth_provider.go b/internal/blockchain/ethereum/geth/geth_provider.go index f9b38a5f..256a1368 100644 --- a/internal/blockchain/ethereum/geth/geth_provider.go +++ b/internal/blockchain/ethereum/geth/geth_provider.go @@ -44,9 +44,10 @@ type GethProvider struct { func (p *GethProvider) WriteConfig(options *types.InitOptions) error { initDir := filepath.Join(constants.StacksDir, p.Stack.Name, "init") for i, member := range p.Stack.Members { + account := member.Account.(*ethereum.Account) // Write the private key to disk for each member // Drop the 0x on the front of the private key here because that's what geth is expecting in the keyfile - if err := p.writeAccountToDisk(p.Stack.InitDir, member.Address, member.PrivateKey); err != nil { + if err := p.writeAccountToDisk(p.Stack.InitDir, account.Address, account.PrivateKey); err != nil { return err } @@ -60,8 +61,9 @@ func (p *GethProvider) WriteConfig(options *types.InitOptions) error { // Create genesis.json addresses := make([]string, len(p.Stack.Members)) for i, member := range p.Stack.Members { + address := member.Account.(*ethereum.Account).Address // Drop the 0x on the front of the address here because that's what geth is expecting in the genesis.json - addresses[i] = member.Address[2:] + addresses[i] = address[2:] } genesis := CreateGenesis(addresses, options.BlockPeriod) if err := genesis.WriteGenesisJson(filepath.Join(initDir, "blockchain", "genesis.json")); err != nil { @@ -95,7 +97,8 @@ func (p *GethProvider) FirstTimeSetup() error { // Mount the directory containing all members' private keys and password, and import the accounts using the geth CLI for _, member := range p.Stack.Members { - p.importAccountToGeth(member.Address) + address := member.Account.(*ethereum.Account).Address + p.importAccountToGeth(address) } // Copy the genesis block information @@ -122,14 +125,8 @@ func (p *GethProvider) PreStart() error { func (p *GethProvider) PostStart() error { // Unlock accounts - for _, m := range p.Stack.Members { - p.Log.Info(fmt.Sprintf("unlocking %s", m.Address)) - if err := p.unlockAccount(m.Address, "correcthorsebatterystaple"); err != nil { - return err - } - } for _, account := range p.Stack.State.Accounts { - address := account.(map[string]string)["address"] + address := account.(*ethereum.Account).Address p.Log.Info(fmt.Sprintf("unlocking account %s", address)) if err := p.unlockAccount(address, "correcthorsebatterystaple"); err != nil { return err @@ -164,13 +161,13 @@ func (p *GethProvider) DeployFireFlyContract() (*core.BlockchainConfig, error) { } func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { - addresses := "" - for i, member := range p.Stack.Members { - addresses = addresses + member.Address - if i+1 < len(p.Stack.Members) { - addresses = addresses + "," - } - } + // addresses := "" + // for i, member := range p.Stack.Members { + // addresses = addresses + member.Address + // if i+1 < len(p.Stack.Members) { + // addresses = addresses + "," + // } + // } gethCommand := fmt.Sprintf(`--datadir /data --syncmode 'full' --port 30311 --http --http.addr "0.0.0.0" --http.port 8545 --http.vhosts "*" --http.api 'admin,personal,eth,net,web3,txpool,miner,clique' --networkid 2021 --miner.gasprice 0 --password /data/password --mine --allow-insecure-unlock --nodiscover`) serviceDefinitions := make([]*docker.ServiceDefinition, 1) @@ -191,9 +188,10 @@ func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition } func (p *GethProvider) GetFireflyConfig(stack *types.Stack, m *types.Member) (blockchainConfig *core.BlockchainConfig, orgConfig *core.OrgConfig) { + account := m.Account.(*ethereum.Account) orgConfig = &core.OrgConfig{ Name: m.OrgName, - Key: m.Address, + Key: account.Address, } blockchainConfig = &core.BlockchainConfig{ @@ -237,7 +235,7 @@ func (p *GethProvider) DeployContract(filename, contractName string, member *typ }, nil } -func (p *GethProvider) CreateAccount() (interface{}, error) { +func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { address, privateKey := ethereum.GenerateAddressAndPrivateKey() if err := p.writeAccountToDisk(p.Stack.RuntimeDir, address, privateKey); err != nil { @@ -263,3 +261,11 @@ func (p *GethProvider) getEthconnectURL(member *types.Member) string { return fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort) } } + +func (p *GethProvider) ParseAccount(account interface{}) interface{} { + accountMap := account.(map[string]interface{}) + return ðereum.Account{ + Address: accountMap["address"].(string), + PrivateKey: accountMap["privateKey"].(string), + } +} diff --git a/internal/blockchain/fabric/fabric_provider.go b/internal/blockchain/fabric/fabric_provider.go index 486af1f9..c590f3df 100644 --- a/internal/blockchain/fabric/fabric_provider.go +++ b/internal/blockchain/fabric/fabric_provider.go @@ -33,6 +33,11 @@ import ( "github.com/hyperledger/firefly-cli/pkg/types" ) +type Account struct { + Name string `json:"name"` + OrgName string `json:"orgName"` +} + type FabricProvider struct { Verbose bool Log log.Logger @@ -46,6 +51,8 @@ func (p *FabricProvider) WriteConfig(options *types.InitOptions) error { blockchainDirectory := path.Join(p.Stack.InitDir, "blockchain") cryptogenYamlPath := path.Join(blockchainDirectory, "cryptogen.yaml") + os.MkdirAll(blockchainDirectory, 0755) + if err := WriteCryptogenConfig(len(p.Stack.Members), cryptogenYamlPath); err != nil { return err } @@ -148,8 +155,13 @@ func (p *FabricProvider) deploySmartContracts() error { return err } - if err := p.registerIdentities(); err != nil { - return err + p.Log.Info("registering identities") + for _, m := range p.Stack.Members { + account, err := p.registerIdentity(m, m.OrgName) + if err != nil { + return err + } + p.Stack.State.Accounts = append(p.Stack.State.Accounts, account) } return nil @@ -415,19 +427,19 @@ func (p *FabricProvider) commitChaincode(channel, chaincode, version string) err ) } -func (p *FabricProvider) registerIdentities() error { - p.Log.Info("registering identities") - for _, m := range p.Stack.Members { - res, err := fabconnect.CreateIdentity(fmt.Sprintf("http://127.0.0.1:%v", m.ExposedConnectorPort), m.OrgName) - if err != nil { - return err - } - _, err = fabconnect.EnrollIdentity(fmt.Sprintf("http://127.0.0.1:%v", m.ExposedConnectorPort), m.OrgName, res.Secret) - if err != nil { - return err - } +func (p *FabricProvider) registerIdentity(member *types.Member, name string) (*Account, error) { + res, err := fabconnect.CreateIdentity(fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort), name) + if err != nil { + return nil, err } - return nil + _, err = fabconnect.EnrollIdentity(fmt.Sprintf("http://127.0.0.1:%v", member.ExposedConnectorPort), name, res.Secret) + if err != nil { + return nil, err + } + return &Account{ + Name: name, + OrgName: member.OrgName, + }, nil } func (p *FabricProvider) GetContracts(filename string, extraArgs []string) ([]string, error) { @@ -487,11 +499,33 @@ func (p *FabricProvider) DeployContract(filename, contractName string, member *t }, nil } -func (p *FabricProvider) CreateAccount() (interface{}, error) { - return nil, fmt.Errorf("creating a new account on a FireFly Fabric is not yet supported") +func (p *FabricProvider) CreateAccount(args []string) (interface{}, error) { + switch { + case len(args) < 1: + return "", fmt.Errorf("org name not set. usage: ff accounts create ") + case len(args) < 2: + return "", fmt.Errorf("account name not set. usage: ff accounts create ") + } + orgName := args[0] + accountName := args[1] + // Find the FireFly member by the org name + for _, member := range p.Stack.Members { + if member.OrgName == orgName { + return p.registerIdentity(member, accountName) + } + } + return nil, fmt.Errorf("unable to find a FireFly org with name: '%s'", orgName) } // As of release 2.4, Hyperledger Fabric only publishes amd64 images, but no arm64 specific images func getDockerPlatform() string { return "linux/amd64" } + +func (p *FabricProvider) ParseAccount(account interface{}) interface{} { + accountMap := account.(map[string]interface{}) + return &Account{ + Name: accountMap["name"].(string), + OrgName: accountMap["orgName"].(string), + } +} diff --git a/internal/core/firefly_config.go b/internal/core/firefly_config.go index 614997e3..7196fdd4 100644 --- a/internal/core/firefly_config.go +++ b/internal/core/firefly_config.go @@ -276,7 +276,7 @@ func getPostgresURL(member *types.Member) string { if !member.External { return fmt.Sprintf("postgres://postgres:f1refly@postgres_%s:5432?sslmode=disable", member.ID) } else { - return fmt.Sprintf("postgres://postgres:f1refly@127.0.0.1:%v?sslmode=disable", member.ExposedPostgresPort) + return fmt.Sprintf("postgres://postgres:f1refly@127.0.0.1:%v?sslmode=disable", member.ExposedDatabasePort) } } diff --git a/internal/docker/docker_config.go b/internal/docker/docker_config.go index 85982dbb..8bdfe0f3 100644 --- a/internal/docker/docker_config.go +++ b/internal/docker/docker_config.go @@ -106,7 +106,7 @@ func CreateDockerCompose(s *types.Stack) *DockerComposeConfig { compose.Services["postgres_"+member.ID] = &Service{ Image: constants.PostgresImageName, ContainerName: fmt.Sprintf("%s_postgres_%s", s.Name, member.ID), - Ports: []string{fmt.Sprintf("%d:5432", member.ExposedPostgresPort)}, + Ports: []string{fmt.Sprintf("%d:5432", member.ExposedDatabasePort)}, Environment: map[string]string{ "POSTGRES_PASSWORD": "f1refly", "PGDATA": "/var/lib/postgresql/data/pgdata", diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index 64b829b4..1798eeb0 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -138,9 +138,8 @@ func (s *StackManager) InitStack(stackName string, memberCount int, options *typ for i := 0; i < memberCount; i++ { externalProcess := i < options.ExternalProcesses s.Stack.Members[i] = createMember(fmt.Sprint(i), i, options, externalProcess) - s.Stack.State.Accounts[i] = map[string]string{ - "address": s.Stack.Members[i].Address, - "privateKey": s.Stack.Members[i].PrivateKey, + if s.Stack.Members[i].Account != nil { + s.Stack.State.Accounts[i] = s.Stack.Members[i].Account } } compose := docker.CreateDockerCompose(s.Stack) @@ -236,6 +235,12 @@ func (s *StackManager) LoadStack(stackName string, verbose bool) error { s.Stack.RuntimeDir = s.Stack.StackDir } + for _, member := range stack.Members { + if member.Account != nil { + member.Account = s.blockchainProvider.ParseAccount(member.Account) + } + } + // For backwards compatability, add a "default" VersionManifest // in memory for stacks that were created with old CLI versions if s.Stack.VersionManifest == nil { @@ -298,6 +303,13 @@ func (s *StackManager) loadStackStateJSON() error { if err := json.Unmarshal(b, &stackState); err != nil { return err } + + for i, account := range stackState.Accounts { + if account != nil { + stackState.Accounts[i] = s.blockchainProvider.ParseAccount(account) + } + } + s.Stack.State = stackState return nil } @@ -431,18 +443,15 @@ func (s *StackManager) copyDataExchangeConfigToVolumes(verbose bool) error { } func createMember(id string, index int, options *types.InitOptions, external bool) *types.Member { - address, privateKey := ethereum.GenerateAddressAndPrivateKey() serviceBase := options.ServicesBasePort + (index * 100) member := &types.Member{ ID: id, Index: &index, - Address: address, - PrivateKey: privateKey, ExposedFireflyPort: options.FireFlyBasePort + index, ExposedFireflyAdminPort: serviceBase + 1, // note shared blockchain node is on zero ExposedConnectorPort: serviceBase + 2, ExposedUIPort: serviceBase + 3, - ExposedPostgresPort: serviceBase + 4, + ExposedDatabasePort: serviceBase + 4, ExposedDataexchangePort: serviceBase + 5, ExposedIPFSApiPort: serviceBase + 6, ExposedIPFSGWPort: serviceBase + 7, @@ -459,6 +468,17 @@ func createMember(id string, index int, options *types.InitOptions, external boo member.ExposedTokensPorts = append(member.ExposedTokensPorts, nextPort) nextPort++ } + + switch options.BlockchainProvider { + case types.GoEthereum, types.HyperledgerBesu: + address, privateKey := ethereum.GenerateAddressAndPrivateKey() + member.Account = ðereum.Account{ + Address: address, + PrivateKey: privateKey, + } + case types.HyperledgerFabric: + // This will be filled in by the Fabric blockchain provider + } return member } @@ -495,10 +515,11 @@ func (s *StackManager) StartStack(verbose bool, options *types.StartOptions) err return finalErr } } - } - err = s.runStartupSequence(s.Stack.RuntimeDir, verbose, false) - if err != nil { - return err + } else { + err = s.runStartupSequence(s.Stack.RuntimeDir, verbose, false) + if err != nil { + return err + } } return s.ensureFireflyNodesUp(true) } @@ -622,7 +643,7 @@ func (s *StackManager) checkPortsAvailable() error { } ports = append(ports, member.ExposedIPFSApiPort) ports = append(ports, member.ExposedIPFSGWPort) - ports = append(ports, member.ExposedPostgresPort) + ports = append(ports, member.ExposedDatabasePort) ports = append(ports, member.ExposedUIPort) ports = append(ports, member.ExposedTokensPorts...) } @@ -693,6 +714,12 @@ func (s *StackManager) copyFireflyConfigToContainer(verbose bool, workingDir str func (s *StackManager) runFirstTimeSetup(verbose bool, options *types.StartOptions) (err error) { configDir := filepath.Join(s.Stack.RuntimeDir, "config") + for i := 0; i < len(s.Stack.Members); i++ { + if s.Stack.Members[i].Account != nil { + s.Stack.State.Accounts = append(s.Stack.State.Accounts, s.Stack.Members[i].Account) + } + } + if err := copy.Copy(s.Stack.InitDir, s.Stack.RuntimeDir); err != nil { return err } @@ -768,7 +795,9 @@ func (s *StackManager) runFirstTimeSetup(verbose bool, options *types.StartOptio return err } } - return nil + + // Update the stack state with any new state that was created as a part of the setup process + return s.writeStackStateJSON(s.Stack.RuntimeDir) } func (s *StackManager) ensureFireflyNodesUp(firstTimeSetup bool) error { @@ -962,8 +991,8 @@ func (s *StackManager) DeployContract(filename, contractName string, memberIndex return string(b), nil } -func (s *StackManager) CreateAccount() (string, error) { - newAccount, err := s.blockchainProvider.CreateAccount() +func (s *StackManager) CreateAccount(args []string) (string, error) { + newAccount, err := s.blockchainProvider.CreateAccount(args) if err != nil { return "", err } diff --git a/pkg/types/stack.go b/pkg/types/stack.go index 5da528b3..abf8a50b 100644 --- a/pkg/types/stack.go +++ b/pkg/types/stack.go @@ -20,7 +20,7 @@ type Stack struct { Name string `json:"name,omitempty"` Members []*Member `json:"members,omitempty"` SwarmKey string `json:"swarmKey,omitempty"` - ExposedBlockchainPort int `json:"exposedGethPort,omitempty"` + ExposedBlockchainPort int `json:"exposedBlockchainPort,omitempty"` Database string `json:"database"` BlockchainProvider string `json:"blockchainProvider"` TokenProviders TokenProviders `json:"tokenProviders"` @@ -35,21 +35,22 @@ type Stack struct { } type Member struct { - ID string `json:"id,omitempty"` - Index *int `json:"index,omitempty"` - Address string `json:"address,omitempty"` - PrivateKey string `json:"privateKey,omitempty"` - ExposedFireflyPort int `json:"exposedFireflyPort,omitempty"` - ExposedFireflyAdminPort int `json:"exposedFireflyAdminPort,omitempty"` - ExposedFireflyMetricsPort int `json:"exposedFireflyMetricsPort,omitempty"` - ExposedConnectorPort int `json:"exposedConnectorPort,omitempty"` - ExposedPostgresPort int `json:"exposedPostgresPort,omitempty"` - ExposedDataexchangePort int `json:"exposedDataexchangePort,omitempty"` - ExposedIPFSApiPort int `json:"exposedIPFSApiPort,omitempty"` - ExposedIPFSGWPort int `json:"exposedIPFSGWPort,omitempty"` - ExposedUIPort int `json:"exposedUiPort,omitempty"` - ExposedTokensPorts []int `json:"exposedTokensPorts,omitempty"` - External bool `json:"external,omitempty"` - OrgName string `json:"orgName,omitempty"` - NodeName string `json:"nodeName,omitempty"` + ID string `json:"id,omitempty"` + Index *int `json:"index,omitempty"` + Account interface{} `json:"account,omitempty"` + ExposedFireflyPort int `json:"exposedFireflyPort,omitempty"` + ExposedFireflyAdminPort int `json:"exposedFireflyAdminPort,omitempty"` + ExposedFireflyMetricsPort int `json:"exposedFireflyMetricsPort,omitempty"` + ExposedConnectorPort int `json:"exposedConnectorPort,omitempty"` + ExposedDatabasePort int `json:"exposedDatabasePort,omitempty"` + ExposedDataexchangePort int `json:"exposedDataexchangePort,omitempty"` + ExposedIPFSApiPort int `json:"exposedIPFSApiPort,omitempty"` + ExposedIPFSGWPort int `json:"exposedIPFSGWPort,omitempty"` + ExposedUIPort int `json:"exposedUiPort,omitempty"` + ExposedTokensPorts []int `json:"exposedTokensPorts,omitempty"` + External bool `json:"external,omitempty"` + OrgName string `json:"orgName,omitempty"` + NodeName string `json:"nodeName,omitempty"` + // Address string `json:"address,omitempty"` + // PrivateKey string `json:"privateKey,omitempty"` }