From c78b9260c2ade8020f75a067f414f357d017e2ff Mon Sep 17 00:00:00 2001 From: rodion Date: Mon, 1 Jul 2024 03:41:36 +0000 Subject: [PATCH] Fix PR review comments Signed-off-by: rodion --- cmd/init.go | 2 +- internal/blockchain/ethereum/ethereum.go | 2 +- internal/blockchain/ethereum/quorum/client.go | 8 +- .../blockchain/ethereum/quorum/genesis.go | 4 +- .../ethereum/quorum/genesis_test.go | 2 +- .../quorum/private_transaction_manager.go | 19 +- internal/blockchain/ethereum/quorum/quorum.go | 33 ++-- .../{geth_provider.go => quorum_provider.go} | 164 +++++++++--------- ...ovider_test.go => quorum_provider_test.go} | 46 ++--- internal/stacks/stack_manager.go | 4 +- 10 files changed, 146 insertions(+), 138 deletions(-) rename internal/blockchain/ethereum/quorum/{geth_provider.go => quorum_provider.go} (67%) rename internal/blockchain/ethereum/quorum/{geth_provider_test.go => quorum_provider_test.go} (91%) diff --git a/cmd/init.go b/cmd/init.go index 0e8e8dfb..e6084fcb 100644 --- a/cmd/init.go +++ b/cmd/init.go @@ -213,7 +213,7 @@ func validateQuorumConsensus(consensusString string) error { } if v != types.QuorumConsensusClique { - return errors.New("support for raft/ibft/qbft will come in future") + return errors.New("consensus algorithms such as raft/ibft/qbft for quorum not supported") } return nil diff --git a/internal/blockchain/ethereum/ethereum.go b/internal/blockchain/ethereum/ethereum.go index 6951d6b5..4e8de22c 100644 --- a/internal/blockchain/ethereum/ethereum.go +++ b/internal/blockchain/ethereum/ethereum.go @@ -34,7 +34,7 @@ import ( type Account struct { Address string `json:"address"` PrivateKey string `json:"privateKey"` - PtmPublicKey string `json:"ptmPublicKey"` + PtmPublicKey string `json:"ptmPublicKey"` // Public key used for Tessera } func GenerateAddressAndPrivateKey() (address string, privateKey string) { diff --git a/internal/blockchain/ethereum/quorum/client.go b/internal/blockchain/ethereum/quorum/client.go index e4c8c761..27d9dc69 100644 --- a/internal/blockchain/ethereum/quorum/client.go +++ b/internal/blockchain/ethereum/quorum/client.go @@ -24,7 +24,7 @@ import ( "net/http" ) -type GethClient struct { +type QuorumClient struct { rpcURL string } @@ -47,13 +47,13 @@ type JSONRPCError struct { Message string `json:"message"` } -func NewGethClient(rpcURL string) *GethClient { - return &GethClient{ +func NewQuorumClient(rpcURL string) *QuorumClient { + return &QuorumClient{ rpcURL: rpcURL, } } -func (g *GethClient) UnlockAccount(address string, password string) error { +func (g *QuorumClient) UnlockAccount(address string, password string) error { requestBody, err := json.Marshal(&JSONRPCRequest{ JSONRPC: "2.0", ID: 0, diff --git a/internal/blockchain/ethereum/quorum/genesis.go b/internal/blockchain/ethereum/quorum/genesis.go index 46ef6bb7..265b10f2 100644 --- a/internal/blockchain/ethereum/quorum/genesis.go +++ b/internal/blockchain/ethereum/quorum/genesis.go @@ -63,7 +63,7 @@ type Alloc struct { func CreateGenesis(addresses []string, blockPeriod int, chainID int64) *Genesis { if blockPeriod == -1 { - blockPeriod = 0 + blockPeriod = 5 } extraData := "0x0000000000000000000000000000000000000000000000000000000000000000" alloc := make(map[string]*Alloc) @@ -92,7 +92,7 @@ func CreateGenesis(addresses []string, blockPeriod int, chainID int64) *Genesis }, }, Nonce: "0x0", - Timestamp: "0x60edb1c7", + Timestamp: "0x0", ExtraData: extraData, GasLimit: "0xffffff", Difficulty: "0x1", diff --git a/internal/blockchain/ethereum/quorum/genesis_test.go b/internal/blockchain/ethereum/quorum/genesis_test.go index 7e47856e..2a396113 100644 --- a/internal/blockchain/ethereum/quorum/genesis_test.go +++ b/internal/blockchain/ethereum/quorum/genesis_test.go @@ -75,7 +75,7 @@ func TestCreateGenesis(t *testing.T) { }, }, Nonce: "0x0", - Timestamp: "0x60edb1c7", + Timestamp: "0x0", ExtraData: extraData, GasLimit: "0xffffff", Difficulty: "0x1", diff --git a/internal/blockchain/ethereum/quorum/private_transaction_manager.go b/internal/blockchain/ethereum/quorum/private_transaction_manager.go index eca97a1f..97676f93 100644 --- a/internal/blockchain/ethereum/quorum/private_transaction_manager.go +++ b/internal/blockchain/ethereum/quorum/private_transaction_manager.go @@ -22,17 +22,16 @@ import ( "fmt" "os" "path/filepath" - "strconv" "strings" "github.com/hyperledger/firefly-cli/internal/docker" ) -var entrypoint = "docker-entrypoint.sh" +var DockerEntrypoint = "docker-entrypoint.sh" var TmQ2tPort = "9101" var TmTpPort = "9080" var TmP2pPort = "9000" -var GethPort = "8545" +var QuorumPort = "8545" type PrivateKeyData struct { Bytes string `json:"bytes"` @@ -43,7 +42,7 @@ type PrivateKey struct { Data PrivateKeyData `json:"data"` } -func CreateTesseraKeys(ctx context.Context, image, outputDirectory, prefix, name, password string) (privateKey, pubKey, path string, err error) { +func CreateTesseraKeys(ctx context.Context, image, outputDirectory, prefix, name string) (privateKey, pubKey, path string, err error) { // generates both .pub and .key files used by Tessera var filename string if prefix != "" { @@ -73,14 +72,13 @@ func CreateTesseraKeys(ctx context.Context, image, outputDirectory, prefix, name if err != nil { return "", "", "", err } - return privateKeyData.Data.Bytes, string(pubKeyBytes[:]), path, nil + return privateKeyData.Data.Bytes, string(pubKeyBytes), path, nil } -func CreateTesseraEntrypoint(ctx context.Context, outputDirectory, volumeName, memberCount, stackName string) error { +func CreateTesseraEntrypoint(ctx context.Context, outputDirectory, stackName string, memberCount int) error { // only tessera v09 onwards is supported var sb strings.Builder - memberCountInt, _ := strconv.Atoi(memberCount) - for i := 0; i < memberCountInt; i++ { + for i := 0; i < memberCount; i++ { sb.WriteString(fmt.Sprintf("{\"url\":\"http://%s_member%dtessera:%s\"},", stackName, i, TmP2pPort)) // construct peer list } peerList := strings.TrimSuffix(sb.String(), ",") @@ -144,7 +142,7 @@ cat < ${DDIR}/tessera-config-09.json EOF /tessera/bin/tessera -configfile ${DDIR}/tessera-config-09.json `, TmTpPort, TmQ2tPort, TmP2pPort, peerList) - filename := filepath.Join(outputDirectory, entrypoint) + filename := filepath.Join(outputDirectory, DockerEntrypoint) file, err := os.OpenFile(filename, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755) if err != nil { return err @@ -154,7 +152,6 @@ EOF if err != nil { return err } - CopyTesseraEntrypointToVolume(ctx, outputDirectory, volumeName) return nil } @@ -162,7 +159,7 @@ func CopyTesseraEntrypointToVolume(ctx context.Context, tesseraEntrypointDirecto if err := docker.MkdirInVolume(ctx, volumeName, ""); err != nil { return err } - if err := docker.CopyFileToVolume(ctx, volumeName, filepath.Join(tesseraEntrypointDirectory, entrypoint), ""); err != nil { + if err := docker.CopyFileToVolume(ctx, volumeName, filepath.Join(tesseraEntrypointDirectory, DockerEntrypoint), ""); err != nil { return err } return nil diff --git a/internal/blockchain/ethereum/quorum/quorum.go b/internal/blockchain/ethereum/quorum/quorum.go index e77d4eed..8678b264 100644 --- a/internal/blockchain/ethereum/quorum/quorum.go +++ b/internal/blockchain/ethereum/quorum/quorum.go @@ -25,18 +25,18 @@ import ( "github.com/hyperledger/firefly-cli/internal/docker" ) -func CreateQuorumEntrypoint(ctx context.Context, outputDirectory, volumeName, memberIndex, consensus, stackName string, chainId int, tesseraEnabled bool) error { +func CreateQuorumEntrypoint(ctx context.Context, outputDirectory, volumeName, consensus, stackName string, memberIndex, chainID, blockPeriodInSeconds int, tesseraEnabled bool) error { discoveryCmd := "BOOTNODE_CMD=\"\"" connectTimeout := 15 - if memberIndex != "0" { - discoveryCmd = fmt.Sprintf(`bootnode=$(curl http://geth_0:%s -s --connect-timeout %[2]d --max-time %[2]d --retry 5 --retry-connrefused --retry-delay 0 --retry-max-time 60 --fail --header "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "admin_nodeInfo", "params": [], "id": 1}' | grep -o "enode://[a-z0-9@.:]*") + if memberIndex != 0 { + discoveryCmd = fmt.Sprintf(`bootnode=$(curl http://quorum_0:%s -s --connect-timeout %[2]d --max-time %[2]d --retry 5 --retry-connrefused --retry-delay 0 --retry-max-time 60 --fail --header "Content-Type: application/json" --data '{"jsonrpc":"2.0", "method": "admin_nodeInfo", "params": [], "id": 1}' | grep -o "enode://[a-z0-9@.:]*") BOOTNODE_CMD="--bootnodes $bootnode" -BOOTNODE_CMD=${BOOTNODE_CMD/127.0.0.1/geth_0}`, GethPort, connectTimeout) +BOOTNODE_CMD=${BOOTNODE_CMD/127.0.0.1/quorum_0}`, QuorumPort, connectTimeout) } tesseraCmd := "" if tesseraEnabled { - tesseraCmd = fmt.Sprintf(`TESSERA_URL=http://%[5]s_member%[1]stessera + tesseraCmd = fmt.Sprintf(`TESSERA_URL=http://%[5]s_member%[1]dtessera TESSERA_TP_PORT=%[2]s TESSERA_Q2T_PORT=%[3]s TESSERA_UPCHECK_URL=$TESSERA_URL:$TESSERA_TP_PORT/upcheck @@ -48,6 +48,12 @@ echo "" `, memberIndex, TmTpPort, TmQ2tPort, connectTimeout, stackName) } + blockPeriod := blockPeriodInSeconds + if blockPeriodInSeconds == -1 { + blockPeriod = 5 + } + blockPeriodInMs := blockPeriod * 60 + content := fmt.Sprintf(`#!/bin/sh set -o errexit @@ -55,11 +61,11 @@ set -o nounset set -o pipefail set -o xtrace -GOQUORUM_CONS_ALGO=%s +GOQUORUM_CONS_ALGO=%[1]s if [ "istanbul" == "$GOQUORUM_CONS_ALGO" ]; then echo "Using istanbul for consensus algorithm..." - export CONSENSUS_ARGS="--istanbul.blockperiod 5 --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints" + export CONSENSUS_ARGS="--istanbul.blockperiod %[6]d --mine --miner.threads 1 --miner.gasprice 0 --emitcheckpoints" export QUORUM_API="istanbul" elif [ "qbft" == "$GOQUORUM_CONS_ALGO" ]; then @@ -69,7 +75,7 @@ then elif [ "raft" == "$GOQUORUM_CONS_ALGO" ]; then echo "Using raft for consensus algorithm..." - export CONSENSUS_ARGS="--raft --raftblocktime 300 --raftport 53000" + export CONSENSUS_ARGS="--raft --raftblocktime %[7]d --raftport 53000" export QUORUM_API="raft" elif [ "clique" == "$GOQUORUM_CONS_ALGO" ]; then @@ -79,15 +85,15 @@ then fi ADDITIONAL_ARGS=${ADDITIONAL_ARGS:-} -%s +%[2]s # discovery -%s +%[3]s echo "bootnode discovery command :: $BOOTNODE_CMD" IP_ADDR=$(cat /etc/hosts | tail -n 1 | awk '{print $1}') -exec geth --datadir /data --nat extip:$IP_ADDR --syncmode 'full' --revertreason --port 30311 --http --http.addr "0.0.0.0" --http.corsdomain="*" -http.port %s --http.vhosts "*" --http.api admin,personal,eth,net,web3,txpool,miner,debug,$QUORUM_API --networkid %d --miner.gasprice 0 --password /data/password --mine --allow-insecure-unlock --verbosity 4 $CONSENSUS_ARGS --miner.gaslimit 16777215 $BOOTNODE_CMD $ADDITIONAL_ARGS`, consensus, tesseraCmd, discoveryCmd, GethPort, chainId) - filename := filepath.Join(outputDirectory, entrypoint) +exec geth --datadir /data --nat extip:$IP_ADDR --syncmode 'full' --revertreason --port 30311 --http --http.addr "0.0.0.0" --http.corsdomain="*" -http.port %[4]s --http.vhosts "*" --http.api admin,personal,eth,net,web3,txpool,miner,debug,$QUORUM_API --networkid %[5]d --miner.gasprice 0 --password /data/password --mine --allow-insecure-unlock --verbosity 4 $CONSENSUS_ARGS --miner.gaslimit 16777215 $BOOTNODE_CMD $ADDITIONAL_ARGS`, consensus, tesseraCmd, discoveryCmd, QuorumPort, chainID, blockPeriod, blockPeriodInMs) + filename := filepath.Join(outputDirectory, DockerEntrypoint) if err := os.MkdirAll(outputDirectory, 0755); err != nil { return err } @@ -100,12 +106,11 @@ exec geth --datadir /data --nat extip:$IP_ADDR --syncmode 'full' --revertreason if err != nil { return err } - CopyQuorumEntrypointToVolume(ctx, outputDirectory, volumeName) return nil } func CopyQuorumEntrypointToVolume(ctx context.Context, quorumEntrypointDirectory, volumeName string) error { - if err := docker.CopyFileToVolume(ctx, volumeName, filepath.Join(quorumEntrypointDirectory, entrypoint), ""); err != nil { + if err := docker.CopyFileToVolume(ctx, volumeName, filepath.Join(quorumEntrypointDirectory, DockerEntrypoint), ""); err != nil { return err } return nil diff --git a/internal/blockchain/ethereum/quorum/geth_provider.go b/internal/blockchain/ethereum/quorum/quorum_provider.go similarity index 67% rename from internal/blockchain/ethereum/quorum/geth_provider.go rename to internal/blockchain/ethereum/quorum/quorum_provider.go index 00bf4bad..6df85af2 100644 --- a/internal/blockchain/ethereum/quorum/geth_provider.go +++ b/internal/blockchain/ethereum/quorum/quorum_provider.go @@ -36,20 +36,20 @@ import ( "github.com/hyperledger/firefly-cli/pkg/types" ) -var gethImage = "quorumengineering/quorum:24.4" +var quorumImage = "quorumengineering/quorum:24.4" var tesseraImage = "quorumengineering/tessera:24.4" var exposedBlockchainPortMultiplier = 10 // TODO: Probably randomize this and make it different per member? var keyPassword = "correcthorsebatterystaple" -type GethProvider struct { +type QuorumProvider struct { ctx context.Context stack *types.Stack connector connector.Connector } -func NewGethProvider(ctx context.Context, stack *types.Stack) *GethProvider { +func NewQuorumProvider(ctx context.Context, stack *types.Stack) *QuorumProvider { var connector connector.Connector switch stack.BlockchainConnector { case types.BlockchainConnectorEthconnect: @@ -58,28 +58,44 @@ func NewGethProvider(ctx context.Context, stack *types.Stack) *GethProvider { connector = evmconnect.NewEvmconnect(ctx) } - return &GethProvider{ + return &QuorumProvider{ ctx: ctx, stack: stack, connector: connector, } } -func (p *GethProvider) WriteConfig(options *types.InitOptions) error { +func (p *QuorumProvider) WriteConfig(options *types.InitOptions) error { + l := log.LoggerFromContext(p.ctx) initDir := filepath.Join(constants.StacksDir, p.stack.Name, "init") for i, member := range p.stack.Members { // Generate the connector config for each member connectorConfigPath := filepath.Join(initDir, "config", fmt.Sprintf("%s_%v.yaml", p.connector.Name(), i)) - if err := p.connector.GenerateConfig(p.stack, member, fmt.Sprintf("geth_%d", i)).WriteConfig(connectorConfigPath, options.ExtraConnectorConfigPath); err != nil { + if err := p.connector.GenerateConfig(p.stack, member, fmt.Sprintf("quorum_%d", i)).WriteConfig(connectorConfigPath, options.ExtraConnectorConfigPath); err != nil { return nil } + + // Generate tessera docker-entrypoint for each member + l.Info(fmt.Sprintf("generating tessera docker-entrypoint file for member %d", i)) + tesseraEntrypointOutputDirectory := filepath.Join(initDir, "tessera", fmt.Sprintf("tessera_%d", i)) + if err := CreateTesseraEntrypoint(p.ctx, tesseraEntrypointOutputDirectory, p.stack.Name, len(p.stack.Members)); err != nil { + return err + } + + // Generate quorum docker-entrypoint for each member + l.Info(fmt.Sprintf("generating quorum docker-entrypoint file for member %d", i)) + quorumEntrypointOutputDirectory := filepath.Join(initDir, "blockchain", fmt.Sprintf("quorum_%d", i)) + quorumVolumeName := fmt.Sprintf("%s_quorum_%d", p.stack.Name, i) + if err := CreateQuorumEntrypoint(p.ctx, quorumEntrypointOutputDirectory, quorumVolumeName, p.stack.QuorumConsensus.String(), p.stack.Name, i, int(p.stack.ChainID()), options.BlockPeriod, p.stack.TesseraEnabled); err != nil { + return err + } } // 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 + // Drop the 0x on the front of the address here because that's what quorum is expecting in the genesis.json addresses[i] = address[2:] } genesis := CreateGenesis(addresses, options.BlockPeriod, p.stack.ChainID()) @@ -90,13 +106,13 @@ func (p *GethProvider) WriteConfig(options *types.InitOptions) error { return nil } -func (p *GethProvider) FirstTimeSetup() error { - gethVolumeName := fmt.Sprintf("%s_geth", p.stack.Name) +func (p *QuorumProvider) FirstTimeSetup() error { + quorumVolumeName := fmt.Sprintf("%s_quorum", p.stack.Name) tesseraVolumeName := fmt.Sprintf("%s_tessera", p.stack.Name) blockchainDir := path.Join(p.stack.RuntimeDir, "blockchain") tesseraDir := path.Join(p.stack.RuntimeDir, "tessera") - tesseraDirWithinContainer := "/" contractsDir := path.Join(p.stack.RuntimeDir, "contracts") + rootDir := "/" if err := p.connector.FirstTimeSetup(p.stack); err != nil { return err @@ -113,49 +129,58 @@ func (p *GethProvider) FirstTimeSetup() error { if err := docker.CopyFileToVolume(p.ctx, connectorConfigVolumeName, connectorConfigPath, "config.yaml"); err != nil { return err } - } - for i := range p.stack.Members { - gethVolumeNameMember := fmt.Sprintf("%s_%d", gethVolumeName, i) + // Volume name instantiation + quorumVolumeNameMember := fmt.Sprintf("%s_%d", quorumVolumeName, i) tesseraVolumeNameMember := fmt.Sprintf("%s_%d", tesseraVolumeName, i) // Copy the wallet files of each member to their respective blockchain volume - keystoreDirectory := filepath.Join(blockchainDir, fmt.Sprintf("geth_%d", i), "keystore") - if err := docker.CopyFileToVolume(p.ctx, gethVolumeNameMember, keystoreDirectory, "/"); err != nil { + keystoreDirectory := filepath.Join(blockchainDir, fmt.Sprintf("quorum_%d", i), "keystore") + if err := docker.CopyFileToVolume(p.ctx, quorumVolumeNameMember, keystoreDirectory, "/"); err != nil { return err } if p.stack.TesseraEnabled { - // Copy member specific tessera key files to each of the tessera volume - if err := docker.MkdirInVolume(p.ctx, tesseraVolumeNameMember, tesseraDirWithinContainer); err != nil { + // Copy member specific tessera key files + if err := docker.MkdirInVolume(p.ctx, tesseraVolumeNameMember, rootDir); err != nil { + return err + } + tmKeystoreDirectory := filepath.Join(tesseraDir, fmt.Sprintf("tessera_%d", i), "keystore") + if err := docker.CopyFileToVolume(p.ctx, tesseraVolumeNameMember, tmKeystoreDirectory, rootDir); err != nil { return err } - tmDirectory := filepath.Join(tesseraDir, fmt.Sprintf("tessera_%d", i), "keystore") - if err := docker.CopyFileToVolume(p.ctx, tesseraVolumeNameMember, tmDirectory, tesseraDirWithinContainer); err != nil { + // Copy tessera docker-entrypoint file + tmEntrypointPath := filepath.Join(tesseraDir, fmt.Sprintf("tessera_%d", i), DockerEntrypoint) + if err := docker.CopyFileToVolume(p.ctx, tesseraVolumeNameMember, tmEntrypointPath, rootDir); err != nil { return err } } + // Copy quorum docker-entrypoint file + quorumEntrypointPath := filepath.Join(blockchainDir, fmt.Sprintf("quorum_%d", i), DockerEntrypoint) + if err := docker.CopyFileToVolume(p.ctx, quorumVolumeNameMember, quorumEntrypointPath, rootDir); err != nil { + return err + } + // Copy the genesis block information - if err := docker.CopyFileToVolume(p.ctx, gethVolumeNameMember, path.Join(blockchainDir, "genesis.json"), "genesis.json"); err != nil { + if err := docker.CopyFileToVolume(p.ctx, quorumVolumeNameMember, path.Join(blockchainDir, "genesis.json"), "genesis.json"); err != nil { return err } // Initialize the genesis block - if err := docker.RunDockerCommand(p.ctx, p.stack.StackDir, "run", "--rm", "-v", fmt.Sprintf("%s:/data", gethVolumeNameMember), gethImage, "--datadir", "/data", "init", "/data/genesis.json"); err != nil { + if err := docker.RunDockerCommand(p.ctx, p.stack.StackDir, "run", "--rm", "-v", fmt.Sprintf("%s:/data", quorumVolumeNameMember), quorumImage, "--datadir", "/data", "init", "/data/genesis.json"); err != nil { return err } - } return nil } -func (p *GethProvider) PreStart() error { +func (p *QuorumProvider) PreStart() error { return nil } -func (p *GethProvider) PostStart(firstTimeSetup bool) error { +func (p *QuorumProvider) PostStart(firstTimeSetup bool) error { l := log.LoggerFromContext(p.ctx) // Unlock accounts for _, account := range p.stack.State.Accounts { @@ -176,14 +201,14 @@ func (p *GethProvider) PostStart(firstTimeSetup bool) error { return nil } -func (p *GethProvider) unlockAccount(address, password string, memberIndex int) error { +func (p *QuorumProvider) unlockAccount(address, password string, memberIndex int) error { l := log.LoggerFromContext(p.ctx) verbose := log.VerbosityFromContext(p.ctx) // exposed blockchain port is the default for node 0, we need to add the port multiplier to get the right rpc for the correct node - gethClient := NewGethClient(fmt.Sprintf("http://127.0.0.1:%v", p.stack.ExposedBlockchainPort+(memberIndex*exposedBlockchainPortMultiplier))) + quorumClient := NewQuorumClient(fmt.Sprintf("http://127.0.0.1:%v", p.stack.ExposedBlockchainPort+(memberIndex*exposedBlockchainPortMultiplier))) retries := 10 for { - if err := gethClient.UnlockAccount(address, password); err != nil { + if err := quorumClient.UnlockAccount(address, password); err != nil { if verbose { l.Debug(err.Error()) } @@ -199,7 +224,7 @@ func (p *GethProvider) unlockAccount(address, password string, memberIndex int) return nil } -func (p *GethProvider) DeployFireFlyContract() (*types.ContractDeploymentResult, error) { +func (p *QuorumProvider) DeployFireFlyContract() (*types.ContractDeploymentResult, error) { contract, err := ethereum.ReadFireFlyContract(p.ctx, p.stack) if err != nil { return nil, err @@ -207,7 +232,7 @@ func (p *GethProvider) DeployFireFlyContract() (*types.ContractDeploymentResult, return p.connector.DeployContract(contract, "FireFly", p.stack.Members[0], nil) } -func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { +func (p *QuorumProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition { memberCount := len(p.stack.Members) serviceDefinitionsCount := memberCount if p.stack.TesseraEnabled { @@ -216,27 +241,9 @@ func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition serviceDefinitions := make([]*docker.ServiceDefinition, serviceDefinitionsCount) connectorDependents := map[string]string{} for i := 0; i < memberCount; i++ { - var dependsOn map[string]map[string]string - if p.stack.TesseraEnabled { - dependsOn = map[string]map[string]string{fmt.Sprintf("tessera_%d", i): {"condition": "service_started"}} - } - serviceDefinitions[i] = &docker.ServiceDefinition{ - ServiceName: fmt.Sprintf("geth_%d", i), - Service: &docker.Service{ - Image: gethImage, - ContainerName: fmt.Sprintf("%s_geth_%d", p.stack.Name, i), - Volumes: []string{fmt.Sprintf("geth_%d:/data", i)}, - Logging: docker.StandardLogOptions, - Ports: []string{fmt.Sprintf("%d:8545", p.stack.ExposedBlockchainPort+(i*exposedBlockchainPortMultiplier))}, // defaults 5100, 5110, 5120, 5130 - Environment: p.stack.EnvironmentVars, - EntryPoint: []string{"/bin/sh", "-c", "/data/docker-entrypoint.sh"}, - DependsOn: dependsOn, - }, - VolumeNames: []string{fmt.Sprintf("geth_%d", i)}, - } - connectorDependents[fmt.Sprintf("geth_%d", i)] = "service_started" - + var quorumDependsOn map[string]map[string]string if p.stack.TesseraEnabled { + quorumDependsOn = map[string]map[string]string{fmt.Sprintf("tessera_%d", i): {"condition": "service_started"}} serviceDefinitions[i+memberCount] = &docker.ServiceDefinition{ ServiceName: fmt.Sprintf("tessera_%d", i), Service: &docker.Service{ @@ -252,12 +259,27 @@ func (p *GethProvider) GetDockerServiceDefinitions() []*docker.ServiceDefinition VolumeNames: []string{fmt.Sprintf("tessera_%d", i)}, } } + serviceDefinitions[i] = &docker.ServiceDefinition{ + ServiceName: fmt.Sprintf("quorum_%d", i), + Service: &docker.Service{ + Image: quorumImage, + ContainerName: fmt.Sprintf("%s_quorum_%d", p.stack.Name, i), + Volumes: []string{fmt.Sprintf("quorum_%d:/data", i)}, + Logging: docker.StandardLogOptions, + Ports: []string{fmt.Sprintf("%d:8545", p.stack.ExposedBlockchainPort+(i*exposedBlockchainPortMultiplier))}, // defaults 5100, 5110, 5120, 5130 + Environment: p.stack.EnvironmentVars, + EntryPoint: []string{"/bin/sh", "-c", "/data/docker-entrypoint.sh"}, + DependsOn: quorumDependsOn, + }, + VolumeNames: []string{fmt.Sprintf("quorum_%d", i)}, + } + connectorDependents[fmt.Sprintf("quorum_%d", i)] = "service_started" } serviceDefinitions = append(serviceDefinitions, p.connector.GetServiceDefinitions(p.stack, connectorDependents)...) return serviceDefinitions } -func (p *GethProvider) GetBlockchainPluginConfig(stack *types.Stack, m *types.Organization) (blockchainConfig *types.BlockchainConfig) { +func (p *QuorumProvider) GetBlockchainPluginConfig(stack *types.Stack, m *types.Organization) (blockchainConfig *types.BlockchainConfig) { var connectorURL string if m.External { connectorURL = p.GetConnectorExternalURL(m) @@ -277,7 +299,7 @@ func (p *GethProvider) GetBlockchainPluginConfig(stack *types.Stack, m *types.Or return } -func (p *GethProvider) GetOrgConfig(stack *types.Stack, m *types.Organization) (orgConfig *types.OrgConfig) { +func (p *QuorumProvider) GetOrgConfig(stack *types.Stack, m *types.Organization) (orgConfig *types.OrgConfig) { account := m.Account.(*ethereum.Account) orgConfig = &types.OrgConfig{ Name: m.OrgName, @@ -286,11 +308,11 @@ func (p *GethProvider) GetOrgConfig(stack *types.Stack, m *types.Organization) ( return } -func (p *GethProvider) Reset() error { +func (p *QuorumProvider) Reset() error { return nil } -func (p *GethProvider) GetContracts(filename string, extraArgs []string) ([]string, error) { +func (p *QuorumProvider) GetContracts(filename string, extraArgs []string) ([]string, error) { contracts, err := ethereum.ReadContractJSON(filename) if err != nil { return []string{}, err @@ -304,7 +326,7 @@ func (p *GethProvider) GetContracts(filename string, extraArgs []string) ([]stri return contractNames, err } -func (p *GethProvider) DeployContract(filename, contractName, instanceName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) { +func (p *QuorumProvider) DeployContract(filename, contractName, instanceName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) { contracts, err := ethereum.ReadContractJSON(filename) if err != nil { return nil, err @@ -312,11 +334,10 @@ func (p *GethProvider) DeployContract(filename, contractName, instanceName strin return p.connector.DeployContract(contracts.Contracts[contractName], instanceName, member, extraArgs) } -func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { +func (p *QuorumProvider) CreateAccount(args []string) (interface{}, error) { l := log.LoggerFromContext(p.ctx) memberIndex := args[2] - memberCount := args[3] - gethVolumeName := fmt.Sprintf("%s_geth_%s", p.stack.Name, memberIndex) + quorumVolumeName := fmt.Sprintf("%s_quorum_%s", p.stack.Name, memberIndex) var directory string stackHasRunBefore, err := p.stack.HasRunBefore() if err != nil { @@ -329,7 +350,7 @@ func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { } prefix := strconv.FormatInt(time.Now().UnixNano(), 10) - outputDirectory := filepath.Join(directory, "blockchain", fmt.Sprintf("geth_%s", memberIndex), "keystore") + outputDirectory := filepath.Join(directory, "blockchain", fmt.Sprintf("quorum_%s", memberIndex), "keystore") keyPair, walletFilePath, err := ethereum.CreateWalletFile(outputDirectory, prefix, keyPassword) if err != nil { return nil, err @@ -338,28 +359,16 @@ func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { // Tessera is an optional add-on to the quorum blockchain node provider var tesseraPubKey, tesseraKeysPath string if p.stack.TesseraEnabled { - tesseraVolumeName := fmt.Sprintf("%s_tessera_%s", p.stack.Name, memberIndex) tesseraKeysOutputDirectory := filepath.Join(directory, "tessera", fmt.Sprintf("tessera_%s", memberIndex), "keystore") - _, tesseraPubKey, tesseraKeysPath, err = CreateTesseraKeys(p.ctx, tesseraImage, tesseraKeysOutputDirectory, "", "tm", keyPassword) + _, tesseraPubKey, tesseraKeysPath, err = CreateTesseraKeys(p.ctx, tesseraImage, tesseraKeysOutputDirectory, "", "tm") if err != nil { return nil, err } l.Info(fmt.Sprintf("keys generated in %s", tesseraKeysPath)) - l.Info(fmt.Sprintf("generating tessera docker-entrypoint file for member %s", memberIndex)) - tesseraEntrypointOutputDirectory := filepath.Join(directory, "tessera", fmt.Sprintf("tessera_%s", memberIndex)) - if err := CreateTesseraEntrypoint(p.ctx, tesseraEntrypointOutputDirectory, tesseraVolumeName, memberCount, p.stack.Name); err != nil { - return nil, err - } - } - - l.Info(fmt.Sprintf("generating quorum docker-entrypoint file for member %s", memberIndex)) - quorumEntrypointOutputDirectory := filepath.Join(directory, "blockchain", fmt.Sprintf("geth_%s", memberIndex)) - if err := CreateQuorumEntrypoint(p.ctx, quorumEntrypointOutputDirectory, gethVolumeName, memberIndex, p.stack.QuorumConsensus.String(), p.stack.Name, int(p.stack.ChainID()), p.stack.TesseraEnabled); err != nil { - return nil, err } if stackHasRunBefore { - if err := ethereum.CopyWalletFileToVolume(p.ctx, walletFilePath, gethVolumeName); err != nil { + if err := ethereum.CopyWalletFileToVolume(p.ctx, walletFilePath, quorumVolumeName); err != nil { return nil, err } if memberIndexInt, err := strconv.Atoi(memberIndex); err != nil { @@ -369,9 +378,6 @@ func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { return nil, err } } - if err := CopyQuorumEntrypointToVolume(p.ctx, quorumEntrypointOutputDirectory, gethVolumeName); err != nil { - return nil, err - } } return ðereum.Account{ @@ -381,7 +387,7 @@ func (p *GethProvider) CreateAccount(args []string) (interface{}, error) { }, nil } -func (p *GethProvider) ParseAccount(account interface{}) interface{} { +func (p *QuorumProvider) ParseAccount(account interface{}) interface{} { accountMap := account.(map[string]interface{}) ptmPublicKey := "" // if we start quorum without tessera, no public key will be generated v, ok := accountMap["ptmPublicKey"] @@ -396,14 +402,14 @@ func (p *GethProvider) ParseAccount(account interface{}) interface{} { } } -func (p *GethProvider) GetConnectorName() string { +func (p *QuorumProvider) GetConnectorName() string { return p.connector.Name() } -func (p *GethProvider) GetConnectorURL(org *types.Organization) string { +func (p *QuorumProvider) GetConnectorURL(org *types.Organization) string { return fmt.Sprintf("http://%s_%s:%v", p.connector.Name(), org.ID, p.connector.Port()) } -func (p *GethProvider) GetConnectorExternalURL(org *types.Organization) string { +func (p *QuorumProvider) GetConnectorExternalURL(org *types.Organization) string { return fmt.Sprintf("http://127.0.0.1:%v", org.ExposedConnectorPort) } diff --git a/internal/blockchain/ethereum/quorum/geth_provider_test.go b/internal/blockchain/ethereum/quorum/quorum_provider_test.go similarity index 91% rename from internal/blockchain/ethereum/quorum/geth_provider_test.go rename to internal/blockchain/ethereum/quorum/quorum_provider_test.go index 136d151d..34282829 100644 --- a/internal/blockchain/ethereum/quorum/geth_provider_test.go +++ b/internal/blockchain/ethereum/quorum/quorum_provider_test.go @@ -15,7 +15,7 @@ import ( "github.com/stretchr/testify/assert" ) -func TestNewGethProvider(t *testing.T) { +func TestNewQuorumProvider(t *testing.T) { var ctx context.Context testcases := []struct { @@ -27,11 +27,11 @@ func TestNewGethProvider(t *testing.T) { Name: "testcase1", Ctx: ctx, Stack: &types.Stack{ - Name: "TestGethWithEvmConnect", + Name: "TestQuorumWithEvmConnect", Members: []*types.Organization{ { OrgName: "Org1", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1234567890abcdef0123456789abcdef6789abcd", PrivateKey: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", @@ -39,7 +39,7 @@ func TestNewGethProvider(t *testing.T) { }, { OrgName: "Org2", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1234567890abcdef012345670000000000000000", PrivateKey: "9876543210987654321098765432109876543210987654321098765432109876", @@ -55,11 +55,11 @@ func TestNewGethProvider(t *testing.T) { Name: "testcase2", Ctx: ctx, Stack: &types.Stack{ - Name: "TestGethWithEthConnect", + Name: "TestQuorumWithEthConnect", Members: []*types.Organization{ { OrgName: "Org55", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1f2a000000000000000000000000000000000000", PrivateKey: "aabbccddeeff0011223344556677889900112233445566778899aabbccddeeff", @@ -74,8 +74,8 @@ func TestNewGethProvider(t *testing.T) { } for _, tc := range testcases { t.Run(tc.Name, func(t *testing.T) { - gethProvider := NewGethProvider(tc.Ctx, tc.Stack) - assert.NotNil(t, gethProvider) + quorumProvider := NewQuorumProvider(tc.Ctx, tc.Stack) + assert.NotNil(t, quorumProvider) }) } } @@ -126,8 +126,8 @@ func TestParseAccount(t *testing.T) { } for _, tc := range testcases { t.Run(tc.Name, func(t *testing.T) { - gethProvider := &GethProvider{} - result := gethProvider.ParseAccount(tc.Address) + quorumProvider := &QuorumProvider{} + result := quorumProvider.ParseAccount(tc.Address) _, ok := result.(*ethereum.Account) if !ok { @@ -152,7 +152,7 @@ func TestGetOrgConfig(t *testing.T) { }, Org: &types.Organization{ OrgName: "Org-1", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1234567890abcdef0123456789abcdef6789abcd", PrivateKey: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", @@ -170,7 +170,7 @@ func TestGetOrgConfig(t *testing.T) { }, Org: &types.Organization{ OrgName: "Org-2", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1f2a000000000000000000000000000000000000", PrivateKey: "9876543210987654321098765432109876543210987654321098765432109876", @@ -188,7 +188,7 @@ func TestGetOrgConfig(t *testing.T) { }, Org: &types.Organization{ OrgName: "Org-3", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0xabcdeffedcba9876543210abcdeffedc00000000", PrivateKey: "aabbccddeeff0011223344556677889900112233445566778899aabbccddeeff", @@ -202,7 +202,7 @@ func TestGetOrgConfig(t *testing.T) { } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { - p := &GethProvider{} + p := &QuorumProvider{} Orgconfig := p.GetOrgConfig(tc.Stack, tc.Org) assert.NotNil(t, Orgconfig) @@ -229,7 +229,7 @@ func TestGetContracts(t *testing.T) { } } }` - p := &GethProvider{} + p := &QuorumProvider{} err := os.WriteFile(testContractFile, []byte(testContractJSON), 0755) if err != nil { @@ -253,7 +253,7 @@ func TestGetConnectorExternal(t *testing.T) { Name: "testcase1", Org: &types.Organization{ OrgName: "Org-1", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0x1f2a000000000000000000000000000000000000", PrivateKey: "9876543210987654321098765432109876543210987654321098765432109876", @@ -266,7 +266,7 @@ func TestGetConnectorExternal(t *testing.T) { Name: "testcase2", Org: &types.Organization{ OrgName: "Org-2", - NodeName: "geth", + NodeName: "quorum", Account: ðereum.Account{ Address: "0xabcdeffedcba9876543210abcdeffedc00000000", PrivateKey: "aabbccddeeff0011223344556677889900112233445566778899aabbccddeeff", @@ -277,7 +277,7 @@ func TestGetConnectorExternal(t *testing.T) { }, } for _, tc := range testcase { - p := &GethProvider{} + p := &QuorumProvider{} result := p.GetConnectorExternalURL(tc.Org) assert.Equal(t, tc.ExpectedPort, result) } @@ -295,32 +295,32 @@ func TestCreateAccount(t *testing.T) { Name: "testcase1", Ctx: ctx, Stack: &types.Stack{ - Name: "Org-1_geth", + Name: "Org-1_quorum", BlockchainProvider: fftypes.FFEnumValue("BlockchainProvider", "Ethereum"), BlockchainConnector: fftypes.FFEnumValue("BlockChainConnector", "Ethconnect"), BlockchainNodeProvider: fftypes.FFEnumValue("BlockchainNodeProvider", "quorum"), InitDir: t.TempDir(), RuntimeDir: t.TempDir(), }, - Args: []string{"Org-1_geth", "Org-1_geth", "0", "1"}, + Args: []string{"Org-1_quorum", "Org-1_quorum", "0", "1"}, }, { Name: "testcase1", Ctx: ctx, Stack: &types.Stack{ - Name: "Org-2_geth", + Name: "Org-2_quorum", BlockchainProvider: fftypes.FFEnumValue("BlockchainProvider", "Ethereum"), BlockchainConnector: fftypes.FFEnumValue("BlockChainConnector", "Ethconnect"), BlockchainNodeProvider: fftypes.FFEnumValue("BlockchainNodeProvider", "quorum"), InitDir: t.TempDir(), RuntimeDir: t.TempDir(), }, - Args: []string{"Org-2_geth", "Org-2_geth", "1", "2"}, + Args: []string{"Org-2_quorum", "Org-2_quorum", "1", "2"}, }, } for _, tc := range testcases { t.Run(tc.Name, func(t *testing.T) { - p := NewGethProvider(tc.Ctx, tc.Stack) + p := NewQuorumProvider(tc.Ctx, tc.Stack) Account, err := p.CreateAccount(tc.Args) if err != nil { t.Log("unable to create account", err) diff --git a/internal/stacks/stack_manager.go b/internal/stacks/stack_manager.go index 3b82b0dd..54faff79 100644 --- a/internal/stacks/stack_manager.go +++ b/internal/stacks/stack_manager.go @@ -591,7 +591,7 @@ func (s *StackManager) createMember(id string, index int, options *types.InitOpt nextPort++ } - account, err := s.blockchainProvider.CreateAccount([]string{member.OrgName, member.OrgName, strconv.Itoa(index), strconv.Itoa(options.MemberCount)}) + account, err := s.blockchainProvider.CreateAccount([]string{member.OrgName, member.OrgName, strconv.Itoa(index)}) if err != nil { return nil, err } @@ -1334,7 +1334,7 @@ func (s *StackManager) getBlockchainProvider() blockchain.IBlockchainProvider { case types.BlockchainNodeProviderBesu: return besu.NewBesuProvider(s.ctx, s.Stack) case types.BlockchainNodeProviderQuorum: - return quorum.NewGethProvider(s.ctx, s.Stack) + return quorum.NewQuorumProvider(s.ctx, s.Stack) case types.BlockchainNodeProviderRemoteRPC: s.Stack.DisableTokenFactories = true return ethremoterpc.NewRemoteRPCProvider(s.ctx, s.Stack)