├── .github └── workflows │ ├── build.yml │ ├── e2e.yml │ └── release.yml ├── .gitignore ├── .golangci.yml ├── .goreleaser.yml ├── .vscode └── launch.json ├── CHANGELOG.md ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── SECURITY.md ├── cmd ├── accounts.go ├── accounts_create.go ├── accounts_create_test.go ├── accounts_list.go ├── accounts_list_test.go ├── deploy.go ├── deploy_ethereum.go ├── deploy_ethereum_test.go ├── deploy_fabric.go ├── deploy_fabric_test.go ├── docs.go ├── info.go ├── info_test.go ├── init.go ├── init_cardano.go ├── init_ethereum.go ├── init_fabric.go ├── init_tezos.go ├── list.go ├── logs.go ├── prompt.go ├── ps.go ├── pull.go ├── remove.go ├── reset.go ├── root.go ├── start.go ├── stop.go ├── upgrade.go └── version.go ├── docs ├── allow_ff.png ├── blocked.png ├── firefly_screenshot.png ├── mac_help.md ├── open.png └── system_preferences.png ├── ff └── main.go ├── go.mod ├── go.sum ├── internal ├── blockchain │ ├── blockchain_provider.go │ ├── cardano │ │ ├── cardano.go │ │ ├── cardanosigner │ │ │ ├── cardanosigner.go │ │ │ └── config.go │ │ ├── connector │ │ │ ├── cardanoconnect │ │ │ │ ├── client.go │ │ │ │ ├── config.go │ │ │ │ └── docker.go │ │ │ └── connector_interface.go │ │ └── remoterpc │ │ │ └── remoterpc_provider.go │ ├── ethereum │ │ ├── accounts.go │ │ ├── accounts_test.go │ │ ├── besu │ │ │ ├── besu_provider.go │ │ │ ├── besu_provider_test.go │ │ │ ├── genesis.go │ │ │ └── genesis_test.go │ │ ├── connector │ │ │ ├── connector_interface.go │ │ │ ├── ethconnect │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── docker.go │ │ │ │ └── docker_test.go │ │ │ └── evmconnect │ │ │ │ ├── client.go │ │ │ │ ├── client_test.go │ │ │ │ ├── config.go │ │ │ │ ├── config_test.go │ │ │ │ ├── docker.go │ │ │ │ └── docker_test.go │ │ ├── contracts.go │ │ ├── contracts_test.go │ │ ├── ethereum.go │ │ ├── ethereum_test.go │ │ ├── ethsigner │ │ │ ├── accounts.go │ │ │ ├── accounts_test.go │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ ├── ethsigner.go │ │ │ └── ethsigner_test.go │ │ ├── ethtypes │ │ │ └── types.go │ │ ├── geth │ │ │ ├── client.go │ │ │ ├── genesis.go │ │ │ ├── genesis_test.go │ │ │ ├── geth_provider.go │ │ │ └── geth_provider_test.go │ │ ├── quorum │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── genesis.go │ │ │ ├── genesis_test.go │ │ │ ├── quorum.go │ │ │ ├── quorum_provider.go │ │ │ ├── quorum_provider_test.go │ │ │ └── quorum_test.go │ │ ├── remoterpc │ │ │ ├── remoterpc_provider.go │ │ │ └── remoterpc_provider_test.go │ │ ├── tessera │ │ │ ├── tessera.go │ │ │ └── tessera_test.go │ │ └── testdata │ │ │ ├── Firefly.json │ │ │ ├── sol.json │ │ │ └── truffle.json │ ├── fabric │ │ ├── configtx.yaml │ │ ├── constants.go │ │ ├── contracts.go │ │ ├── cryptogen_config.go │ │ ├── fabconnect │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── fabconnect_config.go │ │ │ └── fabconnect_config_test.go │ │ ├── fabric_docker.go │ │ ├── fabric_docker_test.go │ │ ├── fabric_provider.go │ │ ├── fabric_provider_test.go │ │ └── network_config.go │ └── tezos │ │ ├── connector │ │ ├── connector_interface.go │ │ └── tezosconnect │ │ │ ├── client.go │ │ │ ├── client_test.go │ │ │ ├── config.go │ │ │ ├── config_test.go │ │ │ ├── docker.go │ │ │ └── docker_test.go │ │ ├── remoterpc │ │ ├── remoterpc_provider.go │ │ └── remoterpc_provider_test.go │ │ ├── tezos.go │ │ └── tezossigner │ │ ├── config.go │ │ ├── config_test.go │ │ └── tezossigner.go ├── constants │ └── constants.go ├── core │ ├── config.go │ ├── http.go │ ├── manifest.go │ └── manifest_test.go ├── docker │ ├── docker.go │ ├── docker_checks.go │ ├── docker_config.go │ ├── docker_config_test.go │ ├── docker_manager.go │ └── mocks │ │ └── docker_manager.go ├── log │ ├── log.go │ ├── spin.go │ └── stdout.go ├── stacks │ ├── data_exchange.go │ ├── firefly_identity.go │ ├── ipfs_config.go │ ├── prometheus_config.go │ └── stack_manager.go ├── tokens │ ├── erc1155 │ │ └── erc1155_provider.go │ ├── erc20erc721 │ │ └── erc20_erc721_provider.go │ └── tokens_provider.go └── utils │ └── testing.go └── pkg └── types ├── contracts.go ├── firefly_config.go ├── hex_address.go ├── manifest.go ├── namespace.go ├── options.go ├── organization.go ├── stack.go └── stackState.go /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | pull_request: 8 | 9 | permissions: 10 | contents: write 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v4 18 | with: 19 | fetch-depth: 0 20 | - name: Set up Go 21 | uses: actions/setup-go@v4 22 | with: 23 | go-version: "1.22" 24 | - name: Compile FireFly CLI 25 | run: make 26 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*" 7 | 8 | permissions: 9 | contents: write 10 | 11 | jobs: 12 | goreleaser: 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Checkout 16 | uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 0 19 | - name: Set up Go 20 | uses: actions/setup-go@v4 21 | with: 22 | go-version: "1.22" 23 | - name: Run GoReleaser 24 | uses: goreleaser/goreleaser-action@v4 25 | with: 26 | distribution: goreleaser 27 | version: latest 28 | args: release --clean 29 | env: 30 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 31 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Copyright © 2024 Kaleido, Inc. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | 18 | .DS_Store 19 | ff/ff 20 | dist/ 21 | *.iml 22 | .idea/ 23 | docs/command_docs 24 | coverage.txt 25 | .vscode 26 | !.vscode/launch.json 27 | !.vscode/settings.json -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | tests: false 3 | skip-dirs: 4 | - "mocks" 5 | - "ffconfig" 6 | - "test/e2e" 7 | linters-settings: 8 | golint: {} 9 | gocritic: 10 | enabled-checks: [] 11 | disabled-checks: 12 | - regexpMust 13 | revive: 14 | rules: 15 | - name: unused-parameter 16 | disabled: true 17 | gosec: 18 | excludes: 19 | - G306 # Needed file permission for local development 20 | - G601 # Appears not to handle taking an address of a sub-structure, within a pointer to a structure within a loop. Which is valid and safe. 21 | goheader: 22 | values: 23 | regexp: 24 | COMPANY: .* 25 | YEAR: '\d\d\d\d(-\d\d\d\d)?' 26 | template: |- 27 | Copyright © {{ YEAR }} {{ COMPANY }} 28 | 29 | SPDX-License-Identifier: Apache-2.0 30 | 31 | Licensed under the Apache License, Version 2.0 (the "License"); 32 | you may not use this file except in compliance with the License. 33 | You may obtain a copy of the License at 34 | 35 | http://www.apache.org/licenses/LICENSE-2.0 36 | 37 | Unless required by applicable law or agreed to in writing, software 38 | distributed under the License is distributed on an "AS IS" BASIS, 39 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 40 | See the License for the specific language governing permissions and 41 | limitations under the License. 42 | linters: 43 | disable-all: false 44 | disable: 45 | - structcheck 46 | enable: 47 | - dogsled 48 | - errcheck 49 | - goconst 50 | - gocritic 51 | - gocyclo 52 | - gofmt 53 | - goheader 54 | - goimports 55 | - goprintffuncname 56 | - gosec 57 | - gosimple 58 | - govet 59 | - ineffassign 60 | - misspell 61 | - nakedret 62 | - revive 63 | - staticcheck 64 | - stylecheck 65 | - typecheck 66 | - unconvert 67 | - unused 68 | -------------------------------------------------------------------------------- /.goreleaser.yml: -------------------------------------------------------------------------------- 1 | # Copyright © 2024 Kaleido, Inc. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | before: 18 | hooks: 19 | - go mod tidy 20 | builds: 21 | - env: 22 | - CGO_ENABLED=0 23 | goos: 24 | - linux 25 | - darwin 26 | main: ./ff 27 | binary: ff 28 | ldflags: 29 | - "-s -w -X 'github.com/hyperledger/firefly-cli/cmd.BuildVersionOverride={{.Version}}' -X 'github.com/hyperledger/firefly-cli/cmd.BuildDate={{.Date}}' -X 'github.com/hyperledger/firefly-cli/cmd.BuildCommit={{.Commit}}'" 30 | archives: 31 | - name_template: >- 32 | {{ .ProjectName }}_{{ .Version }}_ 33 | {{- if eq .Os "darwin" }}macOS 34 | {{- else if eq .Os "linux" }}Linux 35 | {{- else }}{{ .Os }}{{ end }}_ 36 | {{- if eq .Arch "amd64" }}x86_64 37 | {{- else }}{{ .Arch }}{{ end }} 38 | checksum: 39 | name_template: "checksums.txt" 40 | snapshot: 41 | name_template: "{{ incpatch .Tag }}-next" 42 | changelog: 43 | sort: asc 44 | filters: 45 | exclude: 46 | - "^docs:" 47 | - "^test:" 48 | release: 49 | prerelease: auto 50 | git: 51 | # Sort Git tags by date, the default behaviour lists release candidates first regardless of date 52 | tag_sort: -version:creatordate 53 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Debug init mode", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "debug", 9 | "program": "ff/main.go", 10 | "args": ["init", "ethereum", "dev", "2"] 11 | }, 12 | { 13 | "name": "Debug remove mode", 14 | "type": "go", 15 | "request": "launch", 16 | "mode": "debug", 17 | "program": "ff/main.go", 18 | "args": ["remove", "dev", "--force"] 19 | }, 20 | { 21 | "name": "Debug start mode", 22 | "type": "go", 23 | "request": "launch", 24 | "mode": "debug", 25 | "program": "ff/main.go", 26 | "args": ["start", "dev"] 27 | }, 28 | ] 29 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## v0.0.28 4 | 5 | - Binary builds handled by goreleaser -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # SPDX-License-Identifier: Apache-2.0 2 | 3 | - @hyperledger/firefly-cli-maintainers 4 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | Code of Conduct Guidelines 2 | ========================== 3 | 4 | Please review the Hyperledger [Code of 5 | Conduct](https://wiki.hyperledger.org/community/hyperledger-project-code-of-conduct) 6 | before participating. It is important that we keep things civil. 7 | 8 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing 2 | 3 | We welcome contributions to the FireFly Project in many forms, and 4 | there's always plenty to do! 5 | 6 | Please visit the 7 | [contributors guide](https://hyperledger.github.io/firefly/latest/contributors/index) in the 8 | docs to learn how to make contributions to this exciting project. 9 | 10 | Creative Commons License
This work is licensed under a Creative Commons Attribution 4.0 International License. 11 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | The following is the list of current maintainers this repo: 4 | 5 | | Name | GitHub | Email | LFID | 6 | | ----------------- | --------------- | ---------------------------- | ----------------- | 7 | | Enrique Lacal | enriquel8 | enrique.lacal@kaleido.io | enrique.lacal | 8 | | Andrew Richardson | awrichar | andrew.richardson@kaleido.io | Andrew.Richardson | 9 | | Peter Broadhurst | peterbroadhurst | peter.broadhurst@kaleido.io | peterbroadhurst | 10 | 11 | This list is to be kept up to date as maintainers are added or removed. 12 | 13 | For the full list of maintainers across all repos, the expectations of a maintainer and the process for becoming a maintainer, please see the [FireFly Maintainers page on the Hyperledger Wiki](https://wiki.hyperledger.org/display/FIR/Maintainers). 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2024 Kaleido, Inc. 2 | # 3 | # SPDX-License-Identifier: Apache-2.0 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | VGO=go 18 | GOBIN := $(shell $(VGO) env GOPATH)/bin 19 | GITREF := $(shell git rev-parse --short HEAD) 20 | DATE := $(shell date -u +"%Y-%m-%dT%H:%M:%SZ") 21 | LINT := $(GOBIN)/golangci-lint 22 | 23 | all: format build lint test tidy 24 | format: ## Formats all go code 25 | gofmt -s -w . 26 | test: deps 27 | $(VGO) test ./internal/... ./pkg/... ./cmd/... -cover -coverprofile=coverage.txt -covermode=atomic -timeout=30s ${TEST_ARGS} 28 | build: ## Builds all go code 29 | cd ff && go build -ldflags="-X 'github.com/hyperledger/firefly-cli/cmd.BuildDate=$(DATE)' -X 'github.com/hyperledger/firefly-cli/cmd.BuildCommit=$(GITREF)'" 30 | install: ## Installs the package 31 | cd ff && go install -ldflags="-X 'github.com/hyperledger/firefly-cli/cmd.BuildDate=$(DATE)' -X 'github.com/hyperledger/firefly-cli/cmd.BuildCommit=$(GITREF)'" 32 | 33 | lint: ${LINT} ## Checks and reports lint errors 34 | GOGC=20 $(LINT) run -v --timeout 5m 35 | 36 | ${LINT}: 37 | $(VGO) install github.com/golangci/golangci-lint/cmd/golangci-lint@latest 38 | deps: 39 | cd ff && $(VGO) get 40 | help: ## Show this help 41 | @echo 'usage: make [target] ...' 42 | @echo '' 43 | @echo 'targets:' 44 | @egrep '^(.+)\:\ .*##\ (.+)' ${MAKEFILE_LIST} | sed 's/:.*##/#/' | column -t -c 2 -s '#' 45 | tidy: 46 | $(VGO) mod tidy 47 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Security Policy 2 | 3 | ## Reporting a Security Bug 4 | 5 | If you think you have discovered a security issue in any of the Hyperledger projects, we'd love to 6 | hear from you. We will take all security bugs seriously and if confirmed upon investigation we will 7 | patch it within a reasonable amount of time and release a public security bulletin discussing the 8 | impact and credit the discoverer. 9 | 10 | There are two ways to report a security bug. The easiest is to email a description of the flaw and 11 | any related information (e.g. reproduction steps, version) to 12 | [security at hyperledger dot org](mailto:security@hyperledger.org). 13 | 14 | The other way is to file a confidential security bug in our 15 | [JIRA bug tracking system](https://jira.hyperledger.org). Be sure to set the “Security Level” to 16 | “Security issue”. 17 | 18 | The process by which the Hyperledger Security Team handles security bugs is documented further in 19 | our [Defect Response page](https://wiki.hyperledger.org/display/SEC/Defect+Response) on our 20 | [wiki](https://wiki.hyperledger.org). -------------------------------------------------------------------------------- /cmd/accounts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | // accountsCmd represents the deploy command 24 | var accountsCmd = &cobra.Command{ 25 | Use: "accounts", 26 | Short: "Work with accounts in a FireFly stack", 27 | Long: `Work with accounts in a FireFly stack`, 28 | } 29 | 30 | func init() { 31 | rootCmd.AddCommand(accountsCmd) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/accounts_create.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // accountsCreateCmd represents the "accounts create" command 30 | var accountsCreateCmd = &cobra.Command{ 31 | Use: "create ", 32 | Short: "Create a new account in the FireFly stack", 33 | Long: `Create a new account in the FireFly stack`, 34 | Args: cobra.MinimumNArgs(1), 35 | ValidArgsFunction: listStacks, 36 | PreRunE: func(cmd *cobra.Command, args []string) error { 37 | ctx := log.WithVerbosity(context.Background(), verbose) 38 | ctx = log.WithLogger(ctx, logger) 39 | 40 | version, err := docker.CheckDockerConfig() 41 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 42 | cmd.SetContext(ctx) 43 | return err 44 | }, 45 | RunE: func(cmd *cobra.Command, args []string) error { 46 | stackName := args[0] 47 | stackManager := stacks.NewStackManager(cmd.Context()) 48 | if err := stackManager.LoadStack(stackName); err != nil { 49 | return err 50 | } 51 | account, err := stackManager.CreateAccount(args[1:]) 52 | if err != nil { 53 | return fmt.Errorf("%s. usage: %s accounts create ", err.Error(), ExecutableName) 54 | } 55 | fmt.Print(account) 56 | fmt.Print("\n") 57 | return nil 58 | }, 59 | } 60 | 61 | func init() { 62 | accountsCmd.AddCommand(accountsCreateCmd) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/accounts_create_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | 7 | "github.com/hyperledger/firefly-cli/internal/utils" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestCreateAccountCmd(t *testing.T) { 12 | 13 | testcases := []struct { 14 | Name string 15 | Args []string 16 | ExpectedResponse string 17 | }{ 18 | { 19 | Name: "testcase1", 20 | Args: []string{"create", "stack-1"}, 21 | ExpectedResponse: "", 22 | }, 23 | { 24 | Name: "testcase-2", 25 | Args: []string{"create", "stack-2"}, 26 | ExpectedResponse: "", 27 | }, 28 | { 29 | Name: "testcase-3", 30 | Args: []string{"create", "stack-3"}, 31 | ExpectedResponse: "", 32 | }, 33 | { 34 | Name: "testcase-4", 35 | Args: []string{"create", "stack-4"}, 36 | ExpectedResponse: "", 37 | }, 38 | { 39 | Name: "testcase-5", 40 | Args: []string{"create", "stack-5"}, 41 | ExpectedResponse: "", 42 | }, 43 | { 44 | Name: "testcase-6", 45 | Args: []string{"create", "stack-6"}, 46 | ExpectedResponse: "", 47 | }, 48 | } 49 | for _, tc := range testcases { 50 | t.Run(tc.Name, func(t *testing.T) { 51 | 52 | cmd := accountsCreateCmd 53 | cmd.SetArgs(tc.Args) 54 | 55 | // Capture the output 56 | originalOutput, outputBuffer := utils.CaptureOutput() 57 | defer func() { 58 | // Restore the original output after capturing 59 | os.Stdout = originalOutput 60 | }() 61 | cmd.SetOut(outputBuffer) 62 | 63 | // Execute the command 64 | err := cmd.Execute() 65 | if err != nil { 66 | t.Fatalf("Command execution failed: %v", err) 67 | } 68 | 69 | // Get the actual response 70 | actualResponse := outputBuffer.String() 71 | 72 | // Compare actual and expected responses 73 | assert.Equal(t, tc.ExpectedResponse, actualResponse, "Responses do not match") 74 | 75 | assert.NotNil(t, actualResponse) 76 | }) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /cmd/accounts_list.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "fmt" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/docker" 25 | "github.com/hyperledger/firefly-cli/internal/log" 26 | "github.com/hyperledger/firefly-cli/internal/stacks" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | // accountsListCmd represents the "accounts list" command 31 | var accountsListCmd = &cobra.Command{ 32 | Use: "list ", 33 | Short: "List the accounts in the FireFly stack", 34 | Long: `List the accounts in the FireFly stack`, 35 | ValidArgsFunction: listStacks, 36 | Args: cobra.ExactArgs(1), 37 | Aliases: []string{"ls"}, 38 | PreRunE: func(cmd *cobra.Command, args []string) error { 39 | ctx := log.WithVerbosity(context.Background(), verbose) 40 | ctx = log.WithLogger(ctx, logger) 41 | 42 | version, err := docker.CheckDockerConfig() 43 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 44 | cmd.SetContext(ctx) 45 | return err 46 | }, 47 | RunE: func(cmd *cobra.Command, args []string) error { 48 | stackName := args[0] 49 | stackManager := stacks.NewStackManager(cmd.Context()) 50 | if err := stackManager.LoadStack(stackName); err != nil { 51 | return err 52 | } 53 | accounts, err := json.MarshalIndent(stackManager.Stack.State.Accounts, "", " ") 54 | if err != nil { 55 | return err 56 | } 57 | fmt.Printf("%s\n", string(accounts)) 58 | return nil 59 | }, 60 | } 61 | 62 | func init() { 63 | accountsCmd.AddCommand(accountsListCmd) 64 | } 65 | -------------------------------------------------------------------------------- /cmd/accounts_list_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/internal/utils" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestAccountListCmd(t *testing.T) { 11 | testNames := []string{"stack-1", "stack-2", "stack-3", "stack-4", "stack-5"} 12 | for _, stackNames := range testNames { 13 | createCmd := accountsCreateCmd 14 | createCmd.SetArgs([]string{ExecutableName, "create", stackNames}) 15 | err := createCmd.Execute() 16 | if err != nil { 17 | t.Fatalf("Failed to create account for testing: %v", err) 18 | } 19 | Args := []string{"ls"} 20 | t.Run("Test-Account-List", func(t *testing.T) { 21 | cmd := accountsListCmd 22 | cmd.SetArgs(Args) 23 | 24 | _, outputBuffer := utils.CaptureOutput() 25 | cmd.SetOut(outputBuffer) 26 | 27 | err := cmd.Execute() 28 | if err != nil { 29 | t.Fatalf("Command execution failed: %v", err) 30 | } 31 | actualResponse := outputBuffer.String() 32 | 33 | assert.NotNil(t, actualResponse) 34 | 35 | }) 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /cmd/deploy.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "github.com/spf13/cobra" 21 | ) 22 | 23 | // deployCmd represents the deploy command 24 | var deployCmd = &cobra.Command{ 25 | Use: "deploy", 26 | Short: "Deploy a compiled smart contract", 27 | Long: `Deploy a compiled smart contract to the blockchain used by a FireFly stack`, 28 | } 29 | 30 | func init() { 31 | rootCmd.AddCommand(deployCmd) 32 | } 33 | -------------------------------------------------------------------------------- /cmd/deploy_ethereum.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // deployEthereumCmd represents the "deploy ethereum" command 30 | var deployEthereumCmd = &cobra.Command{ 31 | Use: "ethereum [constructor_param1 [constructor_param2 ...]]", 32 | Short: "Deploy a compiled solidity contract", 33 | ValidArgsFunction: listStacks, 34 | Long: `Deploy a solidity contract compiled with solc to the blockchain used by a FireFly stack. If the 35 | contract has a constructor that takes arguments specify them as arguments to the command after the filename. 36 | 37 | To compile a .sol file to a .json file run: 38 | 39 | solc --combined-json abi,bin contract.sol > contract.json 40 | `, 41 | Args: cobra.MinimumNArgs(2), 42 | PreRunE: func(cmd *cobra.Command, args []string) error { 43 | ctx := log.WithVerbosity(context.Background(), verbose) 44 | ctx = log.WithLogger(ctx, logger) 45 | 46 | version, err := docker.CheckDockerConfig() 47 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 48 | cmd.SetContext(ctx) 49 | return err 50 | }, 51 | RunE: func(cmd *cobra.Command, args []string) error { 52 | stackName := args[0] 53 | filename := args[1] 54 | stackManager := stacks.NewStackManager(cmd.Context()) 55 | if err := stackManager.LoadStack(stackName); err != nil { 56 | return err 57 | } 58 | contractNames, err := stackManager.GetContracts(filename, args[2:]) 59 | if err != nil { 60 | return err 61 | } 62 | if len(contractNames) < 1 { 63 | return fmt.Errorf("no contracts found in file: '%s'", filename) 64 | } 65 | selectedContractName := contractNames[0] 66 | if len(contractNames) > 1 { 67 | selectedContractName, err = selectMenu("select the contract to deploy", contractNames) 68 | fmt.Print("\n") 69 | if err != nil { 70 | return err 71 | } 72 | } 73 | location, err := stackManager.DeployContract(filename, selectedContractName, 0, args[2:]) 74 | if err != nil { 75 | return fmt.Errorf("%s. usage: %s deploy ", err.Error(), ExecutableName) 76 | } 77 | fmt.Print(location) 78 | return nil 79 | }, 80 | } 81 | 82 | func init() { 83 | deployCmd.AddCommand(deployEthereumCmd) 84 | } 85 | -------------------------------------------------------------------------------- /cmd/deploy_ethereum_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | "time" 9 | 10 | "github.com/hyperledger/firefly-cli/internal/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestDeployEthereumCmd(t *testing.T) { 15 | var ctx context.Context 16 | ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) 17 | defer cancel() 18 | 19 | createcmd := accountsCreateCmd 20 | createcmd.SetArgs([]string{"create", "stack-2"}) 21 | err := createcmd.ExecuteContext(ctx) 22 | if err != nil { 23 | t.Fatalf("unable to create stack : %v", err) 24 | } 25 | currDir := t.TempDir() 26 | contractFile := filepath.Join(currDir + "eth_deploy.json") 27 | Args := []string{"deploy", "ethereum", "stack-2", contractFile, "param1", "param2"} 28 | ethDeployCmd := deployEthereumCmd 29 | ethDeployCmd.SetArgs(Args) 30 | ethDeployCmd.ExecuteContext(ctx) 31 | 32 | Outputwriter, outputBuffer := utils.CaptureOutput() 33 | defer func() { 34 | os.Stdout = Outputwriter 35 | }() 36 | ethDeployCmd.SetOutput(outputBuffer) 37 | 38 | actualResponse := outputBuffer.String() 39 | assert.NotNil(t, actualResponse) 40 | } 41 | -------------------------------------------------------------------------------- /cmd/deploy_fabric.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // deployFabricCmd represents the "deploy fabric" command 30 | var deployFabricCmd = &cobra.Command{ 31 | Use: "fabric ", 32 | Short: "Deploy fabric chaincode", 33 | ValidArgsFunction: listStacks, 34 | Long: `Deploy a packaged chaincode to the Fabric network used by a FireFly stack`, 35 | Args: cobra.ExactArgs(5), 36 | PreRunE: func(cmd *cobra.Command, args []string) error { 37 | ctx := log.WithVerbosity(context.Background(), verbose) 38 | ctx = log.WithLogger(ctx, logger) 39 | 40 | version, err := docker.CheckDockerConfig() 41 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 42 | cmd.SetContext(ctx) 43 | return err 44 | }, 45 | RunE: func(cmd *cobra.Command, args []string) error { 46 | stackName := args[0] 47 | filename := args[1] 48 | stackManager := stacks.NewStackManager(cmd.Context()) 49 | if err := stackManager.LoadStack(stackName); err != nil { 50 | return err 51 | } 52 | contractAddress, err := stackManager.DeployContract(filename, filename, 0, args[2:]) 53 | if err != nil { 54 | return fmt.Errorf("%s. usage: %s deploy ", err.Error(), ExecutableName) 55 | } 56 | fmt.Print(contractAddress) 57 | return nil 58 | }, 59 | } 60 | 61 | func init() { 62 | deployCmd.AddCommand(deployFabricCmd) 63 | } 64 | -------------------------------------------------------------------------------- /cmd/deploy_fabric_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | "time" 9 | 10 | "github.com/hyperledger/firefly-cli/internal/utils" 11 | "github.com/stretchr/testify/assert" 12 | ) 13 | 14 | func TestDeployFabricCmd(t *testing.T) { 15 | var ctx context.Context 16 | ctx, cancel := context.WithTimeout(context.Background(), 20*time.Second) 17 | defer cancel() 18 | 19 | createCmd := accountsCreateCmd 20 | createCmd.SetArgs([]string{"create", "stack-1"}) 21 | err := createCmd.ExecuteContext(ctx) 22 | if err != nil { 23 | t.Fatalf("unable to execute command :%v", err) 24 | } 25 | currDir := t.TempDir() 26 | chainCodefile := filepath.Join(currDir + "fabric_deploy.json") 27 | Args := []string{"fabric", "stack-1", chainCodefile, "firefly", "fabric-user-1", "1.0"} 28 | 29 | t.Run("Test Deploy Cmd", func(t *testing.T) { 30 | DeployFabric := deployFabricCmd 31 | DeployFabric.SetArgs(Args) 32 | DeployFabric.ExecuteContext(ctx) 33 | 34 | originalOutput, outBuffer := utils.CaptureOutput() 35 | defer func() { 36 | os.Stdout = originalOutput 37 | }() 38 | DeployFabric.SetOutput(outBuffer) 39 | 40 | actualResponse := outBuffer.String() 41 | 42 | assert.NotNil(t, actualResponse) 43 | }) 44 | } 45 | -------------------------------------------------------------------------------- /cmd/docs.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Giwa Oluwatobi 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "errors" 21 | "fmt" 22 | "io/fs" 23 | "os" 24 | 25 | "github.com/spf13/cobra" 26 | "github.com/spf13/cobra/doc" 27 | ) 28 | 29 | // docsCmd represents the docs command 30 | var docsCmd = &cobra.Command{ 31 | Use: "docs [dir]", 32 | Short: "Generate markdown documentation for all command", 33 | Args: cobra.MaximumNArgs(1), 34 | Long: `Generate markdown documentation for the entire command tree. 35 | 36 | The command takes an optional argument specifying directory to put the 37 | generated documentation, default is "{cwd}/docs/command_docs/"`, 38 | RunE: func(cmd *cobra.Command, args []string) error { 39 | var path string 40 | 41 | if len(args) == 0 { 42 | currentWoringDir, err := os.Getwd() 43 | if err != nil { 44 | return err 45 | } 46 | path = fmt.Sprintf("%s/docs/command_docs", currentWoringDir) 47 | if err := os.MkdirAll(path, 0755); err != nil { 48 | return err 49 | } 50 | } else { 51 | path = args[0] 52 | if _, err := os.Stat(path); errors.Is(err, fs.ErrNotExist) || err != nil { 53 | err = fmt.Errorf("path you supplied for documentation is invalid: %v", err) 54 | return err 55 | } 56 | } 57 | 58 | err := doc.GenMarkdownTree(rootCmd, path) 59 | if err != nil { 60 | return err 61 | } 62 | return nil 63 | }, 64 | } 65 | 66 | func init() { 67 | rootCmd.AddCommand(docsCmd) 68 | } 69 | -------------------------------------------------------------------------------- /cmd/info.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | var infoCmd = &cobra.Command{ 30 | Use: "info ", 31 | Short: "Get info about a stack", 32 | ValidArgsFunction: listStacks, 33 | Long: `Get info about a stack such as each container name 34 | and image version.`, 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | ctx := log.WithVerbosity(context.Background(), verbose) 37 | ctx = context.WithValue(ctx, docker.CtxIsLogCmdKey{}, true) 38 | ctx = log.WithLogger(ctx, logger) 39 | 40 | version, err := docker.CheckDockerConfig() 41 | if err != nil { 42 | return err 43 | } 44 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 45 | 46 | stackManager := stacks.NewStackManager(ctx) 47 | if len(args) == 0 { 48 | return fmt.Errorf("no stack specified") 49 | } 50 | stackName := args[0] 51 | 52 | if err := stackManager.LoadStack(stackName); err != nil { 53 | return err 54 | } 55 | if err := stackManager.PrintStackInfo(); err != nil { 56 | return err 57 | } 58 | return nil 59 | }, 60 | } 61 | 62 | func init() { 63 | rootCmd.AddCommand(infoCmd) 64 | } 65 | -------------------------------------------------------------------------------- /cmd/info_test.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/internal/utils" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestInfoCmd(t *testing.T) { 11 | accNames := []string{"acc-1", "acc-2", "acc-3"} 12 | for _, stacks := range accNames { 13 | createAcc := accountsCreateCmd 14 | createAcc.SetArgs([]string{"create", stacks}) 15 | err := createAcc.Execute() 16 | if err != nil { 17 | t.Fatalf("unable to execute command :%v", err) 18 | } 19 | args := []string{"info"} 20 | t.Run("Info Cmd Test", func(t *testing.T) { 21 | InfoCmd := infoCmd 22 | InfoCmd.SetArgs(args) 23 | 24 | _, outBuff := utils.CaptureOutput() 25 | InfoCmd.SetOut(outBuff) 26 | err = InfoCmd.Execute() 27 | if err != nil { 28 | t.Fatalf("Command execution failed: %v", err) 29 | } 30 | actualResponse := outBuff.String() 31 | 32 | assert.NotNil(t, actualResponse) 33 | }) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /cmd/init_cardano.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "path/filepath" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/hyperledger/firefly-cli/pkg/types" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | var initCardanoCmd = &cobra.Command{ 31 | Use: "cardano [stack_name]", 32 | Short: "Create a new FireFly local dev stack using a Cardano blockchain", 33 | Long: "Create a new FireFly local dev stack using a Cardano blockchain", 34 | Args: cobra.MaximumNArgs(1), 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | ctx := log.WithVerbosity(context.Background(), verbose) 37 | ctx = log.WithLogger(ctx, logger) 38 | stackManager := stacks.NewStackManager(ctx) 39 | initOptions.BlockchainProvider = types.BlockchainProviderCardano.String() 40 | initOptions.BlockchainConnector = types.BlockchainConnectorCardanoConnect.String() 41 | initOptions.BlockchainNodeProvider = types.BlockchainNodeProviderRemoteRPC.String() 42 | initOptions.TokenProviders = []string{} 43 | initOptions.MultipartyEnabled = false 44 | if len(args) == 1 { 45 | // stacks are enforced to have 1 member 46 | args = append(args, "1") 47 | } 48 | if err := initCommon(args); err != nil { 49 | return err 50 | } 51 | if err := stackManager.InitStack(&initOptions); err != nil { 52 | if err := stackManager.RemoveStack(); err != nil { 53 | return err 54 | } 55 | return err 56 | } 57 | fmt.Printf("Stack '%s' created!\nTo start your new stack run:\n\n%s start %s\n", initOptions.StackName, rootCmd.Use, initOptions.StackName) 58 | fmt.Printf("\nYour docker compose file for this stack can be found at: %s\n\n", filepath.Join(stackManager.Stack.StackDir, "docker-compose.yml")) 59 | return nil 60 | }, 61 | } 62 | 63 | func init() { 64 | initCardanoCmd.Flags().StringVar(&initOptions.Network, "network", "mainnet", "The name of the network to connect to") 65 | initCardanoCmd.Flags().StringVar(&initOptions.Socket, "socket", "", "Socket to mount for the cardano node to connect to") 66 | initCardanoCmd.Flags().StringVar(&initOptions.BlockfrostKey, "blockfrost-key", "", "Blockfrost key") 67 | 68 | initCmd.AddCommand(initCardanoCmd) 69 | } 70 | -------------------------------------------------------------------------------- /cmd/init_ethereum.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "path/filepath" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/hyperledger/firefly-cli/internal/docker" 27 | "github.com/hyperledger/firefly-cli/internal/log" 28 | "github.com/hyperledger/firefly-cli/internal/stacks" 29 | "github.com/hyperledger/firefly-cli/pkg/types" 30 | "github.com/hyperledger/firefly-common/pkg/fftypes" 31 | ) 32 | 33 | var initEthereumCmd = &cobra.Command{ 34 | Use: "ethereum [stack_name] [member_count]", 35 | Short: "Create a new FireFly local dev stack using an Ethereum blockchain", 36 | Long: `Create a new FireFly local dev stack using an Ethereum blockchain`, 37 | Args: cobra.MaximumNArgs(2), 38 | RunE: func(cmd *cobra.Command, args []string) error { 39 | ctx := log.WithVerbosity(context.Background(), verbose) 40 | ctx = log.WithLogger(ctx, logger) 41 | version, err := docker.CheckDockerConfig() 42 | if err != nil { 43 | return err 44 | } 45 | // Needs this context for cleaning up as part of Remove Stack 46 | // If an error occurs as part of init 47 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 48 | 49 | stackManager := stacks.NewStackManager(ctx) 50 | if err := initCommon(args); err != nil { 51 | return err 52 | } 53 | if err := stackManager.InitStack(&initOptions); err != nil { 54 | verr := stackManager.RemoveStack() 55 | 56 | // log the remove error if present 57 | if verr != nil { 58 | l := log.LoggerFromContext(ctx) 59 | l.Info(fmt.Sprintf("Error whilst removing the stack: %s", verr.Error())) 60 | } 61 | // return the init error to not hide the issue 62 | return err 63 | } 64 | fmt.Printf("Stack '%s' created!\nTo start your new stack run:\n\n%s start %s\n", initOptions.StackName, rootCmd.Use, initOptions.StackName) 65 | fmt.Printf("\nYour docker compose file for this stack can be found at: %s\n\n", filepath.Join(stackManager.Stack.StackDir, "docker-compose.yml")) 66 | return nil 67 | }, 68 | } 69 | 70 | func init() { 71 | initEthereumCmd.Flags().IntVar(&initOptions.BlockPeriod, "block-period", -1, "Block period in seconds. Default is variable based on selected blockchain provider.") 72 | initEthereumCmd.Flags().StringVar(&initOptions.ContractAddress, "contract-address", "", "Do not automatically deploy a contract, instead use a pre-configured address") 73 | initEthereumCmd.Flags().StringVar(&initOptions.RemoteNodeURL, "remote-node-url", "", "For cases where the node is pre-existing and running remotely") 74 | initEthereumCmd.Flags().Int64Var(&initOptions.ChainID, "chain-id", 2021, "The chain ID - also used as the network ID") 75 | initEthereumCmd.Flags().StringVarP(&initOptions.BlockchainConnector, "blockchain-connector", "c", "evmconnect", "Blockchain connector to use. Options are: [evmconnect ethconnect]") 76 | initEthereumCmd.Flags().StringVarP(&initOptions.BlockchainNodeProvider, "blockchain-node", "n", "geth", fmt.Sprintf("Blockchain node type to use. Options are: %v", fftypes.FFEnumValues(types.BlockchainNodeProvider))) 77 | 78 | initCmd.AddCommand(initEthereumCmd) 79 | } 80 | -------------------------------------------------------------------------------- /cmd/init_fabric.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "path/filepath" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/hyperledger/firefly-cli/internal/docker" 27 | "github.com/hyperledger/firefly-cli/internal/log" 28 | "github.com/hyperledger/firefly-cli/internal/stacks" 29 | "github.com/hyperledger/firefly-cli/pkg/types" 30 | ) 31 | 32 | var initFabricCmd = &cobra.Command{ 33 | Use: "fabric [stack_name] [member_count]", 34 | Short: "Create a new FireFly local dev stack using a Fabric network", 35 | Long: `Create a new FireFly local dev stack using a Fabric network`, 36 | Args: cobra.MaximumNArgs(2), 37 | RunE: func(cmd *cobra.Command, args []string) error { 38 | ctx := log.WithVerbosity(context.Background(), verbose) 39 | ctx = log.WithLogger(ctx, logger) 40 | 41 | version, err := docker.CheckDockerConfig() 42 | if err != nil { 43 | return err 44 | } 45 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 46 | 47 | stackManager := stacks.NewStackManager(ctx) 48 | initOptions.BlockchainProvider = types.BlockchainProviderFabric.String() 49 | initOptions.TokenProviders = []string{} 50 | if err := validateFabricFlags(); err != nil { 51 | return err 52 | } 53 | if err := initCommon(args); err != nil { 54 | return err 55 | } 56 | if err := stackManager.InitStack(&initOptions); err != nil { 57 | if err := stackManager.RemoveStack(); err != nil { 58 | return err 59 | } 60 | return err 61 | } 62 | fmt.Printf("Stack '%s' created!\nTo start your new stack run:\n\n%s start %s\n", initOptions.StackName, rootCmd.Use, initOptions.StackName) 63 | fmt.Printf("\nYour docker compose file for this stack can be found at: %s\n\n", filepath.Join(stackManager.Stack.StackDir, "docker-compose.yml")) 64 | return nil 65 | }, 66 | } 67 | 68 | func validateFabricFlags() error { 69 | if len(initOptions.CCPYAMLPaths) != 0 || len(initOptions.MSPPaths) != 0 { 70 | if len(initOptions.CCPYAMLPaths) != len(initOptions.MSPPaths) { 71 | return fmt.Errorf("you must provide ccp and msp flags for each organization") 72 | } 73 | if initOptions.ChannelName == "" || initOptions.ChaincodeName == "" { 74 | return fmt.Errorf("channel and chaincode flags must be set when using an external fabric network") 75 | } 76 | initOptions.MemberCount = len(initOptions.CCPYAMLPaths) 77 | } 78 | return nil 79 | } 80 | 81 | func init() { 82 | initFabricCmd.Flags().StringArrayVar(&initOptions.CCPYAMLPaths, "ccp", nil, "Path to the ccp.yaml file for an org in your Fabric network") 83 | initFabricCmd.Flags().StringArrayVar(&initOptions.MSPPaths, "msp", nil, "Path to the MSP directory for an org in your Fabric network") 84 | initFabricCmd.Flags().StringVar(&initOptions.ChannelName, "channel", "", "The name of the Fabric channel on which the FireFly chaincode has been deployed") 85 | initFabricCmd.Flags().StringVar(&initOptions.ChaincodeName, "chaincode", "", "The name given to the FireFly chaincode when it was deployed") 86 | initFabricCmd.Flags().BoolVar(&initOptions.CustomPinSupport, "custom-pin-support", false, "Configure the blockchain listener to listen for BatchPin events from any chaincode on the channel") 87 | initCmd.AddCommand(initFabricCmd) 88 | } 89 | -------------------------------------------------------------------------------- /cmd/init_tezos.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "path/filepath" 23 | 24 | "github.com/spf13/cobra" 25 | 26 | "github.com/hyperledger/firefly-cli/internal/log" 27 | "github.com/hyperledger/firefly-cli/internal/stacks" 28 | "github.com/hyperledger/firefly-cli/pkg/types" 29 | ) 30 | 31 | var initTezosCmd = &cobra.Command{ 32 | Use: "tezos [stack_name] [member_count]", 33 | Short: "Create a new FireFly local dev stack using an Tezos blockchain", 34 | Long: `Create a new FireFly local dev stack using an Tezos blockchain`, 35 | Args: cobra.MaximumNArgs(2), 36 | RunE: func(cmd *cobra.Command, args []string) error { 37 | ctx := log.WithVerbosity(context.Background(), verbose) 38 | ctx = log.WithLogger(ctx, logger) 39 | stackManager := stacks.NewStackManager(ctx) 40 | initOptions.BlockchainProvider = types.BlockchainProviderTezos.String() 41 | initOptions.BlockchainConnector = types.BlockchainConnectorTezosconnect.String() 42 | initOptions.BlockchainNodeProvider = types.BlockchainNodeProviderRemoteRPC.String() 43 | // By default we turn off multiparty mode while it's not supported yet 44 | initOptions.MultipartyEnabled = false 45 | initOptions.TokenProviders = []string{} 46 | if err := validateTezosFlags(); err != nil { 47 | return err 48 | } 49 | if err := initCommon(args); err != nil { 50 | return err 51 | } 52 | if err := stackManager.InitStack(&initOptions); err != nil { 53 | if err := stackManager.RemoveStack(); err != nil { 54 | return err 55 | } 56 | return err 57 | } 58 | fmt.Printf("Stack '%s' created!\nTo start your new stack run:\n\n%s start %s\n", initOptions.StackName, rootCmd.Use, initOptions.StackName) 59 | fmt.Printf("\nYour docker compose file for this stack can be found at: %s\n\n", filepath.Join(stackManager.Stack.StackDir, "docker-compose.yml")) 60 | return nil 61 | }, 62 | } 63 | 64 | func validateTezosFlags() error { 65 | if initOptions.RemoteNodeURL == "" { 66 | return fmt.Errorf("you must provide 'remote-node-url' flag as local node mode is not supported") 67 | } 68 | return nil 69 | } 70 | 71 | func init() { 72 | initTezosCmd.Flags().IntVar(&initOptions.BlockPeriod, "block-period", -1, "Block period in seconds. Default is variable based on selected blockchain provider.") 73 | initTezosCmd.Flags().StringVar(&initOptions.ContractAddress, "contract-address", "", "Do not automatically deploy a contract, instead use a pre-configured address") 74 | initTezosCmd.Flags().StringVar(&initOptions.RemoteNodeURL, "remote-node-url", "", "For cases where the node is pre-existing and running remotely") 75 | 76 | initCmd.AddCommand(initTezosCmd) 77 | } 78 | -------------------------------------------------------------------------------- /cmd/list.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/spf13/cobra" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/stacks" 25 | ) 26 | 27 | var listCommand = &cobra.Command{ 28 | Use: "list", 29 | Aliases: []string{"ls"}, 30 | Short: "list stacks", 31 | Long: `List stacks`, 32 | Args: cobra.MaximumNArgs(2), 33 | RunE: func(cmd *cobra.Command, args []string) error { 34 | if stacks, err := stacks.ListStacks(); err != nil { 35 | return err 36 | } else { 37 | fmt.Print("FireFly Stacks:\n\n") 38 | for _, s := range stacks { 39 | fmt.Println(s) 40 | } 41 | fmt.Print("\n") 42 | } 43 | return nil 44 | }, 45 | } 46 | 47 | func init() { 48 | rootCmd.AddCommand(listCommand) 49 | } 50 | -------------------------------------------------------------------------------- /cmd/logs.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | var follow bool 30 | 31 | // logsCmd represents the logs command 32 | var logsCmd = &cobra.Command{ 33 | Use: "logs ", 34 | Short: "View log output from a stack", 35 | ValidArgsFunction: listStacks, 36 | Long: `View log output from a stack. 37 | 38 | The most recent logs can be viewed, or you can follow the 39 | output with the -f flag.`, 40 | RunE: func(cmd *cobra.Command, args []string) error { 41 | ctx := log.WithVerbosity(context.Background(), verbose) 42 | ctx = context.WithValue(ctx, docker.CtxIsLogCmdKey{}, true) 43 | ctx = log.WithLogger(ctx, logger) 44 | 45 | version, err := docker.CheckDockerConfig() 46 | if err != nil { 47 | return err 48 | } 49 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 50 | 51 | stackManager := stacks.NewStackManager(ctx) 52 | if len(args) == 0 { 53 | return fmt.Errorf("no stack specified") 54 | } 55 | stackName := args[0] 56 | 57 | if err := stackManager.LoadStack(stackName); err != nil { 58 | return err 59 | } 60 | 61 | stackHasRunBefore, err := stackManager.Stack.HasRunBefore() 62 | if err != nil { 63 | return err 64 | } 65 | 66 | if stackHasRunBefore { 67 | fmt.Println("getting logs... ") 68 | commandLine := []string{} 69 | if fancyFeatures { 70 | commandLine = append(commandLine, "--ansi", "always") 71 | } 72 | commandLine = append(commandLine, "-p", stackName, "logs") 73 | if follow { 74 | commandLine = append(commandLine, "-f") 75 | } 76 | if err := docker.RunDockerComposeCommand(ctx, stackManager.Stack.RuntimeDir, commandLine...); err != nil { 77 | return err 78 | } 79 | } else { 80 | fmt.Println("no logs found - stack has not been started") 81 | } 82 | return nil 83 | }, 84 | } 85 | 86 | func init() { 87 | rootCmd.AddCommand(logsCmd) 88 | logsCmd.Flags().BoolVarP(&follow, "follow", "f", false, "follow log output") 89 | } 90 | -------------------------------------------------------------------------------- /cmd/prompt.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "bufio" 21 | "fmt" 22 | "os" 23 | "strconv" 24 | "strings" 25 | 26 | "github.com/hyperledger/firefly-cli/internal/stacks" 27 | "github.com/spf13/cobra" 28 | ) 29 | 30 | func prompt(promptText string, validate func(string) error) (string, error) { 31 | reader := bufio.NewReader(os.Stdin) 32 | for { 33 | fmt.Print(promptText) 34 | if str, err := reader.ReadString('\n'); err != nil { 35 | return "", err 36 | } else { 37 | str = strings.TrimSpace(str) 38 | if err := validate(str); err != nil { 39 | printError(err) 40 | } else { 41 | return str, nil 42 | } 43 | } 44 | } 45 | } 46 | 47 | func confirm(promptText string) error { 48 | reader := bufio.NewReader(os.Stdin) 49 | for { 50 | fmt.Printf("%s [y/N] ", promptText) 51 | if str, err := reader.ReadString('\n'); err != nil { 52 | return err 53 | } else { 54 | str = strings.ToLower(strings.TrimSpace(str)) 55 | if str == "y" || str == "yes" { 56 | return nil 57 | } else { 58 | return fmt.Errorf("confirmation declined with response: '%s'", str) 59 | } 60 | } 61 | } 62 | } 63 | 64 | func selectMenu(promptText string, options []string) (string, error) { 65 | reader := bufio.NewReader(os.Stdin) 66 | for { 67 | fmt.Print("\n") 68 | for i, option := range options { 69 | fmt.Printf(" %v) %s\n", i+1, option) 70 | } 71 | fmt.Printf("\n%s: ", promptText) 72 | if str, err := reader.ReadString('\n'); err != nil { 73 | return "", err 74 | } else { 75 | str = strings.TrimSpace(str) 76 | index, err := strconv.Atoi(str) 77 | if err != nil { 78 | printError(fmt.Errorf("'%s' is not a valid option", str)) 79 | continue 80 | } 81 | if index < 1 || index > len(options) { 82 | printError(fmt.Errorf("'%s' is not a valid option", str)) 83 | continue 84 | } 85 | return options[index-1], nil 86 | } 87 | } 88 | } 89 | 90 | func printError(err error) { 91 | if fancyFeatures { 92 | fmt.Printf("\u001b[31mError: %s\u001b[0m\n", err.Error()) 93 | } else { 94 | fmt.Printf("Error: %s\n", err.Error()) 95 | } 96 | } 97 | 98 | // listStacks aids in completion, to provide completion to command for stack name. 99 | func listStacks(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { 100 | allStacks, err := stacks.ListStacks() 101 | if err != nil { 102 | return nil, cobra.ShellCompDirectiveError 103 | } 104 | return allStacks, cobra.ShellCompDirectiveNoSpace 105 | } 106 | -------------------------------------------------------------------------------- /cmd/ps.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Giwa Oluwatobi 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "strings" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // psCmd represents the ps command 30 | var psCmd = &cobra.Command{ 31 | Use: "ps [a stack name]...", 32 | Short: "Returns information on running stacks", 33 | Long: `ps returns currently running stacks on your local machine. 34 | 35 | It also takes a continuous list of whitespace optional argument - stack name.`, 36 | Aliases: []string{"process"}, 37 | RunE: func(cmd *cobra.Command, args []string) error { 38 | 39 | ctx := log.WithVerbosity(context.Background(), verbose) 40 | ctx = log.WithLogger(ctx, logger) 41 | 42 | allStacks, err := stacks.ListStacks() 43 | if err != nil { 44 | return err 45 | } 46 | 47 | if len(args) > 0 { 48 | namedStacks := make([]string, 0, len(args)) 49 | for _, stackName := range args { 50 | if contains(allStacks, strings.TrimSpace(stackName)) { 51 | namedStacks = append(namedStacks, stackName) 52 | } else { 53 | fmt.Printf("stack name - %s, is not present on your local machine. Run `%s ls` to see all available stacks.\n", stackName, ExecutableName) 54 | } 55 | } 56 | 57 | allStacks = namedStacks // replace only the user specified stacks in the slice instead. 58 | } 59 | 60 | stackManager := stacks.NewStackManager(ctx) 61 | for _, stackName := range allStacks { 62 | if err := stackManager.LoadStack(stackName); err != nil { 63 | return err 64 | } 65 | 66 | if err := stackManager.IsRunning(); err != nil { 67 | return err 68 | } 69 | } 70 | return nil 71 | }, 72 | } 73 | 74 | func init() { 75 | rootCmd.AddCommand(psCmd) 76 | } 77 | 78 | // contains can be removed if the go mod version is bumped to version Go 1.18 79 | // and replaced with slices.Contains(). 80 | func contains(s []string, str string) bool { 81 | for _, v := range s { 82 | if v == str { 83 | return true 84 | } 85 | } 86 | return false 87 | } 88 | -------------------------------------------------------------------------------- /cmd/pull.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "time" 23 | 24 | "github.com/briandowns/spinner" 25 | "github.com/hyperledger/firefly-cli/internal/docker" 26 | "github.com/hyperledger/firefly-cli/internal/log" 27 | "github.com/hyperledger/firefly-cli/internal/stacks" 28 | "github.com/hyperledger/firefly-cli/pkg/types" 29 | "github.com/spf13/cobra" 30 | ) 31 | 32 | var pullOptions types.PullOptions 33 | 34 | var pullCmd = &cobra.Command{ 35 | Use: "pull ", 36 | Short: "Pull a stack", 37 | ValidArgsFunction: listStacks, 38 | Long: `Pull a stack 39 | 40 | Pull the images for a stack . 41 | `, 42 | RunE: func(cmd *cobra.Command, args []string) error { 43 | var spin *spinner.Spinner 44 | if fancyFeatures && !verbose { 45 | spin = spinner.New(spinner.CharSets[11], 100*time.Millisecond) 46 | logger = log.NewSpinnerLogger(spin) 47 | } 48 | ctx := log.WithVerbosity(context.Background(), verbose) 49 | ctx = log.WithLogger(ctx, logger) 50 | 51 | version, err := docker.CheckDockerConfig() 52 | if err != nil { 53 | return err 54 | } 55 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 56 | 57 | stackManager := stacks.NewStackManager(ctx) 58 | if len(args) == 0 { 59 | return errors.New("no stack specified") 60 | } 61 | stackName := args[0] 62 | 63 | if err := stackManager.LoadStack(stackName); err != nil { 64 | return err 65 | } 66 | if spin != nil { 67 | spin.Start() 68 | } 69 | if err := stackManager.PullStack(&pullOptions); err != nil { 70 | return err 71 | } 72 | if spin != nil { 73 | spin.Stop() 74 | } 75 | return nil 76 | }, 77 | } 78 | 79 | func init() { 80 | pullCmd.Flags().IntVarP(&pullOptions.Retries, "retries", "r", 0, "Retry attempts to perform on image pull failure") 81 | 82 | rootCmd.AddCommand(pullCmd) 83 | } 84 | -------------------------------------------------------------------------------- /cmd/remove.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | 25 | "github.com/hyperledger/firefly-cli/internal/constants" 26 | "github.com/hyperledger/firefly-cli/internal/docker" 27 | "github.com/hyperledger/firefly-cli/internal/log" 28 | "github.com/hyperledger/firefly-cli/internal/stacks" 29 | "github.com/spf13/cobra" 30 | ) 31 | 32 | var removeCmd = &cobra.Command{ 33 | Use: "remove ", 34 | Aliases: []string{"rm"}, 35 | Short: "Completely remove a stack", 36 | ValidArgsFunction: listStacks, 37 | Long: `Completely remove a stack 38 | 39 | This command will completely delete a stack, including all of its data 40 | and configuration.`, 41 | RunE: func(cmd *cobra.Command, args []string) error { 42 | ctx := log.WithVerbosity(context.Background(), verbose) 43 | ctx = log.WithLogger(ctx, logger) 44 | 45 | version, err := docker.CheckDockerConfig() 46 | if err != nil { 47 | return err 48 | } 49 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 50 | 51 | stackManager := stacks.NewStackManager(ctx) 52 | if len(args) == 0 { 53 | return fmt.Errorf("no stack specified") 54 | } 55 | stackName := args[0] 56 | 57 | if err := stackManager.LoadStack(stackName); err != nil { 58 | return err 59 | } 60 | 61 | if !force { 62 | fmt.Println("WARNING: This will completely remove your stack and all of its data. Are you sure this is what you want to do?") 63 | if err := confirm(fmt.Sprintf("completely delete FireFly stack '%s'", stackName)); err != nil { 64 | cancel() 65 | } 66 | } 67 | 68 | if err := stackManager.LoadStack(stackName); err != nil { 69 | return err 70 | } 71 | fmt.Printf("deleting FireFly stack '%s'... ", stackName) 72 | if err := stackManager.StopStack(); err != nil { 73 | return err 74 | } 75 | if err := stackManager.RemoveStack(); err != nil { 76 | return err 77 | } 78 | os.RemoveAll(filepath.Join(constants.StacksDir, stackName)) 79 | fmt.Println("done") 80 | return nil 81 | }, 82 | } 83 | 84 | func init() { 85 | removeCmd.Flags().BoolVarP(&force, "force", "f", false, "Remove the stack without prompting for confirmation") 86 | rootCmd.AddCommand(removeCmd) 87 | } 88 | -------------------------------------------------------------------------------- /cmd/reset.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | var resetCmd = &cobra.Command{ 30 | Use: "reset ", 31 | Short: "Clear all data in a stack", 32 | ValidArgsFunction: listStacks, 33 | Long: `Clear all data in a stack 34 | 35 | This command clears all data in a stack, but leaves the stack configuration. 36 | This is useful for testing when you want to start with a clean slate 37 | but don't want to actually recreate the resources in the stack itself. 38 | Note: this will also stop the stack if it is running. 39 | `, 40 | RunE: func(cmd *cobra.Command, args []string) error { 41 | ctx := log.WithVerbosity(context.Background(), verbose) 42 | ctx = log.WithLogger(ctx, logger) 43 | 44 | version, err := docker.CheckDockerConfig() 45 | if err != nil { 46 | return err 47 | } 48 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 49 | 50 | stackManager := stacks.NewStackManager(ctx) 51 | if len(args) == 0 { 52 | return fmt.Errorf("no stack specified") 53 | } 54 | stackName := args[0] 55 | 56 | if err := stackManager.LoadStack(stackName); err != nil { 57 | return err 58 | } 59 | 60 | if stackManager.IsOldFileStructure { 61 | return fmt.Errorf("the FireFly stack '%s' was created with an older version of the CLI and resetting the stack is not supported. If you want to start fresh, please remove and recreate the stack", stackName) 62 | } 63 | 64 | if !force { 65 | fmt.Println("WARNING: This will completely remove all transactions and data from your FireFly stack. Are you sure you want to do that?") 66 | if err := confirm(fmt.Sprintf("reset all data in FireFly stack '%s'", stackName)); err != nil { 67 | cancel() 68 | } 69 | } 70 | 71 | fmt.Printf("resetting FireFly stack '%s'... ", stackName) 72 | if err := stackManager.StopStack(); err != nil { 73 | return err 74 | } 75 | if err := stackManager.ResetStack(); err != nil { 76 | return err 77 | } 78 | fmt.Printf("done\n\nYour stack has been reset. To start your stack run:\n\n%s start %s\n\n", rootCmd.Use, stackName) 79 | 80 | return nil 81 | }, 82 | } 83 | 84 | func init() { 85 | resetCmd.Flags().BoolVarP(&force, "force", "f", false, "Reset the stack without prompting for confirmation") 86 | rootCmd.AddCommand(resetCmd) 87 | } 88 | -------------------------------------------------------------------------------- /cmd/start.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "errors" 22 | "fmt" 23 | "time" 24 | 25 | "github.com/briandowns/spinner" 26 | "github.com/hyperledger/firefly-cli/internal/docker" 27 | "github.com/hyperledger/firefly-cli/internal/log" 28 | "github.com/hyperledger/firefly-cli/internal/stacks" 29 | "github.com/hyperledger/firefly-cli/pkg/types" 30 | "github.com/spf13/cobra" 31 | ) 32 | 33 | var startOptions types.StartOptions 34 | 35 | var startCmd = &cobra.Command{ 36 | Use: "start ", 37 | Short: "Start a stack", 38 | ValidArgsFunction: listStacks, 39 | Long: `Start a stack 40 | 41 | This command will start a stack and run it in the background. 42 | `, 43 | RunE: func(cmd *cobra.Command, args []string) error { 44 | var spin *spinner.Spinner 45 | if fancyFeatures && !verbose { 46 | spin = spinner.New(spinner.CharSets[11], 100*time.Millisecond) 47 | logger = log.NewSpinnerLogger(spin) 48 | } 49 | ctx := log.WithVerbosity(context.Background(), verbose) 50 | ctx = log.WithLogger(ctx, logger) 51 | 52 | version, err := docker.CheckDockerConfig() 53 | if err != nil { 54 | return err 55 | } 56 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 57 | 58 | stackManager := stacks.NewStackManager(ctx) 59 | if len(args) == 0 { 60 | return errors.New("no stack specified") 61 | } 62 | stackName := args[0] 63 | 64 | if err := stackManager.LoadStack(stackName); err != nil { 65 | return err 66 | } 67 | 68 | if runBefore, err := stackManager.Stack.HasRunBefore(); err != nil { 69 | return err 70 | } else if !runBefore { 71 | fmt.Println("this will take a few seconds longer since this is the first time you're running this stack...") 72 | } 73 | 74 | if spin != nil { 75 | spin.Start() 76 | } 77 | messages, err := stackManager.StartStack(&startOptions) 78 | if err != nil { 79 | return err 80 | } 81 | if spin != nil { 82 | spin.Stop() 83 | } 84 | fmt.Print("\n\n") 85 | for _, message := range messages { 86 | fmt.Printf("%s\n\n", message) 87 | } 88 | for _, member := range stackManager.Stack.Members { 89 | fmt.Printf("Web UI for member '%v': http://127.0.0.1:%v/ui\n", member.ID, member.ExposedFireflyPort) 90 | fmt.Printf("Swagger API UI for member '%v': http://127.0.0.1:%v/api\n", member.ID, member.ExposedFireflyPort) 91 | if stackManager.Stack.SandboxEnabled { 92 | fmt.Printf("Sandbox UI for member '%v': http://127.0.0.1:%v\n\n", member.ID, member.ExposedSandboxPort) 93 | } 94 | } 95 | 96 | if stackManager.Stack.PrometheusEnabled { 97 | fmt.Printf("Web UI for shared Prometheus: http://127.0.0.1:%v\n", stackManager.Stack.ExposedPrometheusPort) 98 | } 99 | 100 | fmt.Printf("\nTo see logs for your stack run:\n\n%s logs %s\n\n", rootCmd.Use, stackName) 101 | return nil 102 | }, 103 | } 104 | 105 | func init() { 106 | startCmd.Flags().BoolVarP(&startOptions.NoRollback, "no-rollback", "b", false, "Do not automatically rollback changes if first time setup fails") 107 | rootCmd.AddCommand(startCmd) 108 | } 109 | -------------------------------------------------------------------------------- /cmd/stop.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/internal/log" 25 | "github.com/hyperledger/firefly-cli/internal/stacks" 26 | "github.com/spf13/cobra" 27 | ) 28 | 29 | // stopCmd represents the stop command 30 | var stopCmd = &cobra.Command{ 31 | Use: "stop ", 32 | Short: "Stop a stack", 33 | Long: `Stop a stack`, 34 | ValidArgsFunction: listStacks, 35 | RunE: func(cmd *cobra.Command, args []string) error { 36 | ctx := log.WithVerbosity(context.Background(), verbose) 37 | ctx = log.WithLogger(ctx, logger) 38 | 39 | version, err := docker.CheckDockerConfig() 40 | if err != nil { 41 | return err 42 | } 43 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, version) 44 | 45 | stackManager := stacks.NewStackManager(ctx) 46 | if len(args) == 0 { 47 | return fmt.Errorf("no stack specified") 48 | } 49 | stackName := args[0] 50 | 51 | if err := stackManager.LoadStack(stackName); err != nil { 52 | return err 53 | } 54 | 55 | fmt.Printf("stopping stack '%s'... ", stackName) 56 | if err := stackManager.StopStack(); err != nil { 57 | return err 58 | } 59 | fmt.Print("done\n") 60 | return nil 61 | }, 62 | } 63 | 64 | func init() { 65 | rootCmd.AddCommand(stopCmd) 66 | } 67 | -------------------------------------------------------------------------------- /cmd/upgrade.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "time" 23 | 24 | "github.com/briandowns/spinner" 25 | "github.com/hyperledger/firefly-cli/internal/docker" 26 | "github.com/hyperledger/firefly-cli/internal/log" 27 | "github.com/hyperledger/firefly-cli/internal/stacks" 28 | "github.com/spf13/cobra" 29 | ) 30 | 31 | var forceUpgrade bool 32 | 33 | var upgradeCmd = &cobra.Command{ 34 | Use: "upgrade ", 35 | Short: "Upgrade a stack to different version", 36 | Long: `Upgrade a stack by pulling updated images. 37 | This operation will stop the stack if running. 38 | If certain containers were pinned to a specific image at init, 39 | this command will have no effect on those containers.`, 40 | Args: cobra.ExactArgs(2), 41 | ValidArgsFunction: listStacks, 42 | RunE: func(cmd *cobra.Command, args []string) error { 43 | var spin *spinner.Spinner 44 | if fancyFeatures && !verbose { 45 | spin = spinner.New(spinner.CharSets[11], 100*time.Millisecond) 46 | logger = log.NewSpinnerLogger(spin) 47 | } 48 | ctx := log.WithVerbosity(context.Background(), verbose) 49 | ctx = log.WithLogger(ctx, logger) 50 | 51 | dockerVersion, err := docker.CheckDockerConfig() 52 | if err != nil { 53 | return err 54 | } 55 | ctx = context.WithValue(ctx, docker.CtxComposeVersionKey{}, dockerVersion) 56 | 57 | stackManager := stacks.NewStackManager(ctx) 58 | if len(args) == 0 { 59 | return fmt.Errorf("no stack specified") 60 | } 61 | stackName := args[0] 62 | if len(args) <= 1 { 63 | return fmt.Errorf("no version specified") 64 | } 65 | version := args[1] 66 | 67 | if err := stackManager.LoadStack(stackName); err != nil { 68 | return err 69 | } 70 | fmt.Printf("upgrading stack '%s'... ", stackName) 71 | if err := stackManager.UpgradeStack(version, forceUpgrade); err != nil { 72 | return err 73 | } 74 | fmt.Printf("\n\nYour stack has been upgraded to %s\n\nTo start your upgraded stack run:\n\n%s start %s\n\n", version, rootCmd.Use, stackName) 75 | return nil 76 | }, 77 | } 78 | 79 | func init() { 80 | upgradeCmd.Flags().BoolVarP(&forceUpgrade, "force", "f", false, "Force upgrade even between unsupported versions. May result in a broken environment. Use with caution.") 81 | rootCmd.AddCommand(upgradeCmd) 82 | } 83 | -------------------------------------------------------------------------------- /cmd/version.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cmd 18 | 19 | import ( 20 | "encoding/json" 21 | "fmt" 22 | "runtime/debug" 23 | 24 | "github.com/spf13/cobra" 25 | "gopkg.in/yaml.v2" 26 | ) 27 | 28 | var shortened = false 29 | var output = "json" 30 | 31 | // set by go-releaser 32 | var ( 33 | BuildDate string 34 | BuildCommit string 35 | BuildVersionOverride string 36 | ) 37 | 38 | // Info creates a formattable struct for version output 39 | type Info struct { 40 | Version string `json:"Version,omitempty" yaml:"Version,omitempty"` 41 | Commit string `json:"Commit,omitempty" yaml:"Commit,omitempty"` 42 | Date string `json:"Date,omitempty" yaml:"Date,omitempty"` 43 | License string `json:"License,omitempty" yaml:"License,omitempty"` 44 | } 45 | 46 | var versionCmd = &cobra.Command{ 47 | Use: "version", 48 | Short: "Prints the version info", 49 | Long: "Prints the version info of the CLI binary", 50 | RunE: func(cmd *cobra.Command, args []string) error { 51 | 52 | info := &Info{ 53 | Version: BuildVersionOverride, 54 | Date: BuildDate, 55 | Commit: BuildCommit, 56 | License: "Apache-2.0", 57 | } 58 | 59 | // Where you are using go install, we will get good version information usefully from Go 60 | // When we're in go-releaser in a Github action, we will have the version passed in explicitly 61 | if info.Version == "" { 62 | buildInfo, ok := debug.ReadBuildInfo() 63 | if ok { 64 | info.Version = buildInfo.Main.Version 65 | } 66 | } 67 | 68 | if shortened { 69 | fmt.Println(info.Version) 70 | } else { 71 | var ( 72 | bytes []byte 73 | err error 74 | ) 75 | 76 | switch output { 77 | case "json": 78 | bytes, err = json.MarshalIndent(info, "", " ") 79 | case "yaml": 80 | bytes, err = yaml.Marshal(info) 81 | default: 82 | return fmt.Errorf("invalid output '%s'", output) 83 | } 84 | 85 | if err != nil { 86 | return err 87 | } 88 | 89 | fmt.Println(string(bytes)) 90 | } 91 | 92 | return nil 93 | }, 94 | } 95 | 96 | func init() { 97 | versionCmd.Flags().BoolVarP(&shortened, "short", "s", false, "print only the version") 98 | versionCmd.Flags().StringVarP(&output, "output", "o", "json", "output format (\"yaml\"|\"json\")") 99 | rootCmd.AddCommand(versionCmd) 100 | } 101 | -------------------------------------------------------------------------------- /docs/allow_ff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger/firefly-cli/0b16ee5d8ae1cf4d3952163850e91ffb0bd6a5a4/docs/allow_ff.png -------------------------------------------------------------------------------- /docs/blocked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger/firefly-cli/0b16ee5d8ae1cf4d3952163850e91ffb0bd6a5a4/docs/blocked.png -------------------------------------------------------------------------------- /docs/firefly_screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger/firefly-cli/0b16ee5d8ae1cf4d3952163850e91ffb0bd6a5a4/docs/firefly_screenshot.png -------------------------------------------------------------------------------- /docs/mac_help.md: -------------------------------------------------------------------------------- 1 | # Allow the FireFly CLI to run on macOS 2 | 3 | On macOS default security settings prevent running arbitrary executable files downloaded from the internet, so you'll need to approve the FireFly CLI. You will only have to go through these steps once after installing, or updating the FireFly CLI. The first time you try to run `ff` in your terminal you will likely see a dialog like this. Simply cancel this dialog. 4 | 5 | ## If you see this dialog, cancel it 6 | 7 | ![ff blocked](blocked.png) 8 | 9 | ## Go to System Preferences and click on Security & Privacy 10 | 11 | ![System Preferences](system_preferences.png) 12 | 13 | ## Click on the Allow Anyway button 14 | 15 | ![Allow ff](allow_ff.png) 16 | 17 | ## Now try running `ff` again and click Open 18 | 19 | Now try running the `ff` command in your terminal and a final confirmation dialog should appear. Click open: 20 | 21 | ![Open dialog](open.png) 22 | 23 | 24 | This time the `ff` command should succeed and you should see something like this in your terminal: 25 | 26 | ![FireFly CLI](firefly_screenshot.png) 27 | -------------------------------------------------------------------------------- /docs/open.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger/firefly-cli/0b16ee5d8ae1cf4d3952163850e91ffb0bd6a5a4/docs/open.png -------------------------------------------------------------------------------- /docs/system_preferences.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hyperledger/firefly-cli/0b16ee5d8ae1cf4d3952163850e91ffb0bd6a5a4/docs/system_preferences.png -------------------------------------------------------------------------------- /ff/main.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package main 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/cmd" 21 | ) 22 | 23 | func main() { 24 | cmd.Execute() 25 | } 26 | -------------------------------------------------------------------------------- /internal/blockchain/blockchain_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package blockchain 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/internal/docker" 21 | "github.com/hyperledger/firefly-cli/pkg/types" 22 | ) 23 | 24 | type IBlockchainProvider interface { 25 | WriteConfig(options *types.InitOptions) error 26 | FirstTimeSetup() error 27 | DeployFireFlyContract() (*types.ContractDeploymentResult, error) 28 | PreStart() error 29 | PostStart(firstTimeSetup bool) error 30 | GetDockerServiceDefinitions() []*docker.ServiceDefinition 31 | GetBlockchainPluginConfig(stack *types.Stack, org *types.Organization) (blockchainConfig *types.BlockchainConfig) 32 | GetOrgConfig(stack *types.Stack, org *types.Organization) (coreConfig *types.OrgConfig) 33 | Reset() error 34 | GetContracts(filename string, extraArgs []string) ([]string, error) 35 | DeployContract(filename, contractName, instanceName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) 36 | CreateAccount(args []string) (interface{}, error) 37 | ParseAccount(interface{}) interface{} 38 | GetConnectorName() string 39 | GetConnectorURL(org *types.Organization) string 40 | GetConnectorExternalURL(org *types.Organization) string 41 | } 42 | -------------------------------------------------------------------------------- /internal/blockchain/cardano/cardano.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cardano 18 | 19 | type Account struct { 20 | Address string `json:"address"` 21 | PrivateKey string `json:"privateKey"` 22 | } 23 | -------------------------------------------------------------------------------- /internal/blockchain/cardano/cardanosigner/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cardanosigner 18 | 19 | import ( 20 | "os" 21 | 22 | "gopkg.in/yaml.v2" 23 | ) 24 | 25 | type Config struct { 26 | API APIConfig `yaml:"api"` 27 | FileWallet FileWalletConfig `yaml:"fileWallet"` 28 | } 29 | 30 | type APIConfig struct { 31 | Address string `yaml:"address"` 32 | Port int `yaml:"port,omitempty"` 33 | } 34 | 35 | type FileWalletConfig struct { 36 | Path string `yaml:"path"` 37 | } 38 | 39 | func (c *Config) WriteConfig(filename string) error { 40 | configYamlBytes, _ := yaml.Marshal(c) 41 | return os.WriteFile(filename, configYamlBytes, 0755) 42 | } 43 | 44 | func GenerateSignerConfig() *Config { 45 | config := &Config{ 46 | API: APIConfig{ 47 | Address: "0.0.0.0", 48 | Port: 8555, 49 | }, 50 | FileWallet: FileWalletConfig{ 51 | Path: "/data/wallet", 52 | }, 53 | } 54 | return config 55 | } 56 | -------------------------------------------------------------------------------- /internal/blockchain/cardano/connector/cardanoconnect/client.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cardanoconnect 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | type Cardanoconnect struct { 24 | ctx context.Context 25 | } 26 | 27 | func NewCardanoconnect(ctx context.Context) *Cardanoconnect { 28 | return &Cardanoconnect{ 29 | ctx: ctx, 30 | } 31 | } 32 | 33 | func (c *Cardanoconnect) Name() string { 34 | return "cardanoconnect" 35 | } 36 | 37 | func (c *Cardanoconnect) Port() int { 38 | return 3000 39 | } 40 | -------------------------------------------------------------------------------- /internal/blockchain/cardano/connector/cardanoconnect/docker.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package cardanoconnect 18 | 19 | import ( 20 | "fmt" 21 | "strings" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/docker" 24 | "github.com/hyperledger/firefly-cli/pkg/types" 25 | ) 26 | 27 | func (c *Cardanoconnect) GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition { 28 | dependsOn := make(map[string]map[string]string) 29 | for dep, state := range dependentServices { 30 | dependsOn[dep] = map[string]string{"condition": state} 31 | } 32 | extraHosts := make([]string, 0) 33 | if strings.Contains(s.RemoteNodeURL, "host.docker.internal") { 34 | extraHosts = append(extraHosts, "host.docker.internal:host-gateway") 35 | } 36 | serviceDefinitions := make([]*docker.ServiceDefinition, len(s.Members)) 37 | for i, member := range s.Members { 38 | serviceDefinitions[i] = &docker.ServiceDefinition{ 39 | ServiceName: "cardanoconnect_" + member.ID, 40 | Service: &docker.Service{ 41 | Image: s.VersionManifest.Cardanoconnect.GetDockerImageString(), 42 | ContainerName: fmt.Sprintf("%s_cardanoconnect_%v", s.Name, i), 43 | Command: "./firefly-cardanoconnect -f /cardanoconnect/config/config.yaml", 44 | DependsOn: dependsOn, 45 | Ports: []string{fmt.Sprintf("%d:%d", member.ExposedConnectorPort, c.Port())}, 46 | ExtraHosts: extraHosts, 47 | Volumes: []string{ 48 | fmt.Sprintf("cardanoconnect_config_%s:/cardanoconnect/config", member.ID), 49 | fmt.Sprintf("cardanoconnect_contracts_%s:/cardanoconnect/contracts", member.ID), 50 | fmt.Sprintf("cardanoconnect_sqlite_%s:/cardanoconnect/sqlite", member.ID), 51 | }, 52 | Logging: docker.StandardLogOptions, 53 | }, 54 | VolumeNames: []string{ 55 | fmt.Sprintf("cardanoconnect_config_%s", member.ID), 56 | fmt.Sprintf("cardanoconnect_contracts_%s", member.ID), 57 | fmt.Sprintf("cardanoconnect_sqlite_%s", member.ID), 58 | }, 59 | } 60 | if s.Socket != "" { 61 | serviceDefinitions[i].Service.Volumes = append(serviceDefinitions[i].Service.Volumes, fmt.Sprintf("%s:/ipc/socket", s.Socket)) 62 | } 63 | } 64 | return serviceDefinitions 65 | } 66 | -------------------------------------------------------------------------------- /internal/blockchain/cardano/connector/connector_interface.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 IOG Singapore and SundaeSwap, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package connector 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/internal/docker" 21 | "github.com/hyperledger/firefly-cli/pkg/types" 22 | ) 23 | 24 | type Connector interface { 25 | GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition 26 | GenerateConfig(stack *types.Stack, member *types.Organization) Config 27 | Name() string 28 | Port() int 29 | } 30 | 31 | type Config interface { 32 | WriteConfig(filename string, extraConnectorConfigPath string) error 33 | } 34 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/accounts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethereum 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | 25 | "github.com/hyperledger/firefly-cli/internal/docker" 26 | "github.com/hyperledger/firefly-signer/pkg/keystorev3" 27 | "github.com/hyperledger/firefly-signer/pkg/secp256k1" 28 | ) 29 | 30 | func CreateWalletFile(outputDirectory, prefix, password string) (*secp256k1.KeyPair, string, error) { 31 | keyPair, err := secp256k1.GenerateSecp256k1KeyPair() 32 | if err != nil { 33 | return nil, "", err 34 | } 35 | wallet := keystorev3.NewWalletFileStandard(password, keyPair) 36 | 37 | if err := os.MkdirAll(outputDirectory, 0755); err != nil { 38 | return nil, "", err 39 | } 40 | 41 | var filename string 42 | if prefix != "" { 43 | filename = filepath.Join(outputDirectory, fmt.Sprintf("%v_%s", prefix, keyPair.Address.String()[2:])) 44 | } else { 45 | filename = filepath.Join(outputDirectory, keyPair.Address.String()[2:]) 46 | } 47 | err = os.WriteFile(filename, wallet.JSON(), 0755) 48 | if err != nil { 49 | return nil, "", err 50 | } 51 | return keyPair, filename, nil 52 | } 53 | 54 | func CopyWalletFileToVolume(ctx context.Context, walletFilePath, volumeName string) error { 55 | if err := docker.MkdirInVolume(ctx, volumeName, "/keystore"); err != nil { 56 | return err 57 | } 58 | if err := docker.CopyFileToVolume(ctx, volumeName, walletFilePath, "/keystore"); err != nil { 59 | return err 60 | } 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/accounts_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestCreateWalletFile(t *testing.T) { 11 | dir := t.TempDir() 12 | 13 | prefix := "WalletPair" 14 | outputDirectory := filepath.Join(dir + "wallet.json") 15 | password := "26371628355334###" 16 | t.Run("TestCreateWalletFile", func(t *testing.T) { 17 | keypair, filename, err := CreateWalletFile(outputDirectory, prefix, password) 18 | if err != nil { 19 | t.Logf("unable to create wallet file %v: ", err) 20 | } 21 | assert.NotNil(t, keypair) 22 | assert.NotNil(t, filename) 23 | }) 24 | } 25 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/connector_interface.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package connector 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/ethtypes" 21 | "github.com/hyperledger/firefly-cli/internal/docker" 22 | "github.com/hyperledger/firefly-cli/pkg/types" 23 | ) 24 | 25 | type Connector interface { 26 | FirstTimeSetup(stack *types.Stack) error 27 | GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition 28 | DeployContract(contract *ethtypes.CompiledContract, contractName string, member *types.Organization, extraArgs []string) (*types.ContractDeploymentResult, error) 29 | GenerateConfig(stack *types.Stack, member *types.Organization, blockchainServiceName string) Config 30 | Name() string 31 | Port() int 32 | } 33 | 34 | type Config interface { 35 | WriteConfig(filename string, extraConnectorConfigPath string) error 36 | } 37 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/ethconnect/client_test.go: -------------------------------------------------------------------------------- 1 | package ethconnect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPort(t *testing.T) { 10 | t.Run("testPort", func(t *testing.T) { 11 | Port := 8080 12 | E := &Ethconnect{} 13 | PortInt := E.Port() 14 | assert.Equal(t, Port, PortInt) 15 | }) 16 | } 17 | 18 | func TestName(t *testing.T) { 19 | t.Run("testName", func(t *testing.T) { 20 | Name := "ethconnect" 21 | E := &Ethconnect{} 22 | NameStr := E.Name() 23 | assert.Equal(t, Name, NameStr) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/ethconnect/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethconnect 18 | 19 | import ( 20 | "fmt" 21 | "os" 22 | "path/filepath" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/connector" 25 | "github.com/hyperledger/firefly-cli/pkg/types" 26 | "github.com/miracl/conflate" 27 | "gopkg.in/yaml.v3" 28 | ) 29 | 30 | type Config struct { 31 | Rest *Rest `yaml:"rest,omitempty"` 32 | } 33 | 34 | type Rest struct { 35 | RestGateway *RestGateway `yaml:"rest-gateway,omitempty"` 36 | } 37 | 38 | type RestGateway struct { 39 | RPC *RPC `yaml:"rpc,omitempty"` 40 | OpenAPI *OpenAPI `yaml:"openapi,omitempty"` 41 | HTTP *HTTP `yaml:"http,omitempty"` 42 | MaxTXWaitTime int `yaml:"maxTXWaitTime,omitempty"` 43 | MaxInFlight int `yaml:"maxInFlight,omitempty"` 44 | } 45 | 46 | type RPC struct { 47 | URL string `yaml:"url,omitempty"` 48 | } 49 | 50 | type OpenAPI struct { 51 | EventPollingIntervalSec int `yaml:"eventPollingIntervalSec,omitempty"` 52 | StoragePath string `yaml:"storagePath,omitempty"` 53 | EventsDB string `yaml:"eventsDB,omitempty"` 54 | } 55 | 56 | type HTTP struct { 57 | Port int `yaml:"port,omitempty"` 58 | } 59 | 60 | func (e *Config) WriteConfig(filename string, extraConnectorConfigPath string) error { 61 | configYamlBytes, _ := yaml.Marshal(e) 62 | basedir := filepath.Dir(filename) 63 | if err := os.MkdirAll(basedir, 0755); err != nil { 64 | return err 65 | } 66 | if err := os.WriteFile(filename, configYamlBytes, 0755); err != nil { 67 | return err 68 | } 69 | if extraConnectorConfigPath != "" { 70 | c, err := conflate.FromFiles(filename, extraConnectorConfigPath) 71 | if err != nil { 72 | return err 73 | } 74 | bytes, err := c.MarshalYAML() 75 | if err != nil { 76 | return err 77 | } 78 | if err := os.WriteFile(filename, bytes, 0755); err != nil { 79 | return err 80 | } 81 | } 82 | return nil 83 | } 84 | 85 | func (e *Ethconnect) GenerateConfig(stack *types.Stack, member *types.Organization, blockchainServiceName string) connector.Config { 86 | return &Config{ 87 | Rest: &Rest{ 88 | RestGateway: &RestGateway{ 89 | MaxTXWaitTime: 60, 90 | MaxInFlight: 10, 91 | RPC: &RPC{URL: fmt.Sprintf("http://%s:8545", blockchainServiceName)}, 92 | OpenAPI: &OpenAPI{ 93 | EventPollingIntervalSec: 1, 94 | StoragePath: "./data/abis", 95 | EventsDB: "./data/events", 96 | }, 97 | HTTP: &HTTP{ 98 | Port: 8080, 99 | }, 100 | }, 101 | }, 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/ethconnect/config_test.go: -------------------------------------------------------------------------------- 1 | package ethconnect 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | ) 7 | 8 | func TestWriteConfig(t *testing.T) { 9 | dir := t.TempDir() 10 | // Construct the paths for config files within the temporary directory 11 | configFilename := filepath.Join(dir, "config.yaml") 12 | extraEvmConfigPath := filepath.Join(dir, "conflate", "extra.yaml") 13 | p := Config{} 14 | t.Run("TestWriteConfig", func(t *testing.T) { 15 | err := p.WriteConfig(configFilename, extraEvmConfigPath) 16 | if err != nil { 17 | t.Logf("unable to write to config files: %v", err) 18 | } 19 | }) 20 | } 21 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/ethconnect/docker.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethconnect 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/hyperledger/firefly-cli/internal/docker" 23 | "github.com/hyperledger/firefly-cli/pkg/types" 24 | ) 25 | 26 | func (e *Ethconnect) GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition { 27 | dependsOn := make(map[string]map[string]string) 28 | for dep, state := range dependentServices { 29 | dependsOn[dep] = map[string]string{"condition": state} 30 | } 31 | serviceDefinitions := make([]*docker.ServiceDefinition, len(s.Members)) 32 | for i, member := range s.Members { 33 | serviceDefinitions[i] = &docker.ServiceDefinition{ 34 | ServiceName: "ethconnect_" + member.ID, 35 | Service: &docker.Service{ 36 | Image: s.VersionManifest.Ethconnect.GetDockerImageString(), 37 | ContainerName: fmt.Sprintf("%s_ethconnect_%v", s.Name, i), 38 | Command: "server -f ./config/config.yaml -d 2", 39 | DependsOn: dependsOn, 40 | Ports: []string{fmt.Sprintf("%d:8080", member.ExposedConnectorPort)}, 41 | Volumes: []string{ 42 | fmt.Sprintf("ethconnect_config_%s:/ethconnect/config", member.ID), 43 | fmt.Sprintf("ethconnect_data_%s:/ethconnect/data", member.ID), 44 | }, 45 | Logging: docker.StandardLogOptions, 46 | Environment: s.EnvironmentVars, 47 | }, 48 | VolumeNames: []string{ 49 | fmt.Sprintf("ethconnect_config_%v", member.ID), 50 | fmt.Sprintf("ethconnect_data_%v", member.ID), 51 | }, 52 | } 53 | } 54 | return serviceDefinitions 55 | } 56 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/ethconnect/docker_test.go: -------------------------------------------------------------------------------- 1 | package ethconnect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/pkg/types" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type MockManfest struct { 11 | types.ManifestEntry 12 | GetDockerImageStringMck func() string 13 | } 14 | 15 | func TestGetServiceDefinition(t *testing.T) { 16 | getManifest := &MockManfest{ 17 | GetDockerImageStringMck: func() string { 18 | return "ethconnect_alpine:latest" 19 | }, 20 | } 21 | testServices := []struct { 22 | Name string 23 | Members *types.Stack 24 | DependentServices map[string]string 25 | ServiceName string 26 | }{ 27 | { 28 | Name: "test_service_1", 29 | Members: &types.Stack{ 30 | Members: []*types.Organization{{ID: "firefly_1", ExposedConnectorPort: 3000}}, 31 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 32 | }, 33 | DependentServices: map[string]string{ 34 | "service1": "running", 35 | "service2": "stopped", 36 | }, 37 | ServiceName: "ethconnect_firefly_1", 38 | }, 39 | { 40 | Name: "test_service_2", 41 | Members: &types.Stack{ 42 | Members: []*types.Organization{{ID: "firefly_2", ExposedConnectorPort: 8002}}, 43 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 44 | }, 45 | DependentServices: map[string]string{ 46 | "service1": "stopped", 47 | "service2": "running", 48 | }, 49 | ServiceName: "ethconnect_firefly_2", 50 | }, 51 | { 52 | Name: "test_service_3", 53 | Members: &types.Stack{ 54 | Members: []*types.Organization{{ID: "firefly_3", ExposedConnectorPort: 8000}}, 55 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 56 | }, 57 | DependentServices: map[string]string{ 58 | "service1": "stopped", 59 | "service2": "stopped", 60 | "service3": "running", 61 | }, 62 | ServiceName: "ethconnect_firefly_3", 63 | }, 64 | { 65 | Name: "test_service_4", 66 | Members: &types.Stack{ 67 | Members: []*types.Organization{{ID: "firefly_4", ExposedConnectorPort: 7892}}, 68 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 69 | }, 70 | DependentServices: map[string]string{ 71 | "service1": "stopped", 72 | "service2": "stopped", 73 | "service3": "stopped", 74 | "service4": "running", 75 | }, 76 | ServiceName: "ethconnect_firefly_4", 77 | }, 78 | { 79 | Name: "test_env_vars_5", 80 | Members: &types.Stack{ 81 | Members: []*types.Organization{{ID: "firefly_5", ExposedConnectorPort: 7892}}, 82 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 83 | EnvironmentVars: map[string]interface{}{"HTTP_PROXY": ""}, 84 | }, 85 | DependentServices: map[string]string{ 86 | "service1": "running", 87 | "service2": "stopped", 88 | }, 89 | ServiceName: "ethconnect_firefly_5", 90 | }, 91 | } 92 | for _, tc := range testServices { 93 | t.Run(tc.Name, func(t *testing.T) { 94 | e := Ethconnect{} 95 | 96 | serviceDefinitions := e.GetServiceDefinitions(tc.Members, tc.DependentServices) 97 | assert.NotNil(t, serviceDefinitions) 98 | 99 | expectedCommand := "server -f ./config/config.yaml -d 2" 100 | if serviceDefinitions[0].Service.Command != expectedCommand { 101 | t.Errorf("Expected Command %q, got %q", expectedCommand, serviceDefinitions[0].Service.Command) 102 | } 103 | if serviceDefinitions[0].ServiceName != tc.ServiceName { 104 | t.Errorf("Expected ServiceName %q, got %q", tc.ServiceName, serviceDefinitions[0].ServiceName) 105 | } 106 | 107 | }) 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/evmconnect/client_test.go: -------------------------------------------------------------------------------- 1 | package evmconnect 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestPort(t *testing.T) { 11 | t.Run("testPort", func(t *testing.T) { 12 | Port := 5008 13 | e := &Evmconnect{} 14 | PortInt := e.Port() 15 | assert.Equal(t, Port, PortInt) 16 | }) 17 | } 18 | 19 | func TestName(t *testing.T) { 20 | t.Run("testName", func(t *testing.T) { 21 | Name := "evmconnect" 22 | e := &Evmconnect{} 23 | NameStr := e.Name() 24 | assert.Equal(t, Name, NameStr) 25 | }) 26 | } 27 | 28 | func TestNewEvmconnect(t *testing.T) { 29 | var Ctx context.Context 30 | EvmConnect := NewEvmconnect(Ctx) 31 | assert.NotNil(t, EvmConnect) 32 | 33 | } 34 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/evmconnect/config_test.go: -------------------------------------------------------------------------------- 1 | package evmconnect 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | ) 7 | 8 | func TestWriteConfig(t *testing.T) { 9 | dir := t.TempDir() 10 | configFilename := filepath.Join(dir + "config.yaml") 11 | extraEvmConfigPath := filepath.Join(dir + "/conflate/extra.yaml") 12 | p := Config{} 13 | t.Run("TestWriteConfig", func(t *testing.T) { 14 | err := p.WriteConfig(configFilename, extraEvmConfigPath) 15 | if err != nil { 16 | t.Logf("unable to write to config files: %v", err) 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/connector/evmconnect/docker.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package evmconnect 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/hyperledger/firefly-cli/internal/docker" 23 | "github.com/hyperledger/firefly-cli/pkg/types" 24 | ) 25 | 26 | func (e *Evmconnect) GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition { 27 | dependsOn := make(map[string]map[string]string) 28 | for dep, state := range dependentServices { 29 | dependsOn[dep] = map[string]string{"condition": state} 30 | } 31 | serviceDefinitions := make([]*docker.ServiceDefinition, len(s.Members)) 32 | for i, member := range s.Members { 33 | dataVolumeName := fmt.Sprintf("evmconnect_data_%s", member.ID) 34 | serviceDefinitions[i] = &docker.ServiceDefinition{ 35 | ServiceName: "evmconnect_" + member.ID, 36 | Service: &docker.Service{ 37 | Image: s.VersionManifest.Evmconnect.GetDockerImageString(), 38 | ContainerName: fmt.Sprintf("%s_evmconnect_%v", s.Name, i), 39 | Command: "-f /evmconnect/config.yaml", 40 | DependsOn: dependsOn, 41 | Ports: []string{fmt.Sprintf("%d:%v", member.ExposedConnectorPort, e.Port())}, 42 | Volumes: []string{ 43 | fmt.Sprintf("%s/config/evmconnect_%s.yaml:/evmconnect/config.yaml", s.RuntimeDir, member.ID), 44 | fmt.Sprintf("%s:/evmconnect/data", dataVolumeName), 45 | }, 46 | Logging: docker.StandardLogOptions, 47 | Environment: s.EnvironmentVars, 48 | }, 49 | VolumeNames: []string{ 50 | dataVolumeName, 51 | }, 52 | } 53 | } 54 | return serviceDefinitions 55 | } 56 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/contracts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethereum 18 | 19 | import ( 20 | "context" 21 | "encoding/json" 22 | "os" 23 | 24 | "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/ethtypes" 25 | "github.com/hyperledger/firefly-cli/internal/docker" 26 | ) 27 | 28 | type truffleCompiledContract struct { 29 | ABI interface{} `json:"abi"` 30 | Bytecode string `json:"bytecode"` 31 | ContractName string `json:"contractName"` 32 | } 33 | 34 | func ReadTruffleCompiledContract(filePath string) (*ethtypes.CompiledContracts, error) { 35 | d, _ := os.ReadFile(filePath) 36 | var truffleCompiledContract *truffleCompiledContract 37 | err := json.Unmarshal(d, &truffleCompiledContract) 38 | if err != nil { 39 | return nil, err 40 | } 41 | contract := ðtypes.CompiledContract{ 42 | ABI: truffleCompiledContract.ABI, 43 | Bytecode: truffleCompiledContract.Bytecode, 44 | } 45 | contracts := ðtypes.CompiledContracts{ 46 | Contracts: map[string]*ethtypes.CompiledContract{ 47 | truffleCompiledContract.ContractName: contract, 48 | }, 49 | } 50 | return contracts, nil 51 | } 52 | 53 | func ReadSolcCompiledContract(filePath string) (*ethtypes.CompiledContracts, error) { 54 | d, _ := os.ReadFile(filePath) 55 | var contracts *ethtypes.CompiledContracts 56 | err := json.Unmarshal(d, &contracts) 57 | if err != nil { 58 | return nil, err 59 | } 60 | return contracts, nil 61 | } 62 | 63 | func ReadContractJSON(filePath string) (*ethtypes.CompiledContracts, error) { 64 | contracts, err := ReadSolcCompiledContract(filePath) 65 | if err != nil { 66 | return nil, err 67 | } 68 | if len(contracts.Contracts) > 0 { 69 | return contracts, nil 70 | } 71 | return ReadTruffleCompiledContract(filePath) 72 | } 73 | 74 | func ExtractContracts(ctx context.Context, containerName, sourceDir, destinationDir string) error { 75 | if err := docker.RunDockerCommand(ctx, destinationDir, "cp", containerName+":"+sourceDir, destinationDir); err != nil { 76 | return err 77 | } 78 | return nil 79 | } 80 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/contracts_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestReadTruffleCompiledContract(t *testing.T) { 11 | dir := "testdata" 12 | contractFile := filepath.Join(dir, "truffle.json") 13 | t.Run("TestTruffleCompilesContract", func(t *testing.T) { 14 | ExpectedContractName := "FireFly_Client" 15 | 16 | compiledContracts, err := ReadTruffleCompiledContract(contractFile) 17 | if err != nil { 18 | t.Logf("unable to read truffle contract : %v", err) 19 | } 20 | contractMap := compiledContracts.Contracts 21 | assert.NotNil(t, compiledContracts) 22 | assert.NotNil(t, contractMap) 23 | 24 | contractName, ok := contractMap[ExpectedContractName] 25 | assert.True(t, ok, "Expected contract '%s' not found", ExpectedContractName) 26 | assert.NotNil(t, contractName) 27 | }) 28 | } 29 | 30 | func TestReadSolCompiledContract(t *testing.T) { 31 | dir := "testdata" 32 | contractFile := filepath.Join(dir, "sol.json") 33 | t.Run("TestReadSolContract", func(t *testing.T) { 34 | SolContract, err := ReadSolcCompiledContract(contractFile) 35 | if err != nil { 36 | t.Logf("Unable to read sol contract: %v", err) 37 | } 38 | assert.NotNil(t, SolContract) 39 | contractMap := SolContract.Contracts 40 | assert.NotNil(t, contractMap) 41 | }) 42 | } 43 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethereum.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethereum 18 | 19 | import ( 20 | "context" 21 | "encoding/hex" 22 | "errors" 23 | "fmt" 24 | "path/filepath" 25 | 26 | secp256k1 "github.com/btcsuite/btcd/btcec/v2" 27 | "github.com/hyperledger/firefly-cli/pkg/types" 28 | "golang.org/x/crypto/sha3" 29 | 30 | "github.com/hyperledger/firefly-cli/internal/blockchain/ethereum/ethtypes" 31 | "github.com/hyperledger/firefly-cli/internal/log" 32 | ) 33 | 34 | type Account struct { 35 | Address string `json:"address"` 36 | PrivateKey string `json:"privateKey"` 37 | PtmPublicKey string `json:"ptmPublicKey"` // Public key used for Tessera 38 | } 39 | 40 | func GenerateAddressAndPrivateKey() (address string, privateKey string) { 41 | newPrivateKey, _ := secp256k1.NewPrivateKey() 42 | privateKeyBytes := newPrivateKey.Serialize() 43 | encodedPrivateKey := "0x" + hex.EncodeToString(privateKeyBytes) 44 | // Remove the "04" Suffix byte when computing the address. This byte indicates that it is an uncompressed public key. 45 | publicKeyBytes := newPrivateKey.PubKey().SerializeUncompressed()[1:] 46 | // Take the hash of the public key to generate the address 47 | hash := sha3.NewLegacyKeccak256() 48 | hash.Write(publicKeyBytes) 49 | // Ethereum addresses only use the lower 20 bytes, so toss the rest away 50 | encodedAddress := "0x" + hex.EncodeToString(hash.Sum(nil)[12:32]) 51 | 52 | return encodedAddress, encodedPrivateKey 53 | } 54 | 55 | func ReadFireFlyContract(ctx context.Context, s *types.Stack) (*ethtypes.CompiledContract, error) { 56 | log := log.LoggerFromContext(ctx) 57 | var containerName string 58 | for _, member := range s.Members { 59 | if !member.External { 60 | containerName = fmt.Sprintf("%s_firefly_core_%s", s.Name, member.ID) 61 | break 62 | } 63 | } 64 | if containerName == "" { 65 | return nil, errors.New("unable to extract contracts from container - no valid firefly core containers found in stack") 66 | } 67 | log.Info("extracting smart contracts") 68 | 69 | if err := ExtractContracts(ctx, containerName, "/firefly/contracts", s.RuntimeDir); err != nil { 70 | return nil, err 71 | } 72 | 73 | var fireflyContract *ethtypes.CompiledContract 74 | contracts, err := ReadContractJSON(filepath.Join(s.RuntimeDir, "contracts", "Firefly.json")) 75 | if err != nil { 76 | return nil, err 77 | } 78 | 79 | fireflyContract, ok := contracts.Contracts["Firefly.sol:Firefly"] 80 | if !ok { 81 | fireflyContract, ok = contracts.Contracts["FireFly"] 82 | if !ok { 83 | return nil, fmt.Errorf("unable to find compiled FireFly contract") 84 | } 85 | } 86 | 87 | return fireflyContract, nil 88 | } 89 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethereum_test.go: -------------------------------------------------------------------------------- 1 | package ethereum 2 | 3 | import ( 4 | "context" 5 | "path/filepath" 6 | "testing" 7 | 8 | "github.com/hyperledger/firefly-cli/internal/log" 9 | "github.com/hyperledger/firefly-cli/pkg/types" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestReadFireFlyContract(t *testing.T) { 14 | ctx := log.WithLogger(context.Background(), &log.StdoutLogger{}) 15 | dir := "testdata" 16 | 17 | Stack := &types.Stack{ 18 | Name: "Firefly_Ethereum", 19 | Members: []*types.Organization{ 20 | { 21 | ID: "user_1", 22 | Account: &Account{ 23 | Address: "0x1f2a000000000000000000000000000000000000", 24 | PrivateKey: "aabbccddeeff0011223344556677889900112233445566778899aabbccddeeff", 25 | }, 26 | External: true, 27 | }, 28 | { 29 | ID: "user_2", 30 | Account: &Account{ 31 | Address: "0x549b5f43a40e1a0522864a004cfff2b0ca473a65", 32 | PrivateKey: "112233445566778899aabbccddeeff00112233445566778899aabbccddeeff00", 33 | }, 34 | External: true, 35 | }, 36 | { 37 | ID: "user_3", 38 | Account: &Account{ 39 | Address: "0x1234567890abcdef0123456789abcdef6789abcd", 40 | PrivateKey: "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff", 41 | }, 42 | External: true, 43 | }, 44 | }, 45 | RuntimeDir: filepath.Join(dir + "Firefly.json"), 46 | } 47 | t.Run("TestReadFileflyContract", func(t *testing.T) { 48 | Contracts, err := ReadFireFlyContract(ctx, Stack) 49 | if err != nil { 50 | t.Logf("unable to read eth firefly contract: %v", err) 51 | } 52 | assert.Nil(t, Contracts) 53 | }) 54 | 55 | } 56 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethsigner/accounts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethsigner 18 | 19 | import ( 20 | "context" 21 | "fmt" 22 | "os" 23 | "path/filepath" 24 | 25 | "github.com/hyperledger/firefly-cli/internal/docker" 26 | ) 27 | 28 | func (p *EthSignerProvider) writeTomlKeyFile(walletFilePath string) (string, error) { 29 | outputDirectory := filepath.Dir(walletFilePath) 30 | keyFile := filepath.Base(walletFilePath) 31 | toml := fmt.Sprintf(`[metadata] 32 | createdAt = 2019-11-05T08:15:30-05:00 33 | description = "File based configuration" 34 | 35 | [signing] 36 | type = "file-based-signer" 37 | key-file = "/data/keystore/%s" 38 | password-file = "/data/password" 39 | `, keyFile) 40 | filename := filepath.Join(outputDirectory, fmt.Sprintf("%s.toml", keyFile)) 41 | return filename, os.WriteFile(filename, []byte(toml), 0755) 42 | } 43 | 44 | func (p *EthSignerProvider) copyTomlFileToVolume(ctx context.Context, tomlFilePath, volumeName string) error { 45 | if err := docker.MkdirInVolume(ctx, volumeName, "/keystore"); err != nil { 46 | return err 47 | } 48 | if err := docker.CopyFileToVolume(ctx, volumeName, tomlFilePath, "/keystore"); err != nil { 49 | return err 50 | } 51 | return nil 52 | } 53 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethsigner/accounts_test.go: -------------------------------------------------------------------------------- 1 | package ethsigner 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestWriteTomlKeyFile(t *testing.T) { 11 | t.Run("TestwriteTomlKeyFile", func(t *testing.T) { 12 | directory := t.TempDir() 13 | FilePath := filepath.Join(directory + "/wallet.toml") 14 | 15 | p := &EthSignerProvider{} 16 | 17 | File, err := p.writeTomlKeyFile(FilePath) 18 | if err != nil { 19 | t.Fatalf("unable to write file: %v", err) 20 | } 21 | assert.NotNil(t, File) 22 | }) 23 | 24 | } 25 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethsigner/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethsigner 18 | 19 | import ( 20 | "os" 21 | 22 | "gopkg.in/yaml.v3" 23 | ) 24 | 25 | type FileWalletFilenamesConfig struct { 26 | With0xPrefix bool `yaml:"with0xPrefix,omitempty"` 27 | PrimaryExt string `yaml:"primaryExt,omitempty"` 28 | PasswordExt string `yaml:"passwordExt,omitempty"` 29 | } 30 | 31 | type FileWalletMetadataConfig struct { 32 | Format string `yaml:"format,omitempty"` 33 | KeyFileProperty string `yaml:"keyFileProperty,omitempty"` 34 | PasswordFileProperty string `yaml:"passwordFileProperty,omitempty"` 35 | } 36 | 37 | type FileWalletConfig struct { 38 | Path string `yaml:"path,omitempty"` 39 | DefaultPasswordFile string `yaml:"defaultPasswordFile,omitempty"` 40 | Filenames *FileWalletFilenamesConfig `yaml:"filenames,omitempty"` 41 | Metadata *FileWalletMetadataConfig `yaml:"metadata,omitempty"` 42 | } 43 | 44 | type ServerConfig struct { 45 | Port int `yaml:"port,omitempty"` 46 | Address string `yaml:"address,omitempty"` 47 | } 48 | 49 | type BackendConfig struct { 50 | ChainID *int64 `yaml:"chainId,omitempty"` 51 | URL string `yaml:"url,omitempty"` 52 | } 53 | 54 | type LogConfig struct { 55 | Level string `yaml:"level,omitempty"` 56 | } 57 | 58 | type Config struct { 59 | Server ServerConfig `yaml:"server"` 60 | Backend BackendConfig `yaml:"backend"` 61 | FileWallet FileWalletConfig `yaml:"fileWallet"` 62 | Log LogConfig `yaml:"log"` 63 | } 64 | 65 | func (e *Config) WriteConfig(filename string) error { 66 | configYamlBytes, _ := yaml.Marshal(e) 67 | return os.WriteFile(filename, configYamlBytes, 0755) 68 | } 69 | 70 | func GenerateSignerConfig(chainID int64, rpcURL string) *Config { 71 | return &Config{ 72 | Server: ServerConfig{ 73 | Port: 8545, 74 | Address: "0.0.0.0", 75 | }, 76 | Backend: BackendConfig{ 77 | URL: rpcURL, 78 | ChainID: &chainID, 79 | }, 80 | FileWallet: FileWalletConfig{ 81 | Path: "/data/keystore", 82 | Filenames: &FileWalletFilenamesConfig{ 83 | PrimaryExt: ".toml", 84 | }, 85 | Metadata: &FileWalletMetadataConfig{ 86 | KeyFileProperty: `{{ index .signing "key-file" }}`, 87 | PasswordFileProperty: `{{ index .signing "password-file" }}`, 88 | }, 89 | }, 90 | Log: LogConfig{ 91 | Level: "debug", 92 | }, 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethsigner/config_test.go: -------------------------------------------------------------------------------- 1 | package ethsigner 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestWriteConfig(t *testing.T) { 11 | dir := t.TempDir() 12 | 13 | testfiles := []struct { 14 | Name string 15 | FileName string 16 | }{ 17 | {Name: "test-1", FileName: filepath.Join(dir + "test1.yaml")}, 18 | {Name: "test-2", FileName: filepath.Join(dir + "test2.yaml")}, 19 | {Name: "test-3", FileName: filepath.Join(dir + "test3.yaml")}, 20 | {Name: "test-4", FileName: filepath.Join(dir + "test4.yaml")}, 21 | {Name: "test-5", FileName: filepath.Join(dir + "test5.yaml")}, 22 | } 23 | for _, tc := range testfiles { 24 | t.Run(tc.Name, func(t *testing.T) { 25 | c := &Config{} 26 | err := c.WriteConfig(tc.FileName) 27 | if err != nil { 28 | t.Logf("unable to write config files :%s", err) 29 | } 30 | }) 31 | } 32 | } 33 | 34 | func TestGenerateSignerConfig(t *testing.T) { 35 | chainID := int64(12345) 36 | rpcURL := "http://localhost:8545" 37 | expectedConfig := &Config{ 38 | Server: ServerConfig{ 39 | Port: 8545, 40 | Address: "0.0.0.0", 41 | }, 42 | Backend: BackendConfig{ 43 | URL: rpcURL, 44 | ChainID: &chainID, 45 | }, 46 | FileWallet: FileWalletConfig{ 47 | Path: "/data/keystore", 48 | Filenames: &FileWalletFilenamesConfig{ 49 | PrimaryExt: ".toml", 50 | }, 51 | Metadata: &FileWalletMetadataConfig{ 52 | KeyFileProperty: `{{ index .signing "key-file" }}`, 53 | PasswordFileProperty: `{{ index .signing "password-file" }}`, 54 | }, 55 | }, 56 | Log: LogConfig{ 57 | Level: "debug", 58 | }, 59 | } 60 | config := GenerateSignerConfig(chainID, rpcURL) 61 | assert.NotNil(t, config.Backend) 62 | assert.NotNil(t, config.Server) 63 | assert.NotNil(t, config.FileWallet) 64 | assert.NotNil(t, config.Log) 65 | 66 | assert.Equal(t, expectedConfig, config, "Generated config should match the expected config") 67 | } 68 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethsigner/ethsigner_test.go: -------------------------------------------------------------------------------- 1 | package ethsigner 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/pkg/types" 7 | ) 8 | 9 | func TestWriteSignerConfig(t *testing.T) { 10 | options := &types.InitOptions{ChainID: int64(689)} 11 | stack := &types.Stack{Name: "firefly_eth"} 12 | rpcURL := "http://localhost:9583" 13 | e := EthSignerProvider{ 14 | stack: stack, 15 | } 16 | err := e.WriteConfig(options, rpcURL) 17 | if err != nil { 18 | t.Logf("unable to write config :%v", err) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/ethtypes/types.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package ethtypes 18 | 19 | type CompiledContracts struct { 20 | Contracts map[string]*CompiledContract `json:"contracts"` 21 | } 22 | 23 | type CompiledContract struct { 24 | Name string `json:"name"` 25 | ABI interface{} `json:"abi"` 26 | Bytecode string `json:"bin"` 27 | } 28 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/geth/client.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package geth 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "fmt" 23 | "io" 24 | "net/http" 25 | ) 26 | 27 | type GethClient struct { 28 | rpcURL string 29 | } 30 | 31 | type JSONRPCRequest struct { 32 | JSONRPC string `json:"jsonrpc"` 33 | ID int `json:"id"` 34 | Method string `json:"method"` 35 | Params []interface{} `json:"params"` 36 | } 37 | 38 | type JSONRPCResponse struct { 39 | JSONRPC string `json:"jsonrpc"` 40 | ID int `json:"id"` 41 | Error *JSONRPCError `json:"error,omitempty"` 42 | Result interface{} `json:"result,omitempty"` 43 | } 44 | 45 | type JSONRPCError struct { 46 | Code int `json:"code"` 47 | Message string `json:"message"` 48 | } 49 | 50 | func NewGethClient(rpcURL string) *GethClient { 51 | return &GethClient{ 52 | rpcURL: rpcURL, 53 | } 54 | } 55 | 56 | func (g *GethClient) UnlockAccount(address string, password string) error { 57 | requestBody, err := json.Marshal(&JSONRPCRequest{ 58 | JSONRPC: "2.0", 59 | ID: 0, 60 | Method: "personal_unlockAccount", 61 | Params: []interface{}{address, password, 0}, 62 | }) 63 | if err != nil { 64 | return err 65 | } 66 | req, err := http.NewRequest("POST", g.rpcURL, bytes.NewBuffer(requestBody)) 67 | if err != nil { 68 | return err 69 | } 70 | req.Header.Set("Content-Type", "application/json") 71 | client := &http.Client{} 72 | resp, err := client.Do(req) 73 | if err != nil { 74 | return err 75 | } 76 | defer resp.Body.Close() 77 | responseBody, err := io.ReadAll(resp.Body) 78 | if err != nil { 79 | return err 80 | } 81 | if resp.StatusCode != 200 { 82 | return fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) 83 | } 84 | var rpcResponse *JSONRPCResponse 85 | err = json.Unmarshal(responseBody, &rpcResponse) 86 | if err != nil { 87 | return err 88 | } 89 | if rpcResponse.Error != nil { 90 | return fmt.Errorf("%s", rpcResponse.Error.Message) 91 | } 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/quorum/client.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package quorum 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "fmt" 23 | "io" 24 | "net/http" 25 | ) 26 | 27 | type QuorumClient struct { 28 | rpcURL string 29 | } 30 | 31 | type JSONRPCRequest struct { 32 | JSONRPC string `json:"jsonrpc"` 33 | ID int `json:"id"` 34 | Method string `json:"method"` 35 | Params []interface{} `json:"params"` 36 | } 37 | 38 | type JSONRPCResponse struct { 39 | JSONRPC string `json:"jsonrpc"` 40 | ID int `json:"id"` 41 | Error *JSONRPCError `json:"error,omitempty"` 42 | Result interface{} `json:"result,omitempty"` 43 | } 44 | 45 | type JSONRPCError struct { 46 | Code int `json:"code"` 47 | Message string `json:"message"` 48 | } 49 | 50 | func NewQuorumClient(rpcURL string) *QuorumClient { 51 | return &QuorumClient{ 52 | rpcURL: rpcURL, 53 | } 54 | } 55 | 56 | func (g *QuorumClient) UnlockAccount(address string, password string) error { 57 | requestBody, err := json.Marshal(&JSONRPCRequest{ 58 | JSONRPC: "2.0", 59 | ID: 0, 60 | Method: "personal_unlockAccount", 61 | Params: []interface{}{address, password, 0}, 62 | }) 63 | if err != nil { 64 | return err 65 | } 66 | req, err := http.NewRequest("POST", g.rpcURL, bytes.NewBuffer(requestBody)) 67 | if err != nil { 68 | return err 69 | } 70 | req.Header.Set("Content-Type", "application/json") 71 | client := &http.Client{} 72 | resp, err := client.Do(req) 73 | if err != nil { 74 | return err 75 | } 76 | defer resp.Body.Close() 77 | responseBody, err := io.ReadAll(resp.Body) 78 | if err != nil { 79 | return err 80 | } 81 | if resp.StatusCode != 200 { 82 | return fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) 83 | } 84 | var rpcResponse *JSONRPCResponse 85 | err = json.Unmarshal(responseBody, &rpcResponse) 86 | if err != nil { 87 | return err 88 | } 89 | if rpcResponse.Error != nil { 90 | return fmt.Errorf("%s", rpcResponse.Error.Message) 91 | } 92 | return nil 93 | } 94 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/quorum/client_test.go: -------------------------------------------------------------------------------- 1 | package quorum 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/hyperledger/firefly-cli/internal/utils" 9 | "github.com/jarcoal/httpmock" 10 | "github.com/stretchr/testify/assert" 11 | ) 12 | 13 | func TestUnlockAccount(t *testing.T) { 14 | tests := []struct { 15 | Name string 16 | RPCUrl string 17 | Address string 18 | Password string 19 | StatusCode int 20 | ApiResponse *JSONRPCResponse 21 | }{ 22 | { 23 | Name: "TestUnlockAccount-1", 24 | RPCUrl: "http://127.0.0.1:8545", 25 | Address: "user-1", 26 | Password: "POST", 27 | StatusCode: 200, 28 | ApiResponse: &JSONRPCResponse{ 29 | JSONRPC: "2.0", 30 | ID: 0, 31 | Error: nil, 32 | Result: "mock result", 33 | }, 34 | }, 35 | { 36 | Name: "TestUnlockAccountError-2", 37 | RPCUrl: "http://127.0.0.1:8545", 38 | Address: "user-1", 39 | Password: "POST", 40 | StatusCode: 200, 41 | ApiResponse: &JSONRPCResponse{ 42 | JSONRPC: "2.0", 43 | ID: 0, 44 | Error: &JSONRPCError{500, "invalid account"}, 45 | Result: "mock result", 46 | }, 47 | }, 48 | { 49 | Name: "TestUnlockAccountHTTPError-3", 50 | RPCUrl: "http://localhost:8545", 51 | Address: "user-1", 52 | Password: "POST", 53 | StatusCode: 500, 54 | ApiResponse: &JSONRPCResponse{ 55 | JSONRPC: "2.0", 56 | ID: 0, 57 | Error: nil, 58 | Result: "mock result", 59 | }, 60 | }, 61 | } 62 | for _, tc := range tests { 63 | t.Run(tc.Name, func(t *testing.T) { 64 | apiResponse, _ := json.Marshal(tc.ApiResponse) 65 | // mockResponse 66 | httpmock.RegisterResponder("POST", tc.RPCUrl, 67 | httpmock.NewStringResponder(tc.StatusCode, string(apiResponse))) 68 | client := NewQuorumClient(tc.RPCUrl) 69 | utils.StartMockServer(t) 70 | err := client.UnlockAccount(tc.Address, tc.Password) 71 | utils.StopMockServer(t) 72 | 73 | // expect errors when returned status code != 200 or ApiResponse comes back with non nil error 74 | if tc.StatusCode != 200 || tc.ApiResponse.Error != nil { 75 | assert.NotNil(t, err, "expects error to be returned when either quorum returns an application error or non 200 http response") 76 | } else { 77 | assert.NoError(t, err, fmt.Sprintf("unable to unlock account: %v", err)) 78 | } 79 | }) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/quorum/quorum_test.go: -------------------------------------------------------------------------------- 1 | package quorum 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/hyperledger/firefly-cli/internal/log" 12 | "github.com/hyperledger/firefly-cli/pkg/types" 13 | "github.com/hyperledger/firefly-common/pkg/fftypes" 14 | "github.com/stretchr/testify/assert" 15 | ) 16 | 17 | func TestCreateQuorumEntrypoint(t *testing.T) { 18 | ctx := log.WithVerbosity(log.WithLogger(context.Background(), &log.StdoutLogger{}), false) 19 | testCases := []struct { 20 | Name string 21 | Stack *types.Stack 22 | Consensus string 23 | StackName string 24 | MemberIndex int 25 | ChainID int 26 | BlockPeriodInSeconds int 27 | PrivateTransactionManager fftypes.FFEnum 28 | }{ 29 | { 30 | Name: "testcase1", 31 | Stack: &types.Stack{ 32 | Name: "Org-1_quorum", 33 | InitDir: t.TempDir(), 34 | }, 35 | Consensus: "ibft", 36 | StackName: "org1", 37 | MemberIndex: 0, 38 | ChainID: 1337, 39 | BlockPeriodInSeconds: -1, 40 | PrivateTransactionManager: types.PrivateTransactionManagerTessera, 41 | }, 42 | { 43 | Name: "testcase2_with_tessera_disabled", 44 | Stack: &types.Stack{ 45 | Name: "Org-2_quorum", 46 | InitDir: t.TempDir(), 47 | }, 48 | Consensus: "clique", 49 | StackName: "org2", 50 | MemberIndex: 1, 51 | ChainID: 1337, 52 | BlockPeriodInSeconds: 3, 53 | PrivateTransactionManager: types.PrivateTransactionManagerNone, 54 | }, 55 | } 56 | for _, tc := range testCases { 57 | t.Run(tc.Name, func(t *testing.T) { 58 | err := CreateQuorumEntrypoint(ctx, tc.Stack.InitDir, tc.Consensus, tc.StackName, tc.MemberIndex, tc.ChainID, tc.BlockPeriodInSeconds, tc.PrivateTransactionManager) 59 | if err != nil { 60 | t.Log("unable to create quorum docker entrypoint", err) 61 | } 62 | path := filepath.Join(tc.Stack.InitDir, "docker-entrypoint.sh") 63 | _, err = os.Stat(path) 64 | assert.NoError(t, err, "docker entrypoint file not created") 65 | 66 | b, err := os.ReadFile(path) 67 | assert.NoError(t, err, "unable to read docker entrypoint file") 68 | output := string(b) 69 | strings.Contains(output, fmt.Sprintf("member%dtessera", tc.MemberIndex)) 70 | strings.Contains(output, fmt.Sprintf("GOQUORUM_CONS_ALGO=%s", tc.Consensus)) 71 | }) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/tessera/tessera_test.go: -------------------------------------------------------------------------------- 1 | package tessera 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os" 7 | "path/filepath" 8 | "strings" 9 | "testing" 10 | 11 | "github.com/hyperledger/firefly-cli/internal/log" 12 | "github.com/hyperledger/firefly-cli/pkg/types" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func TestCreateTesseraKeys(t *testing.T) { 17 | ctx := log.WithVerbosity(log.WithLogger(context.Background(), &log.StdoutLogger{}), false) 18 | testCases := []struct { 19 | Name string 20 | Stack *types.Stack 21 | TesseraImage string 22 | KeysPrefix string 23 | KeysName string 24 | }{ 25 | { 26 | Name: "testcase1", 27 | Stack: &types.Stack{ 28 | Name: "Org-1_quorum", 29 | InitDir: t.TempDir(), 30 | }, 31 | TesseraImage: "quorumengineering/tessera:24.4", 32 | KeysPrefix: "", 33 | KeysName: "tm", 34 | }, 35 | { 36 | Name: "testcase2", 37 | Stack: &types.Stack{ 38 | Name: "Org-1_quorum", 39 | InitDir: t.TempDir(), 40 | }, 41 | TesseraImage: "quorumengineering/tessera:24.4", 42 | KeysPrefix: "xyz", 43 | KeysName: "tm", 44 | }, 45 | } 46 | for _, tc := range testCases { 47 | t.Run(tc.Name, func(t *testing.T) { 48 | privateKey, publicKey, tesseraKeysPath, err := CreateTesseraKeys(ctx, tc.TesseraImage, filepath.Join(tc.Stack.InitDir, "tessera", "tessera_0", "keystore"), tc.KeysPrefix, tc.KeysName) 49 | if err != nil { 50 | t.Log("unable to create tessera keys", err) 51 | } 52 | //validate properties of tessera keys 53 | assert.NotEmpty(t, privateKey) 54 | assert.NotEmpty(t, publicKey) 55 | assert.NotEmpty(t, tesseraKeysPath) 56 | 57 | expectedOutputName := tc.KeysName 58 | if tc.KeysPrefix != "" { 59 | expectedOutputName = fmt.Sprintf("%s_%s", tc.KeysPrefix, expectedOutputName) 60 | } 61 | assert.Equal(t, tesseraKeysPath, filepath.Join(tc.Stack.InitDir, "tessera", "tessera_0", "keystore", expectedOutputName), "invalid output path") 62 | 63 | assert.Nil(t, err) 64 | }) 65 | } 66 | } 67 | 68 | func TestCreateTesseraEntrypoint(t *testing.T) { 69 | ctx := log.WithVerbosity(log.WithLogger(context.Background(), &log.StdoutLogger{}), false) 70 | testCases := []struct { 71 | Name string 72 | Stack *types.Stack 73 | StackName string 74 | MemberCount int 75 | }{ 76 | { 77 | Name: "testcase1", 78 | Stack: &types.Stack{ 79 | Name: "Org-1_quorum", 80 | InitDir: t.TempDir(), 81 | }, 82 | StackName: "org1", 83 | MemberCount: 4, 84 | }, 85 | { 86 | Name: "testcase2", 87 | Stack: &types.Stack{ 88 | Name: "Org-2_quorum", 89 | InitDir: t.TempDir(), 90 | }, 91 | StackName: "org2", 92 | MemberCount: 0, 93 | }, 94 | } 95 | for _, tc := range testCases { 96 | t.Run(tc.Name, func(t *testing.T) { 97 | err := CreateTesseraEntrypoint(ctx, tc.Stack.InitDir, tc.StackName, tc.MemberCount) 98 | if err != nil { 99 | t.Log("unable to create tessera docker entrypoint", err) 100 | } 101 | path := filepath.Join(tc.Stack.InitDir, "docker-entrypoint.sh") 102 | _, err = os.Stat(path) 103 | assert.NoError(t, err, "docker entrypoint file not created") 104 | 105 | b, err := os.ReadFile(path) 106 | assert.NoError(t, err, "unable to read docker entrypoint file") 107 | for i := 0; i < tc.MemberCount; i++ { 108 | strings.Contains(string(b), fmt.Sprintf("member%dtessera", i)) 109 | } 110 | }) 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/testdata/Firefly.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": { 3 | "Firefly.sol:Firefly": { 4 | "abi": [ 5 | { 6 | "constant": false, 7 | "inputs": [ 8 | { 9 | "name": "_value", 10 | "type": "uint256" 11 | } 12 | ], 13 | "name": "setValue", 14 | "outputs": [], 15 | "payable": false, 16 | "stateMutability": "nonpayable", 17 | "type": "function" 18 | }, 19 | { 20 | "constant": true, 21 | "inputs": [], 22 | "name": "getValue", 23 | "outputs": [ 24 | { 25 | "name": "transaction", 26 | "type": "uint256" 27 | } 28 | ], 29 | "payable": false, 30 | "stateMutability": "view", 31 | "type": "function" 32 | }, 33 | { 34 | "constant": true, 35 | "inputs": [], 36 | "name": "getOwner", 37 | "outputs": [ 38 | { 39 | "name": "hyperledger_client", 40 | "type": "address" 41 | } 42 | ], 43 | "payable": false, 44 | "stateMutability": "view", 45 | "type": "function" 46 | } 47 | ], 48 | "bytecode": "0x60606033445566778899aabbccddeeff00112233445566778899aabbccddeeff", 49 | "contractName": "Firefly_Ethereum" 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/testdata/sol.json: -------------------------------------------------------------------------------- 1 | { 2 | "contracts": { 3 | "MyContract.sol": { 4 | "MyContract": { 5 | "abi": [ 6 | { 7 | "constant": false, 8 | "inputs": [ 9 | { 10 | "name": "_value", 11 | "type": "uint256" 12 | } 13 | ], 14 | "name": "setValue", 15 | "outputs": [], 16 | "payable": false, 17 | "stateMutability": "nonpayable", 18 | "type": "function" 19 | }, 20 | { 21 | "constant": true, 22 | "inputs": [], 23 | "name": "getValue", 24 | "outputs": [ 25 | { 26 | "name": "transaction", 27 | "type": "uint256" 28 | } 29 | ], 30 | "payable": false, 31 | "stateMutability": "view", 32 | "type": "function" 33 | } 34 | ], 35 | "evm": { 36 | "bytecode": { 37 | "object": "0x606060..." 38 | } 39 | 40 | } 41 | } 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /internal/blockchain/ethereum/testdata/truffle.json: -------------------------------------------------------------------------------- 1 | { 2 | "abi": [ 3 | { 4 | "constant": false, 5 | "inputs": [ 6 | { 7 | "name": "_value", 8 | "type": "uint256" 9 | } 10 | ], 11 | "name": "setValue", 12 | "outputs": [], 13 | "payable": false, 14 | "stateMutability": "nonpayable", 15 | "type": "function" 16 | }, 17 | { 18 | "constant": true, 19 | "inputs": [], 20 | "name": "getValue", 21 | "outputs": [ 22 | { 23 | "name": "transaction", 24 | "type": "uint256" 25 | } 26 | ], 27 | "payable": false, 28 | "stateMutability": "view", 29 | "type": "function" 30 | }, 31 | { 32 | "constant": true, 33 | "inputs": [], 34 | "name": "getOwner", 35 | "outputs": [ 36 | { 37 | "name": "hyperledger_client", 38 | "type": "address" 39 | } 40 | ], 41 | "payable": false, 42 | "stateMutability": "view", 43 | "type": "function" 44 | } 45 | ], 46 | "bytecode": "0x60606033445566778899aabbccddeeff00112233445566778899aabbccddeeff", 47 | "contractName": "FireFly_Client" 48 | } 49 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package fabric 18 | 19 | // Note: Any change of image tag should be checked if it is published in arm64 format. 20 | // Refer to this commit for when arm64 support was added and the code workaround was removed: 21 | // https://github.com/hyperledger/firefly-cli/pull/323/commits/71237b73b07bfee72b355dea83af9cd874b2a2de 22 | var FabricToolsImageName = "hyperledger/fabric-tools:2.5.6" 23 | var FabricCAImageName = "hyperledger/fabric-ca:1.5" 24 | var FabricOrdererImageName = "hyperledger/fabric-orderer:2.5" 25 | var FabricPeerImageName = "hyperledger/fabric-peer:2.5" 26 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/contracts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package fabric 18 | 19 | type QueryInstalledResponse struct { 20 | InstalledChaincodes []*InstalledChaincode `json:"installed_chaincodes"` 21 | } 22 | 23 | type InstalledChaincode struct { 24 | PackageID string `json:"package_id,omitempty"` 25 | Label string `json:"label,omitempty"` 26 | } 27 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/cryptogen_config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package fabric 18 | 19 | import ( 20 | "os" 21 | 22 | "gopkg.in/yaml.v3" 23 | ) 24 | 25 | type Template struct { 26 | Count int `yaml:"Count,omitempty"` 27 | Hostname string `yaml:"Hostname,omitempty"` 28 | } 29 | 30 | type Users struct { 31 | Count int `yaml:"Count,omitempty"` 32 | } 33 | 34 | type CA struct { 35 | Hostname string `yaml:"Hostname,omitempty"` 36 | Country string `yaml:"Country,omitempty"` 37 | Province string `yaml:"Province,omitempty"` 38 | Locality string `yaml:"Locality,omitempty"` 39 | OrganizationalUnit string `yaml:"OrganizationalUnit,omitempty"` 40 | } 41 | 42 | type Spec struct { 43 | Hostname string `yaml:"Hostname,omitempty"` 44 | } 45 | 46 | type Org struct { 47 | Name string `yaml:"Orderer,omitempty"` 48 | Domain string `yaml:"Domain,omitempty"` 49 | EnableNodeOUs bool `yaml:"EnableNodeOUs"` 50 | Specs []*Spec `yaml:"Specs,omitempty"` 51 | CA *CA `yaml:"CA,omitempty"` 52 | Template *Template `yaml:"Template,omitempty"` 53 | Users *Users `yaml:"Users,omitempty"` 54 | } 55 | 56 | type CryptogenConfig struct { 57 | OrdererOrgs []*Org `yaml:"OrdererOrgs,omitempty"` 58 | PeerOrgs []*Org `yaml:"PeerOrgs,omitempty"` 59 | } 60 | 61 | func WriteCryptogenConfig(memberCount int, path string) error { 62 | cryptogenConfig := &CryptogenConfig{ 63 | OrdererOrgs: []*Org{ 64 | { 65 | Name: "Orderer", 66 | Domain: "example.com", 67 | EnableNodeOUs: true, 68 | Specs: []*Spec{ 69 | {Hostname: "fabric_orderer"}, 70 | }, 71 | }, 72 | }, 73 | PeerOrgs: []*Org{ 74 | { 75 | Name: "Org1", 76 | Domain: "org1.example.com", 77 | EnableNodeOUs: true, 78 | CA: &CA{ 79 | Hostname: "fabric_ca", 80 | Country: "US", 81 | Province: "North Carolina", 82 | Locality: "Raleigh", 83 | OrganizationalUnit: "Hyperledger FireFly", 84 | }, 85 | Template: &Template{ 86 | Count: 1, 87 | Hostname: "fabric_peer", 88 | }, 89 | Users: &Users{ 90 | Count: memberCount, 91 | }, 92 | }, 93 | }, 94 | } 95 | 96 | cryptogenConfigBytes, _ := yaml.Marshal(cryptogenConfig) 97 | return os.WriteFile(path, cryptogenConfigBytes, 0755) 98 | } 99 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/fabconnect/client.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package fabconnect 18 | 19 | import ( 20 | "bytes" 21 | "encoding/json" 22 | "fmt" 23 | "io" 24 | "net/http" 25 | "net/url" 26 | "path" 27 | ) 28 | 29 | type CreateIdentityRequest struct { 30 | Name string 31 | Type string 32 | } 33 | 34 | type CreateIdentityResponse struct { 35 | Name string 36 | Secret string 37 | } 38 | 39 | type EnrollIdentityRequest struct { 40 | Secret string 41 | } 42 | 43 | type EnrollIdentityResponse struct { 44 | Name string 45 | Success bool 46 | } 47 | 48 | func CreateIdentity(fabconnectURL string, signer string) (*CreateIdentityResponse, error) { 49 | u, err := url.Parse(fabconnectURL) 50 | if err != nil { 51 | return nil, err 52 | } 53 | u, err = u.Parse(path.Join("identities")) 54 | if err != nil { 55 | return nil, err 56 | } 57 | requestURL := u.String() 58 | requestBody, err := json.Marshal(&CreateIdentityRequest{Name: signer, Type: "client"}) 59 | if err != nil { 60 | return nil, err 61 | } 62 | req, err := http.NewRequest("POST", requestURL, bytes.NewBuffer(requestBody)) 63 | if err != nil { 64 | return nil, err 65 | } 66 | if err != nil { 67 | return nil, err 68 | } 69 | req.Header.Set("Content-Type", "application/json") 70 | client := &http.Client{} 71 | resp, err := client.Do(req) 72 | if err != nil { 73 | return nil, err 74 | } 75 | defer resp.Body.Close() 76 | responseBody, err := io.ReadAll(resp.Body) 77 | if err != nil { 78 | return nil, err 79 | } 80 | if resp.StatusCode != 200 { 81 | return nil, fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) 82 | } 83 | var createIdentityResponseBody *CreateIdentityResponse 84 | if err := json.Unmarshal(responseBody, &createIdentityResponseBody); err != nil { 85 | return nil, err 86 | } 87 | return createIdentityResponseBody, nil 88 | } 89 | 90 | func EnrollIdentity(fabconnectURL, signer, secret string) (*EnrollIdentityResponse, error) { 91 | u, err := url.Parse(fabconnectURL) 92 | if err != nil { 93 | return nil, err 94 | } 95 | u, err = u.Parse(path.Join("identities", signer, "enroll")) 96 | if err != nil { 97 | return nil, err 98 | } 99 | requestURL := u.String() 100 | requestBody, err := json.Marshal(&EnrollIdentityRequest{Secret: secret}) 101 | if err != nil { 102 | return nil, err 103 | } 104 | req, err := http.NewRequest("POST", requestURL, bytes.NewBuffer(requestBody)) 105 | if err != nil { 106 | return nil, err 107 | } 108 | if err != nil { 109 | return nil, err 110 | } 111 | req.Header.Set("Content-Type", "application/json") 112 | client := &http.Client{} 113 | resp, err := client.Do(req) 114 | if err != nil { 115 | return nil, err 116 | } 117 | defer resp.Body.Close() 118 | responseBody, err := io.ReadAll(resp.Body) 119 | if err != nil { 120 | return nil, err 121 | } 122 | if resp.StatusCode != 200 { 123 | return nil, fmt.Errorf("%s [%d] %s", req.URL, resp.StatusCode, responseBody) 124 | } 125 | var enrollIdentityResponse *EnrollIdentityResponse 126 | if err := json.Unmarshal(responseBody, &enrollIdentityResponse); err != nil { 127 | return nil, err 128 | } 129 | return enrollIdentityResponse, nil 130 | } 131 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/fabconnect/fabconnect_config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package fabconnect 18 | 19 | import ( 20 | "os" 21 | 22 | "gopkg.in/yaml.v3" 23 | ) 24 | 25 | type FabconnectConfig struct { 26 | MaxInFlight int 27 | MaxTXWaitTime int 28 | SendConcurrency int 29 | Receipts *Receipts 30 | Events *Events 31 | HTTP *HTTP 32 | RPC *RPC 33 | } 34 | 35 | type Receipts struct { 36 | MaxDocs int 37 | QueryLimit int 38 | RetryInitialDelay int 39 | RetryTimeout int 40 | LevelDB *LevelDB 41 | } 42 | 43 | type LevelDB struct { 44 | Path string 45 | } 46 | 47 | type Events struct { 48 | WebhooksAllowPrivateIPs bool `yaml:"webhooksAllowPrivateIPs,omitempty"` 49 | LevelDB *LevelDB 50 | } 51 | 52 | type HTTP struct { 53 | Port int 54 | } 55 | 56 | type RPC struct { 57 | ConfigPath string 58 | } 59 | 60 | func WriteFabconnectConfig(filePath string) error { 61 | fabconnectConfig := &FabconnectConfig{ 62 | MaxInFlight: 10, 63 | MaxTXWaitTime: 60, 64 | SendConcurrency: 25, 65 | Receipts: &Receipts{ 66 | MaxDocs: 1000, 67 | QueryLimit: 100, 68 | RetryInitialDelay: 5, 69 | RetryTimeout: 30, 70 | LevelDB: &LevelDB{ 71 | Path: "/fabconnect/receipts", 72 | }, 73 | }, 74 | Events: &Events{ 75 | WebhooksAllowPrivateIPs: true, 76 | LevelDB: &LevelDB{ 77 | Path: "/fabconnect/events", 78 | }, 79 | }, 80 | HTTP: &HTTP{ 81 | Port: 3000, 82 | }, 83 | RPC: &RPC{ 84 | ConfigPath: "/fabconnect/ccp.yaml", 85 | }, 86 | } 87 | 88 | fabconnectConfigBytes, _ := yaml.Marshal(fabconnectConfig) 89 | return os.WriteFile(filePath, fabconnectConfigBytes, 0755) 90 | } 91 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/fabconnect/fabconnect_config_test.go: -------------------------------------------------------------------------------- 1 | package fabconnect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestWriteFabconnectConfig(t *testing.T) { 10 | directory := t.TempDir() 11 | 12 | testCases := []struct { 13 | Name string 14 | filePath string 15 | }{ 16 | {Name: "TestPath-1", filePath: directory + "/fabconfig1.yaml"}, 17 | {Name: "TestPath-2", filePath: directory + "/fabconfig2.yaml"}, 18 | {Name: "TestPath-3", filePath: directory + "/fabconfig3.yaml"}, 19 | {Name: "TestPath-4", filePath: directory + "/fabconfig4.yaml"}, 20 | } 21 | for _, tc := range testCases { 22 | t.Run(tc.Name, func(t *testing.T) { 23 | err := WriteFabconnectConfig(tc.filePath) 24 | if err != nil { 25 | t.Log("cannot write config:", err) 26 | } 27 | }) 28 | assert.NotNil(t, tc.filePath) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /internal/blockchain/fabric/fabric_docker_test.go: -------------------------------------------------------------------------------- 1 | package fabric 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/pkg/types" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type MockManfest struct { 11 | types.ManifestEntry 12 | GetDockerImageStringMck func() string 13 | } 14 | 15 | func TestGetServiceDefinition(t *testing.T) { 16 | getManifest := &MockManfest{ 17 | GetDockerImageStringMck: func() string { 18 | return "ethconnect_alpine:latest" 19 | }, 20 | } 21 | testServices := []struct { 22 | Name string 23 | Members *types.Stack 24 | DependentServices map[string]string 25 | ServiceName string 26 | }{ 27 | { 28 | Name: "test_service_1", 29 | Members: &types.Stack{ 30 | Members: []*types.Organization{{ID: "firefly_1", ExposedConnectorPort: 3000}}, 31 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 32 | }, 33 | DependentServices: map[string]string{ 34 | "service1": "running", 35 | "service2": "stopped", 36 | }, 37 | ServiceName: "fabric_ca", 38 | }, 39 | { 40 | Name: "test_service_2", 41 | Members: &types.Stack{ 42 | Members: []*types.Organization{{ID: "firefly_2", ExposedConnectorPort: 8002}}, 43 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 44 | }, 45 | DependentServices: map[string]string{ 46 | "service1": "stopped", 47 | "service2": "running", 48 | }, 49 | ServiceName: "fabric_ca", 50 | }, 51 | { 52 | Name: "test_service_3", 53 | Members: &types.Stack{ 54 | Members: []*types.Organization{{ID: "firefly_3", ExposedConnectorPort: 8000}}, 55 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 56 | }, 57 | DependentServices: map[string]string{ 58 | "service1": "stopped", 59 | "service2": "stopped", 60 | "service3": "running", 61 | }, 62 | ServiceName: "fabric_ca", 63 | }, 64 | { 65 | Name: "test_service_4", 66 | Members: &types.Stack{ 67 | Members: []*types.Organization{{ID: "firefly_4", ExposedConnectorPort: 7892}}, 68 | VersionManifest: &types.VersionManifest{Ethconnect: &getManifest.ManifestEntry}, 69 | }, 70 | DependentServices: map[string]string{ 71 | "service1": "stopped", 72 | "service2": "stopped", 73 | "service3": "stopped", 74 | "service4": "running", 75 | }, 76 | ServiceName: "fabric_ca", 77 | }, 78 | } 79 | for _, tc := range testServices { 80 | t.Run(tc.Name, func(t *testing.T) { 81 | 82 | serviceDefinitions := GenerateDockerServiceDefinitions(tc.Members) 83 | assert.NotNil(t, serviceDefinitions) 84 | 85 | expectedCommand := "sh -c 'fabric-ca-server start -b admin:adminpw'" 86 | if serviceDefinitions[0].Service.Command != expectedCommand { 87 | t.Errorf("Expected Command %q, got %q", expectedCommand, serviceDefinitions[0].Service.Command) 88 | } 89 | if serviceDefinitions[0].ServiceName != tc.ServiceName { 90 | t.Errorf("Expected ServiceName %q, got %q", tc.ServiceName, serviceDefinitions[0].ServiceName) 91 | } 92 | 93 | }) 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/connector_interface.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package connector 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/internal/docker" 21 | "github.com/hyperledger/firefly-cli/pkg/types" 22 | ) 23 | 24 | type Connector interface { 25 | GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition 26 | GenerateConfig(stack *types.Stack, member *types.Organization, signerHostname, rpcURL string) Config 27 | Name() string 28 | Port() int 29 | } 30 | 31 | type Config interface { 32 | WriteConfig(filename string, extraConnectorConfigPath string) error 33 | } 34 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/tezosconnect/client.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package tezosconnect 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | type Tezosconnect struct { 24 | ctx context.Context 25 | } 26 | 27 | func NewTezosconnect(ctx context.Context) *Tezosconnect { 28 | return &Tezosconnect{ 29 | ctx: ctx, 30 | } 31 | } 32 | 33 | func (t *Tezosconnect) Name() string { 34 | return "tezosconnect" 35 | } 36 | 37 | func (t *Tezosconnect) Port() int { 38 | return 5008 39 | } 40 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/tezosconnect/client_test.go: -------------------------------------------------------------------------------- 1 | package tezosconnect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestPort(t *testing.T) { 10 | t.Run("testPort", func(t *testing.T) { 11 | Port := 5008 12 | T := &Tezosconnect{} 13 | PortInt := T.Port() 14 | assert.Equal(t, Port, PortInt) 15 | }) 16 | } 17 | 18 | func TestName(t *testing.T) { 19 | t.Run("testName", func(t *testing.T) { 20 | Name := "tezosconnect" 21 | T := &Tezosconnect{} 22 | NameStr := T.Name() 23 | assert.Equal(t, Name, NameStr) 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/tezosconnect/config_test.go: -------------------------------------------------------------------------------- 1 | package tezosconnect 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | ) 7 | 8 | func TestWriteConfig(t *testing.T) { 9 | dir := t.TempDir() 10 | configFilename := filepath.Join(dir + "/config.yaml") 11 | extraTezosConfigPath := filepath.Join(dir + "/conflate/extra.yaml") 12 | p := Config{} 13 | t.Run("TestWriteConfig", func(t *testing.T) { 14 | err := p.WriteConfig(configFilename, extraTezosConfigPath) 15 | if err != nil { 16 | t.Logf("unable to write to config files: %v", err) 17 | } 18 | }) 19 | } 20 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/tezosconnect/docker.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package tezosconnect 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/hyperledger/firefly-cli/internal/docker" 23 | "github.com/hyperledger/firefly-cli/pkg/types" 24 | ) 25 | 26 | func (t *Tezosconnect) GetServiceDefinitions(s *types.Stack, dependentServices map[string]string) []*docker.ServiceDefinition { 27 | dependsOn := make(map[string]map[string]string) 28 | for dep, state := range dependentServices { 29 | dependsOn[dep] = map[string]string{"condition": state} 30 | } 31 | serviceDefinitions := make([]*docker.ServiceDefinition, len(s.Members)) 32 | for i, member := range s.Members { 33 | dataVolumeName := fmt.Sprintf("tezosconnect_data_%s", member.ID) 34 | serviceDefinitions[i] = &docker.ServiceDefinition{ 35 | ServiceName: "tezosconnect_" + member.ID, 36 | Service: &docker.Service{ 37 | Image: s.VersionManifest.Tezosconnect.GetDockerImageString(), 38 | ContainerName: fmt.Sprintf("%s_tezosconnect_%v", s.Name, i), 39 | Command: "-f /tezosconnect/config.yaml", 40 | DependsOn: dependsOn, 41 | Ports: []string{fmt.Sprintf("%d:%v", member.ExposedConnectorPort, t.Port())}, 42 | Volumes: []string{ 43 | fmt.Sprintf("%s/config/tezosconnect_%s.yaml:/tezosconnect/config.yaml", s.RuntimeDir, member.ID), 44 | fmt.Sprintf("%s:/tezosconnect/db", dataVolumeName), 45 | }, 46 | Logging: docker.StandardLogOptions, 47 | Environment: s.EnvironmentVars, 48 | }, 49 | VolumeNames: []string{ 50 | dataVolumeName, 51 | }, 52 | } 53 | } 54 | return serviceDefinitions 55 | } 56 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/connector/tezosconnect/docker_test.go: -------------------------------------------------------------------------------- 1 | package tezosconnect 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/pkg/types" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type MockManfest struct { 11 | types.ManifestEntry 12 | GetDockerImageStringMck func() string 13 | } 14 | 15 | func TestGetServiceDefinition(t *testing.T) { 16 | getManifest := &MockManfest{ 17 | GetDockerImageStringMck: func() string { 18 | return "tezosconnect_alpine:latest" 19 | }, 20 | } 21 | testServices := []struct { 22 | Name string 23 | Members *types.Stack 24 | DependentServices map[string]string 25 | ServiceName string 26 | }{ 27 | { 28 | Name: "test_service_1", 29 | Members: &types.Stack{ 30 | Members: []*types.Organization{{ID: "firefly_1", ExposedConnectorPort: 3000}}, 31 | VersionManifest: &types.VersionManifest{Tezosconnect: &getManifest.ManifestEntry}, 32 | }, 33 | DependentServices: map[string]string{ 34 | "service1": "running", 35 | "service2": "stopped", 36 | }, 37 | ServiceName: "tezosconnect_firefly_1", 38 | }, 39 | { 40 | Name: "test_service_2", 41 | Members: &types.Stack{ 42 | Members: []*types.Organization{{ID: "firefly_2", ExposedConnectorPort: 8002}}, 43 | VersionManifest: &types.VersionManifest{Tezosconnect: &getManifest.ManifestEntry}, 44 | }, 45 | DependentServices: map[string]string{ 46 | "service1": "stopped", 47 | "service2": "running", 48 | }, 49 | ServiceName: "tezosconnect_firefly_2", 50 | }, 51 | { 52 | Name: "test_service_3", 53 | Members: &types.Stack{ 54 | Members: []*types.Organization{{ID: "firefly_3", ExposedConnectorPort: 8000}}, 55 | VersionManifest: &types.VersionManifest{Tezosconnect: &getManifest.ManifestEntry}, 56 | }, 57 | DependentServices: map[string]string{ 58 | "service1": "stopped", 59 | "service2": "stopped", 60 | "service3": "running", 61 | }, 62 | ServiceName: "tezosconnect_firefly_3", 63 | }, 64 | { 65 | Name: "test_service_4", 66 | Members: &types.Stack{ 67 | Members: []*types.Organization{{ID: "firefly_4", ExposedConnectorPort: 7892}}, 68 | VersionManifest: &types.VersionManifest{Tezosconnect: &getManifest.ManifestEntry}, 69 | }, 70 | DependentServices: map[string]string{ 71 | "service1": "stopped", 72 | "service2": "stopped", 73 | "service3": "stopped", 74 | "service4": "running", 75 | }, 76 | ServiceName: "tezosconnect_firefly_4", 77 | }, 78 | } 79 | for _, tc := range testServices { 80 | t.Run(tc.Name, func(t *testing.T) { 81 | tezos := Tezosconnect{} 82 | 83 | serviceDefinitions := tezos.GetServiceDefinitions(tc.Members, tc.DependentServices) 84 | assert.NotNil(t, serviceDefinitions) 85 | 86 | expectedCommand := "-f /tezosconnect/config.yaml" 87 | if serviceDefinitions[0].Service.Command != expectedCommand { 88 | t.Errorf("Expected Command %q, got %q", expectedCommand, serviceDefinitions[0].Service.Command) 89 | } 90 | if serviceDefinitions[0].ServiceName != tc.ServiceName { 91 | t.Errorf("Expected ServiceName %q, got %q", tc.ServiceName, serviceDefinitions[0].ServiceName) 92 | } 93 | 94 | }) 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/tezos.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package tezos 18 | 19 | import tz "blockwatch.cc/tzgo/tezos" 20 | 21 | type Account struct { 22 | Address string `json:"address"` 23 | PrivateKey string `json:"privateKey"` 24 | } 25 | 26 | func GenerateAddressAndPrivateKey() (address string, privateKey string, err error) { 27 | prk, err := tz.GenerateKey(tz.KeyTypeEd25519) 28 | if err != nil { 29 | return "", "", err 30 | } 31 | 32 | return prk.Address().String(), prk.String(), nil 33 | } 34 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/tezossigner/config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package tezossigner 18 | 19 | import ( 20 | "os" 21 | 22 | "gopkg.in/yaml.v2" 23 | ) 24 | 25 | type Config struct { 26 | Server ServerConfig `yaml:"server"` 27 | Vaults VaultsConfig `yaml:"vaults"` 28 | Tezos map[string]AccountConfig `yaml:"tezos"` 29 | } 30 | 31 | type ServerConfig struct { 32 | Address string `yaml:"address,omitempty"` 33 | UtilityAddress string `yaml:"utility_address,omitempty"` 34 | } 35 | 36 | type VaultsConfig struct { 37 | LocalSecret LocalSecretConfig `yaml:"local_secret,omitempty"` 38 | } 39 | 40 | type LocalSecretConfig struct { 41 | Driver string `yaml:"driver,omitempty"` 42 | File FileConfig `yaml:"config,omitempty"` 43 | } 44 | 45 | type FileConfig struct { 46 | SecretPath string `yaml:"file,omitempty"` 47 | } 48 | 49 | type AccountConfig struct { 50 | LogPayloads bool `yaml:"log_payloads"` 51 | Allow AllowedTransactionsConfig `yaml:"allow"` 52 | } 53 | 54 | type AllowedTransactionsConfig struct { 55 | Block []string `yaml:"block"` 56 | Endorsement []string `yaml:"endorsement"` 57 | Preendorsement []string `yaml:"preendorsement"` 58 | Generic []string `yaml:"generic"` 59 | } 60 | 61 | func (c *Config) WriteConfig(filename string) error { 62 | configYamlBytes, _ := yaml.Marshal(c) 63 | return os.WriteFile(filename, configYamlBytes, 0755) 64 | } 65 | 66 | func GenerateSignerConfig(accountsAddresses []string) *Config { 67 | config := &Config{ 68 | Server: ServerConfig{ 69 | Address: ":6732", 70 | UtilityAddress: ":9583", 71 | }, 72 | Vaults: VaultsConfig{ 73 | LocalSecret: LocalSecretConfig{ 74 | Driver: "file", 75 | File: FileConfig{ 76 | SecretPath: "/etc/secret.json", 77 | }, 78 | }, 79 | }, 80 | } 81 | 82 | addresses := map[string]AccountConfig{} 83 | // Give accounts the rights to sign certain types of transactions 84 | for _, address := range accountsAddresses { 85 | addresses[address] = AccountConfig{ 86 | LogPayloads: true, 87 | Allow: AllowedTransactionsConfig{ 88 | /* List of [activate_account, ballot, delegation, double_baking_evidence, double_endorsement_evidence, 89 | double_preendorsement_evidence, endorsement, failing_noop, origination, preendorsement, proposals, 90 | register_global_constant, reveal, sc_rollup_add_messages, sc_rollup_cement, sc_rollup_originate, 91 | sc_rollup_publish, seed_nonce_revelation, set_deposits_limit, transaction, transfer_ticket, 92 | tx_rollup_commit, tx_rollup_dispatch_tickets, tx_rollup_finalize_commitment, tx_rollup_origination, 93 | tx_rollup_rejection, tx_rollup_remove_commitment, tx_rollup_return_bond, tx_rollup_submit_batch]*/ 94 | Generic: []string{ 95 | "transaction", 96 | "endorsement", 97 | "reveal", 98 | "origination", 99 | }, 100 | }, 101 | } 102 | } 103 | config.Tezos = addresses 104 | 105 | return config 106 | } 107 | -------------------------------------------------------------------------------- /internal/blockchain/tezos/tezossigner/config_test.go: -------------------------------------------------------------------------------- 1 | package tezossigner 2 | 3 | import ( 4 | "path/filepath" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestWriteConfig(t *testing.T) { 11 | dir := t.TempDir() 12 | 13 | testfiles := []struct { 14 | Name string 15 | FileName string 16 | }{ 17 | {Name: "test-1", FileName: filepath.Join(dir + "test1.yaml")}, 18 | {Name: "test-2", FileName: filepath.Join(dir + "test2.yaml")}, 19 | {Name: "test-3", FileName: filepath.Join(dir + "test3.yaml")}, 20 | {Name: "test-4", FileName: filepath.Join(dir + "test4.yaml")}, 21 | {Name: "test-5", FileName: filepath.Join(dir + "test5.yaml")}, 22 | } 23 | for _, tc := range testfiles { 24 | t.Run(tc.Name, func(t *testing.T) { 25 | c := &Config{} 26 | err := c.WriteConfig(tc.FileName) 27 | if err != nil { 28 | t.Logf("unable to write config files :%s", err) 29 | } 30 | }) 31 | } 32 | } 33 | 34 | func TestGenerateSignerConfig(t *testing.T) { 35 | accountAddresses := []string{"0xAddress21", "0xAddress91", "0xAddress10", "0xAddress17", "0xAddress12"} 36 | 37 | config := GenerateSignerConfig(accountAddresses) 38 | 39 | expectedConfig := &Config{ 40 | Server: ServerConfig{ 41 | Address: ":6732", 42 | UtilityAddress: ":9583", 43 | }, 44 | Vaults: VaultsConfig{ 45 | LocalSecret: LocalSecretConfig{ 46 | Driver: "file", 47 | File: FileConfig{ 48 | SecretPath: "/etc/secret.json", 49 | }, 50 | }, 51 | }, 52 | Tezos: map[string]AccountConfig{ 53 | "0xAddress21": { 54 | LogPayloads: true, 55 | Allow: AllowedTransactionsConfig{ 56 | Generic: []string{ 57 | "transaction", 58 | "endorsement", 59 | "reveal", 60 | "origination", 61 | }, 62 | }, 63 | }, 64 | "0xAddress91": { 65 | LogPayloads: true, 66 | Allow: AllowedTransactionsConfig{ 67 | Generic: []string{ 68 | "transaction", 69 | "endorsement", 70 | "reveal", 71 | "origination", 72 | }, 73 | }, 74 | }, 75 | "0xAddress10": { 76 | LogPayloads: true, 77 | Allow: AllowedTransactionsConfig{ 78 | Generic: []string{ 79 | "transaction", 80 | "endorsement", 81 | "reveal", 82 | "origination", 83 | }, 84 | }, 85 | }, 86 | "0xAddress17": { 87 | LogPayloads: true, 88 | Allow: AllowedTransactionsConfig{ 89 | Generic: []string{ 90 | "transaction", 91 | "endorsement", 92 | "reveal", 93 | "origination", 94 | }, 95 | }, 96 | }, 97 | "0xAddress12": { 98 | LogPayloads: true, 99 | Allow: AllowedTransactionsConfig{ 100 | Generic: []string{ 101 | "transaction", 102 | "endorsement", 103 | "reveal", 104 | "origination", 105 | }, 106 | }, 107 | }, 108 | }, 109 | } 110 | assert.Equal(t, expectedConfig.Server, config.Server, "Server configuration should match") 111 | assert.Equal(t, expectedConfig.Vaults, config.Vaults, "Vaults configuration should match") 112 | assert.Equal(t, expectedConfig.Tezos, config.Tezos, "Tezos configuration should match") 113 | } 114 | -------------------------------------------------------------------------------- /internal/constants/constants.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package constants 18 | 19 | import ( 20 | "os" 21 | "path/filepath" 22 | ) 23 | 24 | var StacksDir = checkHome() 25 | var FireFlyCoreImageName = "ghcr.io/hyperledger/firefly" 26 | var IPFSImageName = "ipfs/go-ipfs:v0.10.0" 27 | var PostgresImageName = "postgres" 28 | var PrometheusImageName = "prom/prometheus" 29 | var SandboxImageName = "ghcr.io/hyperledger/firefly-sandbox:latest" 30 | 31 | func checkHome() string { 32 | var homeDir, _ = os.UserHomeDir() 33 | var StacksDir = filepath.Join(homeDir, ".firefly", "stacks") 34 | var fireflyhome, present = os.LookupEnv("FIREFLY_HOME") 35 | if present { 36 | StacksDir = filepath.Join(fireflyhome, "stacks") 37 | } 38 | return StacksDir 39 | } 40 | -------------------------------------------------------------------------------- /internal/core/http.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package core 18 | 19 | import ( 20 | "bytes" 21 | "context" 22 | "encoding/json" 23 | "fmt" 24 | "io" 25 | "net/http" 26 | "time" 27 | 28 | "github.com/hyperledger/firefly-cli/internal/log" 29 | ) 30 | 31 | var requestTimeout int = -1 32 | 33 | func SetRequestTimeout(customRequestTimeoutSecs int) { 34 | requestTimeout = customRequestTimeoutSecs 35 | } 36 | 37 | func RequestWithRetry(ctx context.Context, method, url string, body, result interface{}) (err error) { 38 | verbose := log.VerbosityFromContext(ctx) 39 | retries := 30 40 | for { 41 | if err := request(method, url, body, result); err != nil { 42 | if retries > 0 { 43 | if verbose { 44 | fmt.Printf("%s - retrying request...", err.Error()) 45 | } 46 | retries-- 47 | time.Sleep(1 * time.Second) 48 | } else { 49 | return err 50 | } 51 | } else { 52 | return nil 53 | } 54 | } 55 | } 56 | 57 | func request(method, url string, body, result interface{}) (err error) { 58 | if body == nil { 59 | body = make(map[string]interface{}) 60 | } 61 | 62 | var bodyReader io.Reader 63 | if body != nil { 64 | requestBody, err := json.Marshal(&body) 65 | if err != nil { 66 | return err 67 | } 68 | bodyReader = bytes.NewReader(requestBody) 69 | } 70 | 71 | req, err := http.NewRequest(method, url, bodyReader) 72 | if err != nil { 73 | return err 74 | } 75 | if requestTimeout > 0 { 76 | req.Header.Set("Request-Timeout", fmt.Sprintf("%d", requestTimeout)) 77 | } 78 | req.Header.Set("Content-Type", "application/json") 79 | if err != nil { 80 | return err 81 | } 82 | client := &http.Client{} 83 | resp, err := client.Do(req) 84 | if err != nil { 85 | return err 86 | } 87 | defer resp.Body.Close() 88 | if resp.StatusCode < 200 || resp.StatusCode >= 300 { 89 | var responseBytes []byte 90 | if resp.StatusCode != 204 { 91 | responseBytes, _ = io.ReadAll(resp.Body) 92 | } 93 | return fmt.Errorf("%s [%d] %s", url, resp.StatusCode, responseBytes) 94 | } 95 | 96 | if resp.StatusCode == 204 { 97 | return nil 98 | } 99 | 100 | return json.NewDecoder(resp.Body).Decode(&result) 101 | } 102 | -------------------------------------------------------------------------------- /internal/core/manifest_test.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package core 18 | 19 | import ( 20 | "testing" 21 | 22 | "github.com/hyperledger/firefly-cli/pkg/types" 23 | "github.com/stretchr/testify/assert" 24 | ) 25 | 26 | func TestGetFireFlyManifest(t *testing.T) { 27 | manifest, err := GetManifestForRelease("main") 28 | assert.NoError(t, err) 29 | assert.NotNil(t, manifest) 30 | assert.NotNil(t, manifest.Ethconnect) 31 | assert.NotNil(t, manifest.Fabconnect) 32 | assert.NotNil(t, manifest.DataExchange) 33 | assert.NotNil(t, manifest.TokensERC1155) 34 | assert.NotNil(t, manifest.TokensERC20ERC721) 35 | } 36 | 37 | func TestGetLatestReleaseManifest(t *testing.T) { 38 | manifest, err := GetManifestForChannel(types.ReleaseChannelStable) 39 | assert.NoError(t, err) 40 | assert.NotNil(t, manifest) 41 | assert.NotNil(t, manifest.FireFly) 42 | assert.NotNil(t, manifest.Ethconnect) 43 | assert.NotNil(t, manifest.Fabconnect) 44 | assert.NotNil(t, manifest.DataExchange) 45 | assert.NotNil(t, manifest.TokensERC1155) 46 | assert.NotNil(t, manifest.TokensERC20ERC721) 47 | } 48 | 49 | func TestIsSupportedVersionUpgrade(t *testing.T) { 50 | assert.NoError(t, ValidateVersionUpgrade("v1.2.1", "v1.2.2")) 51 | assert.NoError(t, ValidateVersionUpgrade("v1.2.0", "v1.2.2")) 52 | assert.NoError(t, ValidateVersionUpgrade("1.2.1", "v1.2.2")) 53 | assert.NoError(t, ValidateVersionUpgrade("v1.2.1", "1.2.2")) 54 | 55 | assert.Error(t, ValidateVersionUpgrade("v1.2.2", "v1.3.0")) 56 | assert.Error(t, ValidateVersionUpgrade("latest", "v1.3.0")) 57 | assert.Error(t, ValidateVersionUpgrade("v1.2.2", "latest")) 58 | } 59 | -------------------------------------------------------------------------------- /internal/docker/docker_checks.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package docker 18 | 19 | import ( 20 | "fmt" 21 | "os/exec" 22 | ) 23 | 24 | func CheckDockerConfig() (DockerComposeVersion, error) { 25 | 26 | dockerCmd := exec.Command("docker", "-v") 27 | _, err := dockerCmd.Output() 28 | if err != nil { 29 | return None, fmt.Errorf("an error occurred while running docker. Is docker installed on your computer?") 30 | } 31 | 32 | dockerDeamonCheck := exec.Command("docker", "ps") 33 | _, err = dockerDeamonCheck.Output() 34 | if err != nil { 35 | return None, fmt.Errorf("an error occurred while running docker. Is docker running on your computer?") 36 | } 37 | 38 | // check for docker-compose (V2) version 39 | dockerComposeCmd := exec.Command("docker", "compose", "version") 40 | _, err = dockerComposeCmd.Output() 41 | if err == nil { 42 | return ComposeV2, nil 43 | } 44 | 45 | // check for docker-compose (v1) version 46 | dockerComposeCmd = exec.Command("docker-compose", "-v") 47 | _, err = dockerComposeCmd.Output() 48 | if err == nil { 49 | return ComposeV1, nil 50 | } 51 | 52 | return None, fmt.Errorf("an error occurred while running docker-compose. Is docker-compose installed on your computer?") 53 | } 54 | -------------------------------------------------------------------------------- /internal/docker/docker_config_test.go: -------------------------------------------------------------------------------- 1 | package docker 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/firefly-cli/pkg/types" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | type MockManfest struct { 11 | types.ManifestEntry 12 | } 13 | 14 | func TestCreateDockerComposeEnvironmentVars(t *testing.T) { 15 | getManifest := &MockManfest{} 16 | testStacks := []struct { 17 | Id int 18 | EnvironmentVars map[string]interface{} 19 | Members []*types.Organization 20 | VersionManifest *types.VersionManifest 21 | }{ 22 | { 23 | Id: 1, 24 | Members: []*types.Organization{{ID: "firefly_1"}}, 25 | VersionManifest: &types.VersionManifest{FireFly: &getManifest.ManifestEntry, DataExchange: &getManifest.ManifestEntry}, 26 | EnvironmentVars: map[string]interface{}{ 27 | "http_proxy": "", 28 | "https_proxy": "", 29 | "HTTP_PROXY": "127.0.0.1:8080", 30 | "HTTPS_PROXY": "127.0.0.1:8080", 31 | "no_proxy": "", 32 | }, 33 | }, 34 | { 35 | Id: 2, 36 | Members: []*types.Organization{{ID: "firefly_2"}}, 37 | VersionManifest: &types.VersionManifest{FireFly: &getManifest.ManifestEntry, DataExchange: &getManifest.ManifestEntry}, 38 | EnvironmentVars: nil, 39 | }, 40 | } 41 | for _, test := range testStacks { 42 | cfg := CreateDockerCompose(&types.Stack{ 43 | Members: test.Members, 44 | VersionManifest: test.VersionManifest, 45 | EnvironmentVars: test.EnvironmentVars, 46 | }) 47 | for _, service := range cfg.Services { 48 | assert.Equal(t, len(test.EnvironmentVars), len(service.Environment), "service [%v] test ID [%v]", service.ContainerName, test.Id) 49 | for envVar := range service.Environment { 50 | assert.Equal(t, test.EnvironmentVars[envVar], service.Environment[envVar]) 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /internal/docker/mocks/docker_manager.go: -------------------------------------------------------------------------------- 1 | // DockerManager is a mock that implements IDockerManager 2 | package mocks 3 | 4 | import "context" 5 | 6 | type DockerManager struct{} 7 | 8 | func NewDockerManager() *DockerManager { 9 | return &DockerManager{} 10 | } 11 | 12 | func (mgr *DockerManager) RunDockerCommand(ctx context.Context, workingDir string, command ...string) error { 13 | return nil 14 | } 15 | 16 | func (mgr *DockerManager) RunDockerCommandLine(ctx context.Context, workingDir string, command string) error { 17 | return nil 18 | } 19 | 20 | func (mgr *DockerManager) RunDockerComposeCommand(ctx context.Context, workingDir string, command ...string) error { 21 | return nil 22 | } 23 | 24 | func (mgr *DockerManager) RunDockerCommandBuffered(ctx context.Context, workingDir string, command ...string) (string, error) { 25 | return "", nil 26 | } 27 | 28 | func (mgr *DockerManager) RunDockerComposeCommandReturnsStdout(workingDir string, command ...string) ([]byte, error) { 29 | return nil, nil 30 | } 31 | 32 | func (mgr *DockerManager) GetImageConfig(image string) (map[string]interface{}, error) { 33 | return nil, nil 34 | } 35 | 36 | func (mgr *DockerManager) GetImageLabel(image, label string) (string, error) { 37 | return "", nil 38 | } 39 | 40 | func (mgr *DockerManager) GetImageDigest(image string) (string, error) { 41 | return "", nil 42 | } 43 | 44 | func (mgr *DockerManager) CreateVolume(ctx context.Context, volumeName string) error { 45 | return nil 46 | } 47 | 48 | func (mgr *DockerManager) CopyFileToVolume(ctx context.Context, volumeName string, sourcePath string, destPath string) error { 49 | return nil 50 | } 51 | 52 | func (mgr *DockerManager) MkdirInVolume(ctx context.Context, volumeName string, directory string) error { 53 | return nil 54 | } 55 | 56 | func (mgr *DockerManager) RemoveVolume(ctx context.Context, volumeName string) error { 57 | return nil 58 | } 59 | 60 | func (mgr *DockerManager) CopyFromContainer(ctx context.Context, containerName string, sourcePath string, destPath string) error { 61 | return nil 62 | } 63 | -------------------------------------------------------------------------------- /internal/log/log.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "context" 21 | ) 22 | 23 | type LogLevel int 24 | 25 | const ( 26 | Trace LogLevel = iota 27 | Debug 28 | Info 29 | Warn 30 | Error 31 | ) 32 | 33 | type Logger interface { 34 | SetLogLevel(l LogLevel) 35 | Trace(s string) 36 | Debug(s string) 37 | Info(s string) 38 | Warn(s string) 39 | Error(e error) 40 | } 41 | 42 | type ( 43 | ctxLogKey struct{} 44 | ctxVerbosityKey struct{} 45 | ) 46 | 47 | func WithLogger(ctx context.Context, log Logger) context.Context { 48 | return context.WithValue(ctx, ctxLogKey{}, log) 49 | } 50 | 51 | func LoggerFromContext(ctx context.Context) Logger { 52 | return ctx.Value(ctxLogKey{}).(Logger) 53 | } 54 | 55 | func WithVerbosity(ctx context.Context, verbose bool) context.Context { 56 | return context.WithValue(ctx, ctxVerbosityKey{}, verbose) 57 | } 58 | 59 | func VerbosityFromContext(ctx context.Context) bool { 60 | return ctx.Value(ctxVerbosityKey{}).(bool) 61 | } 62 | -------------------------------------------------------------------------------- /internal/log/spin.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import ( 20 | "fmt" 21 | 22 | "github.com/briandowns/spinner" 23 | ) 24 | 25 | type SpinnerLogger struct { 26 | Spinner *spinner.Spinner 27 | logLevel LogLevel 28 | } 29 | 30 | func NewSpinnerLogger(spin *spinner.Spinner) *SpinnerLogger { 31 | spin.FinalMSG = "done" 32 | return &SpinnerLogger{ 33 | Spinner: spin, 34 | } 35 | } 36 | 37 | func (l *SpinnerLogger) SetLogLevel(level LogLevel) { 38 | l.logLevel = level 39 | } 40 | 41 | func (l *SpinnerLogger) Trace(s string) { 42 | if l.logLevel <= Trace && l.Spinner != nil { 43 | l.Spinner.Suffix = fmt.Sprintf(" %s...", s) 44 | } 45 | } 46 | 47 | func (l *SpinnerLogger) Debug(s string) { 48 | if l.logLevel <= Debug && l.Spinner != nil { 49 | l.Spinner.Suffix = fmt.Sprintf(" %s...", s) 50 | } 51 | } 52 | 53 | func (l *SpinnerLogger) Info(s string) { 54 | if l.logLevel <= Info && l.Spinner != nil { 55 | l.Spinner.Suffix = fmt.Sprintf(" %s...", s) 56 | } 57 | } 58 | 59 | func (l *SpinnerLogger) Warn(s string) { 60 | if l.logLevel <= Warn && l.Spinner != nil { 61 | l.Spinner.Suffix = fmt.Sprintf(" %s...", s) 62 | } 63 | } 64 | 65 | func (l *SpinnerLogger) Error(e error) { 66 | if l.logLevel <= Error && l.Spinner != nil { 67 | l.Spinner.Suffix = fmt.Sprintf(" Error: %s...", e.Error()) 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /internal/log/stdout.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package log 18 | 19 | import "fmt" 20 | 21 | type StdoutLogger struct { 22 | LogLevel LogLevel 23 | } 24 | 25 | func (l *StdoutLogger) SetLogLevel(level LogLevel) { 26 | l.LogLevel = level 27 | } 28 | 29 | func (l *StdoutLogger) Trace(s string) { 30 | if l.LogLevel <= Trace { 31 | fmt.Println(s) 32 | } 33 | } 34 | 35 | func (l *StdoutLogger) Debug(s string) { 36 | if l.LogLevel <= Debug { 37 | fmt.Println(s) 38 | } 39 | } 40 | 41 | func (l *StdoutLogger) Info(s string) { 42 | if l.LogLevel <= Info { 43 | fmt.Println(s) 44 | } 45 | } 46 | 47 | func (l *StdoutLogger) Warn(s string) { 48 | if l.LogLevel <= Warn { 49 | fmt.Println(s) 50 | } 51 | } 52 | 53 | func (l *StdoutLogger) Error(e error) { 54 | if l.LogLevel <= Trace { 55 | fmt.Println(e.Error()) 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /internal/stacks/data_exchange.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package stacks 18 | 19 | import ( 20 | "fmt" 21 | ) 22 | 23 | type DataExchangeListenerConfig struct { 24 | Hostname string `json:"hostname,omitempty"` 25 | Endpoint string `json:"endpoint,omitempty"` 26 | Port int `json:"port,omitempty"` 27 | } 28 | 29 | type PeerConfig struct { 30 | ID string `json:"id,omitempty"` 31 | Endpoint string `json:"endpoint,omitempty"` 32 | } 33 | 34 | type DataExchangePeerConfig struct { 35 | API *DataExchangeListenerConfig `json:"api,omitempty"` 36 | P2P *DataExchangeListenerConfig `json:"p2p,omitempty"` 37 | Peers []*PeerConfig `json:"peers"` 38 | } 39 | 40 | func (s *StackManager) GenerateDataExchangeHTTPSConfig(memberID string) *DataExchangePeerConfig { 41 | return &DataExchangePeerConfig{ 42 | API: &DataExchangeListenerConfig{ 43 | Hostname: "0.0.0.0", 44 | Port: 3000, 45 | }, 46 | P2P: &DataExchangeListenerConfig{ 47 | Hostname: "0.0.0.0", 48 | Port: 3001, 49 | Endpoint: fmt.Sprintf("https://dataexchange_%s:3001", memberID), 50 | }, 51 | Peers: []*PeerConfig{}, 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /internal/stacks/firefly_identity.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package stacks 18 | 19 | import ( 20 | "fmt" 21 | "net/http" 22 | 23 | "github.com/hyperledger/firefly-cli/internal/core" 24 | ) 25 | 26 | func (s *StackManager) registerFireflyIdentities() error { 27 | emptyObject := make(map[string]interface{}) 28 | 29 | for _, member := range s.Stack.Members { 30 | ffURL := fmt.Sprintf("http://127.0.0.1:%d/api/v1", member.ExposedFireflyPort) 31 | s.Log.Info(fmt.Sprintf("registering org and node for member %s", member.ID)) 32 | 33 | registerOrgURL := fmt.Sprintf("%s/network/organizations/self?confirm=true", ffURL) 34 | err := core.RequestWithRetry(s.ctx, http.MethodPost, registerOrgURL, emptyObject, nil) 35 | if err != nil { 36 | return err 37 | } 38 | 39 | registerNodeURL := fmt.Sprintf("%s/network/nodes/self?confirm=true", ffURL) 40 | err = core.RequestWithRetry(s.ctx, http.MethodPost, registerNodeURL, emptyObject, nil) 41 | if err != nil { 42 | return nil 43 | } 44 | } 45 | return nil 46 | } 47 | -------------------------------------------------------------------------------- /internal/stacks/ipfs_config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package stacks 18 | 19 | import ( 20 | "crypto/rand" 21 | "encoding/hex" 22 | ) 23 | 24 | func GenerateSwarmKey() (string, error) { 25 | key := make([]byte, 32) 26 | if _, err := rand.Read(key); err != nil { 27 | return "", err 28 | } 29 | hexKey := hex.EncodeToString(key) 30 | return "/key/swarm/psk/1.0.0/\n/base16/\n" + hexKey, nil 31 | } 32 | -------------------------------------------------------------------------------- /internal/stacks/prometheus_config.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package stacks 18 | 19 | import "fmt" 20 | 21 | type GlobalConfig struct { 22 | ScrapeInterval string `yaml:"scrape_interval,omitempty"` 23 | ScrapeTimeout string `yaml:"scrape_timeout,omitempty"` 24 | } 25 | 26 | type ScrapeConfig struct { 27 | JobName string `yaml:"job_name,omitempty"` 28 | MetricsPath string `yaml:"metrics_path,omitempty"` 29 | StaticConfigs []*StaticConfig `yaml:"static_configs,omitempty"` 30 | } 31 | 32 | type StaticConfig struct { 33 | Targets []string `yaml:"targets,omitempty"` 34 | } 35 | 36 | type PrometheusConfig struct { 37 | Global *GlobalConfig `yaml:"global,omitempty"` 38 | ScrapeConfigs []*ScrapeConfig `yaml:"scrape_configs,omitempty"` 39 | } 40 | 41 | func (s *StackManager) GeneratePrometheusConfig() *PrometheusConfig { 42 | config := &PrometheusConfig{ 43 | Global: &GlobalConfig{ 44 | ScrapeInterval: "5s", 45 | ScrapeTimeout: "5s", 46 | }, 47 | ScrapeConfigs: []*ScrapeConfig{ 48 | { 49 | JobName: "fireflies", 50 | MetricsPath: "/metrics", 51 | StaticConfigs: []*StaticConfig{ 52 | { 53 | Targets: []string{}, 54 | }, 55 | }, 56 | }, 57 | }, 58 | } 59 | 60 | for i, member := range s.Stack.Members { 61 | config.ScrapeConfigs[0].StaticConfigs[0].Targets = append(config.ScrapeConfigs[0].StaticConfigs[0].Targets, fmt.Sprintf("firefly_core_%d:%d", i, member.ExposedFireflyMetricsPort)) 62 | 63 | if s.blockchainProvider.GetConnectorName() == "evmconnect" { 64 | config.ScrapeConfigs[0].StaticConfigs[0].Targets = append(config.ScrapeConfigs[0].StaticConfigs[0].Targets, fmt.Sprintf("evmconnect_%d:%d", i, member.ExposedConnectorMetricsPort)) 65 | } 66 | } 67 | 68 | return config 69 | } 70 | -------------------------------------------------------------------------------- /internal/tokens/tokens_provider.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package tokens 18 | 19 | import ( 20 | "github.com/hyperledger/firefly-cli/internal/docker" 21 | "github.com/hyperledger/firefly-cli/pkg/types" 22 | ) 23 | 24 | type ITokensProvider interface { 25 | DeploySmartContracts(tokenIndex int) (*types.ContractDeploymentResult, error) 26 | FirstTimeSetup(tokenIdx int) error 27 | GetDockerServiceDefinitions(tokenIdx int) []*docker.ServiceDefinition 28 | GetFireflyConfig(m *types.Organization, tokenIdx int) *types.TokensConfig 29 | GetName() string 30 | } 31 | -------------------------------------------------------------------------------- /internal/utils/testing.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package utils 18 | 19 | import ( 20 | "bytes" 21 | "fmt" 22 | "io" 23 | "os" 24 | "path/filepath" 25 | "reflect" 26 | "runtime" 27 | "sync" 28 | "testing" 29 | 30 | "github.com/jarcoal/httpmock" 31 | "github.com/sirupsen/logrus" 32 | ) 33 | 34 | type TestHelper struct { 35 | FabricURL string 36 | EthConnectURL string 37 | EvmConnectURL string 38 | } 39 | 40 | var logMutex sync.Mutex 41 | 42 | var ( 43 | FabricEndpoint = "http://localhost:7054" 44 | EthConnectEndpoint = "http://localhost:8080" 45 | EvmConnectEndpoint = "http://localhost:5008" 46 | ) 47 | 48 | func StartMockServer(t *testing.T) { 49 | httpmock.Activate() 50 | } 51 | 52 | // mockprotocol endpoints for testing 53 | func NewTestEndPoint(t *testing.T) *TestHelper { 54 | return &TestHelper{ 55 | FabricURL: FabricEndpoint, 56 | EthConnectURL: EthConnectEndpoint, 57 | EvmConnectURL: EvmConnectEndpoint, 58 | } 59 | } 60 | 61 | func StopMockServer(_ *testing.T) { 62 | httpmock.DeactivateAndReset() 63 | } 64 | 65 | // checks if exp value and act value are equal 66 | func Equals(tb testing.TB, exp, act interface{}) { 67 | if !reflect.DeepEqual(exp, act) { 68 | _, file, line, _ := runtime.Caller(1) 69 | fmt.Printf("\033[31m%s:%d:\n\n\texp: %#v\n\n\tgot: %#v\033[39m\n\n", filepath.Base(file), line, exp, act) 70 | tb.FailNow() 71 | } 72 | } 73 | 74 | // ReadFileToString reads the contents of a file and returns it as a string. 75 | func ReadFileToString(filePath string) (string, error) { 76 | content, err := os.ReadFile(filePath) 77 | if err != nil { 78 | return "", err 79 | } 80 | return string(content), nil 81 | } 82 | 83 | // CaptureOutput redirects the standard output to a buffer and returns the original output writer and the captured output. 84 | func CaptureOutput() (*os.File, *bytes.Buffer) { 85 | originalOutput := os.Stdout // Save the original output 86 | 87 | // Create a pipe to capture the output 88 | _, writer, _ := os.Pipe() 89 | os.Stdout = writer 90 | 91 | // Create a buffer to capture the output 92 | buffer := &bytes.Buffer{} 93 | 94 | // Redirect logrus output to the same buffer 95 | logMutex.Lock() 96 | logrus.SetOutput(io.MultiWriter(originalOutput, buffer)) 97 | logMutex.Unlock() 98 | 99 | return originalOutput, buffer 100 | } 101 | -------------------------------------------------------------------------------- /pkg/types/contracts.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | type ContractDeploymentResult struct { 20 | Message string 21 | DeployedContract *DeployedContract 22 | } 23 | -------------------------------------------------------------------------------- /pkg/types/hex_address.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | import "gopkg.in/yaml.v3" 20 | 21 | type HexAddress string 22 | 23 | // Explicitly quote hex addresses so that they are interpreted as string (not int) 24 | func (h HexAddress) MarshalYAML() (interface{}, error) { 25 | return yaml.Node{ 26 | Value: string(h), 27 | Kind: yaml.ScalarNode, 28 | Style: yaml.DoubleQuotedStyle, 29 | }, nil 30 | } 31 | -------------------------------------------------------------------------------- /pkg/types/manifest.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2025 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | import "fmt" 20 | 21 | type GitHubRelease struct { 22 | TagName string `json:"tag_name,omitempty"` 23 | } 24 | 25 | type VersionManifest struct { 26 | FireFly *ManifestEntry `json:"firefly,omitempty"` 27 | Cardanoconnect *ManifestEntry `json:"cardanoconnect"` 28 | Cardanosigner *ManifestEntry `json:"cardanosigner"` 29 | Ethconnect *ManifestEntry `json:"ethconnect"` 30 | Evmconnect *ManifestEntry `json:"evmconnect"` 31 | Tezosconnect *ManifestEntry `json:"tezosconnect"` 32 | Fabconnect *ManifestEntry `json:"fabconnect"` 33 | DataExchange *ManifestEntry `json:"dataexchange-https"` 34 | TokensERC1155 *ManifestEntry `json:"tokens-erc1155"` 35 | TokensERC20ERC721 *ManifestEntry `json:"tokens-erc20-erc721"` 36 | Signer *ManifestEntry `json:"signer"` 37 | } 38 | 39 | func (m *VersionManifest) Entries() []*ManifestEntry { 40 | if m == nil { 41 | return []*ManifestEntry{} 42 | } 43 | return []*ManifestEntry{ 44 | m.FireFly, 45 | m.Cardanoconnect, 46 | m.Ethconnect, 47 | m.Evmconnect, 48 | m.Tezosconnect, 49 | m.Fabconnect, 50 | m.DataExchange, 51 | m.TokensERC1155, 52 | m.TokensERC20ERC721, 53 | m.Signer, 54 | } 55 | } 56 | 57 | type ManifestEntry struct { 58 | Image string `json:"image,omitempty"` 59 | Local bool `json:"local,omitempty"` 60 | Tag string `json:"tag,omitempty"` 61 | SHA string `json:"sha,omitempty"` 62 | } 63 | 64 | func (m *ManifestEntry) GetDockerImageString() string { 65 | if m.SHA != "" { 66 | return fmt.Sprintf("%s@sha256:%s", m.Image, m.SHA) 67 | } else if m.Tag != "" { 68 | return fmt.Sprintf("%s:%s", m.Image, m.Tag) 69 | } 70 | return m.Image 71 | } 72 | -------------------------------------------------------------------------------- /pkg/types/namespace.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | type Namespace struct { 20 | Name string `yaml:"name"` 21 | Description string `yaml:"description,omitempty"` 22 | Plugins []string `yaml:"plugins"` 23 | Multiparty *MultipartyConfig `yaml:"multiparty,omitempty"` 24 | DefaultKey interface{} `yaml:"defaultKey"` 25 | } 26 | 27 | type Plugins struct { 28 | Database []*DatabaseConfig `yaml:"database,omitempty"` 29 | Blockchain []*BlockchainConfig `yaml:"blockchain,omitempty"` 30 | SharedStorage []*SharedStorageConfig `yaml:"sharedstorage,omitempty"` 31 | DataExchange []*DataExchangeConfig `yaml:"dataexchange,omitempty"` 32 | Tokens []*TokensConfig `yaml:"tokens,omitempty"` 33 | } 34 | 35 | type MultipartyConfig struct { 36 | Enabled bool `yaml:"enabled"` 37 | Org *OrgConfig `yaml:"org"` 38 | Node *NodeConfig `yaml:"node"` 39 | Contract []*ContractConfig `yaml:"contract"` 40 | } 41 | 42 | type ContractConfig struct { 43 | Location interface{} `yaml:"location"` 44 | FirstEvent string `yaml:"firstEvent,omitempty"` 45 | Options interface{} `yaml:"options"` 46 | } 47 | 48 | type MultipartyOrgConfig struct { 49 | Name string `yaml:"name"` 50 | Description string `yaml:"description,omitempty"` 51 | Key string `yaml:"key"` 52 | } 53 | -------------------------------------------------------------------------------- /pkg/types/organization.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | type Organization struct { 20 | ID string `json:"id,omitempty"` 21 | Index *int `json:"index,omitempty"` 22 | Account interface{} `json:"account,omitempty"` 23 | ExposedFireflyPort int `json:"exposedFireflyPort,omitempty"` 24 | ExposedFireflyAdminSPIPort int `json:"exposedFireflyAdminPort,omitempty"` // stack.json still contains the word "Admin" (rather than SPI) for migration 25 | ExposedFireflyMetricsPort int `json:"exposedFireflyMetricsPort,omitempty"` 26 | ExposedConnectorPort int `json:"exposedConnectorPort,omitempty"` 27 | ExposedConnectorMetricsPort int `json:"exposedConnectorMetricsPort,omitempty"` 28 | ExposedDatabasePort int `json:"exposedPostgresPort,omitempty"` 29 | ExposedDataexchangePort int `json:"exposedDataexchangePort,omitempty"` 30 | ExposedIPFSApiPort int `json:"exposedIPFSApiPort,omitempty"` 31 | ExposedIPFSGWPort int `json:"exposedIPFSGWPort,omitempty"` 32 | ExposedUIPort int `json:"exposedUiPort,omitempty"` 33 | ExposedSandboxPort int `json:"exposedSandboxPort,omitempty"` 34 | ExposedTokensPorts []int `json:"exposedTokensPorts,omitempty"` 35 | ExposePtmTpPort int `json:"exposePtmTpPort,omitempty"` 36 | External bool `json:"external,omitempty"` 37 | OrgName string `json:"orgName,omitempty"` 38 | NodeName string `json:"nodeName,omitempty"` 39 | Namespaces []*Namespace `json:"namespaces"` 40 | } 41 | -------------------------------------------------------------------------------- /pkg/types/stackState.go: -------------------------------------------------------------------------------- 1 | // Copyright © 2024 Kaleido, Inc. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | // 5 | // Licensed under the Apache License, Version 2.0 (the "License"); 6 | // you may not use this file except in compliance with the License. 7 | // You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, software 12 | // distributed under the License is distributed on an "AS IS" BASIS, 13 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | // See the License for the specific language governing permissions and 15 | // limitations under the License. 16 | 17 | package types 18 | 19 | type DeployedContract struct { 20 | Name string `json:"name"` 21 | Location interface{} `json:"location"` 22 | } 23 | 24 | type StackState struct { 25 | DeployedContracts []*DeployedContract `json:"deployedContracts"` 26 | Accounts []interface{} `json:"accounts"` 27 | } 28 | --------------------------------------------------------------------------------