Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support quorum tessera blockchain node #310

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
6f4f577
Add environment vars cli flag to stack init
rodion-lim-partior May 31, 2024
6c69867
Update concatenate environment variable function name
rodion-lim-partior Jun 28, 2024
4bc7744
Add quorum blockchain node provider
rodion-lim-partior May 31, 2024
2056886
Start multiple quorum and tessera containers
rodion-lim-partior Jun 20, 2024
85013cf
Switch to using entrypoint file for quorum
rodion-lim-partior Jun 20, 2024
7df502d
Unlock only member specific accounts per quorum node
rodion-lim-partior Jun 21, 2024
ddd5226
Store private transaction manager keys in stack state and add tests
rodion-lim-partior Jun 21, 2024
8f0eefb
Expose ptm base port as a cli flag
rodion-lim-partior Jun 21, 2024
fe36fcb
Add tesseraEnabled flag and explicitly only allow quorum with tessera…
rodion-lim-partior Jun 27, 2024
91c6a1f
Make quorum consensus configurable via cli flag
rodion-lim-partior Jun 27, 2024
a8910c1
Remove private ptm private key references during stack creation
rodion-lim-partior Jun 27, 2024
23480c5
Remove duplicate dd for tessera
rodion-lim-partior Jun 27, 2024
e780adf
Refactor quorum docker entrypoint creation to its own file
rodion-lim-partior Jun 28, 2024
1589b82
Check that Tessera ports are available
rodion-lim-partior Jun 28, 2024
55a8385
Update tessera container name to have stack name
rodion-lim-partior Jun 28, 2024
c78b926
Fix PR review comments
rodion-lim-partior Jul 1, 2024
3af75a7
Remove unused volume parameter when creating quorum entrypoint
rodion-lim-partior Jul 1, 2024
19cb418
Fix incorrect consensus args
rodion-lim-partior Jul 1, 2024
2a12af0
Add tests for quorum package
rodion-lim-partior Jul 1, 2024
608c838
Update gas limit for quorum
rodion-lim-partior Jul 2, 2024
18dc785
Make consensus and tessera args generic
rodion-lim-partior Jul 10, 2024
ae22e11
Disable evmconnect with tessera temporarily
rodion-lim-partior Jul 10, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,15 @@ func initCommon(args []string) error {
if err := validateIPFSMode(initOptions.IPFSMode); err != nil {
return err
}
if err := validateConsensus(initOptions.Consensus); err != nil {
return err
}
if err := validatePrivateTransactionManagerSelection(initOptions.PrivateTransactionManager, initOptions.BlockchainNodeProvider); err != nil {
return err
}
if err := validatePrivateTransactionManagerBlockchainConnectorCombination(initOptions.PrivateTransactionManager, initOptions.BlockchainConnector); err != nil {
return err
}

fmt.Println("initializing new FireFly stack...")

Expand Down Expand Up @@ -200,6 +209,57 @@ func validateBlockchainProvider(providerString, nodeString string) error {
return nil
}

func validateConsensus(consensusString string) error {
v, err := fftypes.FFEnumParseString(context.Background(), types.Consensus, consensusString)
if err != nil {
return nil
}

if v != types.ConsensusClique {
return errors.New("currently only Clique consensus is supported")
}

return nil
}

func validatePrivateTransactionManagerSelection(privateTransactionManagerInput string, nodeString string) error {
privateTransactionManager, err := fftypes.FFEnumParseString(context.Background(), types.PrivateTransactionManager, privateTransactionManagerInput)
if err != nil {
return err
}

if !privateTransactionManager.Equals(types.PrivateTransactionManagerNone) {
v, err := fftypes.FFEnumParseString(context.Background(), types.BlockchainNodeProvider, nodeString)
if err != nil {
return nil
}

if v != types.BlockchainNodeProviderQuorum {
return errors.New("private transaction manager can only be enabled if blockchain node provider is Quorum")
}
}
return nil
}

func validatePrivateTransactionManagerBlockchainConnectorCombination(privateTransactionManagerInput string, blockchainConnectorInput string) error {
privateTransactionManager, err := fftypes.FFEnumParseString(context.Background(), types.PrivateTransactionManager, privateTransactionManagerInput)
if err != nil {
return err
}

blockchainConnector, err := fftypes.FFEnumParseString(context.Background(), types.BlockchainConnector, blockchainConnectorInput)
if err != nil {
return nil
}

if !privateTransactionManager.Equals(types.PrivateTransactionManagerNone) {
if !blockchainConnector.Equals(types.BlockchainConnectorEthconnect) {
return errors.New("currently only Ethconnect blockchain connector is supported with a private transaction manager")
}
}
return nil
}

func validateTokensProvider(input []string, blockchainNodeProviderInput string) error {
tokenProviders := make([]fftypes.FFEnum, len(input))
for i, t := range input {
Expand Down Expand Up @@ -246,10 +306,13 @@ func randomHexString(length int) (string, error) {
func init() {
initCmd.PersistentFlags().IntVarP(&initOptions.FireFlyBasePort, "firefly-base-port", "p", 5000, "Mapped port base of FireFly core API (1 added for each member)")
initCmd.PersistentFlags().IntVarP(&initOptions.ServicesBasePort, "services-base-port", "s", 5100, "Mapped port base of services (100 added for each member)")
initCmd.PersistentFlags().IntVar(&initOptions.PtmBasePort, "ptm-base-port", 4100, "Mapped port base of private transaction manager (10 added for each member)")
rodion-lim-partior marked this conversation as resolved.
Show resolved Hide resolved
initCmd.PersistentFlags().StringVarP(&initOptions.DatabaseProvider, "database", "d", "sqlite3", fmt.Sprintf("Database type to use. Options are: %v", fftypes.FFEnumValues(types.DatabaseSelection)))
initCmd.Flags().StringVarP(&initOptions.BlockchainConnector, "blockchain-connector", "c", "evmconnect", fmt.Sprintf("Blockchain connector to use. Options are: %v", fftypes.FFEnumValues(types.BlockchainConnector)))
initCmd.Flags().StringVarP(&initOptions.BlockchainProvider, "blockchain-provider", "b", "ethereum", fmt.Sprintf("Blockchain to use. Options are: %v", fftypes.FFEnumValues(types.BlockchainProvider)))
initCmd.Flags().StringVarP(&initOptions.BlockchainNodeProvider, "blockchain-node", "n", "geth", fmt.Sprintf("Blockchain node type to use. Options are: %v", fftypes.FFEnumValues(types.BlockchainNodeProvider)))
initCmd.PersistentFlags().StringVar(&initOptions.PrivateTransactionManager, "private-transaction-manager", "none", fmt.Sprintf("Private Transaction Manager to use. Options are: %v", fftypes.FFEnumValues(types.PrivateTransactionManager)))
initCmd.PersistentFlags().StringVar(&initOptions.Consensus, "consensus", "clique", fmt.Sprintf("Consensus algorithm to use. Options are %v", fftypes.FFEnumValues(types.Consensus)))
initCmd.PersistentFlags().StringArrayVarP(&initOptions.TokenProviders, "token-providers", "t", []string{"erc20_erc721"}, fmt.Sprintf("Token providers to use. Options are: %v", fftypes.FFEnumValues(types.TokenProvider)))
initCmd.PersistentFlags().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.PersistentFlags().StringVarP(&initOptions.FireFlyVersion, "release", "r", "latest", fmt.Sprintf("Select the FireFly release version to use. Options are: %v", fftypes.FFEnumValues(types.ReleaseChannelSelection)))
Expand All @@ -271,5 +334,6 @@ func init() {
initCmd.PersistentFlags().StringArrayVar(&initOptions.OrgNames, "org-name", []string{}, "Organization name")
initCmd.PersistentFlags().StringArrayVar(&initOptions.NodeNames, "node-name", []string{}, "Node name")
initCmd.PersistentFlags().BoolVar(&initOptions.RemoteNodeDeploy, "remote-node-deploy", false, "Enable or disable deployment of FireFly contracts on remote nodes")
initCmd.PersistentFlags().StringToStringVar(&initOptions.EnvironmentVars, "environment-vars", map[string]string{}, "Common environment variables to set on all containers in FireFly stack")
rodion-lim-partior marked this conversation as resolved.
Show resolved Hide resolved
rootCmd.AddCommand(initCmd)
}
3 changes: 2 additions & 1 deletion internal/blockchain/ethereum/besu/besu_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,8 @@ func (p *BesuProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition
Volumes: []string{
"besu:/data",
},
Logging: docker.StandardLogOptions,
Logging: docker.StandardLogOptions,
Environment: p.stack.EnvironmentVars,
},

VolumeNames: []string{"besu"},
Expand Down
5 changes: 5 additions & 0 deletions internal/blockchain/ethereum/connector/ethconnect/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package ethconnect
import (
"fmt"
"os"
"path/filepath"

"github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/connector"
"github.com/hyperledger/firefly-cli/pkg/types"
Expand Down Expand Up @@ -58,6 +59,10 @@ type HTTP struct {

func (e *Config) WriteConfig(filename string, extraConnectorConfigPath string) error {
configYamlBytes, _ := yaml.Marshal(e)
basedir := filepath.Dir(filename)
if err := os.MkdirAll(basedir, 0755); err != nil {
return err
}
if err := os.WriteFile(filename, configYamlBytes, 0755); err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion internal/blockchain/ethereum/connector/ethconnect/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func (e *Ethconnect) GetServiceDefinitions(s *types.Stack, dependentServices map
fmt.Sprintf("ethconnect_config_%s:/ethconnect/config", member.ID),
fmt.Sprintf("ethconnect_data_%s:/ethconnect/data", member.ID),
},
Logging: docker.StandardLogOptions,
Logging: docker.StandardLogOptions,
Environment: s.EnvironmentVars,
},
VolumeNames: []string{
fmt.Sprintf("ethconnect_config_%v", member.ID),
Expand Down
13 changes: 13 additions & 0 deletions internal/blockchain/ethereum/connector/ethconnect/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ func TestGetServiceDefinition(t *testing.T) {
},
ServiceName: "ethconnect_firefly_4",
},
{
Name: "test_env_vars_5",
Members: &types.Stack{
Members: []*types.Organization{{ID: "firefly_5", ExposedConnectorPort: 7892}},
VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry},
EnvironmentVars: map[string]interface{}{"HTTP_PROXY": ""},
},
DependentServices: map[string]string{
"service1": "running",
"service2": "stopped",
},
ServiceName: "ethconnect_firefly_5",
},
}
for _, tc := range testServices {
t.Run(tc.Name, func(t *testing.T) {
Expand Down
5 changes: 5 additions & 0 deletions internal/blockchain/ethereum/connector/evmconnect/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package evmconnect
import (
"fmt"
"os"
"path/filepath"

"github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/connector"
"github.com/hyperledger/firefly-cli/pkg/types"
Expand Down Expand Up @@ -75,6 +76,10 @@ type GasOracleConfig struct {

func (e *Config) WriteConfig(filename string, extraEvmconnectConfigPath string) error {
configYamlBytes, _ := yaml.Marshal(e)
basedir := filepath.Dir(filename)
if err := os.MkdirAll(basedir, 0755); err != nil {
return err
}
if err := os.WriteFile(filename, configYamlBytes, 0755); err != nil {
return err
}
Expand Down
3 changes: 2 additions & 1 deletion internal/blockchain/ethereum/connector/evmconnect/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ func (e *Evmconnect) GetServiceDefinitions(s *types.Stack, dependentServices map
fmt.Sprintf("%s/config/evmconnect_%s.yaml:/evmconnect/config.yaml", s.RuntimeDir, member.ID),
fmt.Sprintf("%s:/evmconnect/data", dataVolumeName),
},
Logging: docker.StandardLogOptions,
Logging: docker.StandardLogOptions,
Environment: s.EnvironmentVars,
},
VolumeNames: []string{
dataVolumeName,
Expand Down
20 changes: 19 additions & 1 deletion internal/blockchain/ethereum/connector/evmconnect/docker_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,19 @@ func TestGetServiceDefinition(t *testing.T) {
},
ServiceName: "evmconnect_firefly_4",
},
{
Name: "test_env_vars_5",
Members: &types.Stack{
Members: []*types.Organization{{ID: "firefly_5", ExposedConnectorPort: 7892}},
VersionManifest: &types.VersionManifest{Evmconnect: &getManifest.ManifestEntry},
EnvironmentVars: map[string]interface{}{"HTTP_PROXY": ""},
},
DependentServices: map[string]string{
"service1": "running",
"service2": "stopped",
},
ServiceName: "evmconnect_firefly_5",
},
}
for _, tc := range testServices {
t.Run(tc.Name, func(t *testing.T) {
Expand All @@ -90,7 +103,12 @@ func TestGetServiceDefinition(t *testing.T) {
if serviceDefinitions[0].ServiceName != tc.ServiceName {
t.Errorf("Expected ServiceName %q, got %q", tc.ServiceName, serviceDefinitions[0].ServiceName)
}

if len(tc.Members.EnvironmentVars) != len(serviceDefinitions[0].Service.Environment) {
t.Errorf("Expected EnvironmentVars %q, got %q", tc.Members.EnvironmentVars, serviceDefinitions[0].Service.Environment)
}
for k := range tc.Members.EnvironmentVars {
assert.Equal(t, tc.Members.EnvironmentVars[k], serviceDefinitions[0].Service.Environment[k])
}
})
}

Expand Down
5 changes: 3 additions & 2 deletions internal/blockchain/ethereum/ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ import (
)

type Account struct {
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
Address string `json:"address"`
PrivateKey string `json:"privateKey"`
PtmPublicKey string `json:"ptmPublicKey"` // Public key used for Tessera
}

func GenerateAddressAndPrivateKey() (address string, privateKey string) {
Expand Down
3 changes: 2 additions & 1 deletion internal/blockchain/ethereum/ethsigner/ethsigner.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ func (p *EthSignerProvider) GetDockerServiceDefinition(rpcURL string) *docker.Se
Interval: "15s", // 6000 requests in a day
Retries: 60,
},
Ports: []string{fmt.Sprintf("%d:8545", p.stack.ExposedBlockchainPort)},
Ports: []string{fmt.Sprintf("%d:8545", p.stack.ExposedBlockchainPort)},
Environment: p.stack.EnvironmentVars,
},
VolumeNames: []string{
"ethsigner",
Expand Down
1 change: 1 addition & 0 deletions internal/blockchain/ethereum/geth/geth_provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,7 @@ func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition
Volumes: []string{"geth:/data"},
Logging: docker.StandardLogOptions,
Ports: []string{fmt.Sprintf("%d:8545", p.stack.ExposedBlockchainPort)},
Environment: p.stack.EnvironmentVars,
},
VolumeNames: []string{"geth"},
}
Expand Down
93 changes: 93 additions & 0 deletions internal/blockchain/ethereum/quorum/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright © 2024 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 quorum

import (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
)

type QuorumClient struct {
rpcURL string
}

type JSONRPCRequest struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Method string `json:"method"`
Params []interface{} `json:"params"`
}

type JSONRPCResponse struct {
JSONRPC string `json:"jsonrpc"`
ID int `json:"id"`
Error *JSONRPCError `json:"error,omitempty"`
Result interface{} `json:"result,omitempty"`
}

type JSONRPCError struct {
Code int `json:"code"`
Message string `json:"message"`
}

func NewQuorumClient(rpcURL string) *QuorumClient {
return &QuorumClient{
rpcURL: rpcURL,
}
}

func (g *QuorumClient) UnlockAccount(address string, password string) error {
requestBody, err := json.Marshal(&JSONRPCRequest{
JSONRPC: "2.0",
ID: 0,
Method: "personal_unlockAccount",
Params: []interface{}{address, password, 0},
})
if err != nil {
return err
}
req, err := http.NewRequest("POST", g.rpcURL, bytes.NewBuffer(requestBody))
if err != nil {
return err
}
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
return err
}
defer resp.Body.Close()
responseBody, err := io.ReadAll(resp.Body)
if err != nil {
return err
}
if resp.StatusCode != 200 {
return fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody)
}
var rpcResponse *JSONRPCResponse
err = json.Unmarshal(responseBody, &rpcResponse)
if err != nil {
return err
}
if rpcResponse.Error != nil {
return fmt.Errorf(rpcResponse.Error.Message)
}
return nil
}
Loading
Loading