├── .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 | 
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 | 
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 | 
8 |
9 | ## Go to System Preferences and click on Security & Privacy
10 |
11 | 
12 |
13 | ## Click on the Allow Anyway button
14 |
15 | 
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 | 
22 |
23 |
24 | This time the `ff` command should succeed and you should see something like this in your terminal:
25 |
26 | 
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 |
--------------------------------------------------------------------------------