diff --git a/.dockerignore b/.dockerignore
index 168f6c6..6eca100 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -1 +1 @@
-/.git/
\ No newline at end of file
+/.git/
diff --git a/.drone.yml b/.drone.yml
deleted file mode 100644
index ffbf94a..0000000
--- a/.drone.yml
+++ /dev/null
@@ -1,32 +0,0 @@
-workspace:
- base: /root
- path: go/src/github.com/vulpemventures/nigiri
-
-pipeline:
- test:
- image: docker/compose:1.24.0
- environment:
- - DOCKER_HOST=tcp://docker:2375
- commands:
- - apk update && apk add --no-cache git curl wget bash make build-base
- - mkdir -p /root/go/bin /root/go/pkg
- # Install Go
- - wget -q https://dl.google.com/go/go1.13.4.linux-amd64.tar.gz
- - tar -xf go1.13.4.linux-amd64.tar.gz -C /usr/local && rm -rf go*
- - export GOROOT=/usr/local/go
- - export GOPATH=$HOME/go
- - export PATH=$GOPATH/bin:$GOROOT/bin:$PATH
- # Test
- - export GO111MODULE=on
- - go mod init
- - bash scripts/install
- - go test -v ./...
-
-services:
- docker:
- image: docker:dind
- privileged: true
-
- compose:
- image: docker/compose:1.24.0
- privileged: true
\ No newline at end of file
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9812c8f..7abceee 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -16,7 +16,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
- go-version: 1.15.x
+ go-version: 1.16.x
- name: Cache Go modules
uses: actions/cache@v1
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index b6f4cd0..0e0cc6a 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,12 +11,23 @@ jobs:
name: Unit Tests
runs-on: ubuntu-latest
steps:
- - name: Set up Go 1.x
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ fetch-depth: 0
+
+ - name: Set up Go
uses: actions/setup-go@v2
- id: go
+ with:
+ go-version: 1.16.x
- - name: Check out code into the Go module directory
- uses: actions/checkout@v2
+ - name: Cache Go modules
+ uses: actions/cache@v1
+ with:
+ path: ~/go/pkg/mod
+ key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
+ restore-keys: |
+ ${{ runner.os }}-go-
- name: Get dependencies
run: go get -v -t -d ./...
@@ -25,5 +36,4 @@ jobs:
run: |
make fmt
make install
- sudo chmod -R 777 .
make test-ci
diff --git a/.gitignore b/.gitignore
index 19a5844..41a780b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,4 +3,4 @@
vendor/
build/
-dist/
\ No newline at end of file
+dist/
diff --git a/.goreleaser.yml b/.goreleaser.yml
index 4438e1c..4b96041 100644
--- a/.goreleaser.yml
+++ b/.goreleaser.yml
@@ -1,7 +1,7 @@
builds:
- - main: ./cli/main.go
+ - main: ./cmd/nigiri
ldflags:
- - -s -w -X github.com/vulpemventures/nigiri/cli/cmd.version={{.Version}} -X github.com/vulpemventures/nigiri/cli/cmd.commit={{.Commit}} -X github.com/vulpemventures/nigiri/cli/cmd.date={{.Date}}
+ - -s -X 'main.version={{.Version}}' -X 'main.commit={{.Commit}}' -X 'main.date={{.Date}}'
env:
- CGO_ENABLED=0
goos:
@@ -9,17 +9,22 @@ builds:
- darwin
goarch:
- amd64
+
checksum:
- name_template: 'checksums.txt'
+ name_template: "checksums.txt"
+
snapshot:
name_template: "{{ .Tag }}-next"
+
changelog:
sort: asc
filters:
exclude:
- - '^docs:'
- - '^test:'
+ - "^docs:"
+ - "^test:"
archives:
- -
- format: binary
- name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
\ No newline at end of file
+ - format: binary
+ name_template: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
+
+release:
+ prerelease: auto
diff --git a/Makefile b/Makefile
index 20393ae..763dabb 100644
--- a/Makefile
+++ b/Makefile
@@ -1,30 +1,28 @@
-.PHONY: install build release dry-release clean cov fmt help vet test
+.PHONY: install clean build release dry-release cov fmt help vet test
## install: installs dependencies
install:
- export GO111MODULE=on
- chmod u+x ./scripts/install
- ./scripts/install
+ go mod download
+ go mod tidy
-## build: build binary for ARM
+## clean: cleans the binary
+clean:
+ @echo "Cleaning..."
+ go clean
+
+## build: build binary
build:
- export GO111MODULE=on
chmod u+x ./scripts/build
./scripts/build
+## release: build and upload binaries to Github Releases
release:
goreleaser
+## dry-release: build and test goreleaser
dry-release:
goreleaser --snapshot --skip-publish --rm-dist
-## clean: cleans the binary
-clean:
- @echo "Cleaning..."
- export GO111MODULE=on
- chmod u+x ./scripts/clean
- ./scripts/clean
-
## help: prints this help message
help:
@echo "Usage: \n"
@@ -46,7 +44,7 @@ test: clean install
go test -v -count=1 -race ./...
## test-ci: runs travis tests
-test-ci:
+test-ci: clean
@echo "Testing..."
go test -short -v ./...
diff --git a/README.md b/README.md
index 5bdbe4e..ff529e3 100644
--- a/README.md
+++ b/README.md
@@ -6,8 +6,6 @@ It offers a [JSON HTTP proxy passtrough](https://github.com/vulpemventures/nigir
You can have Elements too with the `--liquid` flag.
-Are you looking to spin-up Nigiri in Travis or Github Action? Look [here](https://github.com/vulpemventures/nigiri-travis)
-
# No time to make a Nigiri yourself?
## Pre-built binary
@@ -51,7 +49,6 @@ $ nigiri rpc --liquid getnewaddress "" "bech32"
el1qqwwx9gyrcrjrhgnrnjq9dq9t4hykmr6ela46ej63dnkdkcg8veadrvg5p0xg0zd6j3aug74cv9m4cf4jslwdqnha2w2nsg9x3
```
-
# Make from scratch
## Utensils
@@ -83,14 +80,11 @@ $ git clone https://github.com/vulpemventures/nigiri.git
$ make install
```
-This will create `~/.nigiri` copying there the `{bitcoin|elements}.conf` you can modify.
* Build binary
+
```
-# MacOSX
-$ make build-mac
-# Linux
-$ make build-linux
+$ make build
```
Done! You should be able to find the binary in the local `./build` folder. Give it permission to execute and move/rename into your PATH.
@@ -187,6 +181,21 @@ $ nigiri rpc --liquid getnewaddress "" "bech32"
el1qqwwx9gyrcrjrhgnrnjq9dq9t4hykmr6ela46ej63dnkdkcg8veadrvg5p0xg0zd6j3aug74cv9m4cf4jslwdqnha2w2nsg9x3
```
+* Run in headless mode (without Esplora)
+If you are looking to spin-up Nigiri in Travis or Github Action you can use the `--ci` flag.
+
+```
+$ nigiri start --ci [--liquid]
+```
+
+
+* Update the docker images
+
+```
+$ nigiri update
+```
+
+
Nigiri uses the default directory `~/.nigiri` to store configuration files and docker-compose files.
diff --git a/cli/cmd/faucet.go b/cli/cmd/faucet.go
deleted file mode 100644
index 6a96a6d..0000000
--- a/cli/cmd/faucet.go
+++ /dev/null
@@ -1,120 +0,0 @@
-package cmd
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "strconv"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var FaucetCmd = &cobra.Command{
- Args: func(cmd *cobra.Command, args []string) error {
-
- if len(args) < 1 {
- return errors.New("missing address")
- }
- return nil
- },
- Use: "faucet
[amount] [asset]",
- Short: "Generate and send bitcoin to given address",
- RunE: faucet,
- PreRunE: faucetChecks,
-}
-
-func faucetChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
- if len(args) < 1 {
- return constants.ErrInvalidArgs
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- return constants.ErrNigiriNotRunning
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
-
- if isLiquidService && isLiquidService != ctl.GetConfigBoolField(constants.AttachLiquid) {
- return constants.ErrNigiriLiquidNotEnabled
- }
-
- return nil
-}
-
-func faucet(cmd *cobra.Command, args []string) error {
- isLiquidService, err := cmd.Flags().GetBool("liquid")
- datadir, _ := cmd.Flags().GetString("datadir")
- if err != nil {
- return err
- }
- request := map[string]interface{}{
- "address": args[0],
- }
- if len(args) >= 2 {
- amountFloat, err := strconv.ParseFloat(args[1], 64)
- if err != nil {
- return fmt.Errorf("invalid amount: %v", err)
- }
- request["amount"] = amountFloat
- }
- if len(args) == 3 {
- request["asset"] = args[2]
- }
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
- envPath := ctl.GetResourcePath(datadir, "env")
- env, _ := ctl.ReadComposeEnvironment(envPath)
- envPorts := env["ports"].(map[string]map[string]int)
- requestPort := envPorts["bitcoin"]["chopsticks"]
- if isLiquidService {
- requestPort = envPorts["liquid"]["chopsticks"]
- }
- payload, err := json.Marshal(request)
- if err != nil {
- return err
- }
- res, err := http.Post("http://127.0.0.1:"+strconv.Itoa(requestPort)+"/faucet", "application/json", bytes.NewBuffer(payload))
- if err != nil {
- return err
- }
- data, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return err
- }
- if res.StatusCode != http.StatusOK {
- return errors.New(string(data))
- }
-
- var dat map[string]string
- if err := json.Unmarshal([]byte(data), &dat); err != nil {
- return errors.New("internal error, please try again")
- }
- if dat["txId"] == "" {
- return errors.New("Not Successful")
- }
- fmt.Println("txId: " + dat["txId"])
- return nil
-}
diff --git a/cli/cmd/faucet_test.go b/cli/cmd/faucet_test.go
deleted file mode 100644
index eadaec5..0000000
--- a/cli/cmd/faucet_test.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package cmd
-
-import (
- "testing"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-const (
- btcAddress = "mpSGWQvbAiRt2UNLST1CdWUufoPVsVwLyK"
- liquidAddress = "CTEsqL1x9ooWWG9HBaHUpvS2DGJJ4haYdkTQPKj9U8CCdwT5vcudhbYUT8oQwwoS11aYtdznobfgT8rj"
-)
-
-func TestFaucetBitcoinServices(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- if err := testCommand("faucet", btcAddress, bitcoin); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestFaucetLiquidServices(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("faucet", liquidAddress, liquid); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestFaucetShouldFail(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- expectedError := constants.ErrNigiriNotRunning.Error()
-
- err := testCommand("faucet", btcAddress, bitcoin)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- err = testCommand("faucet", liquidAddress, liquid)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-}
-
-func TestStartBitcoinAndFaucetNigiriServicesShouldFail(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- expectedError := constants.ErrNigiriLiquidNotEnabled.Error()
-
- err := testCommand("faucet", liquidAddress, liquid)
- if err == nil {
- t.Fatal("Should return error when trying logging liquid services if not running")
- }
-
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- testDelete(t)
-}
diff --git a/cli/cmd/flags.go b/cli/cmd/flags.go
deleted file mode 100644
index d48fb1d..0000000
--- a/cli/cmd/flags.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package cmd
-
-import (
- "encoding/json"
- "os"
-
- log "github.com/sirupsen/logrus"
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/config"
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-var (
- flagDatadir string
- flagNetwork string
- flagDelete bool
- flagAttachLiquid bool
- flagLiquidService bool
- flagEnv string
- flagRpcWallet string
-)
-
-var RootCmd = &cobra.Command{
- Use: "nigiri",
- Short: "Nigiri lets you manage a full dockerized bitcoin environment",
- Long: "Nigiri lets you create your dockerized environment with a bitcoin and optionally a liquid node + block explorer powered by an electrum server for every network",
-}
-
-func init() {
- c := &config.Config{}
- viper := c.Viper()
- defaultDir := c.GetPath()
- defaultJSON, _ := json.Marshal(constants.DefaultEnv)
-
- RootCmd.PersistentFlags().StringVar(&flagDatadir, "datadir", defaultDir, "Set nigiri default directory")
- StartCmd.PersistentFlags().StringVar(&flagNetwork, "network", "regtest", "Set bitcoin network - regtest only for now")
- StartCmd.PersistentFlags().BoolVar(&flagAttachLiquid, "liquid", false, "Enable liquid sidechain")
- StartCmd.PersistentFlags().StringVar(&flagEnv, "env", string(defaultJSON), "Set compose env in JSON format")
- StopCmd.PersistentFlags().BoolVar(&flagDelete, "delete", false, "Stop and delete nigiri")
- LogsCmd.PersistentFlags().BoolVar(&flagLiquidService, "liquid", false, "Set to see logs of a liquid service")
- FaucetCmd.PersistentFlags().BoolVar(&flagLiquidService, "liquid", false, "Set to donate liquid btc")
- PushCmd.PersistentFlags().BoolVar(&flagLiquidService, "liquid", false, "Set to use liquid")
- RpcCmd.PersistentFlags().BoolVar(&flagLiquidService, "liquid", false, "Set to use liquid node")
- RpcCmd.PersistentFlags().StringVar(&flagRpcWallet, "rpcwallet", "", "rpcwallet to be used for node JSONRPC commands")
-
- RootCmd.AddCommand(StartCmd)
- RootCmd.AddCommand(StopCmd)
- RootCmd.AddCommand(LogsCmd)
- RootCmd.AddCommand(FaucetCmd)
- RootCmd.AddCommand(RpcCmd)
- RootCmd.AddCommand(MintCmd)
- RootCmd.AddCommand(PushCmd)
- RootCmd.AddCommand(VersionCmd)
-
- viper.BindPFlag(constants.Datadir, RootCmd.PersistentFlags().Lookup("datadir"))
- viper.BindPFlag(constants.Network, StartCmd.PersistentFlags().Lookup("network"))
- viper.BindPFlag(constants.AttachLiquid, StartCmd.PersistentFlags().Lookup("liquid"))
-
- cobra.OnInitialize(func() {
- log.SetOutput(os.Stdout)
- log.SetLevel(log.InfoLevel)
- })
-}
diff --git a/cli/cmd/logs.go b/cli/cmd/logs.go
deleted file mode 100644
index cd659af..0000000
--- a/cli/cmd/logs.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package cmd
-
-import (
- "errors"
- "os"
- "os/exec"
-
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-
- "github.com/spf13/cobra"
-)
-
-var logsDescription = "Check Service logs. Requires one Service: " + servicesList()
-
-var LogsCmd = &cobra.Command{
- Args: func(cmd *cobra.Command, args []string) error {
-
- if len(args) != 1 {
- return errors.New(logsDescription)
- }
- _, found := controller.Services[args[0]]
- if !found {
- return errors.New(logsDescription)
- }
- return nil
- },
- Use: "logs ",
- Short: logsDescription,
- Long: logsDescription,
- RunE: logs,
- PreRunE: logsChecks,
-}
-
-func servicesList() string {
- var servicesString string
- for key, _ := range controller.Services {
- servicesString += key
- servicesString += " | "
- }
- return servicesString[:len(servicesString)-3]
-}
-
-func logsChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
- if len(args) != 1 {
- return constants.ErrInvalidArgs
- }
-
- service := args[0]
- if err := ctl.ParseServiceName(service); err != nil {
- return err
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- return constants.ErrNigiriNotRunning
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
-
- if isLiquidService && isLiquidService != ctl.GetConfigBoolField(constants.AttachLiquid) {
- return constants.ErrNigiriLiquidNotEnabled
- }
-
- return nil
-}
-
-func logs(cmd *cobra.Command, args []string) error {
- service := args[0]
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- serviceName := ctl.GetServiceName(service, isLiquidService)
- composePath := ctl.GetResourcePath(datadir, "compose")
- envPath := ctl.GetResourcePath(datadir, "env")
- env := ctl.LoadComposeEnvironment(envPath)
-
- bashCmd := exec.Command("docker-compose", "-f", composePath, "logs", serviceName)
- bashCmd.Stdout = os.Stdout
- bashCmd.Stderr = os.Stderr
- bashCmd.Env = env
-
- if err := bashCmd.Run(); err != nil {
- return err
- }
-
- return nil
-}
diff --git a/cli/cmd/logs_test.go b/cli/cmd/logs_test.go
deleted file mode 100644
index 36b06cf..0000000
--- a/cli/cmd/logs_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package cmd
-
-import (
- "testing"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-var (
- serviceList = []string{"node", "electrs", "esplora", "chopsticks"}
-)
-
-func TestLogBitcoinServices(t *testing.T) {
- if err := testCommand("start", "", bitcoin); err != nil {
- t.Fatal(err)
- }
-
- for _, service := range serviceList {
- if err := testCommand("logs", service, bitcoin); err != nil {
- t.Fatal(err)
- }
- }
-
- if err := testCommand("stop", "", delete); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestLogLiquidServices(t *testing.T) {
- if err := testCommand("start", "", liquid); err != nil {
- t.Fatal(err)
- }
-
- for _, service := range serviceList {
- if err := testCommand("logs", service, liquid); err != nil {
- t.Fatal(err)
- }
- }
-
- if err := testCommand("stop", "", delete); err != nil {
- t.Fatal(err)
- }
-}
-
-func TestLogShouldFail(t *testing.T) {
- expectedError := constants.ErrNigiriNotRunning.Error()
-
- err := testCommand("logs", serviceList[0], bitcoin)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- err = testCommand("logs", serviceList[0], liquid)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-}
-
-func TestStartBitcoinAndLogNigiriServicesShouldFail(t *testing.T) {
- if err := testCommand("start", "", bitcoin); err != nil {
- t.Fatal(err)
- }
-
- expectedError := constants.ErrNigiriLiquidNotEnabled.Error()
-
- err := testCommand("logs", serviceList[0], liquid)
- if err == nil {
- t.Fatal("Should return error when trying logging liquid services if not running")
- }
-
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- if err := testCommand("stop", "", delete); err != nil {
- t.Fatal(err)
- }
-}
diff --git a/cli/cmd/mint.go b/cli/cmd/mint.go
deleted file mode 100644
index a2fdf5a..0000000
--- a/cli/cmd/mint.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package cmd
-
-import (
- "bytes"
- "encoding/json"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "strconv"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var MintCmd = &cobra.Command{
- Use: "mint [name] [ticker]",
- Short: "Liquid only: Issue and send a given quantity of an asset",
- RunE: mint,
- PreRunE: mintChecks,
-}
-
-func mintChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
- if len(args) < 2 {
- return errors.New("missing required arguments")
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- return constants.ErrNigiriNotRunning
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
-
- if ctl.GetConfigBoolField(constants.AttachLiquid) != true {
- return constants.ErrNigiriLiquidNotEnabled
- }
-
- return nil
-}
-
-func mint(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
-
- var request struct {
- Address string `json:"address"`
- Quantity int `json:"quantity"`
- Name string `json:"name"`
- Ticker string `json:"ticker"`
- }
- request.Address = args[0]
- request.Quantity, _ = strconv.Atoi(args[1])
- if len(args) >= 3 {
- request.Name = args[2]
- }
- if len(args) == 4 {
- request.Ticker = args[3]
- }
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
- envPath := ctl.GetResourcePath(datadir, "env")
- env, _ := ctl.ReadComposeEnvironment(envPath)
- envPorts := env["ports"].(map[string]map[string]int)
- requestPort := envPorts["liquid"]["chopsticks"]
-
- payload, err := json.Marshal(request)
- if err != nil {
- return err
- }
- res, err := http.Post("http://127.0.0.1:"+strconv.Itoa(requestPort)+"/mint", "application/json", bytes.NewBuffer(payload))
- if err != nil {
- return err
- }
-
- data, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return err
- }
- if res.StatusCode != http.StatusOK {
- return errors.New(string(data))
- }
- var dat map[string]interface{}
- var resp string
- if err := json.Unmarshal([]byte(data), &dat); err != nil {
- return errors.New("Internal error. Try again.")
- }
- if dat["txId"] == "" {
- return errors.New("Not Successful")
- }
- for key, element := range dat {
- if key == "issuance_txin" {
- myMap := element.(map[string]interface{})
- resp += key + ":\n"
- for key2, element2 := range myMap {
- resp += " "
- resp += key2 + ": " + fmt.Sprintf("%v", element2) + "\n"
- }
- continue
- }
- resp += key + ": " + fmt.Sprintf("%v", element) + "\n"
- }
- fmt.Println(resp[:len(resp)-1])
- return nil
-}
diff --git a/cli/cmd/mint_test.go b/cli/cmd/mint_test.go
deleted file mode 100644
index 5ef2031..0000000
--- a/cli/cmd/mint_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package cmd
-
-import (
- "testing"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-func TestMintOneArg(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("mint", "ert1q90dz89u8eudeswzynl3p2jke564ejc2cnfcwuq 1000", liquid); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestMintTwoArgs(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("mint", "ert1q90dz89u8eudeswzynl3p2jke564ejc2cnfcwuq 2000 Test", liquid); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestMintThreeArgs(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("mint", "ert1q90dz89u8eudeswzynl3p2jke564ejc2cnfcwuq 3000 TEST TST", liquid); err != nil {
- t.Fatal(err)
- }
- testDelete(t)
-
-}
-
-func TestStartBitcoinAndMintShouldFail(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- expectedError := constants.ErrNigiriLiquidNotEnabled.Error()
-
- err := testCommand("mint", "ert1q90dz89u8eudeswzynl3p2jke564ejc2cnfcwuq 1000", liquid)
- if err == nil {
- t.Fatal("Should return error when trying logging liquid services if not running")
- }
-
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- testDelete(t)
-}
diff --git a/cli/cmd/push.go b/cli/cmd/push.go
deleted file mode 100644
index bec8bdb..0000000
--- a/cli/cmd/push.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package cmd
-
-import (
- "bytes"
- "errors"
- "fmt"
- "io/ioutil"
- "net/http"
- "strconv"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var PushCmd = &cobra.Command{
- Args: func(cmd *cobra.Command, args []string) error {
-
- if len(args) != 1 {
- return errors.New("Missing hex encoded transaction")
- }
- return nil
- },
- Use: "push ",
- Short: "Broadcast raw transaction",
- RunE: push,
- PreRunE: pushChecks,
-}
-
-func pushChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
- if len(args) != 1 {
- return constants.ErrInvalidArgs
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- return constants.ErrNigiriNotRunning
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
-
- if isLiquidService && isLiquidService != ctl.GetConfigBoolField(constants.AttachLiquid) {
- return constants.ErrNigiriLiquidNotEnabled
- }
-
- return nil
-}
-
-func push(cmd *cobra.Command, args []string) error {
- isLiquidService, err := cmd.Flags().GetBool("liquid")
- datadir, _ := cmd.Flags().GetString("datadir")
- if err != nil {
- return err
- }
- request := args[0]
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
- envPath := ctl.GetResourcePath(datadir, "env")
- env, _ := ctl.ReadComposeEnvironment(envPath)
- envPorts := env["ports"].(map[string]map[string]int)
- requestPort := envPorts["bitcoin"]["chopsticks"]
- if isLiquidService {
- requestPort = envPorts["liquid"]["chopsticks"]
- }
- hex := []byte(request)
- res, err := http.Post("http://127.0.0.1:"+strconv.Itoa(requestPort)+"/tx", "application/string", bytes.NewBuffer(hex))
- if err != nil {
- return err
- }
- data, err := ioutil.ReadAll(res.Body)
- if err != nil {
- return err
- }
- if res.StatusCode != http.StatusOK {
- return errors.New(string(data))
- }
-
- if string(data) == "" {
- return errors.New("Not Successful")
- }
- fmt.Println("\ntxId: " + string(data))
- return nil
-}
diff --git a/cli/cmd/push_test.go b/cli/cmd/push_test.go
deleted file mode 100644
index 0b913b4..0000000
--- a/cli/cmd/push_test.go
+++ /dev/null
@@ -1,167 +0,0 @@
-package cmd
-
-import (
- "encoding/json"
- "errors"
- "fmt"
- "os/exec"
- "strconv"
- "strings"
- "testing"
-)
-
-/* func TestPushBitcoinTransaction(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
- hex, err := getNewSignedTransaction(bitcoin)
- if err != nil {
- t.Fatal(err)
- }
- if err := testCommand("push", hex, bitcoin); err != nil {
- t.Fatal(err)
- }
- testDelete(t)
-} */
-
-func TestPushLiquidTransaction(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
- hex, err := getNewSignedTransaction(liquid)
- if err != nil {
- t.Fatal(err)
- }
- if err := testCommand("push", hex, liquid); err != nil {
- t.Fatal(err)
- }
- testDelete(t)
-}
-
-func getNewSignedTransaction(isLiquid bool) (string, error) {
- txId, vout, amount, asset, err := listUnspent(isLiquid)
- if err != nil {
- return "", err
- }
- hex, err := createRawTransaction(txId, vout, amount, asset, isLiquid)
- if err != nil {
- return "", err
- }
- hexFinal, err := signRawTransaction(hex, isLiquid)
- if err != nil {
- return "", err
- }
- return hexFinal, nil
-}
-
-func listUnspent(isLiquid bool) (string, string, string, string, error) {
- bashCmd, err := execCommand([]string{"listunspent"}, isLiquid)
- if err != nil {
- return "", "", "", "", err
- }
- type unspent map[string]interface{}
- var unspentList []unspent
- if err := json.Unmarshal([]byte(bashCmd), &unspentList); err != nil {
- return "", "", "", "", errors.New("Internal error. Try again.")
- }
- txId := fmt.Sprintf("%v", unspentList[0]["txid"])
- vout := fmt.Sprintf("%v", unspentList[0]["vout"])
- amount := fmt.Sprintf("%v", unspentList[0]["amount"])
- asset := ""
- if isLiquid {
- asset = fmt.Sprintf("%v", unspentList[0]["asset"])
- }
- return txId, vout, amount, asset, nil
-}
-
-func createRawTransaction(txId string, vout string, amount string, asset string, isLiquid bool) (string, error) {
- type inputs map[string]interface{}
- var inputsList [1]inputs
- voutInt, err := strconv.Atoi(vout)
- if err != nil {
- return "", err
- }
- inputsList[0] = make(inputs, 2)
- inputsList[0]["txid"] = txId
- inputsList[0]["vout"] = voutInt
- inputsJson, err := json.Marshal(inputsList)
- if err != nil {
- return "", err
- }
- fee := 0.00001
- amountInt, err := strconv.ParseFloat(amount, 64)
- if err != nil {
- return "", err
- }
- amountSend := fmt.Sprintf("%.6f", amountInt-fee)
- sendAdress, err := execCommand([]string{"getnewaddress"}, isLiquid)
- sendAdressString := string(sendAdress)
- if err != nil {
- return "", err
- }
- outputs := make(map[string]interface{})
- if isLiquid {
- addressInfoJson, err := execCommand([]string{"getaddressinfo", string(sendAdress)}, isLiquid)
- if err != nil {
- return "", err
- }
- var adressInfo map[string]interface{}
- if err := json.Unmarshal([]byte(addressInfoJson), &adressInfo); err != nil {
- return "", errors.New("Internal error. Try again.")
- }
- sendAdressString = fmt.Sprintf("%v", adressInfo["unconfidential"])
- outputs["fee"] = fee
- }
- outputs[sendAdressString], err = strconv.ParseFloat(amountSend, 64)
- if err != nil {
- return "", err
- }
- outputsJson, err := json.Marshal(outputs)
- if err != nil {
- return "", err
- }
- commandArgs := []string{"createrawtransaction", string(inputsJson), string(outputsJson)}
- if isLiquid {
- output_assets := make(map[string]interface{})
- output_assets[sendAdressString] = asset
- output_assets["fee"] = asset
- output_assetsJson, err := json.Marshal(output_assets)
- if err != nil {
- return "", err
- }
- commandArgs = append(commandArgs, []string{"0", "false", string(output_assetsJson)}...)
- }
- bashCmd, err := execCommand(commandArgs, isLiquid)
- if err != nil {
- return "", err
- }
- return strings.Fields(string(bashCmd))[0], nil
-}
-
-func signRawTransaction(hex string, isLiquid bool) (string, error) {
- bashCmd, err := execCommand([]string{"signrawtransactionwithwallet", hex}, isLiquid)
- if err != nil {
- return "", err
- }
- var hexJson map[string]interface{}
- if err := json.Unmarshal([]byte(bashCmd), &hexJson); err != nil {
- return "", errors.New("Internal error. Try again.")
- }
- hex = fmt.Sprintf("%v", hexJson["hex"])
- return hex, nil
-}
-
-func execCommand(args []string, isLiquid bool) ([]byte, error) {
- rpcArgs := []string{"exec", "bitcoin", "bitcoin-cli", "-datadir=config"}
- if isLiquid {
- rpcArgs = []string{"exec", "liquid", "elements-cli", "-datadir=config"}
- }
- cmdArgs := append(rpcArgs, args...)
- bashCmd, err := exec.Command("docker", cmdArgs...).Output()
- if err != nil {
- return nil, err
- }
- return bashCmd, nil
-}
diff --git a/cli/cmd/rpc.go b/cli/cmd/rpc.go
deleted file mode 100644
index 0c7860a..0000000
--- a/cli/cmd/rpc.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package cmd
-
-import (
- "os"
- "os/exec"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var RpcCmd = &cobra.Command{
- Use: "rpc ",
- Short: "Invoke the bitcoin-cli or elements-cli",
- RunE: rpc,
- PreRunE: rpcChecks,
-}
-
-func rpcChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- return constants.ErrNigiriNotRunning
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
-
- if isLiquidService && isLiquidService != ctl.GetConfigBoolField(constants.AttachLiquid) {
- return constants.ErrNigiriLiquidNotEnabled
- }
-
- return nil
-}
-
-func rpc(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- isLiquidService, _ := cmd.Flags().GetBool("liquid")
- rpcWallet, _ := cmd.Flags().GetString("rpcwallet")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- envPath := ctl.GetResourcePath(datadir, "env")
- env := ctl.LoadComposeEnvironment(envPath)
-
- rpcArgs := []string{"exec", "bitcoin", "bitcoin-cli", "-datadir=config", "-rpcwallet=" + rpcWallet}
- if isLiquidService {
- rpcArgs = []string{"exec", "liquid", "elements-cli", "-datadir=config", "-rpcwallet=" + rpcWallet}
- }
- cmdArgs := append(rpcArgs, args...)
- bashCmd := exec.Command("docker", cmdArgs...)
- bashCmd.Stdout = os.Stdout
- bashCmd.Stderr = os.Stderr
- bashCmd.Env = env
-
- if err := bashCmd.Run(); err != nil {
- return err
- }
-
- return nil
-}
diff --git a/cli/cmd/rpc_test.go b/cli/cmd/rpc_test.go
deleted file mode 100644
index 56cb942..0000000
--- a/cli/cmd/rpc_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package cmd
-
-import (
- "testing"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-func TestRpcBitcoinCommand(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- if err := testCommand("rpc", "getblockchaininfo", bitcoin); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestRpcBitcoinTwoCommands(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- if err := testCommand("rpc", "getblockhash 0", bitcoin); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestRpcLiquidCommand(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("rpc", "getblockchaininfo", liquid); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestRpcLiquidTwoCommands(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, liquid)
-
- if err := testCommand("rpc", "getblockhash 0", liquid); err != nil {
- t.Fatal(err)
- }
-
- testDelete(t)
-}
-
-func TestRpcShouldFail(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- expectedError := constants.ErrNigiriNotRunning.Error()
-
- err := testCommand("rpc", "getblockchaininfo", bitcoin)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- err = testCommand("rpc", "getblockchaininfo", liquid)
- if err == nil {
- t.Fatal("Should return error when Nigiri is stopped")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-}
-
-func TestStartBitcoinAndRpcNigiriServicesShouldFail(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping testing in short mode")
- }
- testStart(t, bitcoin)
-
- expectedError := constants.ErrNigiriLiquidNotEnabled.Error()
-
- err := testCommand("rpc", "getblockchaininfo", liquid)
- if err == nil {
- t.Fatal("Should return error when trying logging liquid services if not running")
- }
-
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- testDelete(t)
-}
diff --git a/cli/cmd/start.go b/cli/cmd/start.go
deleted file mode 100644
index 4883868..0000000
--- a/cli/cmd/start.go
+++ /dev/null
@@ -1,136 +0,0 @@
-package cmd
-
-import (
- "fmt"
- "os"
- "os/exec"
- "strings"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var StartCmd = &cobra.Command{
- Use: "start",
- Short: "Build and start Nigiri",
- RunE: start,
- PreRunE: startChecks,
-}
-
-func startChecks(cmd *cobra.Command, args []string) error {
- network, _ := cmd.Flags().GetString("network")
- datadir, _ := cmd.Flags().GetString("datadir")
- env, _ := cmd.Flags().GetString("env")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseNetwork(network); err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
- composeEnv, err := ctl.ParseEnv(env)
- if err != nil {
- return err
- }
-
- // if nigiri is already running return error
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if isRunning {
- return constants.ErrNigiriAlreadyRunning
- }
-
- // scratch datadir if not exists
- if err := os.MkdirAll(datadir, 0755); err != nil {
- return err
- }
-
- // if datadir is set we must copy the resources directory from ~/.nigiri
- // to the new one
- if datadir != ctl.GetDefaultDatadir() {
- if err := ctl.NewDatadirFromDefault(datadir); err != nil {
- return err
- }
- }
-
- // if nigiri not exists, we need to write the configuration file and then
- // read from it to get viper updated, otherwise we just read from it.
- if isStopped, err := ctl.IsNigiriStopped(); err != nil {
- return err
- } else if isStopped {
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
- } else {
- filedir := ctl.GetResourcePath(datadir, "config")
- if err := ctl.WriteConfigFile(filedir); err != nil {
- return err
- }
- // .env must be in the directory where docker-compose is run from, not where YAML files are placed
- // https://docs.docker.com/compose/env-file/
- filedir = ctl.GetResourcePath(datadir, "env")
- if err := ctl.WriteComposeEnvironment(filedir, composeEnv); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-func start(cmd *cobra.Command, args []string) error {
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- datadir, _ := cmd.Flags().GetString("datadir")
- liquidEnabled := ctl.GetConfigBoolField(constants.AttachLiquid)
-
- envPath := ctl.GetResourcePath(datadir, "env")
- composePath := ctl.GetResourcePath(datadir, "compose")
-
- bashCmd := exec.Command("docker-compose", "-f", composePath, "up", "-d")
- if isStopped, err := ctl.IsNigiriStopped(); err != nil {
- return err
- } else if isStopped {
- bashCmd = exec.Command("docker-compose", "-f", composePath, "start")
- }
- bashCmd.Stdout = os.Stdout
- bashCmd.Stderr = os.Stderr
- bashCmd.Env = ctl.LoadComposeEnvironment(envPath)
-
- if err := bashCmd.Run(); err != nil {
- return err
- }
-
- path := ctl.GetResourcePath(datadir, "env")
- env, err := ctl.ReadComposeEnvironment(path)
- if err != nil {
- return err
- }
-
- prettyPrintServices := func(chain string, services map[string]int) {
- fmt.Printf("%s services:\n", strings.Title(chain))
- for name, port := range services {
- formatName := fmt.Sprintf("%s:", name)
- fmt.Printf(" %-14s localhost:%d\n", formatName, port)
- }
- }
-
- for chain, services := range env["ports"].(map[string]map[string]int) {
- if chain == "bitcoin" {
- prettyPrintServices(chain, services)
- } else if liquidEnabled {
- prettyPrintServices(chain, services)
- }
- }
-
- return nil
-}
diff --git a/cli/cmd/start_stop_test.go b/cli/cmd/start_stop_test.go
deleted file mode 100644
index 43acf54..0000000
--- a/cli/cmd/start_stop_test.go
+++ /dev/null
@@ -1,185 +0,0 @@
-package cmd
-
-import (
- "fmt"
- "strings"
- "testing"
- "time"
-
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-const (
- liquid = true
- bitcoin = false
- delete = true
-)
-
-var (
- stopCmd = []string{"stop"}
- // deleteCmd = append(stopCmd, "--delete")
- startCmd = []string{"start"}
- // liquidStartCmd = append(startCmd, "--liquid")
-)
-
-func TestStartStopLiquid(t *testing.T) {
- // Start/Stop
- testStart(t, liquid)
- testStop(t)
- // Start/Delete
- testStart(t, liquid)
- testDelete(t)
-}
-
-func TestStartStopBitcoin(t *testing.T) {
- // Start/Stop
- testStart(t, bitcoin)
- testStop(t)
- // Start/Delete
- testStart(t, bitcoin)
- testDelete(t)
-}
-
-func TestStopBeforeStartShouldFail(t *testing.T) {
- expectedError := constants.ErrNigiriNotRunning.Error()
-
- err := testCommand("stop", "", !delete)
- if err == nil {
- t.Fatal("Should return error when trying to stop before starting")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- expectedError = constants.ErrNigiriNotExisting.Error()
- err = testCommand("stop", "", delete)
- if err == nil {
- t.Fatal("Should return error when trying to delete before starting")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-}
-
-func TestStartAfterStartShouldFail(t *testing.T) {
- expectedError := constants.ErrNigiriAlreadyRunning.Error()
-
- if err := testCommand("start", "", bitcoin); err != nil {
- t.Fatal(err)
- }
-
- err := testCommand("start", "", bitcoin)
- if err == nil {
- t.Fatal("Should return error when trying to start Nigiri if already started")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- err = testCommand("start", "", liquid)
- if err == nil {
- t.Fatal("Should return error when trying to start Nigiri if already started")
- }
- if err.Error() != expectedError {
- t.Fatalf("Expected error: %s, got: %s", expectedError, err)
- }
-
- if err := testCommand("stop", "", delete); err != nil {
- t.Fatal(err)
- }
-}
-
-func testStart(t *testing.T, flag bool) {
- ctl, err := controller.NewController()
- if err != nil {
- t.Fatal(err)
- }
- if err := testCommand("start", "", flag); err != nil {
- t.Fatal(err)
- }
- //Give some time to nigiri to be ready before calling
- time.Sleep(5 * time.Second)
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- t.Fatal(err)
- } else if !isRunning {
- t.Fatal("Nigiri should have been started but services have not been found among running containers")
- }
-}
-
-func testStop(t *testing.T) {
- ctl, err := controller.NewController()
- if err != nil {
- t.Fatal(err)
- }
- if err := testCommand("stop", "", !delete); err != nil {
- t.Fatal(err)
- }
- //Give some time to nigiri to be ready before calling
- time.Sleep(5 * time.Second)
- if isStopped, err := ctl.IsNigiriStopped(); err != nil {
- t.Fatal(err)
- } else if !isStopped {
- t.Fatal("Nigiri should have been stopped but services have not been found among stopped containers")
- }
-}
-
-func testDelete(t *testing.T) {
- ctl, err := controller.NewController()
- if err != nil {
- t.Fatal(err)
- }
-
- if err := testCommand("stop", "", delete); err != nil {
- t.Fatal(err)
- }
- if isStopped, err := ctl.IsNigiriStopped(); err != nil {
- t.Fatal(err)
- } else if isStopped {
- t.Fatal("Nigiri should have been terminated at this point but services have been found among stopped containers")
- }
-}
-
-func testCommand(command, arg string, flag bool) error {
- cmd := RootCmd
- cmd.SetArgs(nil)
-
- if command == "start" {
- args := append(startCmd, fmt.Sprintf("--liquid=%t", flag))
- cmd.SetArgs(args)
- }
- if command == "stop" {
- args := append(stopCmd, fmt.Sprintf("--delete=%t", flag))
- cmd.SetArgs(args)
- }
- if command == "logs" {
- logsCmd := []string{command, arg, fmt.Sprintf("--liquid=%t", flag)}
- cmd.SetArgs(logsCmd)
- }
- if command == "faucet" {
- faucetCmd := []string{command, arg, fmt.Sprintf("--liquid=%t", flag)}
- cmd.SetArgs(faucetCmd)
- }
- if command == "rpc" {
- logsCmd := []string{command, fmt.Sprintf("--liquid=%t", flag)}
- args := strings.Fields(arg)
- logsCmd = append(logsCmd, args...)
- cmd.SetArgs(logsCmd)
- }
- if command == "mint" {
- logsCmd := []string{command}
- args := strings.Fields(arg)
- logsCmd = append(logsCmd, args...)
- cmd.SetArgs(logsCmd)
- }
- if command == "push" {
- pushCmd := []string{command, arg, fmt.Sprintf("--liquid=%t", flag)}
- cmd.SetArgs(pushCmd)
- }
-
- if err := cmd.Execute(); err != nil {
- return err
- }
-
- return nil
-}
diff --git a/cli/cmd/stop.go b/cli/cmd/stop.go
deleted file mode 100644
index b915727..0000000
--- a/cli/cmd/stop.go
+++ /dev/null
@@ -1,103 +0,0 @@
-package cmd
-
-import (
- "fmt"
- "os"
- "os/exec"
-
- "github.com/spf13/cobra"
- "github.com/vulpemventures/nigiri/cli/constants"
- "github.com/vulpemventures/nigiri/cli/controller"
-)
-
-var StopCmd = &cobra.Command{
- Use: "stop",
- Short: "Stop and/or delete nigiri",
- RunE: stop,
- PreRunE: stopChecks,
-}
-
-func stopChecks(cmd *cobra.Command, args []string) error {
- datadir, _ := cmd.Flags().GetString("datadir")
- delete, _ := cmd.Flags().GetBool("delete")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- if err := ctl.ParseDatadir(datadir); err != nil {
- return err
- }
-
- if _, err := os.Stat(datadir); os.IsNotExist(err) {
- return constants.ErrDatadirNotExisting
- }
-
- if isRunning, err := ctl.IsNigiriRunning(); err != nil {
- return err
- } else if !isRunning {
- if delete {
- if isStopped, err := ctl.IsNigiriStopped(); err != nil {
- return err
- } else if !isStopped {
- return constants.ErrNigiriNotExisting
- }
- } else {
- return constants.ErrNigiriNotRunning
- }
- }
-
- if err := ctl.ReadConfigFile(datadir); err != nil {
- return err
- }
- return nil
-}
-
-func stop(cmd *cobra.Command, args []string) error {
- delete, _ := cmd.Flags().GetBool("delete")
- datadir, _ := cmd.Flags().GetString("datadir")
-
- ctl, err := controller.NewController()
- if err != nil {
- return err
- }
-
- composePath := ctl.GetResourcePath(datadir, "compose")
- configPath := ctl.GetResourcePath(datadir, "config")
- envPath := ctl.GetResourcePath(datadir, "env")
- env := ctl.LoadComposeEnvironment(envPath)
-
- bashCmd := exec.Command("docker-compose", "-f", composePath, "stop")
- if delete {
- bashCmd = exec.Command("docker-compose", "-f", composePath, "down")
- }
- bashCmd.Stdout = os.Stdout
- bashCmd.Stderr = os.Stderr
- bashCmd.Env = env
-
- if err := bashCmd.Run(); err != nil {
- return err
- }
-
- if delete {
- fmt.Println("Removing data from volumes...")
- if err := ctl.CleanResourceVolumes(datadir); err != nil {
- return err
- }
-
- fmt.Println("Removing configuration file...")
- if err := os.Remove(configPath); err != nil {
- return err
- }
-
- fmt.Println("Removing environmet file...")
- if err := os.Remove(envPath); err != nil {
- return err
- }
-
- fmt.Println("Nigiri has been cleaned up successfully.")
- }
-
- return nil
-}
diff --git a/cli/cmd/version.go b/cli/cmd/version.go
deleted file mode 100644
index 3b6a226..0000000
--- a/cli/cmd/version.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package cmd
-
-import (
- "fmt"
-
- "github.com/spf13/cobra"
-)
-
-var (
- version = "dev"
- commit = "none"
- date = "unknown"
- VersionCmd = &cobra.Command{
- Use: "version",
- Short: "Show the current version",
- RunE: versionFunc,
- }
-)
-
-func versionFunc(cmd *cobra.Command, address []string) error {
- fmt.Println("Version: " + version)
- fmt.Println("Commit: " + commit)
- fmt.Println("Date: " + date)
- return nil
-}
diff --git a/cli/config/main.go b/cli/config/main.go
deleted file mode 100644
index 54b2893..0000000
--- a/cli/config/main.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package config
-
-import (
- "path/filepath"
-
- homedir "github.com/mitchellh/go-homedir"
- "github.com/spf13/viper"
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-var vip *viper.Viper
-
-func init() {
- vip = viper.New()
- defaults := viper.New()
-
- newDefaultConfig(defaults)
- setConfigFromDefaults(vip, defaults)
-}
-
-type Config struct{}
-
-func (c *Config) Viper() *viper.Viper {
- return vip
-}
-
-func (c *Config) ReadFromFile(path string) error {
- vip.SetConfigFile(filepath.Join(path, constants.Filename))
- return vip.ReadInConfig()
-}
-
-func (c *Config) WriteConfig(path string) error {
- vip.SetConfigFile(path)
- return vip.WriteConfig()
-}
-
-func (c *Config) GetString(str string) string {
- return vip.GetString(str)
-}
-
-func (c *Config) GetBool(str string) bool {
- return vip.GetBool(str)
-}
-
-func (c *Config) GetPath() string {
- return getPath()
-}
-
-func getPath() string {
- home, _ := homedir.Expand("~")
- return filepath.Join(home, ".nigiri")
-}
-
-func newDefaultConfig(v *viper.Viper) {
- v.SetDefault(constants.Datadir, getPath())
- v.SetDefault(constants.Network, "regtest")
- v.SetDefault(constants.AttachLiquid, false)
-}
-
-func setConfigFromDefaults(v *viper.Viper, d *viper.Viper) {
- for key, value := range d.AllSettings() {
- v.SetDefault(key, value)
- }
-}
diff --git a/cli/constants/constants.go b/cli/constants/constants.go
deleted file mode 100644
index 6e0cde7..0000000
--- a/cli/constants/constants.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package constants
-
-import (
- "errors"
-)
-
-const (
- // Datadir key in config json
- Datadir = "datadir"
- // Network key in config json
- Network = "network"
- // Filename key in config json
- Filename = "nigiri.config.json"
- // AttachLiquid key in config json
- AttachLiquid = "attachLiquid"
- // Version key in config json
- Version = "version"
-)
-
-var (
- AvaliableNetworks = []string{"regtest"}
- NigiriBitcoinImages = []string{
- "ghcr.io/vulpemventures/bitcoin:latest",
- "ghcr.io/vulpemventures/electrs:latest",
- "ghcr.io/vulpemventures/esplora:latest",
- "ghcr.io/vulpemventures/nigiri-chopsticks:latest",
- }
- NigiriLiquidImages = []string{
- "ghcr.io/vulpemventures/elements:latest",
- "ghcr.io/vulpemventures/electrs-liquid:latest",
- }
- NigiriImages = append(NigiriBitcoinImages, NigiriLiquidImages...)
- DefaultEnv = map[string]interface{}{
- "ports": map[string]map[string]int{
- "bitcoin": {
- "peer": 18432,
- "node": 18433,
- "esplora": 5000,
- "electrs": 3002,
- "electrs_rpc": 51401,
- "chopsticks": 3000,
- },
- "liquid": {
- "peer": 7040,
- "node": 7041,
- "esplora": 5001,
- "electrs": 3012,
- "electrs_rpc": 60401,
- "chopsticks": 3001,
- },
- },
- "urls": map[string]string{
- "bitcoin_esplora": "http://localhost:3000",
- "liquid_esplora": "http://localhost:3001",
- },
- }
-
- ErrInvalidNetwork = errors.New("Network provided is not valid")
- ErrInvalidDatadir = errors.New("Datadir provided is not valid: it must be an absolute path")
- ErrInvalidServiceName = errors.New("Service provided is not valid")
- ErrInvalidArgs = errors.New("Invalid number of args")
- ErrInvalidJSON = errors.New("JSON environment provided is not valid: missing required fields")
- ErrMalformedJSON = errors.New("Failed to parse malformed JSON environment")
- ErrEmptyJSON = errors.New("JSON environment provided is not valid: it must not be empty")
- ErrDatadirNotExisting = errors.New("Datadir provided is not valid: it must be an existing path")
- ErrNigiriNotRunning = errors.New("Nigiri is not running")
- ErrNigiriNotExisting = errors.New("Nigiri does not exists, cannot delete")
- ErrNigiriAlreadyRunning = errors.New("Nigiri is already running, please stop it first")
- ErrNigiriLiquidNotEnabled = errors.New("Nigiri has been started with no Liquid sidechain.\nPlease stop and restart it using the --liquid flag")
- ErrDockerNotRunning = errors.New("Nigiri requires the Docker daemon to be running, but it not seems to be started")
-)
diff --git a/cli/controller/controller.go b/cli/controller/controller.go
deleted file mode 100644
index 3e18a82..0000000
--- a/cli/controller/controller.go
+++ /dev/null
@@ -1,187 +0,0 @@
-package controller
-
-import (
- "fmt"
- "os/exec"
- "path/filepath"
-
- "github.com/vulpemventures/nigiri/cli/config"
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-var Services = map[string]bool{
- "node": true,
- "esplora": true,
- "electrs": true,
- "chopsticks": true,
-}
-
-// Controller implements useful functions to securely parse flags provided at run-time
-// and to interact with the resources used by Nigiri:
-// * docker
-// * .env for docker-compose
-// * nigiri.config.json config file
-type Controller struct {
- config *config.Config
- parser *Parser
- docker *Docker
- env *Env
-}
-
-// NewController returns a new Controller instance or error
-func NewController() (*Controller, error) {
- c := &Controller{}
-
- dockerClient := &Docker{}
- if err := dockerClient.New(); err != nil {
- return nil, err
- }
- c.env = &Env{}
- c.parser = newParser(Services)
- c.docker = dockerClient
- c.config = &config.Config{}
- return c, nil
-}
-
-// ParseNetwork checks if a valid network has been provided
-func (c *Controller) ParseNetwork(network string) error {
- return c.parser.parseNetwork(network)
-}
-
-// ParseDatadir checks if a valid datadir has been provided
-func (c *Controller) ParseDatadir(path string) error {
- return c.parser.parseDatadir(path)
-}
-
-// ParseEnv checks if a valid JSON format for docker compose has been provided
-func (c *Controller) ParseEnv(env string) (string, error) {
- return c.parser.parseEnvJSON(env)
-}
-
-// ParseServiceName checks if a valid service has been provided
-func (c *Controller) ParseServiceName(name string) error {
- return c.parser.parseServiceName(name)
-}
-
-// IsNigiriRunning checks if nigiri is running by looking if the bitcoin
-// services are in the list of docker running containers
-func (c *Controller) IsNigiriRunning() (bool, error) {
- if !c.docker.isDockerRunning() {
- return false, constants.ErrDockerNotRunning
- }
- return c.docker.isNigiriRunning(), nil
-}
-
-// IsNigiriStopped checks that nigiri is not actually running and that
-// the bitcoin services appear in the list of non running containers
-func (c *Controller) IsNigiriStopped() (bool, error) {
- if !c.docker.isDockerRunning() {
- return false, constants.ErrDockerNotRunning
- }
- return c.docker.isNigiriStopped(), nil
-}
-
-// WriteComposeEnvironment creates a .env in datadir used by
-// the docker-compose YAML file resource
-func (c *Controller) WriteComposeEnvironment(datadir, env string) error {
- return c.env.writeEnvForCompose(datadir, env)
-}
-
-// ReadComposeEnvironment reads from .env and returns it as a useful type
-func (c *Controller) ReadComposeEnvironment(datadir string) (map[string]interface{}, error) {
- return c.env.readEnvForCompose(datadir)
-}
-
-// LoadComposeEnvironment returns an os.Environ created from datadir/.env resource
-func (c *Controller) LoadComposeEnvironment(datadir string) []string {
- return c.env.load(datadir)
-}
-
-// WriteConfigFile writes the configuration handled by the underlying viper
-// into the file at filedir path
-func (c *Controller) WriteConfigFile(filedir string) error {
- return c.config.WriteConfig(filedir)
-}
-
-// ReadConfigFile reads the configuration of the file at filedir path
-func (c *Controller) ReadConfigFile(filedir string) error {
- return c.config.ReadFromFile(filedir)
-}
-
-// GetConfigBoolField returns a bool field of the config file
-func (c *Controller) GetConfigBoolField(field string) bool {
- return c.config.GetBool(field)
-}
-
-// GetConfigStringField returns a string field of the config file
-func (c *Controller) GetConfigStringField(field string) string {
- return c.config.GetString(field)
-}
-
-// NewDatadirFromDefault copies the default ~/.nigiri at the desidered path
-// and cleans the docker volumes to make a fresh Nigiri instance
-func (c *Controller) NewDatadirFromDefault(datadir string) error {
- defaultDatadir := c.config.GetPath()
- cmd := exec.Command("cp", "-R", filepath.Join(defaultDatadir, "resources"), datadir)
- if err := cmd.Run(); err != nil {
- return err
- }
- c.CleanResourceVolumes(datadir)
- return nil
-}
-
-// GetResourcePath returns the absolute path of the requested resource
-func (c *Controller) GetResourcePath(datadir, resource string) string {
- if resource == "compose" {
- network := c.config.GetString(constants.Network)
- if c.config.GetBool(constants.AttachLiquid) {
- network += "-liquid"
- }
- return filepath.Join(datadir, "resources", fmt.Sprintf("docker-compose-%s.yml", network))
- }
- if resource == "env" {
- return filepath.Join(datadir, ".env")
- }
- if resource == "config" {
- return filepath.Join(datadir, "nigiri.config.json")
- }
- return ""
-}
-
-// CleanResourceVolumes recursively deletes the content of the
-// docker volumes in the resource path
-func (c *Controller) CleanResourceVolumes(datadir string) error {
- network := c.config.GetString(constants.Network)
- attachLiquid := c.config.GetBool(constants.AttachLiquid)
- if attachLiquid {
- network = fmt.Sprintf("liquid%s", network)
- }
- volumedir := filepath.Join(datadir, "resources", "volumes", network)
-
- return c.docker.cleanVolumes(volumedir)
-}
-
-// GetDefaultDatadir returns the absolute path of Nigiri default directory
-func (c *Controller) GetDefaultDatadir() string {
- return c.config.GetPath()
-}
-
-// GetServiceName returns the right name of the requested service
-// If requesting a name for a Liquid service, then the suffix -liquid
-// is appended to the canonical name except for "node" that is mapped
-// to either "bitcoin" or "liquid"
-func (c *Controller) GetServiceName(name string, liquid bool) string {
- service := name
- if service == "node" {
- service = "bitcoin"
- }
- if liquid {
- if service == "bitcoin" {
- service = "liquid"
- } else {
- service = fmt.Sprintf("%s-liquid", service)
- }
- }
-
- return service
-}
diff --git a/cli/controller/docker.go b/cli/controller/docker.go
deleted file mode 100644
index f9a5ba8..0000000
--- a/cli/controller/docker.go
+++ /dev/null
@@ -1,98 +0,0 @@
-package controller
-
-import (
- "context"
- "io/ioutil"
- "os"
- "path/filepath"
-
- "github.com/docker/docker/api/types"
- "github.com/docker/docker/client"
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-// Docker type handles interfaction with containers via docker and docker-compose
-type Docker struct {
- cli *client.Client
-}
-
-// New initialize a new Docker handler
-func (d *Docker) New() error {
- cli, err := client.NewEnvClient()
- if err != nil {
- return err
- }
- d.cli = cli
- return nil
-}
-
-func (d *Docker) isDockerRunning() bool {
- _, err := d.cli.ContainerList(context.Background(), types.ContainerListOptions{All: false})
- if err != nil {
- return false
- }
- return true
-}
-
-func (d *Docker) findNigiriContainers(listAllContainers bool) bool {
- containers, _ := d.cli.ContainerList(context.Background(), types.ContainerListOptions{All: listAllContainers})
-
- if len(containers) <= 0 {
- return false
- }
-
- images := []string{}
- for _, c := range containers {
- images = append(images, c.Image)
- }
-
- for _, nigiriImage := range constants.NigiriBitcoinImages {
- // just check if services for bitcoin chain are up and running
- if !contains(images, nigiriImage) {
- return false
- }
- }
- return true
-}
-
-func (d *Docker) isNigiriRunning() bool {
- return d.findNigiriContainers(false)
-}
-
-func (d *Docker) isNigiriStopped() bool {
- isRunning := d.isNigiriRunning()
- if !isRunning {
- return d.findNigiriContainers(true)
- }
- return false
-}
-
-func (d *Docker) cleanVolumes(path string) error {
- subdirs, err := ioutil.ReadDir(path)
- if err != nil {
- return err
- }
-
- for _, d := range subdirs {
- path := filepath.Join(path, d.Name())
- subsubdirs, _ := ioutil.ReadDir(path)
- for _, sd := range subsubdirs {
- if sd.IsDir() {
- if err := os.RemoveAll(filepath.Join(path, sd.Name())); err != nil {
- return err
- }
- }
- }
- }
-
- return nil
-}
-
-func contains(list []string, elem string) bool {
- for _, l := range list {
- if l == elem {
- return true
- }
- }
- return false
-}
diff --git a/cli/controller/env.go b/cli/controller/env.go
deleted file mode 100644
index 83045c2..0000000
--- a/cli/controller/env.go
+++ /dev/null
@@ -1,125 +0,0 @@
-package controller
-
-import (
- "bufio"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "os"
- "strconv"
- "strings"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-// Env implements functions for interacting with the environment file used by
-// docker-compose to dynamically set service ports and variables
-type Env struct{}
-
-func (e *Env) writeEnvForCompose(path, strJSON string) error {
- var env map[string]interface{}
- err := json.Unmarshal([]byte(strJSON), &env)
- if err != nil {
- return constants.ErrMalformedJSON
- }
-
- fileContent := ""
- for chain, services := range env["ports"].(map[string]interface{}) {
- for k, v := range services.(map[string]interface{}) {
- fileContent += fmt.Sprintf("%s_%s_PORT=%d\n", strings.ToUpper(chain), strings.ToUpper(k), int(v.(float64)))
- }
- }
- for hostname, url := range env["urls"].(map[string]interface{}) {
- fileContent += fmt.Sprintf("%s_URL=%s\n", strings.ToUpper(hostname), url.(string))
- }
-
- return ioutil.WriteFile(path, []byte(fileContent), os.ModePerm)
-}
-
-func (e *Env) readEnvForCompose(path string) (map[string]interface{}, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close()
-
- scanner := bufio.NewScanner(file)
- scanner.Split(bufio.ScanLines)
-
- ports := map[string]map[string]int{
- "bitcoin": map[string]int{},
- "liquid": map[string]int{},
- }
- urls := map[string]string{}
- // Each line is in the format PREFIX_SERVICE_NAME_SUFFIX=value
- // PREFIX is either 'BITCOIN' or 'LIQUID', while SUFFIX is either 'PORT' or 'URL'
- for scanner.Scan() {
- line := scanner.Text()
- splitLine := strings.Split(line, "=")
- key := splitLine[0]
-
- if strings.Contains(key, "PORT") {
- value, _ := strconv.Atoi(splitLine[1])
- chain := "bitcoin"
- if strings.HasPrefix(key, strings.ToUpper("liquid")) {
- chain = "liquid"
- }
- prefix := strings.ToUpper(fmt.Sprintf("%s_", chain))
- suffix := "_PORT"
- trimmedKey := strings.ToLower(
- strings.TrimSuffix(strings.TrimPrefix(key, prefix), suffix),
- )
- ports[chain][trimmedKey] = value
- } else {
- // Here the prefix is not trimmed
- value := splitLine[1]
- suffix := "_URL"
- trimmedKey := strings.ToLower(strings.TrimSuffix(key, suffix))
- urls[trimmedKey] = value
- }
- }
-
- return map[string]interface{}{"ports": ports, "urls": urls}, nil
-}
-
-func (e *Env) load(path string) []string {
- content, _ := ioutil.ReadFile(path)
- lines := strings.Split(string(content), "\n")
- env := os.Environ()
- for _, line := range lines {
- if line != "" {
- env = append(env, line)
- }
- }
-
- return env
-}
-
-type envPortsData struct {
- Peer int `json:"peer,omitempty"`
- Node int `json:"node,omitempty"`
- Esplora int `json:"esplora,omitempty"`
- Electrs int `json:"electrs,omitempty"`
- ElectrsRPC int `json:"electrs_rpc,omitempty"`
- Chopsticks int `json:"chopsticks,omitempty"`
-}
-type envPorts struct {
- Bitcoin *envPortsData `json:"bitcoin,omitempty"`
- Liquid *envPortsData `json:"liquid,omitempty"`
-}
-type envUrls struct {
- BitcoinEsplora string `json:"bitcoin_esplora,omitempty"`
- LiquidEsplora string `json:"liquid_esplora,omitempty"`
-}
-type envJSON struct {
- Ports *envPorts `json:"ports,omitempty"`
- Urls *envUrls `json:"urls,omitempty"`
-}
-
-func (e envJSON) copy() envJSON {
- var v envJSON
- bytes, _ := json.Marshal(e)
- json.Unmarshal(bytes, &v)
-
- return v
-}
diff --git a/cli/controller/parser.go b/cli/controller/parser.go
deleted file mode 100644
index aeb512f..0000000
--- a/cli/controller/parser.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package controller
-
-import (
- "encoding/json"
- "fmt"
- "path/filepath"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-// Parser implements functions for parsing flags, JSON files
-// and system directories passed to the CLI commands
-type Parser struct {
- services map[string]bool
-}
-
-func newParser(services map[string]bool) *Parser {
- p := &Parser{services}
- return p
-}
-
-func (p *Parser) parseNetwork(network string) error {
- for _, n := range constants.AvaliableNetworks {
- if network == n {
- return nil
- }
- }
- return constants.ErrInvalidNetwork
-}
-
-func (p *Parser) parseDatadir(path string) error {
- if !filepath.IsAbs(path) {
- return constants.ErrInvalidDatadir
- }
- return nil
-}
-
-func (p *Parser) parseEnvJSON(strJSON string) (string, error) {
- // merge default json and incoming json by parsing DefaultEnv to
- // envJSON type and then parsing the incoming json using the same variable
- var parsedJSON envJSON
- defaultJSON, _ := json.Marshal(constants.DefaultEnv)
- json.Unmarshal(defaultJSON, &parsedJSON)
- err := json.Unmarshal([]byte(strJSON), &parsedJSON)
- if err != nil {
- fmt.Println(err)
- return "", constants.ErrMalformedJSON
- }
- merged, _ := json.Marshal(parsedJSON)
- return string(merged), nil
-}
-
-func (p *Parser) parseServiceName(name string) error {
- if !p.services[name] {
- return constants.ErrInvalidServiceName
- }
- return nil
-}
diff --git a/cli/controller/parser_test.go b/cli/controller/parser_test.go
deleted file mode 100644
index c20a338..0000000
--- a/cli/controller/parser_test.go
+++ /dev/null
@@ -1,162 +0,0 @@
-package controller
-
-import (
- "encoding/json"
- "os"
- "testing"
-
- "github.com/vulpemventures/nigiri/cli/constants"
-)
-
-func TestParserParseNetwork(t *testing.T) {
- p := &Parser{}
-
- validNetworks := []string{"regtest"}
- for _, n := range validNetworks {
- err := p.parseNetwork(n)
- if err != nil {
- t.Fatal(err)
- }
- }
-}
-
-func TestParserParseDatadir(t *testing.T) {
- p := &Parser{}
-
- currentDir, _ := os.Getwd()
- validDatadirs := []string{currentDir}
- for _, n := range validDatadirs {
- err := p.parseDatadir(n)
- if err != nil {
- t.Fatal(err)
- }
- }
-}
-
-func TestParserParseEnvJSON(t *testing.T) {
- p := &Parser{}
-
- for _, e := range testJSONs {
- parsedJSON, _ := json.Marshal(e)
- mergedJSON, err := p.parseEnvJSON(string(parsedJSON))
- if err != nil {
- t.Fatal(err)
- }
- t.Log(mergedJSON)
- }
-}
-
-func TestParserParseNetworkShouldFail(t *testing.T) {
- p := &Parser{}
-
- invalidNetworks := []string{"simnet", "testnet"}
- for _, n := range invalidNetworks {
- err := p.parseNetwork(n)
- if err == nil {
- t.Fatalf("Should have been failed before")
- }
- if err != constants.ErrInvalidNetwork {
- t.Fatalf("Got: %s, wanted: %s", err, constants.ErrInvalidNetwork)
- }
- }
-}
-
-func TestParserParseDatadirShouldFail(t *testing.T) {
- p := &Parser{}
-
- invalidDatadirs := []string{"."}
- for _, d := range invalidDatadirs {
- err := p.parseDatadir(d)
- if err == nil {
- t.Fatalf("Should have been failed before")
- }
- if err != constants.ErrInvalidDatadir {
- t.Fatalf("Got: %s, wanted: %s", err, constants.ErrInvalidDatadir)
- }
- }
-}
-
-var testJSONs = []map[string]interface{}{
- // only btc services
- {
- "urls": map[string]string{
- "bitcoin_esplora": "https://blockstream.info/",
- },
- "ports": map[string]map[string]int{
- "bitcoin": map[string]int{
- "peer": 1111,
- "node": 2222,
- "esplora": 3333,
- "electrs": 4444,
- "chopsticks": 5555,
- },
- },
- },
- // btc and liquid services
- {
- "urls": map[string]string{
- "bitcoin_esplora": "https://blockstream.info/",
- "liquid_esplora": "http://blockstream.info/liquid",
- },
- "ports": map[string]map[string]int{
- "bitcoin": map[string]int{
- "peer": 1111,
- "node": 2222,
- "esplora": 3333,
- "electrs": 4444,
- "chopsticks": 5555,
- },
- "liquid": map[string]int{
- "peer": 6666,
- "node": 7777,
- "esplora": 8888,
- "electrs": 9999,
- "chopsticks": 1010,
- },
- },
- },
- // incomplete examples:
- // incomplete bitcoin services
- {
- "ports": map[string]map[string]int{
- "bitcoin": map[string]int{
- "esplora": 1111,
- "electrs": 2222,
- "chopsticks": 3333,
- },
- },
- "urls": map[string]string{
- "bitcoin_esplora": "http://test.com/api",
- },
- },
- // bitcoin services ports and liquid service url
- {
- "ports": map[string]map[string]int{
- "bitcoin": map[string]int{
- "node": 1111,
- "esplora": 2222,
- "electrs": 3333,
- "chopsticks": 4444,
- },
- },
- "urls": map[string]string{
- "liquid_esplora": "http://test.com/liquid/api",
- },
- },
- // liquid services ports and bitcoin service url
- {
- "ports": map[string]map[string]int{
- "liquid": map[string]int{
- "node": 1111,
- "esplora": 2222,
- "electrs": 3333,
- "chopsticks": 4444,
- },
- },
- "urls": map[string]string{
- "bitcoin_esplora": "http://test.com/api",
- },
- },
- // empty config
- {},
-}
diff --git a/cli/main.go b/cli/main.go
deleted file mode 100644
index 3f83d44..0000000
--- a/cli/main.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package main
-
-import "github.com/vulpemventures/nigiri/cli/cmd"
-
-func main() {
- cmd.RootCmd.Execute()
-}
diff --git a/cmd/nigiri/faucet.go b/cmd/nigiri/faucet.go
new file mode 100644
index 0000000..f073640
--- /dev/null
+++ b/cmd/nigiri/faucet.go
@@ -0,0 +1,83 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/docker"
+)
+
+var faucet = cli.Command{
+ Name: "faucet",
+ Usage: "generate and send bitcoin to given address",
+ ArgsUsage: " [amount] [asset]",
+ Action: faucetAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ },
+}
+
+func faucetAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); !isRunning {
+ return errors.New("nigiri is not running")
+ }
+
+ if ctx.NArg() < 1 || ctx.NArg() > 3 {
+ return errors.New("wrong number of arguments")
+ }
+
+ isLiquid := ctx.Bool("liquid")
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ var serviceName string = "chopsticks"
+ if isLiquid {
+ serviceName = "chopsticks-liquid"
+ }
+
+ portSlice, err := docker.GetPortsForService(composePath, serviceName)
+ if err != nil {
+ return err
+ }
+ mappedPorts := strings.Split(portSlice[0], ":")
+
+ request := map[string]interface{}{
+ "address": ctx.Args().First(),
+ }
+ requestPort := mappedPorts[0]
+ payload, err := json.Marshal(request)
+ if err != nil {
+ return err
+ }
+ res, err := http.Post("http://127.0.0.1:"+requestPort+"/faucet", "application/json", bytes.NewBuffer(payload))
+ if err != nil {
+ return err
+ }
+ data, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return err
+ }
+ if res.StatusCode != http.StatusOK {
+ return errors.New(string(data))
+ }
+
+ var dat map[string]string
+ if err := json.Unmarshal([]byte(data), &dat); err != nil {
+ return errors.New("internal error, please try again")
+ }
+ if dat["txId"] == "" {
+ return errors.New("not successful")
+ }
+ fmt.Println("txId: " + dat["txId"])
+
+ return nil
+}
diff --git a/cmd/nigiri/logs.go b/cmd/nigiri/logs.go
new file mode 100644
index 0000000..e485276
--- /dev/null
+++ b/cmd/nigiri/logs.go
@@ -0,0 +1,46 @@
+package main
+
+import (
+ "errors"
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+)
+
+var logs = cli.Command{
+ Name: "logs",
+ Usage: "check Service logs",
+ Action: logsAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ },
+}
+
+func logsAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); !isRunning {
+ return errors.New("nigiri is not running")
+ }
+
+ if ctx.NArg() != 1 {
+ return errors.New("missing service name")
+ }
+
+ serviceName := ctx.Args().First()
+
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ bashCmd := exec.Command("docker-compose", "-f", composePath, "logs", serviceName)
+ bashCmd.Stdout = os.Stdout
+ bashCmd.Stderr = os.Stderr
+
+ if err := bashCmd.Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/nigiri/main.go b/cmd/nigiri/main.go
new file mode 100644
index 0000000..6a1d66d
--- /dev/null
+++ b/cmd/nigiri/main.go
@@ -0,0 +1,190 @@
+package main
+
+import (
+ "embed"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/user"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/state"
+)
+
+var (
+ version = "dev"
+ commit = "none"
+ date = "unknown"
+
+ nigiriState = state.New(config.DefaultPath, config.InitialState)
+)
+
+var liquidFlag = cli.BoolFlag{
+ Name: "liquid",
+ Usage: "enable liquid",
+ Value: false,
+}
+
+var datadirFlag = cli.StringFlag{
+ Name: "datadir",
+ Usage: "use different data directory",
+ Value: config.DefaultDatadir,
+}
+
+//go:embed resources/docker-compose.yml
+//go:embed resources/bitcoin.conf
+//go:embed resources/elements.conf
+var f embed.FS
+
+func main() {
+ app := cli.NewApp()
+
+ app.Version = formatVersion()
+ app.Name = "nigiri CLI"
+ app.Usage = "create your dockerized environment with a bitcoin and liquid node, with a block explorer and developer tools"
+ app.Flags = append(app.Flags, &datadirFlag)
+ app.Commands = append(
+ app.Commands,
+ &rpc,
+ &stop,
+ &logs,
+ &mint,
+ &push,
+ &start,
+ &update,
+ &faucet,
+ &versionCmd,
+ )
+
+ app.Before = func(ctx *cli.Context) error {
+
+ dataDir := config.DefaultDatadir
+
+ if ctx.IsSet("datadir") {
+ dataDir = cleanAndExpandPath(ctx.String("datadir"))
+ nigiriState = state.New(filepath.Join(dataDir, config.DefaultName), config.InitialState)
+ }
+
+ if err := provisionResourcesToDatadir(dataDir); err != nil {
+ return err
+ }
+
+ return nil
+ }
+
+ err := app.Run(os.Args)
+ if err != nil {
+ fatal(err)
+ }
+}
+
+func fatal(err error) {
+ _, _ = fmt.Fprintf(os.Stderr, "[nigiri] %v\n", err)
+ os.Exit(1)
+}
+
+// Provisioning Nigiri reosurces
+func provisionResourcesToDatadir(datadir string) error {
+
+ isReady, err := nigiriState.GetBool("ready")
+ if err != nil {
+ return err
+ }
+
+ if isReady {
+ return nil
+ }
+
+ // create folders in volumes/{bitcoin,elements} for node datadirs
+ if err := makeDirectoryIfNotExists(filepath.Join(datadir, "volumes", "bitcoin")); err != nil {
+ return err
+ }
+ if err := makeDirectoryIfNotExists(filepath.Join(datadir, "volumes", "elements")); err != nil {
+ return err
+ }
+
+ // copy resources into the Nigiri data directory
+ if err := copyFromResourcesToDatadir(
+ filepath.Join("resources", config.DefaultCompose),
+ filepath.Join(datadir, config.DefaultCompose),
+ ); err != nil {
+ return err
+ }
+
+ if err := copyFromResourcesToDatadir(
+ filepath.Join("resources", "bitcoin.conf"),
+ filepath.Join(datadir, "volumes", "bitcoin", "bitcoin.conf"),
+ ); err != nil {
+ return err
+ }
+
+ if err := copyFromResourcesToDatadir(
+ filepath.Join("resources", "elements.conf"),
+ filepath.Join(datadir, "volumes", "elements", "elements.conf"),
+ ); err != nil {
+ return err
+ }
+
+ if err := nigiriState.Set(map[string]string{"ready": strconv.FormatBool(true)}); err != nil {
+ return err
+ }
+
+ return nil
+}
+
+func formatVersion() string {
+ return fmt.Sprintf(
+ "\nVersion: %s\nCommit: %s\nDate: %s",
+ version, commit, date,
+ )
+}
+
+func copyFromResourcesToDatadir(src string, dest string) error {
+ data, err := f.ReadFile(src)
+ if err != nil {
+ return fmt.Errorf("read embed: %w", err)
+ }
+ err = ioutil.WriteFile(dest, data, 0777)
+ if err != nil {
+ return fmt.Errorf("write %s to %s: %w", src, dest, err)
+ }
+
+ return nil
+}
+
+func makeDirectoryIfNotExists(path string) error {
+ if _, err := os.Stat(path); os.IsNotExist(err) {
+ return os.MkdirAll(path, os.ModeDir|0755)
+ }
+ return nil
+}
+
+// cleanAndExpandPath expands environment variables and leading ~ in the
+// passed path, cleans the result, and returns it.
+// This function is taken from https://github.com/btcsuite/btcd
+func cleanAndExpandPath(path string) string {
+ if path == "" {
+ return ""
+ }
+
+ // Expand initial ~ to OS specific home directory.
+ if strings.HasPrefix(path, "~") {
+ var homeDir string
+ u, err := user.Current()
+ if err == nil {
+ homeDir = u.HomeDir
+ } else {
+ homeDir = os.Getenv("HOME")
+ }
+
+ path = strings.Replace(path, "~", homeDir, 1)
+ }
+
+ // NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
+ // but the variables can still be expanded via POSIX-style $VARIABLE.
+ return filepath.Clean(os.ExpandEnv(path))
+}
diff --git a/cmd/nigiri/mint.go b/cmd/nigiri/mint.go
new file mode 100644
index 0000000..fb3f84c
--- /dev/null
+++ b/cmd/nigiri/mint.go
@@ -0,0 +1,101 @@
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/docker"
+)
+
+var mint = cli.Command{
+ Name: "mint",
+ Usage: "liquid only: issue and send a given quantity of an asset",
+ ArgsUsage: " [name] [ticker]",
+ Action: mintAction,
+}
+
+func mintAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); !isRunning {
+ return errors.New("nigiri is not running")
+ }
+
+ if ctx.NArg() < 2 || ctx.NArg() > 5 {
+ return errors.New("wrong number of arguments")
+ }
+
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ serviceName := "chopsticks-liquid"
+
+ portSlice, err := docker.GetPortsForService(composePath, serviceName)
+ if err != nil {
+ return err
+ }
+ mappedPorts := strings.Split(portSlice[0], ":")
+
+ var request struct {
+ Address string `json:"address"`
+ Quantity int `json:"quantity"`
+ Name string `json:"name"`
+ Ticker string `json:"ticker"`
+ }
+ request.Address = ctx.Args().First()
+ request.Quantity, _ = strconv.Atoi(ctx.Args().Get(1))
+ if ctx.Args().Len() >= 3 {
+ request.Name = ctx.Args().Get(2)
+ }
+ if ctx.Args().Len() == 4 {
+ request.Ticker = ctx.Args().Get(3)
+ }
+
+ requestPort := mappedPorts[0]
+ payload, err := json.Marshal(request)
+ if err != nil {
+ return err
+ }
+ res, err := http.Post("http://127.0.0.1:"+requestPort+"/mint", "application/json", bytes.NewBuffer(payload))
+ if err != nil {
+ return err
+ }
+
+ data, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return err
+ }
+ if res.StatusCode != http.StatusOK {
+ return errors.New(string(data))
+ }
+ var dat map[string]interface{}
+ var resp string
+ if err := json.Unmarshal([]byte(data), &dat); err != nil {
+ return errors.New("internal error try again")
+ }
+ if dat["txId"] == "" {
+ return errors.New("not successful")
+ }
+ for key, element := range dat {
+ if key == "issuance_txin" {
+ myMap := element.(map[string]interface{})
+ resp += key + ":\n"
+ for key2, element2 := range myMap {
+ resp += " "
+ resp += key2 + ": " + fmt.Sprintf("%v", element2) + "\n"
+ }
+ continue
+ }
+ resp += key + ": " + fmt.Sprintf("%v", element) + "\n"
+ }
+ fmt.Println(resp[:len(resp)-1])
+ return nil
+}
diff --git a/cmd/nigiri/push.go b/cmd/nigiri/push.go
new file mode 100644
index 0000000..b72947a
--- /dev/null
+++ b/cmd/nigiri/push.go
@@ -0,0 +1,71 @@
+package main
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "net/http"
+ "path/filepath"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/docker"
+)
+
+var push = cli.Command{
+ Name: "push",
+ Usage: "broadcast raw transaction",
+ ArgsUsage: "",
+ Action: pushAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ },
+}
+
+func pushAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); !isRunning {
+ return errors.New("nigiri is not running")
+ }
+
+ if ctx.NArg() != 1 {
+ return errors.New("wrong number of arguments")
+ }
+
+ isLiquid := ctx.Bool("liquid")
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ var serviceName string = "chopsticks"
+ if isLiquid {
+ serviceName = "chopsticks-liquid"
+ }
+
+ portSlice, err := docker.GetPortsForService(composePath, serviceName)
+ if err != nil {
+ return err
+ }
+ mappedPorts := strings.Split(portSlice[0], ":")
+ requestPort := mappedPorts[0]
+ hex := []byte(ctx.Args().First())
+
+ res, err := http.Post("http://127.0.0.1:"+requestPort+"/tx", "application/string", bytes.NewBuffer(hex))
+ if err != nil {
+ return err
+ }
+ data, err := ioutil.ReadAll(res.Body)
+ if err != nil {
+ return err
+ }
+ if res.StatusCode != http.StatusOK {
+ return errors.New(string(data))
+ }
+
+ if string(data) == "" {
+ return errors.New("not successful")
+ }
+ fmt.Println("\ntxId: " + string(data))
+ return nil
+}
diff --git a/resources/volumes/liquidregtest/config/bitcoin.conf b/cmd/nigiri/resources/bitcoin.conf
similarity index 80%
rename from resources/volumes/liquidregtest/config/bitcoin.conf
rename to cmd/nigiri/resources/bitcoin.conf
index c462cbc..f7e765e 100644
--- a/resources/volumes/liquidregtest/config/bitcoin.conf
+++ b/cmd/nigiri/resources/bitcoin.conf
@@ -4,11 +4,11 @@ dnsseed=0
upnp=0
[regtest]
-port=19000
-rpcport=19001
+port=18444
+rpcport=18443
server=1
-txindex=0
+txindex=1
rpcuser=admin1
rpcpassword=123
diff --git a/resources/docker-compose-regtest-liquid.yml b/cmd/nigiri/resources/docker-compose.yml
similarity index 60%
rename from resources/docker-compose-regtest-liquid.yml
rename to cmd/nigiri/resources/docker-compose.yml
index 1d4ec8f..95c30e3 100644
--- a/resources/docker-compose-regtest-liquid.yml
+++ b/cmd/nigiri/resources/docker-compose.yml
@@ -1,35 +1,31 @@
-version: '3'
+version: '3.7'
services:
- # RPC daemons
+ # RPC daemon
bitcoin:
image: ghcr.io/vulpemventures/bitcoin:latest
container_name: bitcoin
command:
- -datadir=config
- networks:
- local:
- ipv4_address: 10.10.0.10
ports:
- - ${BITCOIN_PEER_PORT}:19000
- - ${BITCOIN_NODE_PORT}:19001
+ - 18443:18433
+ - 18444:18444
volumes:
- - ./volumes/liquidregtest/config/:/config
+ - ./volumes/bitcoin/:/config
restart: unless-stopped
+
liquid:
image: ghcr.io/vulpemventures/elements:latest
container_name: liquid
command:
- -datadir=config
- networks:
- local:
- ipv4_address: 10.10.0.11
ports:
- - ${LIQUID_NODE_PORT}:18884
- - ${LIQUID_PEER_PORT}:18886
+ - 18884:18884
+ - 18886:18886
volumes:
- - ./volumes/liquidregtest/liquid-config/:/config
+ - ./volumes/elements/:/config
restart: unless-stopped
- # Block explorer REST servers
+
+ # Block explorer server
electrs:
image: ghcr.io/vulpemventures/electrs:latest
container_name: electrs
@@ -42,26 +38,24 @@ services:
- --daemon-dir
- /config
- --daemon-rpc-addr
- - 10.10.0.10:19001
+ - bitcoin:18443
- --cookie
- admin1:123
- --http-addr
- - 0.0.0.0:3002
+ - 0.0.0.0:30000
- --electrum-rpc-addr
- - 0.0.0.0:60401
+ - 0.0.0.0:50000
- --cors
- "*"
- networks:
- local:
- ipv4_address: 10.10.0.12
depends_on:
- bitcoin
ports:
- - ${BITCOIN_ELECTRS_RPC_PORT}:60401
- - ${BITCOIN_ELECTRS_PORT}:3002
+ - 50000:50000
+ - 30000:30000
volumes:
- - ./volumes/liquidregtest/config/:/config
+ - ./volumes/bitcoin/:/config
restart: unless-stopped
+
electrs-liquid:
image: ghcr.io/vulpemventures/electrs-liquid:latest
container_name: electrs-liquid
@@ -76,52 +70,45 @@ services:
- --daemon-dir
- /config
- --daemon-rpc-addr
- - 10.10.0.11:18884
+ - liquid:18884
- --cookie
- admin1:123
- --http-addr
- - 0.0.0.0:3002
+ - 0.0.0.0:30001
- --electrum-rpc-addr
- - 0.0.0.0:60401
+ - 0.0.0.0:50001
- --cors
- "*"
- networks:
- local:
- ipv4_address: 10.10.0.13
depends_on:
- liquid
ports:
- - ${LIQUID_ELECTRS_RPC_PORT}:60401
- - ${LIQUID_ELECTRS_PORT}:3002
+ - 50001:50001
+ - 30001:30001
volumes:
- - ./volumes/liquidregtest/liquid-config/:/config
+ - ./volumes/elements/:/config
restart: unless-stopped
- # Block explorer frontends
+
+ # Block explorer frontend
esplora:
image: ghcr.io/vulpemventures/esplora:latest
container_name: esplora
- networks:
- local:
- ipv4_address: 10.10.0.14
depends_on:
- chopsticks
- environment:
- API_URL: ${BITCOIN_ESPLORA_URL}
+ environment:
+ API_URL: http://localhost:3000
ports:
- - ${BITCOIN_ESPLORA_PORT}:5000
+ - 5000:5000
restart: unless-stopped
+
esplora-liquid:
image: ghcr.io/vulpemventures/esplora:latest
container_name: esplora-liquid
- networks:
- local:
- ipv4_address: 10.10.0.15
depends_on:
- chopsticks-liquid
environment:
- API_URL: ${LIQUID_ESPLORA_URL}
+ API_URL: http://localhost:3001
ports:
- - ${LIQUID_ESPLORA_PORT}:5000
+ - 5001:5000
restart: unless-stopped
# Chopsticks
chopsticks:
@@ -132,20 +119,18 @@ services:
- --use-mining
- --use-logger
- --rpc-addr
- - 10.10.0.10:19001
+ - bitcoin:18443
- --electrs-addr
- - 10.10.0.12:3002
+ - electrs:30000
- --addr
- 0.0.0.0:3000
depends_on:
- bitcoin
- electrs
ports:
- - ${BITCOIN_CHOPSTICKS_PORT}:3000
- networks:
- local:
- ipv4_address: 10.10.0.16
+ - 3000:3000
restart: unless-stopped
+
chopsticks-liquid:
image: ghcr.io/vulpemventures/nigiri-chopsticks:latest
container_name: chopsticks-liquid
@@ -154,9 +139,9 @@ services:
- --use-mining
- --use-logger
- --rpc-addr
- - 10.10.0.11:18884
+ - liquid:18884
- --electrs-addr
- - 10.10.0.13:3002
+ - electrs-liquid:30001
- --addr
- 0.0.0.0:3000
- --chain
@@ -165,15 +150,9 @@ services:
- liquid
- electrs-liquid
ports:
- - ${LIQUID_CHOPSTICKS_PORT}:3000
- networks:
- local:
- ipv4_address: 10.10.0.17
+ - 3001:3000
restart: unless-stopped
networks:
- local:
- driver: bridge
- ipam:
- config:
- - subnet: 10.10.0.0/24
+ default:
+ name: nigiri
diff --git a/resources/volumes/liquidregtest/liquid-config/elements.conf b/cmd/nigiri/resources/elements.conf
similarity index 94%
rename from resources/volumes/liquidregtest/liquid-config/elements.conf
rename to cmd/nigiri/resources/elements.conf
index a8732e8..32b70d5 100644
--- a/resources/volumes/liquidregtest/liquid-config/elements.conf
+++ b/cmd/nigiri/resources/elements.conf
@@ -10,8 +10,8 @@ rpcpassword=123
rpcallowip=0.0.0.0/0
rpcbind=0.0.0.0
-mainchainrpcport=19001
-mainchainrpchost=10.10.0.10
+mainchainrpcport=18443
+#mainchainrpchost=10.10.0.10
mainchainrpcuser=admin1
mainchainrpcpassword=123
diff --git a/cmd/nigiri/rpc.go b/cmd/nigiri/rpc.go
new file mode 100644
index 0000000..55b9d65
--- /dev/null
+++ b/cmd/nigiri/rpc.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+ "errors"
+ "os"
+ "os/exec"
+
+ "github.com/urfave/cli/v2"
+)
+
+var rpc = cli.Command{
+ Name: "rpc",
+ Usage: "invoke bitcoin-cli or elements-cli",
+ Action: rpcAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ &cli.StringFlag{
+ Name: "rpcwallet",
+ Usage: "rpcwallet to be used for node JSONRPC commands",
+ Value: "",
+ },
+ },
+}
+
+func rpcAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); !isRunning {
+ return errors.New("nigiri is not running")
+ }
+
+ isLiquid := ctx.Bool("liquid")
+ rpcWallet := ctx.String("rpcwallet")
+
+ rpcArgs := []string{"exec", "bitcoin", "bitcoin-cli", "-datadir=config", "-rpcwallet=" + rpcWallet}
+ if isLiquid {
+ rpcArgs = []string{"exec", "liquid", "elements-cli", "-datadir=config", "-rpcwallet=" + rpcWallet}
+ }
+ cmdArgs := append(rpcArgs, ctx.Args().Slice()...)
+ bashCmd := exec.Command("docker", cmdArgs...)
+ bashCmd.Stdout = os.Stdout
+ bashCmd.Stderr = os.Stderr
+
+ if err := bashCmd.Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/nigiri/start.go b/cmd/nigiri/start.go
new file mode 100644
index 0000000..d006992
--- /dev/null
+++ b/cmd/nigiri/start.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+ "errors"
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+ "strings"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/docker"
+)
+
+var start = cli.Command{
+ Name: "start",
+ Usage: "start nigiri",
+ Action: startAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ &cli.BoolFlag{
+ Name: "ci",
+ Usage: "runs in headless mode without esplora for continuous integration environments",
+ Value: false,
+ },
+ },
+}
+
+func startAction(ctx *cli.Context) error {
+
+ if isRunning, _ := nigiriState.GetBool("running"); isRunning {
+ return errors.New("nigiri is already running, please stop it first")
+ }
+
+ isLiquid := ctx.Bool("liquid")
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ // spin up all the services in the compose file
+ bashCmd := exec.Command("docker-compose", "-f", composePath, "up", "-d", "esplora")
+ if isLiquid {
+ //this will only run chopsticks & chopsticks-liquid and servives they depends on
+ bashCmd = exec.Command("docker-compose", "-f", composePath, "up", "-d", "esplora", "esplora-liquid")
+ }
+
+ if ctx.Bool("ci") {
+ //this will only run chopsticks and servives it depends on
+ bashCmd = exec.Command("docker-compose", "-f", composePath, "up", "-d", "chopsticks")
+ if isLiquid {
+ //this will only run chopsticks & chopsticks-liquid and servives they depends on
+ bashCmd = exec.Command("docker-compose", "-f", composePath, "up", "-d", "chopsticks", "chopsticks-liquid")
+ }
+ }
+
+ bashCmd.Stdout = os.Stdout
+ bashCmd.Stderr = os.Stderr
+
+ if err := bashCmd.Run(); err != nil {
+ return err
+ }
+
+ if err := nigiriState.Set(map[string]string{
+ "running": strconv.FormatBool(true),
+ }); err != nil {
+ return err
+ }
+
+ services, err := docker.GetServices(composePath)
+ if err != nil {
+ return err
+ }
+
+ fmt.Println()
+ fmt.Println("ENDPOINTS")
+
+ for _, nameAndEndpoint := range services {
+ name := nameAndEndpoint[0]
+ endpoint := nameAndEndpoint[1]
+
+ if !isLiquid && strings.Contains(name, "liquid") {
+ continue
+ }
+
+ fmt.Println(name + " " + endpoint)
+ }
+
+ return nil
+}
diff --git a/cmd/nigiri/stop.go b/cmd/nigiri/stop.go
new file mode 100644
index 0000000..1cfb6e7
--- /dev/null
+++ b/cmd/nigiri/stop.go
@@ -0,0 +1,67 @@
+package main
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strconv"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+)
+
+var stop = cli.Command{
+ Name: "stop",
+ Usage: "stop nigiri",
+ Action: stopAction,
+ Flags: []cli.Flag{
+ &liquidFlag,
+ &cli.BoolFlag{
+ Name: "delete",
+ Usage: "clean node data directories",
+ Value: false,
+ },
+ },
+}
+
+func stopAction(ctx *cli.Context) error {
+
+ delete := ctx.Bool("delete")
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ bashCmd := exec.Command("docker-compose", "-f", composePath, "stop")
+ if delete {
+ bashCmd = exec.Command("docker-compose", "-f", composePath, "down", "--volumes")
+ }
+ bashCmd.Stdout = os.Stdout
+ bashCmd.Stderr = os.Stderr
+
+ if err := bashCmd.Run(); err != nil {
+ return err
+ }
+
+ if delete {
+ fmt.Println("Removing data from volumes...")
+
+ datadir := ctx.String("datadir")
+ if err := os.RemoveAll(datadir); err != nil {
+ return err
+ }
+
+ if err := provisionResourcesToDatadir(datadir); err != nil {
+ return err
+ }
+
+ fmt.Println("Nigiri has been cleaned up successfully.")
+ } else {
+ if err := nigiriState.Set(map[string]string{
+ "running": strconv.FormatBool(false),
+ }); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/nigiri/update.go b/cmd/nigiri/update.go
new file mode 100644
index 0000000..0ad9463
--- /dev/null
+++ b/cmd/nigiri/update.go
@@ -0,0 +1,31 @@
+package main
+
+import (
+ "os"
+ "os/exec"
+ "path/filepath"
+
+ "github.com/urfave/cli/v2"
+ "github.com/vulpemventures/nigiri/internal/config"
+)
+
+var update = cli.Command{
+ Name: "update",
+ Usage: "check for updates and pull new docker images",
+ Action: updateAction,
+}
+
+func updateAction(ctx *cli.Context) error {
+ datadir := ctx.String("datadir")
+ composePath := filepath.Join(datadir, config.DefaultCompose)
+
+ bashCmd := exec.Command("docker-compose", "-f", composePath, "pull")
+ bashCmd.Stdout = os.Stdout
+ bashCmd.Stderr = os.Stderr
+
+ if err := bashCmd.Run(); err != nil {
+ return err
+ }
+
+ return nil
+}
diff --git a/cmd/nigiri/version.go b/cmd/nigiri/version.go
new file mode 100644
index 0000000..21c926d
--- /dev/null
+++ b/cmd/nigiri/version.go
@@ -0,0 +1,18 @@
+package main
+
+import (
+ "fmt"
+
+ "github.com/urfave/cli/v2"
+)
+
+var versionCmd = cli.Command{
+ Name: "version",
+ Action: versionAction,
+}
+
+func versionAction(ctx *cli.Context) error {
+ fmt.Println("nigiri CLI version")
+ fmt.Println(formatVersion())
+ return nil
+}
diff --git a/go.mod b/go.mod
index 0096fe1..bfc1812 100644
--- a/go.mod
+++ b/go.mod
@@ -1,25 +1,11 @@
module github.com/vulpemventures/nigiri
-go 1.12
+go 1.16
require (
- github.com/BurntSushi/toml v0.3.1 // indirect
- github.com/Microsoft/go-winio v0.4.12 // indirect
- github.com/docker/distribution v2.7.1+incompatible // indirect
- github.com/docker/docker v1.13.1
- github.com/docker/go-connections v0.4.0 // indirect
- github.com/docker/go-units v0.3.3 // indirect
- github.com/inconshreveable/mousetrap v1.0.0 // indirect
- github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
- github.com/mitchellh/go-homedir v1.1.0
- github.com/opencontainers/go-digest v1.0.0-rc1 // indirect
- github.com/pkg/errors v0.8.1 // indirect
- github.com/sirupsen/logrus v1.4.0
- github.com/spf13/afero v1.2.2 // indirect
- github.com/spf13/cobra v0.0.3
- github.com/spf13/jwalterweatherman v1.1.0 // indirect
- github.com/spf13/viper v1.3.2
- golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c // indirect
- golang.org/x/net v0.0.0-20190419010253-1f3472d942ba // indirect
- golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc // indirect
+ github.com/btcsuite/btcd v0.21.0-beta // indirect
+ github.com/btcsuite/btcutil v1.0.2
+ github.com/compose-spec/compose-go v0.0.0-20210729195839-de56f4f0cb3c
+ github.com/urfave/cli/v2 v2.3.0
+ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
)
diff --git a/go.sum b/go.sum
index 22ee2b4..5bd5dbc 100644
--- a/go.sum
+++ b/go.sum
@@ -1,80 +1,214 @@
-github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
+cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
+github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
+github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
-github.com/Microsoft/go-winio v0.4.12 h1:xAfWHN1IrQ0NJ9TBC0KBZoqLjzDTr1ML+4MywiUOryc=
-github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
+github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
+github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
+github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
+github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/aws/aws-sdk-go v1.34.9/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
+github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
+github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
+github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
+github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
+github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
+github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
+github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
+github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
+github.com/btcsuite/btcd v0.21.0-beta/go.mod h1:ZSWyehm27aAuS9bvkATT+Xte3hjHZ+MRgMY/8NJ7K94=
+github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
+github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
+github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts=
+github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts=
+github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
+github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
+github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I=
+github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
+github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
+github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
+github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
+github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
+github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
+github.com/compose-spec/compose-go v0.0.0-20210729195839-de56f4f0cb3c h1:lSR4wokZlq+Q8uJpgZuFMs3VoLaYVV07cJOZHa1zRBg=
+github.com/compose-spec/compose-go v0.0.0-20210729195839-de56f4f0cb3c/go.mod h1:5V65rPnTvvQagtoMxTneJ2QicLq6ZRQQ7fOgPN226fo=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY=
+github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug=
-github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo=
-github.com/docker/docker v1.13.1/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
+github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
+github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
+github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e h1:n81KvOMrLZa+VWHwST7dun9f0G98X3zREHS1ztYzZKU=
+github.com/distribution/distribution/v3 v3.0.0-20210316161203-a01c71e2477e/go.mod h1:xpWTC2KnJMiDLkoawhsPQcXjvwATEBcbq0xevG2YR9M=
+github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-units v0.3.3 h1:Xk8S3Xj5sLGlG5g67hJmYMmUgXv5N4PhkjJHHqrwnTk=
-github.com/docker/go-units v0.3.3/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
+github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
+github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
+github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
+github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
+github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
+github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
+github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
+github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
+github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
+github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
+github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
+github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
+github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/gomodule/redigo v1.8.2/go.mod h1:P9dn9mFrCBvWhGE1wpxx6fgq7BAeLBk+UUUzlpkBYO0=
+github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
+github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
+github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
+github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU=
+github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
+github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
+github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
+github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
+github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
+github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
+github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
+github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
+github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
+github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
+github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
+github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
+github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
+github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
+github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
+github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
+github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
+github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
+github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
+github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
+github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
+github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
+github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
+github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
+github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
+github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/sirupsen/logrus v1.4.0 h1:yKenngtzGh+cUSSh6GWbxW2abRqhYUSR/t/6+2QqNvE=
-github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
+github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
+github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
+github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
+github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
+github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
+github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
+github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
+github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
+github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
+github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
+github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q=
+github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
+github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
+github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
+github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
+github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
+github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
+github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
+github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
-github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
-github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M=
-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
+github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
+github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
+github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
+github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
+github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
+github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
+github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
+github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
+github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
+github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
+golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c h1:Vj5n4GlwjmQteupaxJ9+0FNOmBrHfq7vN4btdGoDZgI=
-golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/net v0.0.0-20190419010253-1f3472d942ba h1:h0zCzEL5UW1mERvwTN6AXcc75PpLkY6OcReia6Dq1BM=
-golang.org/x/net v0.0.0-20190419010253-1f3472d942ba/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200128174031-69ecbb4d6d5d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
+golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
+golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
+golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
+golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
+golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc h1:4gbWbmmPFp4ySWICouJl6emP0MyS31yy9SrTlAGFT+g=
-golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd h1:xhmwyvizuTgC2qz7ZlMluP20uW+C3Rm0FD/WLDX8884=
+golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
+google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
+google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
+google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
+gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789 h1:NMiUjDZiD6qDVeBOzpImftxXzQHCp2Y2QLdmaqU9MRk=
+gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
+gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
+gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
+gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0=
+gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
diff --git a/internal/config/config.go b/internal/config/config.go
new file mode 100644
index 0000000..3456244
--- /dev/null
+++ b/internal/config/config.go
@@ -0,0 +1,22 @@
+package config
+
+import (
+ "path/filepath"
+ "strconv"
+
+ "github.com/btcsuite/btcutil"
+)
+
+var (
+ DefaultName = "nigiri.config.json"
+ DefaultCompose = "docker-compose.yml"
+
+ DefaultDatadir = btcutil.AppDataDir("nigiri", false)
+ DefaultPath = filepath.Join(DefaultDatadir, DefaultName)
+
+ InitialState = map[string]string{
+ "network": "regtest",
+ "ready": strconv.FormatBool(false),
+ "running": strconv.FormatBool(false),
+ }
+)
diff --git a/internal/docker/docker.go b/internal/docker/docker.go
new file mode 100644
index 0000000..121d10d
--- /dev/null
+++ b/internal/docker/docker.go
@@ -0,0 +1,76 @@
+package docker
+
+import (
+ "errors"
+ "io/ioutil"
+ "strings"
+
+ "github.com/compose-spec/compose-go/loader"
+)
+
+func GetServices(composeFile string) ([][]string, error) {
+
+ composeBytes, err := ioutil.ReadFile(composeFile)
+ if err != nil {
+ return nil, err
+ }
+
+ parsed, err := loader.ParseYAML(composeBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, ok := parsed["services"]; !ok {
+ return nil, errors.New("missing services in compose")
+ }
+
+ serviceMap := parsed["services"].(map[string]interface{})
+
+ var services [][]string
+ for k, v := range serviceMap {
+ m := v.(map[string]interface{})
+ i := m["ports"].([]interface{})
+ for _, j := range i {
+ port := j.(string)
+ exposedPorts := strings.Split(port, ":")
+ endpoint := "localhost:" + exposedPorts[0]
+ services = append(services, []string{k, endpoint})
+ }
+
+ }
+
+ return services, nil
+}
+
+func GetPortsForService(composeFile string, serviceName string) ([]string, error) {
+
+ composeBytes, err := ioutil.ReadFile(composeFile)
+ if err != nil {
+ return nil, err
+ }
+
+ parsed, err := loader.ParseYAML(composeBytes)
+ if err != nil {
+ return nil, err
+ }
+
+ if _, ok := parsed["services"]; !ok {
+ return nil, errors.New("missing services in compose")
+ }
+
+ serviceMap := parsed["services"].(map[string]interface{})
+
+ var ports []string
+ for k, v := range serviceMap {
+ if k == serviceName {
+ m := v.(map[string]interface{})
+ i := m["ports"].([]interface{})
+ for _, j := range i {
+ port := j.(string)
+ ports = append(ports, port)
+ }
+ }
+ }
+
+ return ports, nil
+}
diff --git a/internal/state/state.go b/internal/state/state.go
new file mode 100644
index 0000000..1dca7f0
--- /dev/null
+++ b/internal/state/state.go
@@ -0,0 +1,104 @@
+package state
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "strconv"
+)
+
+type State struct {
+ directory string
+ filePath string
+ initialState map[string]string
+}
+
+func New(filePath string, initialState map[string]string) *State {
+ dir := filepath.Dir(filePath)
+ return &State{
+ directory: dir,
+ filePath: filePath,
+ initialState: initialState,
+ }
+}
+
+func (s *State) Get() (map[string]string, error) {
+ file, err := ioutil.ReadFile(s.filePath)
+ if err != nil {
+ if !os.IsNotExist(err) {
+ return nil, err
+ }
+ if err := s.Set(s.initialState); err != nil {
+ return nil, err
+ }
+ return s.initialState, nil
+ }
+
+ data := map[string]string{}
+ json.Unmarshal(file, &data)
+
+ return data, nil
+}
+
+func (s *State) Set(data map[string]string) error {
+ if _, err := os.Stat(s.directory); os.IsNotExist(err) {
+ os.Mkdir(s.directory, os.ModeDir|0755)
+ }
+
+ file, err := os.OpenFile(s.filePath, os.O_RDONLY|os.O_CREATE, 0644)
+ if err != nil {
+ return err
+ }
+ if err := file.Close(); err != nil {
+ return err
+ }
+
+ currentData, err := s.Get()
+ if err != nil {
+ return err
+ }
+
+ mergedData := merge(currentData, data)
+
+ jsonString, err := json.Marshal(mergedData)
+ if err != nil {
+ return err
+ }
+ err = ioutil.WriteFile(s.filePath, jsonString, 0755)
+ if err != nil {
+ return fmt.Errorf("writing to file: %w", err)
+ }
+
+ return nil
+}
+
+func (s *State) GetBool(key string) (bool, error) {
+ stateData, err := s.Get()
+ if err != nil {
+ return false, err
+ }
+
+ if _, ok := stateData[key]; !ok {
+ return false, errors.New("config: missing key " + key)
+ }
+
+ value, err := strconv.ParseBool(stateData[key])
+ if err != nil {
+ return false, err
+ }
+
+ return value, nil
+}
+
+func merge(maps ...map[string]string) map[string]string {
+ merge := make(map[string]string)
+ for _, m := range maps {
+ for k, v := range m {
+ merge[k] = v
+ }
+ }
+ return merge
+}
diff --git a/resources/docker-compose-regtest.yml b/resources/docker-compose-regtest.yml
deleted file mode 100644
index 08391cf..0000000
--- a/resources/docker-compose-regtest.yml
+++ /dev/null
@@ -1,94 +0,0 @@
-version: '3'
-services:
- # RPC daemon
- bitcoin:
- image: ghcr.io/vulpemventures/bitcoin:latest
- container_name: bitcoin
- command:
- - -datadir=config
- networks:
- local:
- ipv4_address: 10.10.0.10
- ports:
- - ${BITCOIN_PEER_PORT}:19000
- - ${BITCOIN_NODE_PORT}:19001
- volumes:
- - ./volumes/regtest/config/:/config
- restart: unless-stopped
- # Block explorer server
- electrs:
- image: ghcr.io/vulpemventures/electrs:latest
- container_name: electrs
- entrypoint:
- - /build/electrs
- command:
- - -vvvv
- - --network
- - regtest
- - --daemon-dir
- - /config
- - --daemon-rpc-addr
- - 10.10.0.10:19001
- - --cookie
- - admin1:123
- - --http-addr
- - 0.0.0.0:3002
- - --electrum-rpc-addr
- - 0.0.0.0:60401
- - --cors
- - "*"
- networks:
- local:
- ipv4_address: 10.10.0.11
- depends_on:
- - bitcoin
- ports:
- - ${BITCOIN_ELECTRS_RPC_PORT}:60401
- - ${BITCOIN_ELECTRS_PORT}:3002
- volumes:
- - ./volumes/regtest/config/:/config
- restart: unless-stopped
- # Block explorer frontend
- esplora:
- image: ghcr.io/vulpemventures/esplora:latest
- container_name: esplora
- networks:
- local:
- ipv4_address: 10.10.0.12
- depends_on:
- - chopsticks
- environment:
- API_URL: ${BITCOIN_ESPLORA_URL}
- ports:
- - ${BITCOIN_ESPLORA_PORT}:5000
- restart: unless-stopped
- # Chopsticks
- chopsticks:
- image: ghcr.io/vulpemventures/nigiri-chopsticks:latest
- container_name: chopsticks
- command:
- - --use-faucet
- - --use-mining
- - --use-logger
- - --rpc-addr
- - 10.10.0.10:19001
- - --electrs-addr
- - 10.10.0.11:3002
- - --addr
- - 0.0.0.0:3000
- networks:
- local:
- ipv4_address: 10.10.0.13
- depends_on:
- - bitcoin
- - electrs
- ports:
- - ${BITCOIN_CHOPSTICKS_PORT}:3000
- restart: unless-stopped
-
-networks:
- local:
- driver: bridge
- ipam:
- config:
- - subnet: 10.10.0.0/24
diff --git a/resources/volumes/regtest/config/bitcoin.conf b/resources/volumes/regtest/config/bitcoin.conf
deleted file mode 100644
index c462cbc..0000000
--- a/resources/volumes/regtest/config/bitcoin.conf
+++ /dev/null
@@ -1,17 +0,0 @@
-regtest=1
-testnet=0
-dnsseed=0
-upnp=0
-
-[regtest]
-port=19000
-rpcport=19001
-
-server=1
-txindex=0
-
-rpcuser=admin1
-rpcpassword=123
-rpcallowip=0.0.0.0/0
-rpcbind=0.0.0.0
-fallbackfee=0.00001
diff --git a/scripts/build b/scripts/build
index a42b977..2901324 100755
--- a/scripts/build
+++ b/scripts/build
@@ -12,5 +12,5 @@ ARCH=$(eval "go env GOARCH")
pushd $PARENT_PATH
mkdir -p build
-GO111MODULE=on go build -ldflags="-s -w" -o build/nigiri-$OS-$ARCH cli/main.go
+GO111MODULE=on CGO_ENABLED=0 go build -ldflags="-s -w" -o build/nigiri-$OS-$ARCH cmd/nigiri/*.go
popd
diff --git a/scripts/clean b/scripts/clean
deleted file mode 100755
index 0498ac9..0000000
--- a/scripts/clean
+++ /dev/null
@@ -1,24 +0,0 @@
-#!/bin/bash
-
-set -ex
-
-PARENT_PATH=$(dirname $(cd $(dirname $0); pwd -P))
-
-pushd $PARENT_PATH
-
-case $(uname -s) in
- Darwin) OS="darwin";;
- Linux) OS="linux";;
- *) echo "OS $OS not supported"; exit 1;;
-esac
-
-case $(uname -m) in
- amd64) ARCH="amd64";;
- x86_64) ARCH="amd64";;
- *) echo "Architecture $ARCH not supported"; exit 1;;
-esac
-
-./build/nigiri-$OS-$ARCH stop --delete &>/dev/null
-rm -rf build vendor ~/.nigiri
-
-popd
\ No newline at end of file
diff --git a/scripts/install b/scripts/install
deleted file mode 100755
index 0757c19..0000000
--- a/scripts/install
+++ /dev/null
@@ -1,12 +0,0 @@
-#!/bin/bash
-set -ex
-
-PARENT_PATH=$(dirname $(cd $(dirname $0); pwd -P))
-
-pushd $PARENT_PATH
-
-go generate ./...
-
-mkdir -p $HOME/.nigiri
-cp -R resources $HOME/.nigiri
-popd
\ No newline at end of file
diff --git a/test/start_stop_test.go b/test/start_stop_test.go
new file mode 100644
index 0000000..6f7acf3
--- /dev/null
+++ b/test/start_stop_test.go
@@ -0,0 +1,115 @@
+package test
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "testing"
+ "time"
+
+ "github.com/vulpemventures/nigiri/internal/config"
+ "github.com/vulpemventures/nigiri/internal/state"
+)
+
+const (
+ liquid = true
+ bitcoin = false
+ delete = true
+)
+
+var (
+ stopCmd = "stop"
+ // deleteCmd = append(stopCmd, "--delete")
+ startCmd = "start"
+ // liquidStartCmd = append(startCmd, "--liquid")
+ tmpDatadir = filepath.Join(os.TempDir(), "nigiri-tmp")
+ nigiriState = state.New(filepath.Join(tmpDatadir, config.DefaultName), config.InitialState)
+)
+
+func TestStartStopLiquid(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping testing in short mode")
+ }
+ // Start/Stop
+ testStart(t, liquid)
+ testStop(t)
+ // Start/Delete
+ testStart(t, liquid)
+ testDelete(t)
+}
+
+func TestStartStopBitcoin(t *testing.T) {
+ if testing.Short() {
+ t.Skip("skipping testing in short mode")
+ }
+ // Start/Stop
+ testStart(t, bitcoin)
+ testStop(t)
+ // Start/Delete
+ testStart(t, bitcoin)
+ testDelete(t)
+}
+
+func testStart(t *testing.T, flag bool) {
+
+ if err := testCommand("start", "", flag); err != nil {
+ t.Fatal(err)
+ }
+ //Give some time to nigiri to be ready before calling
+ time.Sleep(5 * time.Second)
+ if isRunning, err := nigiriState.GetBool("running"); err != nil {
+ t.Fatal(err)
+ } else if !isRunning {
+ t.Fatal("Nigiri should have been started but services have not been found among running containers")
+ }
+}
+
+func testStop(t *testing.T) {
+
+ if err := testCommand("stop", "", !delete); err != nil {
+ t.Fatal(err)
+ }
+ //Give some time to nigiri to be ready before calling
+ time.Sleep(5 * time.Second)
+ if isRunning, err := nigiriState.GetBool("running"); err != nil {
+ t.Fatal(err)
+ } else if isRunning {
+ t.Fatal("Nigiri should have been stopped but services have not been found among stopped containers")
+ }
+}
+
+func testDelete(t *testing.T) {
+
+ if err := testCommand("stop", "", delete); err != nil {
+ t.Fatal(err)
+ }
+ if isRunning, err := nigiriState.GetBool("running"); err != nil {
+ t.Fatal(err)
+ } else if isRunning {
+ t.Fatal("Nigiri should have been terminated at this point but services have been found among stopped containers")
+ }
+}
+
+func testCommand(command, arg string, flag bool) error {
+
+ cmd := exec.Command("go", "run", "./cmd/nigiri")
+ env := "NIGIRI_DATADIR=" + tmpDatadir
+ cmd.Env = []string{env}
+ cmd.Stderr = os.Stderr
+ cmd.Stdout = os.Stdout
+
+ if command == "start" {
+ cmd.Args = append(cmd.Args, startCmd, fmt.Sprintf("--liquid=%t", flag))
+ }
+ if command == "stop" {
+ cmd.Args = append(cmd.Args, stopCmd, fmt.Sprintf("--delete=%t", flag))
+ }
+
+ err := cmd.Start()
+ if err != nil {
+ return fmt.Errorf("name: %v, args: %v, err: %v", command, arg, err.Error())
+ }
+
+ return nil
+}