diff --git a/Makefile b/Makefile index 4a6182572f..ed67d1a0c1 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ .PHONY : check lint install-linters dep test .PHONY : build clean install format .PHONY : host-apps bin -.PHONY : run stop +.PHONY : run stop config .PHONY : docker-image docker-clean docker-network .PHONY : docker-apps docker-bin docker-volume .PHONY : docker-run docker-stop @@ -17,12 +17,14 @@ check: lint test ## Run linters and tests build: dep host-apps bin ## Install dependencies, build apps and binaries. `go build` with ${OPTS} -run: stop build ## Run skywire-node on host - ./skywire-node +run: stop build config ## Run skywire-node on host + ./skywire-node skywire.json stop: ## Stop running skywire-node on host -bash -c "kill $$(ps aux |grep '[s]kywire-node' |awk '{print $$2}')" +config: ## Generate skywire.json + -./skywire-cli node gen-config -o ./skywire.json -r clean: ## Clean project: remove created binaries and apps -rm -rf ./apps @@ -31,6 +33,12 @@ clean: ## Clean project: remove created binaries and apps install: ## Install `skywire-node`, `skywire-cli`, `manager-node`, `therealssh-cli` ${OPTS} go install ./cmd/skywire-node ./cmd/skywire-cli ./cmd/manager-node ./cmd/therealssh-cli +rerun: stop + ${OPTS} go build -race -o ./skywire-node ./cmd/skywire-node + -./skywire-cli node gen-config -o ./skywire.json -r + perl -pi -e 's/localhost//g' ./skywire.json + ./skywire-node skywire.json + lint: ## Run linters. Use make install-linters first ${OPTS} golangci-lint run -c .golangci.yml ./... @@ -49,8 +57,22 @@ vendorcheck: ## Run vendorcheck #GO111MODULE=off vendorcheck ./cmd/therealssh-cli/... test: ## Run tests for net + -go clean -testcache ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./internal/... - ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/... + #${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/app/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/cipher/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/manager/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/messaging-discovery/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/node/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/route-finder/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/router/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/routing/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/setup/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/transport/... + ${OPTS} go test -race -tags no_ci -cover -timeout=5m ./pkg/transport-discovery/... + ${OPTS} go test -tags no_ci -cover -timeout=5m ./pkg/messaging/... + install-linters: ## Install linters - VERSION=1.13.2 ./ci_scripts/install-golangci-lint.sh @@ -71,19 +93,33 @@ dep: ## Sorts dependencies # Apps host-apps: ## Build app - ${OPTS} go build -o ./apps/chat.v1.0 ./cmd/apps/chat - ${OPTS} go build -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld - ${OPTS} go build -o ./apps/therealproxy.v1.0 ./cmd/apps/therealproxy - ${OPTS} go build -o ./apps/therealproxy-client.v1.0 ./cmd/apps/therealproxy-client - ${OPTS} go build -o ./apps/therealssh.v1.0 ./cmd/apps/therealssh - ${OPTS} go build -o ./apps/therealssh-client.v1.0 ./cmd/apps/therealssh-client + ${OPTS} go build -race -o ./apps/chat.v1.0 ./cmd/apps/chat + ${OPTS} go build -race -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld + ${OPTS} go build -race -o ./apps/therealproxy.v1.0 ./cmd/apps/therealproxy + ${OPTS} go build -race -o ./apps/therealproxy-client.v1.0 ./cmd/apps/therealproxy-client + ${OPTS} go build -race -o ./apps/therealssh.v1.0 ./cmd/apps/therealssh + ${OPTS} go build -race -o ./apps/therealssh-client.v1.0 ./cmd/apps/therealssh-client # Bin bin: ## Build `skywire-node`, `skywire-cli`, `manager-node`, `therealssh-cli` + ${OPTS} go build -race -o ./skywire-node ./cmd/skywire-node + ${OPTS} go build -race -o ./skywire-cli ./cmd/skywire-cli + ${OPTS} go build -race -o ./manager-node ./cmd/manager-node + ${OPTS} go build -race -o ./therealssh-cli ./cmd/therealssh-cli + +release: ## Build skywire-node`, skywire-cli, manager-node, therealssh-cli and apps without -race flag ${OPTS} go build -o ./skywire-node ./cmd/skywire-node ${OPTS} go build -o ./skywire-cli ./cmd/skywire-cli ${OPTS} go build -o ./manager-node ./cmd/manager-node ${OPTS} go build -o ./therealssh-cli ./cmd/therealssh-cli + ${OPTS} go build -o ./apps/chat.v1.0 ./cmd/apps/chat + ${OPTS} go build -o ./apps/helloworld.v1.0 ./cmd/apps/helloworld + ${OPTS} go build -o ./apps/therealproxy.v1.0 ./cmd/apps/therealproxy + ${OPTS} go build -o ./apps/therealproxy-client.v1.0 ./cmd/apps/therealproxy-client + ${OPTS} go build -o ./apps/therealssh.v1.0 ./cmd/apps/therealssh + ${OPTS} go build -o ./apps/therealssh-client.v1.0 ./cmd/apps/therealssh-client + + # Dockerized skywire-node docker-image: ## Build docker image `skywire-runner` @@ -97,26 +133,33 @@ docker-network: ## Create docker network ${DOCKER_NETWORK} -docker network create ${DOCKER_NETWORK} docker-apps: ## Build apps binaries for dockerized skywire-node. `go build` with ${DOCKER_OPTS} - -${DOCKER_OPTS} go build -o ./node/apps/chat.v1.0 ./cmd/apps/chat - -${DOCKER_OPTS} go build -o ./node/apps/helloworld.v1.0 ./cmd/apps/helloworld - -${DOCKER_OPTS} go build -o ./node/apps/therealproxy.v1.0 ./cmd/apps/therealproxy - -${DOCKER_OPTS} go build -o ./node/apps/therealproxy-client.v1.0 ./cmd/apps/therealproxy-client - -${DOCKER_OPTS} go build -o ./node/apps/therealssh.v1.0 ./cmd/apps/therealssh - -${DOCKER_OPTS} go build -o ./node/apps/therealssh-client.v1.0 ./cmd/apps/therealssh-client + -${DOCKER_OPTS} go build -race -o ./node/apps/chat.v1.0 ./cmd/apps/chat + -${DOCKER_OPTS} go build -race -o ./node/apps/helloworld.v1.0 ./cmd/apps/helloworld + -${DOCKER_OPTS} go build -race -o ./node/apps/therealproxy.v1.0 ./cmd/apps/therealproxy + -${DOCKER_OPTS} go build -race -o ./node/apps/therealproxy-client.v1.0 ./cmd/apps/therealproxy-client + -${DOCKER_OPTS} go build -race -o ./node/apps/therealssh.v1.0 ./cmd/apps/therealssh + -${DOCKER_OPTS} go build -race -o ./node/apps/therealssh-client.v1.0 ./cmd/apps/therealssh-client docker-bin: ## Build `skywire-node`, `skywire-cli`, `manager-node`, `therealssh-cli`. `go build` with ${DOCKER_OPTS} - ${DOCKER_OPTS} go build -o ./node/skywire-node ./cmd/skywire-node + ${DOCKER_OPTS} go build -race -o ./node/skywire-node ./cmd/skywire-node docker-volume: docker-apps docker-bin bin ## Prepare docker volume for dockerized skywire-node - ./skywire-cli node gen-config -o ./node/skywire.json -r + -./skywire-cli node gen-config -o ./node/skywire.json -r + perl -pi -e 's/localhost//g' ./node/skywire.json # To make node accessible from outside with skywire-cli docker-run: docker-clean docker-image docker-network docker-volume ## Run dockerized skywire-node ${DOCKER_NODE} in image ${DOCKER_IMAGE} with network ${DOCKER_NETWORK} docker run -it -v $(shell pwd)/node:/sky --network=${DOCKER_NETWORK} \ - --name=${DOCKER_NODE} ${DOCKER_IMAGE} bash -c "cd /sky && ./skywire-node" + --name=${DOCKER_NODE} ${DOCKER_IMAGE} bash -c "cd /sky && ./skywire-node skywire.json" docker-stop: ## Stop running dockerized skywire-node ${DOCKER_NODE} -docker container stop ${DOCKER_NODE} +docker-rerun: docker-stop + -./skywire-cli gen-config -o ./node/skywire.json -r + perl -pi -e 's/localhost//g' ./node/skywire.json # To make node accessible from outside with skywire-cli + ${DOCKER_OPTS} go build -race -o ./node/skywire-node ./cmd/skywire-node + docker container start -i ${DOCKER_NODE} + help: @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}' diff --git a/cmd/skywire-cli/commands/tpdisc/root.go b/cmd/skywire-cli/commands/tpdisc/root.go index e0fb6399df..d72036e914 100644 --- a/cmd/skywire-cli/commands/tpdisc/root.go +++ b/cmd/skywire-cli/commands/tpdisc/root.go @@ -70,7 +70,7 @@ func printTransportEntries(entries ...*transport.EntryWithStatus) { internal.Catch(err) for _, e := range entries { _, err := fmt.Fprintf(w, "%s\t%s\t%t\t%d\t%t\t%s\t%s\t%t\t%t\n", - e.Entry.ID, e.Entry.Type, e.Entry.Public, e.Registered, e.IsUp, e.Entry.Edges[0], e.Entry.Edges[1], e.Statuses[0], e.Statuses[1]) + e.Entry.ID, e.Entry.Type, e.Entry.Public, e.Registered, e.IsUp, e.Entry.Edges()[0], e.Entry.Edges()[1], e.Statuses[0], e.Statuses[1]) internal.Catch(err) } internal.Catch(w.Flush()) diff --git a/go.mod b/go.mod index 30d355c698..cf3596c80d 100644 --- a/go.mod +++ b/go.mod @@ -3,28 +3,63 @@ module github.com/skycoin/skywire go 1.12 require ( + github.com/FiloSottile/vendorcheck v0.0.0-20160511012517-d6d54d1b5894 // indirect + github.com/Masterminds/semver v1.4.2 // indirect + github.com/Masterminds/vcs v1.13.0 // indirect + github.com/acroca/go-symbols v0.1.1 // indirect + github.com/armon/go-radix v1.0.0 // indirect github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 + github.com/boltdb/bolt v1.3.1 // indirect + github.com/calebthompson/ftree v0.2.0 // indirect + github.com/dvyukov/go-fuzz v0.0.0-20190402070214-9cfa592d5792 // indirect + github.com/elazarl/go-bindata-assetfs v1.0.0 // indirect + github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 // indirect + github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 // indirect github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 github.com/go-chi/chi v4.0.2+incompatible + github.com/go-delve/delve v1.2.0 // indirect + github.com/godbus/dbus v4.1.0+incompatible // indirect + github.com/golang/dep v0.5.1 // indirect + github.com/golangci/golangci-lint v1.16.0 // indirect github.com/google/uuid v1.1.1 github.com/gorilla/securecookie v1.1.1 github.com/gorilla/sessions v1.1.3 // indirect + github.com/hanwen/go-fuse v1.0.0 // indirect github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d - github.com/inconshreveable/mousetrap v1.0.0 // indirect - github.com/kr/pretty v0.1.0 // indirect + github.com/jmank88/nuts v0.3.0 // indirect + github.com/karrick/godirwalk v1.8.0 // indirect + github.com/keegancsmith/rpc v1.1.0 // indirect + github.com/kr/fs v0.1.0 // indirect github.com/kr/pty v1.1.3 github.com/mattn/go-colorable v0.1.1 // indirect + github.com/mattn/go-sqlite3 v1.10.0 // indirect + github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f // indirect github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b // indirect + github.com/mibk/dupl v1.0.0 // indirect github.com/mitchellh/go-homedir v1.1.0 + github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 // indirect + github.com/oniony/TMSU v0.7.4 // indirect + github.com/pkg/errors v0.8.1 // indirect + github.com/ramya-rao-a/go-outline v0.0.0-20181122025142-7182a932836a // indirect + github.com/rogpeppe/godef v1.1.1 // indirect + github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 // indirect github.com/sirupsen/logrus v1.4.0 // indirect github.com/skycoin/skycoin v0.25.1 github.com/spf13/cobra v0.0.3 github.com/spf13/pflag v1.0.3 // indirect + github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 // indirect + github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997 // indirect + github.com/stephens2424/writerset v1.0.2 // indirect github.com/stretchr/testify v1.3.0 + github.com/tools/godep v0.0.0-20180126220526-ce0bfadeb516 // indirect + github.com/uudashr/gopkgs v2.0.1+incompatible // indirect + github.com/visualfc/fastmod v0.0.0-20190131104758-c069a47540eb // indirect + github.com/visualfc/gocode v0.0.0-20190319040811-fab173807d9b // indirect + github.com/visualfc/gotools v0.0.0-20190218124934-6d12497f1bd5 // indirect go.etcd.io/bbolt v1.3.2 golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c + golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 // indirect golang.org/x/net v0.0.0-20190324223953-e3b2ff56ed87 golang.org/x/sys v0.0.0-20190322080309-f49334f85ddc // indirect golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04 // indirect - gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect ) diff --git a/go.sum b/go.sum index 2d4bbd63e1..fded45e4e4 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,127 @@ +9fans.net/go v0.0.0-20181112161441-237454027057 h1:OcHlKWkAMJEF1ndWLGxp5dnJQkYM/YImUOvsBoz6h5E= +9fans.net/go v0.0.0-20181112161441-237454027057/go.mod h1:diCsxrliIURU9xsYtjCp5AbpQKqdhKmf0ujWDUSkfoY= +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/FiloSottile/vendorcheck v0.0.0-20160511012517-d6d54d1b5894 h1:qPUfImRXGY2gSwpT9nsRkJ/k0NqhglgF0o8ks4dEOc0= +github.com/FiloSottile/vendorcheck v0.0.0-20160511012517-d6d54d1b5894/go.mod h1:6ESDF5hks2bKFzhGWxWv9Aalqz+Q2TJAPljRwfjpQ3A= +github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74= +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/vcs v1.13.0 h1:USF5TvZGYgIpcbNAEMLfFhHqP08tFZVlUVrmTSpqnyA= +github.com/Masterminds/vcs v1.13.0/go.mod h1:N09YCmOQr6RLxC6UNHzuVwAdodYbbnycGHSmwVJjcKA= +github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2 h1:HTOmFEEYrWi4MW5ZKUx6xfeyM10Sx3kQF65xiQJMPYA= +github.com/OpenPeeDeeP/depguard v0.0.0-20180806142446-a69c782687b2/go.mod h1:7/4sitnI9YlQgTLLk734QlzXT8DuHVnAyztLplQjk+o= +github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/acroca/go-symbols v0.1.1 h1:q3IzaMNYocw/Bnc2a8jkXf0hM3+POfLoq30x8HYuaPE= +github.com/acroca/go-symbols v0.1.1/go.mod h1:RKAIDWtcELAw6/wjNJGWRYZ7QEinSWoJeJ2H5cfK6AM= +github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/ayuryshev/arena v0.0.0-20190302131351-028723a0b932 h1:pM2ppQJfvZOznzaU9ZAX9EwzuOh/sKoMUVmR+ybiEIM= +github.com/boltdb/bolt v1.3.1 h1:JQmyP4ZBrce+ZQu0dY660FMfatumYDLun9hBCUVIkF4= +github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= +github.com/calebthompson/ftree v0.2.0 h1:jMwReBavkpqHUY9g0COuWTHxvqhRcpCo9Xh3Z2QnFUA= +github.com/calebthompson/ftree v0.2.0/go.mod h1:nh/clP4boz45PfchjE6vhCbYw5SdT18XNUkQWwgZvZo= +github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8= +github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ= +github.com/cpuguy83/go-md2man v1.0.8/go.mod h1:N6JayAiVKtlHSnuTCeuLSQVs75hb8q+dYQLjr7cDsKY= github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8= 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/derekparker/delve v1.2.0 h1:kxzyrc2Di/Yxw+sD+H2I9rzIqWjpTSB9Ve4guZpfG6Y= +github.com/dvyukov/go-fuzz v0.0.0-20190402070214-9cfa592d5792 h1:UJcJo+Ja8EnbMijmxI/i4fF9R2YrdK1TAPH8zNgJRSk= +github.com/dvyukov/go-fuzz v0.0.0-20190402070214-9cfa592d5792/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw= +github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk= +github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4= +github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607 h1:cTavhURetDkezJCvxFggiyLeP40Mrk/TtVg2+ycw1Es= +github.com/ernesto-jimenez/gogen v0.0.0-20180125220232-d7d4131e6607/go.mod h1:Cg4fM0vhYWOZdgM7RIOSTRNIc8/VT7CXClC3Ni86lu4= +github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= +github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= +github.com/fatih/color v1.6.0 h1:66qjqZk8kalYAvDRtM1AdAJQI0tj4Wrue3Eq3B3pmFU= +github.com/fatih/color v1.6.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6 h1:iXJdM8Uob6EPOG/PFr5q0J124ysiZdJfACHqICBb3b8= +github.com/fatih/gomodifytags v0.0.0-20180914191908-141225bf62b6/go.mod h1:p2/x7bnOQsbq/deXsDIlj2yLiKFGPkD2nuoYqwn8R4Y= +github.com/fatih/structtag v1.0.0 h1:pTHj65+u3RKWYPSGaU290FpI/dXxTaHdVwVwbcPKmEc= +github.com/fatih/structtag v1.0.0/go.mod h1:IKitwq45uXL/yqi5mYghiD3w9H6eTOvI9vnk8tXMphA= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6 h1:u/UEqS66A5ckRmS4yNpjmVH56sVtS/RfclBAYocb4as= github.com/flynn/noise v0.0.0-20180327030543-2492fe189ae6/go.mod h1:1i71OnUq3iUe1ma7Lr6yG6/rjvM3emb6yoL7xLFzcVQ= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-chi/chi v4.0.2+incompatible h1:maB6vn6FqCxrpz4FqWdh4+lwpyZIQS7YEAUcHlgXVRs= github.com/go-chi/chi v4.0.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ= +github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead h1:qwmAYufKDopQnFdeMw+iHJVxAd2CbF+VFKHyJJwnPKk= +github.com/go-critic/go-critic v0.0.0-20181204210945-ee9bf5809ead/go.mod h1:3MzXZKJdeXqdU9cj+rvZdNiN7SZ8V9OjybF8loZDmHU= +github.com/go-delve/delve v1.2.0 h1:uwGyfYO0WsWqbnDWvxCBKOr2qFLpii3tLxwM+fTJs70= +github.com/go-delve/delve v1.2.0/go.mod h1:yP+LD36s/ud5nm4lsQY0TwNhYu2PAwk6xItz+442j74= +github.com/go-lintpack/lintpack v0.5.2 h1:DI5mA3+eKdWeJ40nU4d6Wc26qmdG8RCi/btYq0TuRN0= +github.com/go-lintpack/lintpack v0.5.2/go.mod h1:NwZuYi2nUHho8XEIZ6SIxihrnPoqBTDqfpXvXAN0sXM= +github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= +github.com/go-toolsmith/astcast v0.0.0-20181028201508-b7a89ed70af1 h1:h+1eMw+tZAlgTVclcVN0/rdPaBI/RUzG0peblT6df+Q= +github.com/go-toolsmith/astcast v0.0.0-20181028201508-b7a89ed70af1/go.mod h1:TEo3Ghaj7PsZawQHxT/oBvo4HK/sl1RcuUHDKTTju+o= +github.com/go-toolsmith/astcopy v0.0.0-20180903214859-79b422d080c4 h1:wVs9OMjICHbAryp9hcIuWqUOi+NqEbUSZy9zMe3W//I= +github.com/go-toolsmith/astcopy v0.0.0-20180903214859-79b422d080c4/go.mod h1:c9CPdq2AzM8oPomdlPniEfPAC6g1s7NqZzODt8y6ib8= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6 h1:aTBUNRTatDDU24gbOEKEoLiDwxtc98ga6K/iMTm6fvs= +github.com/go-toolsmith/astequal v0.0.0-20180903214952-dcb477bfacd6/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086 h1:EIMuvbE9fbtQtimdLe5yeXjuC5CeKbQt8zH6GwtIrhM= +github.com/go-toolsmith/astfmt v0.0.0-20180903215011-8f8ee99c3086/go.mod h1:mP93XdblcopXwlyN4X4uodxXQhldPGZbcEJIimQHrkg= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30 h1:zRJPftZJNLPDiOtvYbFRwjSbaJAcVOf80TeEmWGe2kQ= +github.com/go-toolsmith/astp v0.0.0-20180903215135-0af7e3c24f30/go.mod h1:SV2ur98SGypH1UjcPpCatrV5hPazG6+IfNHbkDXBRrk= +github.com/go-toolsmith/pkgload v0.0.0-20181119091011-e9e65178eee8/go.mod h1:WoMrjiy4zvdS+Bg6z9jZH82QXwkcgCBX6nOfnmdaHks= +github.com/go-toolsmith/strparse v0.0.0-20180903215201-830b6daa1241 h1:ZRDeQioMGTBLeJxcPxXfFifEUgYxzR7fXw7w2WR+1bo= +github.com/go-toolsmith/strparse v0.0.0-20180903215201-830b6daa1241/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8= +github.com/go-toolsmith/typep v0.0.0-20181030061450-d63dc7650676 h1:6Qrsp0+25KEkaS2bB26UE0giFgRrIc8mYXboDL5OVMA= +github.com/go-toolsmith/typep v0.0.0-20181030061450-d63dc7650676/go.mod h1:JSQCQMUPdRlMZFswiq3TGpNp1GMktqkR2Ns5AIQkATU= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8= +github.com/godbus/dbus v4.1.0+incompatible h1:WqqLRTsQic3apZUK9qC5sGNfXthmPXzUZ7nQPrNITa4= +github.com/godbus/dbus v4.1.0+incompatible/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/golang/dep v0.5.1 h1:gKbgq5zV8Ec2fR54yr8fmFPK0v2idQ46kNZniWUtZsc= +github.com/golang/dep v0.5.1/go.mod h1:6RZ2Wai7dSWk7qL55sDYk+8UPFqcW7all2KDBraPPFA= +github.com/golang/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:14pqJU+T8BQvc6B0Ed0UJaMPoZR3WJ0P1NAIP+jjx9M= +github.com/golang/mock v1.1.1 h1:G5FRp8JnTd7RQH5kemVNlMeyXQAztQ3mOWV95KxsXH8= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 h1:23T5iq8rbUYlhpt5DB4XJkc6BU31uODLD1o1gKvZmD0= +github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2/go.mod h1:k9Qvh+8juN+UKMCS/3jFtGICgW8O96FVaZsaxdzDkR4= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM= +github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk= +github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6 h1:i2jIkQFb8RG45DuQs+ElyROY848cSJIoIkBM+7XXypA= +github.com/golangci/errcheck v0.0.0-20181003203344-ef45e06d44b6/go.mod h1:DbHgvLiFKX1Sh2T1w8Q/h4NAI8MHIpzCdnBUDTXU3I0= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613 h1:9kfjN3AdxcbsZBf8NjltjWihK2QfBBBZuv91cMFfDHw= +github.com/golangci/go-misc v0.0.0-20180628070357-927a3d87b613/go.mod h1:SyvUF2NxV+sN8upjjeVYr5W7tyxaT1JVtvhKhOn2ii8= +github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196 h1:9rtVlONXLF1rJZzvLt4tfOXtnAFUEhxCJ64Ibzj6ECo= +github.com/golangci/go-tools v0.0.0-20180109140146-af6baa5dc196/go.mod h1:unzUULGw35sjyOYjUt0jMTXqHlZPpPc6e+xfO4cd6mM= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3 h1:pe9JHs3cHHDQgOFXJJdYkK6fLz2PWyYtP4hthoCMvs8= +github.com/golangci/goconst v0.0.0-20180610141641-041c5f2b40f3/go.mod h1:JXrF4TWy4tXYn62/9x8Wm/K/dm06p8tCKwFRDPZG/1o= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee h1:J2XAy40+7yz70uaOiMbNnluTg7gyQhtGqLQncQh+4J8= +github.com/golangci/gocyclo v0.0.0-20180528134321-2becd97e67ee/go.mod h1:ozx7R9SIwqmqf5pRP90DhR2Oay2UIjGuKheCBCNwAYU= +github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98 h1:ir6/L2ZOJfFrJlOTsuf/hlzdPuUwXV/VzkSlgS6f1vs= +github.com/golangci/gofmt v0.0.0-20181105071733-0b8337e80d98/go.mod h1:9qCChq59u/eW8im404Q2WWTrnBUQKjpNYKMbU4M7EFU= +github.com/golangci/golangci-lint v1.16.0 h1:PcWAN9JHflZzJQaZVY1JXZE0Tgjq+jO2v4QLqJ/Azvw= +github.com/golangci/golangci-lint v1.16.0/go.mod h1:uySrAxrUmZYnxyccYSnwuAEm+3144Zg5IAUueIW8+fA= +github.com/golangci/golangci-lint/vendor/github.com/kisielk/gotool v0.0.0-20190402065613-de1d1ad903cd h1:xJwt2dkhFdX4mX27a38SBPvRMJIQhDJcx5/cmM0P/0I= +github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547 h1:qMomh8bv+kDazm1dSLZ9S3zZ2PJZMHL4ilfBjxFOlmI= +github.com/golangci/gosec v0.0.0-20180901114220-66fb7fc33547/go.mod h1:0qUabqiIQgfmlAmulqxyiGkkyF6/tOGSnY2cnPVwrzU= +github.com/golangci/ineffassign v0.0.0-20180808204949-2ee8f2867dde h1:qEGp3ZF1Qw6TkbWKn6GdJ12Ssu/CpJBaBcJ4hrUjrSo= +github.com/golangci/ineffassign v0.0.0-20180808204949-2ee8f2867dde/go.mod h1:e5tpTHCfVze+7EpLEozzMB3eafxo2KT5veNg1k6byQU= +github.com/golangci/lint-1 v0.0.0-20180610141402-4bf9709227d1 h1:PHK2kIh21Zt4IcG0bBRzQwEDVKF64LnkoSXnm8lfJUk= +github.com/golangci/lint-1 v0.0.0-20180610141402-4bf9709227d1/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca h1:kNY3/svz5T29MYHubXix4aDDuE3RWHkPvopM/EDv/MA= +github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca/go.mod h1:tvlJhZqDe4LMs4ZHD0oMUlt9G2LWuDGoisJTBzLMV9o= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770 h1:EL/O5HGrF7Jaq0yNhBLucz9hTuRzj2LdwGBOaENgxIk= +github.com/golangci/misspell v0.0.0-20180809174111-950f5d19e770/go.mod h1:dEbvlSfYbMQDtrpRMQU675gSDLDNa8sCPPChZ7PhiVA= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21 h1:leSNB7iYzLYSSx3J/s5sVf4Drkc68W2wm4Ixh/mr0us= +github.com/golangci/prealloc v0.0.0-20180630174525-215b22d4de21/go.mod h1:tf5+bzsHdTM0bsB7+8mt0GUMvjCgwLpTapNZHU8AajI= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0 h1:HVfrLniijszjS1aiNg8JbBMO2+E1WIQ+j/gL4SQqGPg= +github.com/golangci/revgrep v0.0.0-20180526074752-d9c87f5ffaf0/go.mod h1:qOQCunEYvmd/TLamH+7LlVccLvUH5kZNhbCgTHoBbp4= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 h1:zwtduBRr5SSWhqsYNgcuWO2kFlpdOZbP0+yRjmvPGys= +github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4/go.mod h1:Izgrg8RkN3rCIMLGE9CyYmU9pY2Jer6DgANEnZ/L/cQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= @@ -16,12 +130,29 @@ github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyC github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= github.com/gorilla/sessions v1.1.3 h1:uXoZdcdA5XdXF3QzuSlheVRUvjl+1rKY7zBXL68L9RU= github.com/gorilla/sessions v1.1.3/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= +github.com/hanwen/go-fuse v1.0.0 h1:GxS9Zrn6c35/BnfiVsZVWmsG803xwE7eVRDvcf/BEVc= +github.com/hanwen/go-fuse v1.0.0/go.mod h1:unqXarDXqzAk0rt98O2tVndEPIpUgLD9+rwFisZH3Ok= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno= +github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ= github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/ianthehat/godef v0.0.0-20181121200540-1ef9e6bcd64c h1:ilHBod5iwnm8drLfHO+B8z/k8drykf1jDFZgojTMrb0= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jmank88/nuts v0.3.0 h1:UZUboV1LXVkBUTHLRTEZrDfAL7QYgj9jEsBCiJHrxEM= +github.com/jmank88/nuts v0.3.0/go.mod h1:kTf5cyoLibZUQg9Lns/gteKO1d/5XrhacD1QVKviAKk= +github.com/karrick/godirwalk v1.8.0 h1:ycpSqVon/QJJoaT1t8sae0tp1Stg21j+dyuS7OoagcA= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/keegancsmith/rpc v1.1.0 h1:bXVRk3EzbtrEegTGKxNTc+St1lR7t/Z1PAO8misBnCc= +github.com/keegancsmith/rpc v1.1.0/go.mod h1:Xow74TKX34OPPiPCdz6x1o9c0SCxRqGxDuKGk7ZOo8s= +github.com/kisielk/gotool v0.0.0-20161130080628-0de1eaf82fa3/go.mod h1:jxZFDH7ILpTPQTk+E2s+z4CUas9lVNjIuKR4c5/zKgM= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -29,48 +160,181 @@ github.com/kr/pty v1.1.3 h1:/Um6a/ZmD5tF7peoOJ5oN5KMQ0DrGVQSXLNwyckutPk= github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= +github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54= +github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/mattn/go-colorable v0.0.0-20170327083344-ded68f7a9561/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-isatty v0.0.5 h1:tHXDdz1cpzGaovsTB+TVB8q90WEokoVmfMqoVcrLUgw= github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-sqlite3 v1.10.0 h1:jbhqpg7tQe4SupckyijYiy0mJJ/pRyHvXf7JdWK860o= +github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw= +github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f h1:ee+twVCignaZjt7jpbMSLxAeTN/Nfq9W/nm91E7QO1A= +github.com/mdempsky/gocode v0.0.0-20190203001940-7fb65232883f/go.mod h1:hltEC42XzfMNgg0S1v6JTywwra2Mu6F6cLR03debVQ8= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/mibk/dupl v1.0.0 h1:aZc3jqrF9n0tUHwHt/+jsRxA8cRgA0Gdl56M7W7PoqE= +github.com/mibk/dupl v1.0.0/go.mod h1:pCr4pNxxIbFGvtyCOi0c7LVjmV6duhKWV+ex5vh38ME= +github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 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/go-ps v0.0.0-20170309133038-4fdf99ab2936/go.mod h1:r1VsdOzOPt1ZSrGZWFoNhsAedKnEd6r9Np1+5blZCWk= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E= +github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mozilla/tls-observatory v0.0.0-20180409132520-8791a200eb40/go.mod h1:SrKMQvPiws7F7iqYp8/TX+IhxCYhzr6N/1yb8cwHsGk= +github.com/nbutton23/zxcvbn-go v0.0.0-20160627004424-a22cb81b2ecd/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663 h1:Ri1EhipkbhWsffPJ3IPlrb4SkTOPa2PfRXp3jchBczw= +github.com/nbutton23/zxcvbn-go v0.0.0-20171102151520-eafdab6b0663/go.mod h1:o96djdrsSGy3AWPyBgZMAGfxZNfgntdJG+11KU4QvbU= +github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443 h1:+2OJrU8cmOstEoh0uQvYemRGVH1O6xtO2oANUWHFnP0= +github.com/nightlyone/lockfile v0.0.0-20180618180623-0ad87eef1443/go.mod h1:JbxfV1Iifij2yhRjXai0oFrbpxszXHRx1E5RuM26o4Y= +github.com/oniony/TMSU v0.7.4 h1:vCogZaPLJc+4FL7oTt4aKtw0c8sIZMPfqsWfy9p9K+E= +github.com/oniony/TMSU v0.7.4/go.mod h1:e+jNOLsdK7K38lfBAwc/HFWE5Z2z7pcxhVTEVyvrAMk= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM= +github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b h1:8uaXtUkxiy+T/zdLWuxa/PG4so0TPZDZfafFNNSaptE= +github.com/peterh/liner v0.0.0-20170317030525-88609521dc4b/go.mod h1:xIteQHvHuaLYG9IFj6mSxM0fCKrs34IrEQUhOYuGPHc= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v0.0.0-20170413231811-06b906832ed0/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= 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/ramya-rao-a/go-outline v0.0.0-20181122025142-7182a932836a h1:rJS9v8WlLfIQ/22PlTXc47p5jB8RaY9XnTkX8Uols7w= +github.com/ramya-rao-a/go-outline v0.0.0-20181122025142-7182a932836a/go.mod h1:1WL5IqM+CnRCAbXetRnL1YVoS9KtU2zMhOi/5oAVPo4= +github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/godef v1.1.1 h1:NujOtt9q9vIClRTB3sCZpavac+NMRaIayzrcz1h4fSE= +github.com/rogpeppe/godef v1.1.1/go.mod h1:oEo1eMy1VUEHUzUIX4F7IqvMJRiz9UId44mvnR8oPlQ= +github.com/russross/blackfriday v0.0.0-20180428102519-11635eb403ff/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/ryanuber/go-glob v0.0.0-20170128012129-256dc444b735/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc= +github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353 h1:tnWWLf0nI2TI62Wd/ZOea4XYqE+y1sf2pdm+VItsc0c= +github.com/sdboyer/constext v0.0.0-20170321163424-836a14457353/go.mod h1:5HStXbIikwtDAgAIqiQIqVgMn7mlvZa6PTpwiAVYGYg= +github.com/shirou/gopsutil v0.0.0-20180427012116-c95755e4bcd7/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/sirupsen/logrus v0.0.0-20180523074243-ea8897e79973/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.5/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= 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/skycoin/skycoin v0.25.1 h1:4tYXHiTX00pFydjUWHmqSnAYfrJ59xsa9pfmEu7tki8= github.com/skycoin/skycoin v0.25.1/go.mod h1:78nHjQzd8KG0jJJVL/j0xMmrihXi70ti63fh8vXScJw= +github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4= +github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg= +github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cobra v0.0.0-20170417170307-b6cb39589372/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.2/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig= +github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170417173400-9e4c21054fa1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= 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.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso= +github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518 h1:iD+PFTQwKEmbwSdwfvP5ld2WEI/g7qbdhmHJ2ASfYGs= +github.com/sqs/goreturns v0.0.0-20181028201513-538ac6014518/go.mod h1:CKI4AZ4XmGV240rTHfO0hfE83S6/a3/Q1siZJ/vXf7A= +github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997 h1:LF81AGV63kJoxjmSgQPT8FARAMHeY46CYQ4TNoVDWHM= +github.com/stamblerre/gocode v0.0.0-20190327203809-810592086997/go.mod h1:EM2T8YDoTCvGXbEpFHxarbpv7VE26QD1++Cb1Pbh7Gs= +github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8= +github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1 h1:2vfRuCMp5sSVIDSqO8oNnWJq7mPa6KVP3iPIwFBuy8A= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/tools/godep v0.0.0-20180126220526-ce0bfadeb516 h1:h4a8ZFxjlRVGsFGP4l/AdnoUYcF3pfxzyepS3oKZ8mE= +github.com/tools/godep v0.0.0-20180126220526-ce0bfadeb516/go.mod h1:OGh2HQGYVW+2+ZdB+DgJhI75kivkKWtVcIxI/pesDsY= +github.com/uudashr/gopkgs v2.0.1+incompatible h1:SuNs9p/XbGcQezR7SguZrZzxqCQozxtd/N8UKBWbWjk= +github.com/uudashr/gopkgs v2.0.1+incompatible/go.mod h1:MtCdKVJkxW7hNKWXPNWfpaeEp8+Ml3Q8myb4yWhn2Hg= +github.com/visualfc/fastmod v0.0.0-20190131104758-c069a47540eb h1:SsMc1pWQd9VeSFNtMJi5Y4DIhAt50JgA5e+a/1JvrNM= +github.com/visualfc/fastmod v0.0.0-20190131104758-c069a47540eb/go.mod h1:qklLSKOIVNIn5SdfGlics3Ljx4kOC29a2gwOS+NjDnQ= +github.com/visualfc/gocode v0.0.0-20190319040811-fab173807d9b h1:4mjeewpDQGUq1j+8tZFaM7Zi7niKRGg4QQdzdd0sJg4= +github.com/visualfc/gocode v0.0.0-20190319040811-fab173807d9b/go.mod h1:jpZqxVZ6mC+EjvX9RrJnIKbM40y/DSWI8bisCpS8Tfw= +github.com/visualfc/gotools v0.0.0-20190218124934-6d12497f1bd5 h1:2HHOotezuYHn1MPMA8CwQtlHNCXDP4byVAChu8tPeoQ= +github.com/visualfc/gotools v0.0.0-20190218124934-6d12497f1bd5/go.mod h1:L4JCuK2/abY1jUXNDGB3gS9uWSQut22gKWkTe1R8b70= go.etcd.io/bbolt v1.3.2 h1:Z/90sZLPOeCy2PwprqkFa25PdkusRzaj9P8zm/KNyvk= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +golang.org/x/arch v0.0.0-20171004143515-077ac972c2e4 h1:TP7YcWHbnFq4v8/3wM2JwgM0SRRtsYJ7Z6Oj0arz2bs= +golang.org/x/arch v0.0.0-20171004143515-077ac972c2e4/go.mod h1:cYlCBUl1MsqxdiKgmc4uh7TxZfWSFLOGSRR090WDxt8= +golang.org/x/crypto v0.0.0-20180614174826-fd5f17ee7299/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/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-20190313024323-a1f597ede03a/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/crypto v0.0.0-20190404164418-38d8ce5564a5 h1:bselrhR0Or1vomJZC8ZIjWtbDmn9OYFLX5Ik9alpJpE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3 h1:XQyxROzUlZH+WIQwySDgnISgOivlhjIEwaQaJEJrrN0= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20170915142106-8351a756f30f/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-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190324223953-e3b2ff56ed87 h1:yh5/K199RObPR6zqVBYf+AyJuweAqx+fOe9s3cekn1Y= golang.org/x/net v0.0.0-20190324223953-e3b2ff56ed87/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20171026204733-164713f0dfce/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180614134839-8883426083c0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/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-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 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/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998= +golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20170915040203-e531a2a1c15f/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180824175216-6c1c5e93cdc1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181117154741-2ddaf7f79a09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181120060634-fc4f04983f62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181130195746-895048a75ecf/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181205014116-22934f0fdb62/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190121143147-24cd39ecf745/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190314010720-f0bfdbff1f9c/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04 h1:SRYGE+BqJRgY8JH4p2NmwTPeuREKqKYw5IuEmthTHKQ= golang.org/x/tools v0.0.0-20190325223049-1d95b17f1b04/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190405141802-1058ed41f41a h1:EFFVpQu3uyLJ7paXZH/6lwZFSJuU5p8ktVXrfcXng6g= +golang.org/x/tools v0.0.0-20190405180640-052fc3cfdbc2 h1:rlaAa9eBBj6AI2C90gKs2Q/XF6YFbBDpGSX+npdfPlk= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170407172122-cd8b52f8269e/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed h1:WX1yoOaKQfddO/mLzdV4wptyWgoH/6hwLs7QHTixo0I= +mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed/go.mod h1:Xkxe497xwlCKkIaQYRfC7CSLworTXY9RMqwhhCm+8Nc= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b h1:DxJ5nJdkhDlLok9K6qO+5290kphDJbHOQO1DFFFTeBo= +mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b/go.mod h1:2odslEg/xrtNQqCYg2/jCoyKnw3vv5biOc3JnIcYfL4= +mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34 h1:B1LAOfRqg2QUyCdzfjf46quTSYUTAK5OCwbh6pljHbM= +mvdan.cc/unparam v0.0.0-20190124213536-fbb59629db34/go.mod h1:H6SUd1XjIs+qQCyskXg5OFSrilMRUkD8ePJpHKDPaeY= +sourcegraph.com/sourcegraph/go-diff v0.5.1-0.20190210232911-dee78e514455 h1:qoQ5Kt+Zm+GXBtz49YwD3juBhr/E0U25jO6bBzxW6NI= +sourcegraph.com/sourcegraph/go-diff v0.5.1-0.20190210232911-dee78e514455/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4 h1:JPJh2pk3+X4lXAkZIk2RuE/7/FoK9maXw+TNPJhVS/c= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/pkg/app/packet.go b/pkg/app/packet.go index fe00c6892a..aff7522375 100644 --- a/pkg/app/packet.go +++ b/pkg/app/packet.go @@ -6,6 +6,21 @@ import ( "github.com/skycoin/skywire/pkg/cipher" ) +// Addr implements net.Addr for App connections. +type Addr struct { + PubKey cipher.PubKey `json:"pk"` + Port uint16 `json:"port"` +} + +// Network returns custom skywire Network type. +func (addr *Addr) Network() string { + return "skywire" +} + +func (addr *Addr) String() string { + return fmt.Sprintf("%s:%d", addr.PubKey, addr.Port) +} + // LoopAddr stores addressing parameters of a loop packets. type LoopAddr struct { Port uint16 `json:"port"` @@ -21,18 +36,3 @@ type Packet struct { Addr *LoopAddr `json:"addr"` Payload []byte `json:"payload"` } - -// Addr implements net.Addr for App connections. -type Addr struct { - PubKey cipher.PubKey `json:"pk"` - Port uint16 `json:"port"` -} - -// Network returns custom skywire Network type. -func (addr *Addr) Network() string { - return "skywire" -} - -func (addr *Addr) String() string { - return fmt.Sprintf("%s:%d", addr.PubKey, addr.Port) -} diff --git a/pkg/app/packet_test.go b/pkg/app/packet_test.go new file mode 100644 index 0000000000..7e7b6f1f8e --- /dev/null +++ b/pkg/app/packet_test.go @@ -0,0 +1,21 @@ +package app + +import ( + "fmt" + + "github.com/skycoin/skywire/pkg/cipher" +) + +func ExamplePacket() { + pk := cipher.PubKey{} + addr := Addr{pk, 0} + loopAddr := LoopAddr{0, addr} + + fmt.Println(addr.Network()) + fmt.Printf("%v\n", addr) + fmt.Printf("%v\n", loopAddr) + + //Output: skywire + // {000000000000000000000000000000000000000000000000000000000000000000 0} + // {0 {000000000000000000000000000000000000000000000000000000000000000000 0}} +} diff --git a/pkg/messaging/channel.go b/pkg/messaging/channel.go index e8b97b1fec..a14a67583f 100644 --- a/pkg/messaging/channel.go +++ b/pkg/messaging/channel.go @@ -10,6 +10,7 @@ import ( "github.com/skycoin/skywire/internal/ioutil" "github.com/skycoin/skywire/internal/noise" "github.com/skycoin/skywire/pkg/cipher" + "github.com/skycoin/skywire/pkg/transport" ) type channel struct { @@ -29,6 +30,11 @@ type channel struct { noise *noise.Noise } +// Edges returns the public keys of the channel's edge nodes +func (c *channel) Edges() [2]cipher.PubKey { + return transport.SortPubKeys(c.link.Local(), c.remotePK) +} + func newChannel(initiator bool, secKey cipher.SecKey, remote cipher.PubKey, link *Link) (*channel, error) { noiseConf := noise.Config{ LocalSK: secKey, @@ -123,14 +129,6 @@ func (c *channel) Close() error { return nil } -func (c *channel) Local() cipher.PubKey { - return c.link.Local() -} - -func (c *channel) Remote() cipher.PubKey { - return c.remotePK -} - func (c *channel) SetDeadline(t time.Time) error { c.deadline = t return nil diff --git a/pkg/messaging/channel_test.go b/pkg/messaging/channel_test.go index c519f18161..aa117408af 100644 --- a/pkg/messaging/channel_test.go +++ b/pkg/messaging/channel_test.go @@ -137,7 +137,7 @@ func handshakeChannel(t *testing.T, c *channel, pk cipher.PubKey, sk cipher.SecK noiseConf := noise.Config{ LocalSK: sk, LocalPK: pk, - RemotePK: c.Local(), + RemotePK: c.link.Local(), Initiator: false, } diff --git a/pkg/messaging/client.go b/pkg/messaging/client.go index 8ae8940d06..d829002176 100644 --- a/pkg/messaging/client.go +++ b/pkg/messaging/client.go @@ -49,6 +49,7 @@ type Config struct { } // Client sends messages to remote client nodes via relay Server. +// Implements Transport type Client struct { Logger *logging.Logger diff --git a/pkg/node/node.go b/pkg/node/node.go index e4127bc6ac..a294a30543 100644 --- a/pkg/node/node.go +++ b/pkg/node/node.go @@ -332,7 +332,7 @@ func (node *Node) SpawnApp(config *AppConfig, startCh chan<- struct{}) error { node.startedMu.Lock() if node.startedApps[config.App] != nil { node.startedMu.Unlock() - return fmt.Errorf("App %s is already started", config.App) + return fmt.Errorf("app %s is already started", config.App) } node.startedApps[config.App] = bind diff --git a/pkg/node/node_test.go b/pkg/node/node_test.go index 5edef3b8e5..40085ce754 100644 --- a/pkg/node/node_test.go +++ b/pkg/node/node_test.go @@ -136,7 +136,7 @@ func TestNodeSpawnAppValidations(t *testing.T) { err string }{ {&AppConfig{App: "chat", Version: "1.0", Port: 2}, "can't bind to reserved port 2"}, - {&AppConfig{App: "chat", Version: "1.0", Port: 10}, "App chat is already started"}, + {&AppConfig{App: "chat", Version: "1.0", Port: 10}, "app chat is already started"}, {&AppConfig{App: "foo", Version: "1.0", Port: 11}, "failed to run app executable: foo"}, } diff --git a/pkg/node/rpc.go b/pkg/node/rpc.go index 876577d775..4ab05069b9 100644 --- a/pkg/node/rpc.go +++ b/pkg/node/rpc.go @@ -47,17 +47,22 @@ type TransportSummary struct { Log *transport.LogEntry `json:"log,omitempty"` } -func newTransportSummary(tp *transport.ManagedTransport, includeLogs bool) *TransportSummary { - summary := TransportSummary{ +func newTransportSummary(tm *transport.Manager, tp *transport.ManagedTransport, includeLogs bool) *TransportSummary { + remote, ok := tm.Remote(tp.Edges()) + if !ok { + return &TransportSummary{} + } + + summary := &TransportSummary{ ID: tp.ID, - Local: tp.Local(), - Remote: tp.Remote(), + Local: tm.Local(), + Remote: remote, Type: tp.Type(), } if includeLogs { summary.Log = tp.LogEntry } - return &summary + return summary } // Summary provides a summary of an AppNode. @@ -74,7 +79,7 @@ type Summary struct { func (r *RPC) Summary(_ *struct{}, out *Summary) error { var summaries []*TransportSummary r.node.tm.WalkTransports(func(tp *transport.ManagedTransport) bool { - summaries = append(summaries, newTransportSummary(tp, false)) + summaries = append(summaries, newTransportSummary(r.node.tm, tp, false)) return true }) *out = Summary{ @@ -161,10 +166,13 @@ func (r *RPC) Transports(in *TransportsIn, out *[]*TransportSummary) error { return true } r.node.tm.WalkTransports(func(tp *transport.ManagedTransport) bool { - if typeIncluded(tp.Type()) && pkIncluded(tp.Local(), tp.Remote()) { - *out = append(*out, newTransportSummary(tp, in.ShowLogs)) + if remote, ok := r.node.tm.Remote(tp.Edges()); ok { + if typeIncluded(tp.Type()) && pkIncluded(r.node.tm.Local(), remote) { + *out = append(*out, newTransportSummary(r.node.tm, tp, in.ShowLogs)) + } + return true } - return true + return false }) return nil } @@ -175,7 +183,7 @@ func (r *RPC) Transport(in *uuid.UUID, out *TransportSummary) error { if tp == nil { return ErrNotFound } - *out = *newTransportSummary(tp, true) + *out = *newTransportSummary(r.node.tm, tp, true) return nil } @@ -200,7 +208,7 @@ func (r *RPC) AddTransport(in *AddTransportIn, out *TransportSummary) error { if err != nil { return err } - *out = *newTransportSummary(tp, false) + *out = *newTransportSummary(r.node.tm, tp, false) return nil } diff --git a/pkg/node/rpc_client.go b/pkg/node/rpc_client.go index 9b5c37c612..e2e1f6374e 100644 --- a/pkg/node/rpc_client.go +++ b/pkg/node/rpc_client.go @@ -191,7 +191,7 @@ func NewMockRPCClient(r *rand.Rand, maxTps int, maxRules int) (cipher.PubKey, RP for i := range tps { remotePK, _ := cipher.GenerateKeyPair() tps[i] = &TransportSummary{ - ID: uuid.New(), + ID: transport.MakeTransportID(localPK, remotePK, types[r.Int()%len(types)], true), Local: localPK, Remote: remotePK, Type: types[r.Int()%len(types)], @@ -361,7 +361,7 @@ func (mc *mockRPCClient) Transport(tid uuid.UUID) (*TransportSummary, error) { // AddTransport implements RPCClient. func (mc *mockRPCClient) AddTransport(remote cipher.PubKey, tpType string, public bool, _ time.Duration) (*TransportSummary, error) { summary := &TransportSummary{ - ID: uuid.New(), + ID: transport.MakeTransportID(mc.s.PubKey, remote, tpType, public), Local: mc.s.PubKey, Remote: remote, Type: tpType, diff --git a/pkg/node/rpc_test.go b/pkg/node/rpc_test.go index 26569791be..a374cd6651 100644 --- a/pkg/node/rpc_test.go +++ b/pkg/node/rpc_test.go @@ -14,8 +14,6 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/skycoin/skywire/pkg/cipher" - "github.com/skycoin/skywire/pkg/routing" "github.com/skycoin/skywire/pkg/transport" ) @@ -88,39 +86,12 @@ func TestStartStopApp(t *testing.T) { node.startedMu.Unlock() } -func mockTransportManagers() (pk1, pk2 cipher.PubKey, m1, m2 *transport.Manager, errCh chan error, err error) { - discovery := transport.NewDiscoveryMock() - logs := transport.InMemoryTransportLogStore() - - var sk1, sk2 cipher.SecKey - pk1, sk1 = cipher.GenerateKeyPair() - pk2, sk2 = cipher.GenerateKeyPair() - - c1 := &transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: discovery, LogStore: logs} - c2 := &transport.ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: discovery, LogStore: logs} - - f1, f2 := transport.NewMockFactory(pk1, pk2) - - if m1, err = transport.NewManager(c1, f1); err != nil { - return - } - if m2, err = transport.NewManager(c2, f2); err != nil { - return - } - - errCh = make(chan error) - go func() { errCh <- m1.Serve(context.TODO()) }() - go func() { errCh <- m2.Serve(context.TODO()) }() - - return -} - func TestRPC(t *testing.T) { r := new(mockRouter) executer := new(MockExecuter) defer os.RemoveAll("chat") - pk1, _, tm1, tm2, errCh, err := mockTransportManagers() + pk1, _, tm1, tm2, errCh, err := transport.MockTransportManagersPair() require.NoError(t, err) defer func() { require.NoError(t, tm1.Close()) diff --git a/pkg/route-finder/client/mock.go b/pkg/route-finder/client/mock.go index 25b2ace7b9..57914181e3 100644 --- a/pkg/route-finder/client/mock.go +++ b/pkg/route-finder/client/mock.go @@ -1,10 +1,9 @@ package client import ( - "github.com/google/uuid" - "github.com/skycoin/skywire/pkg/cipher" "github.com/skycoin/skywire/pkg/routing" + "github.com/skycoin/skywire/pkg/transport" ) // MockClient implements mock route finder client. @@ -23,7 +22,7 @@ func (r *mockClient) SetError(err error) { r.err = err } -// PairedRoutes implements Clien for MockClient +// PairedRoutes implements Client for MockClient func (r *mockClient) PairedRoutes(src, dst cipher.PubKey, minHops, maxHops uint16) ([]routing.Route, []routing.Route, error) { if r.err != nil { return nil, nil, r.err @@ -34,7 +33,7 @@ func (r *mockClient) PairedRoutes(src, dst cipher.PubKey, minHops, maxHops uint1 &routing.Hop{ From: src, To: dst, - Transport: uuid.New(), + Transport: transport.MakeTransportID(src, dst, "", true), }, }, }, []routing.Route{ @@ -42,7 +41,7 @@ func (r *mockClient) PairedRoutes(src, dst cipher.PubKey, minHops, maxHops uint1 &routing.Hop{ From: src, To: dst, - Transport: uuid.New(), + Transport: transport.MakeTransportID(src, dst, "", true), }, }, }, nil diff --git a/pkg/router/route_manager.go b/pkg/router/route_manager.go index 7cc62f9db5..7163690fd7 100644 --- a/pkg/router/route_manager.go +++ b/pkg/router/route_manager.go @@ -74,9 +74,9 @@ func (rm *routeManager) RemoveLoopRule(addr *app.LoopAddr) error { } func (rm *routeManager) Serve(rw io.ReadWriter) error { - proto := setup.NewProtocol(rw) - + proto := setup.NewSetupProtocol(rw) t, body, err := proto.ReadPacket() + if err != nil { fmt.Println("err:", err) return err @@ -98,11 +98,13 @@ func (rm *routeManager) Serve(rw io.ReadWriter) error { } if err != nil { + rm.Logger.Infof("Setup request with type %s failed: %s", t, err) return proto.WritePacket(setup.RespFailure, err.Error()) } return proto.WritePacket(setup.RespSuccess, respBody) + } func (rm *routeManager) addRoutingRules(data []byte) ([]routing.RouteID, error) { diff --git a/pkg/router/route_manager_test.go b/pkg/router/route_manager_test.go index 8784549014..4d96b98f7e 100644 --- a/pkg/router/route_manager_test.go +++ b/pkg/router/route_manager_test.go @@ -79,7 +79,7 @@ func TestRouteManagerAddRemoveRule(t *testing.T) { errCh <- rm.Serve(out) }() - proto := setup.NewProtocol(in) + proto := setup.NewSetupProtocol(in) rule := routing.ForwardRule(time.Now(), 3, uuid.New()) id, err := setup.AddRule(proto, rule) @@ -105,7 +105,7 @@ func TestRouteManagerDeleteRules(t *testing.T) { errCh <- rm.Serve(out) }() - proto := setup.NewProtocol(in) + proto := setup.NewSetupProtocol(in) rule := routing.ForwardRule(time.Now(), 3, uuid.New()) id, err := rt.AddRule(rule) @@ -140,7 +140,7 @@ func TestRouteManagerConfirmLoop(t *testing.T) { errCh <- rm.Serve(out) }() - proto := setup.NewProtocol(in) + proto := setup.NewSetupProtocol(in) pk, _ := cipher.GenerateKeyPair() rule := routing.AppRule(time.Now(), 3, pk, 3, 2) require.NoError(t, rt.SetRule(2, rule)) @@ -185,7 +185,7 @@ func TestRouteManagerLoopClosed(t *testing.T) { errCh <- rm.Serve(out) }() - proto := setup.NewProtocol(in) + proto := setup.NewSetupProtocol(in) pk, _ := cipher.GenerateKeyPair() diff --git a/pkg/router/router.go b/pkg/router/router.go index 21c2f38740..2a5c3d5f1e 100644 --- a/pkg/router/router.go +++ b/pkg/router/router.go @@ -137,8 +137,8 @@ func (r *Router) ServeApp(conn net.Conn, port uint16, appConf *app.Config) error r.wg.Add(1) defer r.wg.Done() - proto := app.NewProtocol(conn) - if err := r.pm.Open(port, proto); err != nil { + appProto := app.NewProtocol(conn) + if err := r.pm.Open(port, appProto); err != nil { return err } @@ -151,12 +151,12 @@ func (r *Router) ServeApp(conn net.Conn, port uint16, appConf *app.Config) error CloseLoop: r.closeLoop, Forward: r.forwardAppPacket, } - am := &appManager{r.Logger, proto, appConf, callbacks} + am := &appManager{r.Logger, appProto, appConf, callbacks} err := am.Serve() - for _, port := range r.pm.AppPorts(proto) { + for _, port := range r.pm.AppPorts(appProto) { for _, addr := range r.pm.Close(port) { - r.closeLoop(proto, &app.LoopAddr{Port: port, Remote: addr}) // nolint: errcheck + r.closeLoop(appProto, &app.LoopAddr{Port: port, Remote: addr}) // nolint: errcheck } } @@ -426,20 +426,18 @@ func (r *Router) destroyLoop(addr *app.LoopAddr) error { return r.rm.RemoveLoopRule(addr) } -func (r *Router) setupProto(ctx context.Context) (proto *setup.Protocol, tr transport.Transport, err error) { +func (r *Router) setupProto(ctx context.Context) (*setup.Protocol, transport.Transport, error) { if len(r.config.SetupNodes) == 0 { - err = errors.New("route setup: no nodes") - return + return nil, nil, errors.New("route setup: no nodes") } - tr, err = r.tm.CreateTransport(ctx, r.config.SetupNodes[0], "messaging", false) + tr, err := r.tm.CreateTransport(ctx, r.config.SetupNodes[0], "messaging", false) if err != nil { - err = fmt.Errorf("transport: %s", err) - return + return nil, nil, fmt.Errorf("transport: %s", err) } - proto = setup.NewProtocol(tr) - return + sProto := setup.NewSetupProtocol(tr) + return sProto, tr, nil } func (r *Router) fetchBestRoutes(source, destination cipher.PubKey) (routing.Route, routing.Route, error) { @@ -485,7 +483,8 @@ func (r *Router) advanceNoiseHandshake(addr *app.LoopAddr, noiseMsg []byte) (ni func (r *Router) isSetupTransport(tr transport.Transport) bool { for _, pk := range r.config.SetupNodes { - if tr.Remote() == pk { + remote, ok := r.tm.Remote(tr.Edges()) + if ok && (remote == pk) { return true } } diff --git a/pkg/router/router_test.go b/pkg/router/router_test.go index 4b56caf9f9..1134071392 100644 --- a/pkg/router/router_test.go +++ b/pkg/router/router_test.go @@ -41,8 +41,8 @@ func TestRouterForwarding(t *testing.T) { c2 := &transport.ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: client, LogStore: logStore} c3 := &transport.ManagerConfig{PubKey: pk3, SecKey: sk3, DiscoveryClient: client, LogStore: logStore} - f1, f2 := transport.NewMockFactory(pk1, pk2) - f3, f4 := transport.NewMockFactory(pk2, pk3) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) + f3, f4 := transport.NewMockFactoryPair(pk2, pk3) f3.SetType("mock2") f4.SetType("mock2") @@ -144,7 +144,7 @@ func TestRouterApp(t *testing.T) { c1 := &transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client, LogStore: logStore} c2 := &transport.ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: client, LogStore: logStore} - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) m1, err := transport.NewManager(c1, f1) require.NoError(t, err) @@ -278,7 +278,7 @@ func TestRouterSetup(t *testing.T) { c1 := &transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client, LogStore: logStore} c2 := &transport.ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: client, LogStore: logStore} - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) m1, err := transport.NewManager(c1, f1) require.NoError(t, err) @@ -302,21 +302,21 @@ func TestRouterSetup(t *testing.T) { tr, err := m2.CreateTransport(context.TODO(), pk1, "mock", false) require.NoError(t, err) - sProto := setup.NewProtocol(tr) + sProto := setup.NewSetupProtocol(tr) rw1, rwIn1 := net.Pipe() go r.ServeApp(rwIn1, 2, &app.Config{}) // nolint: errcheck - proto1 := app.NewProtocol(rw1) + appProto1 := app.NewProtocol(rw1) dataCh := make(chan []byte) - go proto1.Serve(func(_ app.Frame, p []byte) (interface{}, error) { // nolint: errcheck,unparam + go appProto1.Serve(func(_ app.Frame, p []byte) (interface{}, error) { // nolint: errcheck,unparam go func() { dataCh <- p }() return nil, nil }) rw2, rwIn2 := net.Pipe() go r.ServeApp(rwIn2, 4, &app.Config{}) // nolint: errcheck - proto2 := app.NewProtocol(rw2) - go proto2.Serve(func(_ app.Frame, p []byte) (interface{}, error) { // nolint: errcheck,unparam + appProto2 := app.NewProtocol(rw2) + go appProto2.Serve(func(_ app.Frame, p []byte) (interface{}, error) { // nolint: errcheck,unparam go func() { dataCh <- p }() return nil, nil }) @@ -332,7 +332,7 @@ func TestRouterSetup(t *testing.T) { assert.Equal(t, tr.ID, rule.TransportID()) }) - t.Run("confirm loop - responder", func(t *testing.T) { + t.Run("`confirm loop - responder", func(t *testing.T) { confI := noise.Config{ LocalSK: sk2, LocalPK: pk2, @@ -464,7 +464,7 @@ func TestRouterSetupLoop(t *testing.T) { pk1, sk1 := cipher.GenerateKeyPair() pk2, sk2 := cipher.GenerateKeyPair() - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) f1.SetType("messaging") f2.SetType("messaging") @@ -490,7 +490,7 @@ func TestRouterSetupLoop(t *testing.T) { acceptCh, _ := m2.Observe() tr := <-acceptCh - proto := setup.NewProtocol(tr) + proto := setup.NewSetupProtocol(tr) p, data, err := proto.ReadPacket() if err != nil { errCh <- err @@ -518,11 +518,11 @@ func TestRouterSetupLoop(t *testing.T) { rw, rwIn := net.Pipe() go r.ServeApp(rwIn, 5, &app.Config{}) // nolint: errcheck - proto := app.NewProtocol(rw) - go proto.Serve(nil) // nolint: errcheck + appProto := app.NewProtocol(rw) + go appProto.Serve(nil) // nolint: errcheck addr := &app.Addr{} - require.NoError(t, proto.Send(app.FrameCreateLoop, &app.Addr{PubKey: pk2, Port: 6}, addr)) + require.NoError(t, appProto.Send(app.FrameCreateLoop, &app.Addr{PubKey: pk2, Port: 6}, addr)) require.NoError(t, <-errCh) ll, err := r.pm.GetLoop(10, &app.Addr{PubKey: pk2, Port: 6}) @@ -567,7 +567,7 @@ func TestRouterCloseLoop(t *testing.T) { pk2, sk2 := cipher.GenerateKeyPair() pk3, _ := cipher.GenerateKeyPair() - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) f1.SetType("messaging") m1, err := transport.NewManager(&transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client, LogStore: logStore}, f1) @@ -596,7 +596,7 @@ func TestRouterCloseLoop(t *testing.T) { acceptCh, _ := m2.Observe() tr := <-acceptCh - proto := setup.NewProtocol(tr) + proto := setup.NewSetupProtocol(tr) p, data, err := proto.ReadPacket() if err != nil { errCh <- err @@ -655,7 +655,7 @@ func TestRouterCloseLoopOnAppClose(t *testing.T) { pk2, sk2 := cipher.GenerateKeyPair() pk3, _ := cipher.GenerateKeyPair() - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) f1.SetType("messaging") m1, err := transport.NewManager(&transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client, LogStore: logStore}, f1) @@ -684,7 +684,7 @@ func TestRouterCloseLoopOnAppClose(t *testing.T) { acceptCh, _ := m2.Observe() tr := <-acceptCh - proto := setup.NewProtocol(tr) + proto := setup.NewSetupProtocol(tr) p, data, err := proto.ReadPacket() if err != nil { errCh <- err @@ -741,7 +741,7 @@ func TestRouterCloseLoopOnRouterClose(t *testing.T) { pk2, sk2 := cipher.GenerateKeyPair() pk3, _ := cipher.GenerateKeyPair() - f1, f2 := transport.NewMockFactory(pk1, pk2) + f1, f2 := transport.NewMockFactoryPair(pk1, pk2) f1.SetType("messaging") m1, err := transport.NewManager(&transport.ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client, LogStore: logStore}, f1) @@ -770,7 +770,7 @@ func TestRouterCloseLoopOnRouterClose(t *testing.T) { acceptCh, _ := m2.Observe() tr := <-acceptCh - proto := setup.NewProtocol(tr) + proto := setup.NewSetupProtocol(tr) p, data, err := proto.ReadPacket() if err != nil { errCh <- err @@ -798,8 +798,8 @@ func TestRouterCloseLoopOnRouterClose(t *testing.T) { rw, rwIn := net.Pipe() go r.ServeApp(rwIn, 5, &app.Config{}) // nolint: errcheck - proto := app.NewProtocol(rw) - go proto.Serve(nil) // nolint: errcheck + appProto := app.NewProtocol(rw) + go appProto.Serve(nil) // nolint: errcheck time.Sleep(100 * time.Millisecond) diff --git a/pkg/setup/protocol.go b/pkg/setup/protocol.go index c3ff773661..8acb32691e 100644 --- a/pkg/setup/protocol.go +++ b/pkg/setup/protocol.go @@ -68,8 +68,8 @@ type Protocol struct { rw io.ReadWriter } -// NewProtocol constructs a new Protocol. -func NewProtocol(rw io.ReadWriter) *Protocol { +// NewSetupProtocol constructs a new setup Protocol. +func NewSetupProtocol(rw io.ReadWriter) *Protocol { return &Protocol{rw} } diff --git a/pkg/setup/protocol_test.go b/pkg/setup/protocol_test.go index 6bb37bc6c3..a27b6941ed 100644 --- a/pkg/setup/protocol_test.go +++ b/pkg/setup/protocol_test.go @@ -2,6 +2,7 @@ package setup import ( "encoding/json" + "fmt" "net" "testing" @@ -9,10 +10,20 @@ import ( "github.com/stretchr/testify/require" ) +func ExampleNewSetupProtocol() { + in, _ := net.Pipe() + defer in.Close() + + sProto := NewSetupProtocol(in) + fmt.Printf("Success: %v\n", sProto != nil) + + // Output: Success: true +} + func TestNewProtocol(t *testing.T) { connA, connB := net.Pipe() - protoA := NewProtocol(connA) - protoB := NewProtocol(connB) + protoA := NewSetupProtocol(connA) + protoB := NewSetupProtocol(connB) cases := []struct { Type PacketType diff --git a/pkg/transport-discovery/client/client_test.go b/pkg/transport-discovery/client/client_test.go index 11dca0f009..0aba20c20d 100644 --- a/pkg/transport-discovery/client/client_test.go +++ b/pkg/transport-discovery/client/client_test.go @@ -10,7 +10,6 @@ import ( "sync" "testing" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -24,12 +23,14 @@ var testPubKey, testSecKey = cipher.GenerateKeyPair() func newTestEntry() *transport.Entry { pk1, _ := cipher.GenerateKeyPair() - return &transport.Entry{ - ID: uuid.New(), - Edges: [2]cipher.PubKey{pk1, testPubKey}, + entry := &transport.Entry{ + ID: transport.MakeTransportID(pk1, testPubKey, "messaging", false), Type: "messaging", Public: true, } + entry.SetEdges([2]cipher.PubKey{pk1, testPubKey}) + + return entry } func TestClientAuth(t *testing.T) { @@ -145,7 +146,8 @@ func TestRegisterTransportResponses(t *testing.T) { } func TestRegisterTransports(t *testing.T) { - sEntry := &transport.SignedEntry{Entry: newTestEntry(), Signatures: [2]cipher.Sig{}} + // Signatures does not matter in this test + sEntry := &transport.SignedEntry{Entry: newTestEntry()} srv := httptest.NewServer(authHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { assert.Equal(t, "/transports/", r.URL.String()) @@ -182,14 +184,14 @@ func TestGetTransportByID(t *testing.T) { func TestGetTransportsByEdge(t *testing.T) { entry := &transport.EntryWithStatus{Entry: newTestEntry(), IsUp: true} srv := httptest.NewServer(authHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - assert.Equal(t, fmt.Sprintf("/transports/edge:%s", entry.Entry.Edges[0]), r.URL.String()) + assert.Equal(t, fmt.Sprintf("/transports/edge:%s", entry.Entry.Edges()[0]), r.URL.String()) json.NewEncoder(w).Encode([]*transport.EntryWithStatus{entry}) // nolint: errcheck }))) defer srv.Close() c, err := NewHTTP(srv.URL, testPubKey, testSecKey) require.NoError(t, err) - entries, err := c.GetTransportsByEdge(context.Background(), entry.Entry.Edges[0]) + entries, err := c.GetTransportsByEdge(context.Background(), entry.Entry.Edges()[0]) require.NoError(t, err) require.Len(t, entries, 1) diff --git a/pkg/transport/discovery.go b/pkg/transport/discovery.go index c458f5ce46..78f83ae097 100644 --- a/pkg/transport/discovery.go +++ b/pkg/transport/discovery.go @@ -64,7 +64,7 @@ func (td *mockDiscoveryClient) GetTransportsByEdge(ctx context.Context, pk ciphe td.Lock() res := []*EntryWithStatus{} for _, entry := range td.entries { - if entry.Entry.Edges[0] == pk || entry.Entry.Edges[1] == pk { + if entry.Entry.Edges()[0] == pk || entry.Entry.Edges()[1] == pk { e := &EntryWithStatus{} *e = entry res = append(res, e) diff --git a/pkg/transport/discovery_test.go b/pkg/transport/discovery_test.go new file mode 100644 index 0000000000..db01261433 --- /dev/null +++ b/pkg/transport/discovery_test.go @@ -0,0 +1,48 @@ +package transport + +import ( + "context" + "fmt" + + "github.com/skycoin/skywire/pkg/cipher" +) + +func ExampleNewDiscoveryMock() { + dc := NewDiscoveryMock() + pk1, _ := cipher.GenerateKeyPair() + pk2, _ := cipher.GenerateKeyPair() + entry := &Entry{Type: "mock", EdgeKeys: SortPubKeys(pk1, pk2)} + + sEntry := &SignedEntry{Entry: entry} + + if err := dc.RegisterTransports(context.TODO(), sEntry); err == nil { + fmt.Println("RegisterTransport success") + } else { + fmt.Println(err.Error()) + } + + if entryWS, err := dc.GetTransportByID(context.TODO(), sEntry.Entry.ID); err == nil { + fmt.Println("GetTransportByID success") + fmt.Printf("entryWS.Entry.ID == sEntry.Entry.ID is %v\n", entryWS.Entry.ID == sEntry.Entry.ID) + } else { + fmt.Printf("%v", entryWS) + } + + if entriesWS, err := dc.GetTransportsByEdge(context.TODO(), entry.Edges()[0]); err == nil { + fmt.Println("GetTransportsByEdge success") + fmt.Printf("entriesWS[0].Entry.Edges()[0] == entry.Edges()[0] is %v\n", entriesWS[0].Entry.Edges()[0] == entry.Edges()[0]) + } + + if _, err := dc.UpdateStatuses(context.TODO(), &Status{}); err == nil { + fmt.Println("UpdateStatuses success") + } else { + fmt.Println(err.Error()) + } + + // Output: RegisterTransport success + // GetTransportByID success + // entryWS.Entry.ID == sEntry.Entry.ID is true + // GetTransportsByEdge success + // entriesWS[0].Entry.Edges()[0] == entry.Edges()[0] is true + // UpdateStatuses success +} diff --git a/pkg/transport/entry.go b/pkg/transport/entry.go index 1df61e05ae..35313609e6 100644 --- a/pkg/transport/entry.go +++ b/pkg/transport/entry.go @@ -16,7 +16,7 @@ type Entry struct { ID uuid.UUID `json:"t_id"` // Edges contains the public keys of the Transport's edge nodes (should only have 2 edges and the least-significant edge should come first). - Edges [2]cipher.PubKey `json:"edges"` + EdgeKeys [2]cipher.PubKey `json:"edges"` // Type represents the transport type. Type string `json:"type"` @@ -26,6 +26,27 @@ type Entry struct { Public bool `json:"public"` } +// NewEntry constructs *Entry +func NewEntry(edgeA, edgeB cipher.PubKey, tpType string, public bool) *Entry { + return &Entry{ + ID: MakeTransportID(edgeA, edgeB, tpType, public), + EdgeKeys: SortPubKeys(edgeA, edgeB), + Type: tpType, + Public: public, + } +} + +// Edges returns the public keys of the Transport's edge nodes (should only have 2 edges and the least-significant edge should come first). +func (e *Entry) Edges() [2]cipher.PubKey { + return SortPubKeys(e.EdgeKeys[0], e.EdgeKeys[1]) +} + +// SetEdges sets edges of Entry +func (e *Entry) SetEdges(edges [2]cipher.PubKey) { + e.ID = MakeTransportID(edges[0], edges[1], e.Type, e.Public) + e.EdgeKeys = SortPubKeys(edges[0], edges[1]) +} + // String implements stringer func (e *Entry) String() string { res := "" @@ -37,19 +58,20 @@ func (e *Entry) String() string { res += fmt.Sprintf("\ttype: %s\n", e.Type) res += fmt.Sprintf("\tid: %s\n", e.ID) res += fmt.Sprintf("\tedges:\n") - res += fmt.Sprintf("\t\tedge 1: %s\n", e.Edges[0]) - res += fmt.Sprintf("\t\tedge 2: %s\n", e.Edges[1]) + res += fmt.Sprintf("\t\tedge 1: %s\n", e.Edges()[0]) + res += fmt.Sprintf("\t\tedge 2: %s\n", e.Edges()[1]) return res } -// ToBinary returns binary representation of a Signature. +// ToBinary returns binary representation of an Entry func (e *Entry) ToBinary() []byte { - bEntry := e.ID[:] - for _, edge := range e.Edges { - bEntry = append(bEntry, edge[:]...) - } - return append(bEntry, []byte(e.Type)...) + edges := e.Edges() + return append( + append( + append(e.ID[:], edges[0][:]...), + edges[1][:]...), + []byte(e.Type)...) } // Signature returns signature for Entry calculated from binary @@ -70,6 +92,45 @@ type SignedEntry struct { Registered int64 `json:"registered,omitempty"` } +// Index returns position of a given pk in edges +func (se *SignedEntry) Index(pk cipher.PubKey) int8 { + if pk == se.Entry.Edges()[1] { + return 1 + } + if pk == se.Entry.Edges()[0] { + return 0 + } + return -1 +} + +// Sign sets Signature for a given PubKey in correct position +func (se *SignedEntry) Sign(pk cipher.PubKey, secKey cipher.SecKey) bool { + + idx := se.Index(pk) + if idx == -1 { + return false + } + se.Signatures[idx] = se.Entry.Signature(secKey) + + return true +} + +// Signature gets Signature for a given PubKey from correct position +func (se *SignedEntry) Signature(pk cipher.PubKey) (cipher.Sig, bool) { + idx := se.Index(pk) + if idx == -1 { + return cipher.Sig{}, false + } + return se.Signatures[idx], true +} + +// NewSignedEntry creates a SignedEntry with first signature +func NewSignedEntry(entry *Entry, pk cipher.PubKey, secKey cipher.SecKey) (*SignedEntry, bool) { + se := &SignedEntry{Entry: entry} + return se, se.Sign(pk, secKey) + +} + // Status represents the current state of a Transport from the perspective // from a Transport's single edge. Each Transport will have two perspectives; // one from each of it's edges. diff --git a/pkg/transport/entry_test.go b/pkg/transport/entry_test.go new file mode 100644 index 0000000000..974dee8153 --- /dev/null +++ b/pkg/transport/entry_test.go @@ -0,0 +1,155 @@ +package transport + +import ( + "fmt" + + "github.com/google/uuid" + + "github.com/skycoin/skywire/pkg/cipher" +) + +// ExampleNewEntry shows that with different order of edges: +// - Entry.ID is the same +// - Edges() call is the same +func ExampleNewEntry() { + pkA, _ := cipher.GenerateKeyPair() + pkB, _ := cipher.GenerateKeyPair() + + entryAB := NewEntry(pkA, pkB, "", true) + entryBA := NewEntry(pkB, pkA, "", true) + + if entryAB.ID == entryBA.ID { + fmt.Println("entryAB.ID == entryBA.ID") + } + if entryAB.Edges() == entryBA.Edges() { + fmt.Println("entryAB.Edges() == entryBA.Edges()") + } + // Output: entryAB.ID == entryBA.ID + // entryAB.Edges() == entryBA.Edges() +} + +func ExampleEntry_Edges() { + pkA, _ := cipher.GenerateKeyPair() + pkB, _ := cipher.GenerateKeyPair() + + entryAB := Entry{ + ID: uuid.UUID{}, + EdgeKeys: [2]cipher.PubKey{pkA, pkB}, + Type: "", + Public: true, + } + + entryBA := Entry{ + ID: uuid.UUID{}, + EdgeKeys: [2]cipher.PubKey{pkB, pkA}, + Type: "", + Public: true, + } + + if entryAB.EdgeKeys != entryBA.EdgeKeys { + fmt.Println("entryAB.EdgeKeys != entryBA.EdgeKeys") + } + + if entryAB.Edges() == entryBA.Edges() { + fmt.Println("entryAB.Edges() == entryBA.Edges()") + } + + // Output: entryAB.EdgeKeys != entryBA.EdgeKeys + // entryAB.Edges() == entryBA.Edges() +} + +func ExampleEntry_SetEdges() { + pkA, _ := cipher.GenerateKeyPair() + pkB, _ := cipher.GenerateKeyPair() + + entryAB, entryBA := Entry{}, Entry{} + + entryAB.SetEdges([2]cipher.PubKey{pkA, pkB}) + entryBA.SetEdges([2]cipher.PubKey{pkA, pkB}) + + if entryAB.EdgeKeys == entryBA.EdgeKeys { + fmt.Println("entryAB.EdgeKeys == entryBA.EdgeKeys") + } + + if (entryAB.ID == entryBA.ID) && (entryAB.ID != uuid.UUID{}) { + fmt.Println("entryAB.ID != uuid.UUID{}") + fmt.Println("entryAB.ID == entryBA.ID") + } + + // Output: entryAB.EdgeKeys == entryBA.EdgeKeys + // entryAB.ID != uuid.UUID{} + // entryAB.ID == entryBA.ID +} + +func ExampleSignedEntry_Sign() { + pkA, skA := cipher.GenerateKeyPair() + pkB, skB := cipher.GenerateKeyPair() + + entry := NewEntry(pkA, pkB, "mock", true) + sEntry := &SignedEntry{Entry: entry} + + if sEntry.Signatures[0].Null() && sEntry.Signatures[1].Null() { + fmt.Println("No signatures set") + } + + if ok := sEntry.Sign(pkA, skA); !ok { + fmt.Println("error signing with skA") + } + if (!sEntry.Signatures[0].Null() && sEntry.Signatures[1].Null()) || + (!sEntry.Signatures[1].Null() && sEntry.Signatures[0].Null()) { + fmt.Println("One signature set") + } + + if ok := sEntry.Sign(pkB, skB); !ok { + fmt.Println("error signing with skB") + } + + if !sEntry.Signatures[0].Null() && !sEntry.Signatures[1].Null() { + fmt.Println("Both signatures set") + } else { + fmt.Printf("sEntry.Signatures:\n%v\n", sEntry.Signatures) + } + + // Output: No signatures set + // One signature set + // Both signatures set +} + +func ExampleSignedEntry_Signature() { + pkA, skA := cipher.GenerateKeyPair() + pkB, skB := cipher.GenerateKeyPair() + + entry := NewEntry(pkA, pkB, "mock", true) + sEntry := &SignedEntry{Entry: entry} + if ok := sEntry.Sign(pkA, skA); !ok { + fmt.Println("Error signing sEntry with (pkA,skA)") + } + if ok := sEntry.Sign(pkB, skB); !ok { + fmt.Println("Error signing sEntry with (pkB,skB)") + } + + idxA := sEntry.Index(pkA) + idxB := sEntry.Index(pkB) + + sigA, okA := sEntry.Signature(pkA) + sigB, okB := sEntry.Signature(pkB) + + if okA && sigA == sEntry.Signatures[idxA] { + fmt.Println("SignatureA got") + } + + if okB && (sigB == sEntry.Signatures[idxB]) { + fmt.Println("SignatureB got") + } + + // Incorrect case + pkC, _ := cipher.GenerateKeyPair() + if _, ok := sEntry.Signature(pkC); !ok { + fmt.Printf("SignatureC got error: invalid pubkey") + } + + // + // Output: SignatureA got + // SignatureB got + // SignatureC got error: invalid pubkey +} diff --git a/pkg/transport/handshake.go b/pkg/transport/handshake.go index dd784b4dff..4a78e87a65 100644 --- a/pkg/transport/handshake.go +++ b/pkg/transport/handshake.go @@ -7,8 +7,6 @@ import ( "fmt" "time" - "github.com/google/uuid" - "github.com/skycoin/skywire/pkg/cipher" ) @@ -30,38 +28,47 @@ func (handshake settlementHandshake) Do(tm *Manager, tr Transport, timeout time. } } -func settlementInitiatorHandshake(id uuid.UUID, public bool) settlementHandshake { +func settlementInitiatorHandshake(public bool) settlementHandshake { return func(tm *Manager, tr Transport) (*Entry, error) { entry := &Entry{ - ID: id, - Edges: [2]cipher.PubKey{tr.Local(), tr.Remote()}, - Type: tr.Type(), - Public: public, + ID: MakeTransportID(tr.Edges()[0], tr.Edges()[1], tr.Type(), public), + EdgeKeys: tr.Edges(), + Type: tr.Type(), + Public: public, } - newEntry := id == uuid.UUID{} - if newEntry { - entry.ID = uuid.New() + sEntry, ok := NewSignedEntry(entry, tm.config.PubKey, tm.config.SecKey) + if !ok { + return nil, errors.New("error creating signed entry") + } + if err := validateSignedEntry(sEntry, tr, tm.config.PubKey); err != nil { + return nil, fmt.Errorf("settlementInitiatorHandshake NewSignedEntry: %s\n sEntry: %v", err, sEntry) } - sEntry := &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{entry.Signature(tm.config.SecKey)}} if err := json.NewEncoder(tr).Encode(sEntry); err != nil { return nil, fmt.Errorf("write: %s", err) } - if err := json.NewDecoder(tr).Decode(sEntry); err != nil { + respSEntry := &SignedEntry{} + if err := json.NewDecoder(tr).Decode(respSEntry); err != nil { return nil, fmt.Errorf("read: %s", err) } - if err := verifySig(sEntry, 1, tr.Remote()); err != nil { + // Verifying remote signature + remote, ok := tm.Remote(tr.Edges()) + if !ok { + return nil, errors.New("configured PubKey not found in edges") + } + if err := verifySig(respSEntry, remote); err != nil { return nil, err } + newEntry := tm.walkEntries(func(e *Entry) bool { return *e == *respSEntry.Entry }) == nil if newEntry { tm.addEntry(entry) } - return sEntry.Entry, nil + return respSEntry.Entry, nil } } @@ -71,11 +78,19 @@ func settlementResponderHandshake(tm *Manager, tr Transport) (*Entry, error) { return nil, fmt.Errorf("read: %s", err) } - if err := validateEntry(sEntry, tr); err != nil { + remote, ok := tm.Remote(tr.Edges()) + if !ok { + return nil, errors.New("configured PubKey not found in edges") + } + + if err := validateSignedEntry(sEntry, tr, remote); err != nil { return nil, err } - sEntry.Signatures[1] = sEntry.Entry.Signature(tm.config.SecKey) + if ok := sEntry.Sign(tm.Local(), tm.config.SecKey); !ok { + return nil, errors.New("invalid pubkey for signing entry") + } + newEntry := tm.walkEntries(func(e *Entry) bool { return *e == *sEntry.Entry }) == nil var err error @@ -102,23 +117,29 @@ func settlementResponderHandshake(tm *Manager, tr Transport) (*Entry, error) { return sEntry.Entry, nil } -func validateEntry(sEntry *SignedEntry, tr Transport) error { +func validateSignedEntry(sEntry *SignedEntry, tr Transport, pk cipher.PubKey) error { entry := sEntry.Entry if entry.Type != tr.Type() { return errors.New("invalid entry type") } - if entry.Edges != [2]cipher.PubKey{tr.Remote(), tr.Local()} { + if entry.Edges() != tr.Edges() { return errors.New("invalid entry edges") } - if sEntry.Signatures[0].Null() { + // Weak check here + if sEntry.Signatures[0].Null() && sEntry.Signatures[1].Null() { return errors.New("invalid entry signature") } - return verifySig(sEntry, 0, tr.Remote()) + return verifySig(sEntry, pk) } -func verifySig(sEntry *SignedEntry, idx int, pk cipher.PubKey) error { - return cipher.VerifyPubKeySignedPayload(pk, sEntry.Signatures[idx], sEntry.Entry.ToBinary()) +func verifySig(sEntry *SignedEntry, pk cipher.PubKey) error { + sig, ok := sEntry.Signature(pk) + if !ok { + return errors.New("invalid pubkey for retrieving signature") + } + + return cipher.VerifyPubKeySignedPayload(pk, sig, sEntry.Entry.ToBinary()) } diff --git a/pkg/transport/handshake_test.go b/pkg/transport/handshake_test.go index d22ab462a6..0b7fa37ed4 100644 --- a/pkg/transport/handshake_test.go +++ b/pkg/transport/handshake_test.go @@ -2,17 +2,31 @@ package transport import ( "context" + "fmt" "net" "testing" - "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/skycoin/skywire/pkg/cipher" ) -func TestSettlementHandshake(t *testing.T) { +type hsMockEnv struct { + client DiscoveryClient + pk1 cipher.PubKey + sk1 cipher.SecKey + pk2 cipher.PubKey + sk2 cipher.SecKey + tr1 *MockTransport + tr2 *MockTransport + m1 *Manager + err1 error + m2 *Manager + err2 error +} + +func newHsMockEnv() *hsMockEnv { client := NewDiscoveryMock() pk1, sk1 := cipher.GenerateKeyPair() @@ -22,20 +36,154 @@ func TestSettlementHandshake(t *testing.T) { tr1 := NewMockTransport(in, pk1, pk2) tr2 := NewMockTransport(out, pk2, pk1) - m1, err := NewManager(&ManagerConfig{SecKey: sk1, DiscoveryClient: client}) - require.NoError(t, err) - m2, err := NewManager(&ManagerConfig{SecKey: sk2, DiscoveryClient: client}) - require.NoError(t, err) + m1, err1 := NewManager(&ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: client}) + m2, err2 := NewManager(&ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: client}) + + return &hsMockEnv{ + client: client, + pk1: pk1, + sk1: sk1, + pk2: pk2, + sk2: sk2, + tr1: tr1, + tr2: tr2, + m1: m1, + err1: err1, + m2: m2, + err2: err2, + } +} + +func TestHsMock(t *testing.T) { + mockEnv := newHsMockEnv() + require.NoError(t, mockEnv.err1) + require.NoError(t, mockEnv.err2) +} + +func Example_newHsMock() { + mockEnv := newHsMockEnv() + + fmt.Printf("client is set: %v\n", mockEnv.client != nil) + fmt.Printf("pk1 is set: %v\n", mockEnv.pk1 != cipher.PubKey{}) + fmt.Printf("sk1 is set: %v\n", mockEnv.sk1 != cipher.SecKey{}) + fmt.Printf("pk2 is set: %v\n", mockEnv.pk2 != cipher.PubKey{}) + fmt.Printf("sk2 is set: %v\n", mockEnv.sk2 != cipher.SecKey{}) + fmt.Printf("tr1 is set: %v\n", mockEnv.tr1 != nil) + fmt.Printf("tr2 is set: %v\n", mockEnv.tr2 != nil) + fmt.Printf("m1 is set: %v\n", mockEnv.m1 != nil) + fmt.Printf("err1 is nil: %v\n", mockEnv.err1 == nil) + fmt.Printf("m2 is set: %v\n", mockEnv.m2 != nil) + fmt.Printf("err2 is nil: %v\n", mockEnv.err2 == nil) + + // Output: client is set: true + // pk1 is set: true + // sk1 is set: true + // pk2 is set: true + // sk2 is set: true + // tr1 is set: true + // tr2 is set: true + // m1 is set: true + // err1 is nil: true + // m2 is set: true + // err2 is nil: true +} + +func Example_validateEntry() { + pk1, sk1 := cipher.GenerateKeyPair() + pk2, _ := cipher.GenerateKeyPair() + pk3, _ := cipher.GenerateKeyPair() + tr := NewMockTransport(nil, pk1, pk2) + + entryInvalidEdges := &SignedEntry{ + Entry: &Entry{Type: "mock", + EdgeKeys: SortPubKeys(pk2, pk3), + }} + if err := validateSignedEntry(entryInvalidEdges, tr, pk1); err != nil { + fmt.Println(err.Error()) + } + + entry := NewEntry(pk1, pk2, "mock", true) + sEntry, ok := NewSignedEntry(entry, pk1, sk1) + if !ok { + fmt.Println("error creating signed entry") + } + if err := validateSignedEntry(sEntry, tr, pk1); err != nil { + fmt.Println(err.Error()) + } + + // Output: invalid entry edges +} + +func TestValidateEntry(t *testing.T) { + pk1, sk1 := cipher.GenerateKeyPair() + pk2, sk2 := cipher.GenerateKeyPair() + pk3, _ := cipher.GenerateKeyPair() + tr := NewMockTransport(nil, pk1, pk2) + + entry := &Entry{Type: "mock", EdgeKeys: SortPubKeys(pk2, pk1)} + tcs := []struct { + sEntry *SignedEntry + err string + }{ + { + &SignedEntry{Entry: &Entry{Type: "foo"}}, + "invalid entry type", + }, + { + &SignedEntry{Entry: &Entry{Type: "mock", EdgeKeys: SortPubKeys(pk1, pk3)}}, + "invalid entry edges", + }, + { + &SignedEntry{Entry: &Entry{Type: "mock", EdgeKeys: SortPubKeys(pk2, pk1)}}, + "invalid entry signature", + }, + { + &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{}}, + "invalid entry signature", + }, + { + func() *SignedEntry { + sEntry := &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{}} + _ = sEntry.Sign(pk1, sk2) // nolint + _ = sEntry.Sign(pk2, sk1) // nolint + return sEntry + }(), + "Recovered pubkey does not match pubkey", + }, + } + + for _, tc := range tcs { + t.Run(tc.err, func(t *testing.T) { + err := validateSignedEntry(tc.sEntry, tr, pk2) + require.Error(t, err) + assert.Equal(t, tc.err, err.Error()) + }) + } + + sEntry := &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{}} + require.True(t, sEntry.Sign(pk1, sk1)) + require.True(t, sEntry.Sign(pk2, sk2)) + + require.NoError(t, validateSignedEntry(sEntry, tr, pk1)) +} + +func TestSettlementHandshake(t *testing.T) { + + mockEnv := newHsMockEnv() + t.Run("Create Mock Env", func(t *testing.T) { + require.NoError(t, mockEnv.err1) + require.NoError(t, mockEnv.err2) + }) errCh := make(chan error) var resEntry *Entry go func() { - e, err := settlementResponderHandshake(m2, tr2) + e, err := settlementResponderHandshake(mockEnv.m2, mockEnv.tr2) resEntry = e errCh <- err }() - entry, err := settlementInitiatorHandshake(uuid.UUID{}, true)(m1, tr1) + entry, err := settlementInitiatorHandshake(true)(mockEnv.m1, mockEnv.tr1) require.NoError(t, <-errCh) require.NoError(t, err) @@ -43,66 +191,53 @@ func TestSettlementHandshake(t *testing.T) { require.NotNil(t, entry) assert.Equal(t, entry.ID, resEntry.ID) - dEntry, err := client.GetTransportByID(context.TODO(), entry.ID) + + dEntry, err := mockEnv.client.GetTransportByID(context.TODO(), entry.ID) require.NoError(t, err) assert.Equal(t, entry, dEntry.Entry) + } +/* func TestSettlementHandshakeInvalidSig(t *testing.T) { - client := NewDiscoveryMock() - - pk1, sk1 := cipher.GenerateKeyPair() - pk2, sk2 := cipher.GenerateKeyPair() - - in, out := net.Pipe() - tr1 := NewMockTransport(in, pk1, pk2) - tr2 := NewMockTransport(out, pk2, pk1) + mockEnv := newHsMockEnv() - m1, err := NewManager(&ManagerConfig{SecKey: sk1, DiscoveryClient: client}) - require.NoError(t, err) - m2, err := NewManager(&ManagerConfig{SecKey: sk2, DiscoveryClient: client}) - require.NoError(t, err) + require.NoError(t, mockEnv.err1) + require.NoError(t, mockEnv.err2) - go settlementInitiatorHandshake(uuid.UUID{}, true)(m2, tr1) // nolint: errcheck - _, err = settlementResponderHandshake(m2, tr2) + go settlementInitiatorHandshake(true)(mockEnv.m2, mockEnv.tr1) // nolint: errcheck + _, err := settlementResponderHandshake(mockEnv.m2, mockEnv.tr2) require.Error(t, err) assert.Equal(t, "Recovered pubkey does not match pubkey", err.Error()) - in, out = net.Pipe() - tr1 = NewMockTransport(in, pk1, pk2) - tr2 = NewMockTransport(out, pk2, pk1) + in, out := net.Pipe() + tr1 := NewMockTransport(in, mockEnv.pk1, mockEnv.pk2) + tr2 := NewMockTransport(out, mockEnv.pk2, mockEnv.pk1) - go settlementResponderHandshake(m1, tr2) // nolint: errcheck - _, err = settlementInitiatorHandshake(uuid.UUID{}, true)(m1, tr1) + go settlementResponderHandshake(mockEnv.m1, tr2) // nolint: errcheck + _, err = settlementInitiatorHandshake(true)(mockEnv.m1, tr1) require.Error(t, err) assert.Equal(t, "Recovered pubkey does not match pubkey", err.Error()) + } +*/ func TestSettlementHandshakePrivate(t *testing.T) { - client := NewDiscoveryMock() - - pk1, sk1 := cipher.GenerateKeyPair() - pk2, sk2 := cipher.GenerateKeyPair() - - in, out := net.Pipe() - tr1 := NewMockTransport(in, pk1, pk2) - tr2 := NewMockTransport(out, pk2, pk1) + mockEnv := newHsMockEnv() - m1, err := NewManager(&ManagerConfig{SecKey: sk1, DiscoveryClient: client}) - require.NoError(t, err) - m2, err := NewManager(&ManagerConfig{SecKey: sk2, DiscoveryClient: client}) - require.NoError(t, err) + require.NoError(t, mockEnv.err1) + require.NoError(t, mockEnv.err2) errCh := make(chan error) var resEntry *Entry go func() { - e, err := settlementResponderHandshake(m2, tr2) + e, err := settlementResponderHandshake(mockEnv.m2, mockEnv.tr2) resEntry = e errCh <- err }() - entry, err := settlementInitiatorHandshake(uuid.UUID{}, false)(m1, tr1) + entry, err := settlementInitiatorHandshake(false)(mockEnv.m1, mockEnv.tr1) require.NoError(t, <-errCh) require.NoError(t, err) @@ -110,47 +245,46 @@ func TestSettlementHandshakePrivate(t *testing.T) { require.NotNil(t, entry) assert.Equal(t, entry.ID, resEntry.ID) - _, err = client.GetTransportByID(context.TODO(), entry.ID) + _, err = mockEnv.client.GetTransportByID(context.TODO(), entry.ID) require.Error(t, err) + } func TestSettlementHandshakeExistingTransport(t *testing.T) { - client := NewDiscoveryMock() - - pk1, sk1 := cipher.GenerateKeyPair() - pk2, sk2 := cipher.GenerateKeyPair() + mockEnv := newHsMockEnv() - in, out := net.Pipe() - tr1 := NewMockTransport(in, pk1, pk2) - tr2 := NewMockTransport(out, pk2, pk1) - - m1, err := NewManager(&ManagerConfig{SecKey: sk1, DiscoveryClient: client}) - require.NoError(t, err) - m2, err := NewManager(&ManagerConfig{SecKey: sk2, DiscoveryClient: client}) - require.NoError(t, err) + require.NoError(t, mockEnv.err1) + require.NoError(t, mockEnv.err2) + tpType := "mock" entry := &Entry{ - ID: uuid.New(), - Edges: [2]cipher.PubKey{pk1, pk2}, - Type: "mock", - Public: true, + ID: MakeTransportID(mockEnv.pk1, mockEnv.pk2, tpType, true), + EdgeKeys: SortPubKeys(mockEnv.pk1, mockEnv.pk2), + Type: tpType, + Public: true, } - m1.entries = append(m1.entries, entry) - m2.entries = append(m2.entries, entry) - require.NoError(t, client.RegisterTransports(context.TODO(), &SignedEntry{Entry: entry})) - _, err = client.UpdateStatuses(context.Background(), &Status{ID: entry.ID, IsUp: false}) - require.NoError(t, err) + mockEnv.m1.entries = append(mockEnv.m1.entries, entry) + mockEnv.m2.entries = append(mockEnv.m2.entries, entry) + + t.Run("RegisterTransports", func(t *testing.T) { + require.NoError(t, mockEnv.client.RegisterTransports(context.TODO(), &SignedEntry{Entry: entry})) + }) + + t.Run("UpdateStatuses", func(t *testing.T) { + _, err := mockEnv.client.UpdateStatuses(context.Background(), &Status{ID: entry.ID, IsUp: false}) + require.NoError(t, err) + }) errCh := make(chan error) var resEntry *Entry go func() { - e, err := settlementResponderHandshake(m2, tr2) + e, err := settlementResponderHandshake(mockEnv.m2, mockEnv.tr2) resEntry = e errCh <- err }() - entry, err = settlementInitiatorHandshake(entry.ID, true)(m1, tr1) + entry, err := settlementInitiatorHandshake(true)(mockEnv.m1, mockEnv.tr1) require.NoError(t, <-errCh) require.NoError(t, err) @@ -158,52 +292,60 @@ func TestSettlementHandshakeExistingTransport(t *testing.T) { require.NotNil(t, entry) assert.Equal(t, entry.ID, resEntry.ID) - dEntry, err := client.GetTransportByID(context.TODO(), entry.ID) + dEntry, err := mockEnv.client.GetTransportByID(context.TODO(), entry.ID) require.NoError(t, err) assert.True(t, dEntry.IsUp) + } -func TestValidateEntry(t *testing.T) { - pk1, sk1 := cipher.GenerateKeyPair() - pk2, sk2 := cipher.GenerateKeyPair() - tr := NewMockTransport(nil, pk1, pk2) +func Example_validateSignedEntry() { + mockEnv := newHsMockEnv() - entry := &Entry{Type: "mock", Edges: [2]cipher.PubKey{pk2, pk1}} - tcs := []struct { - sEntry *SignedEntry - err string - }{ - { - &SignedEntry{Entry: &Entry{Type: "foo"}}, - "invalid entry type", - }, - { - &SignedEntry{Entry: &Entry{Type: "mock", Edges: [2]cipher.PubKey{pk1, pk2}}}, - "invalid entry edges", - }, - { - &SignedEntry{Entry: &Entry{Type: "mock", Edges: [2]cipher.PubKey{pk2, pk1}}}, - "invalid entry signature", - }, - { - &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{}}, - "invalid entry signature", - }, - { - &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{entry.Signature(sk1)}}, - "Recovered pubkey does not match pubkey", - }, + tm, tr := mockEnv.m1, mockEnv.tr1 + entry := NewEntry(mockEnv.pk1, mockEnv.pk2, "mock", true) + sEntry, ok := NewSignedEntry(entry, tm.config.PubKey, tm.config.SecKey) + if !ok { + fmt.Println("error creating signed entry") } - - for _, tc := range tcs { - t.Run(tc.err, func(t *testing.T) { - err := validateEntry(tc.sEntry, tr) - require.Error(t, err) - assert.Equal(t, tc.err, err.Error()) - }) + if err := validateSignedEntry(sEntry, tr, tm.config.PubKey); err != nil { + fmt.Printf("NewSignedEntry: %v", err.Error()) } - sEntry := &SignedEntry{Entry: entry, Signatures: [2]cipher.Sig{entry.Signature(sk2)}} - require.NoError(t, validateEntry(sEntry, tr)) + fmt.Printf("System is working") + // Output: System is working +} + +func Example_settlementInitiatorHandshake() { + mockEnv := newHsMockEnv() + + initHandshake := settlementInitiatorHandshake(true) + respondHandshake := settlementResponderHandshake + + errCh := make(chan error) + go func() { + entry, err := initHandshake(mockEnv.m1, mockEnv.tr1) + if err != nil { + fmt.Printf("initHandshake error: %v\n entry:\n%v\n", err.Error(), entry) + errCh <- err + } + errCh <- nil + }() + + go func() { + if _, err := respondHandshake(mockEnv.m2, mockEnv.tr2); err != nil { + fmt.Printf("respondHandshake error: %v\n", err.Error()) + errCh <- err + } + errCh <- nil + }() + + <-errCh + <-errCh + + _ = mockEnv + _ = initHandshake + _ = respondHandshake + fmt.Println("System is working") + // Output: System is working } diff --git a/pkg/transport/manager.go b/pkg/transport/manager.go index c214da842f..6d00fe9b7f 100644 --- a/pkg/transport/manager.go +++ b/pkg/transport/manager.go @@ -121,15 +121,18 @@ func (tm *Manager) ReconnectTransports(ctx context.Context) { entries := tm.entries tm.mu.RUnlock() for _, entry := range entries { - if entry.Edges[0] != tm.config.PubKey { + + if tm.Transport(entry.ID) != nil { continue } - if tm.Transport(entry.ID) != nil { + remote, ok := tm.Remote(entry.Edges()) + if !ok { + tm.Logger.Warnf("Failed to re-establish transport: remote pk not found in edges") continue } - _, err := tm.createTransport(ctx, entry.Edges[1], entry.Type, entry.ID, entry.Public) + _, err := tm.createTransport(ctx, remote, entry.Type, entry.Public) if err != nil { tm.Logger.Warnf("Failed to re-establish transport: %s", err) continue @@ -141,12 +144,30 @@ func (tm *Manager) ReconnectTransports(ctx context.Context) { } } +// Local returns Manager.config.PubKey +func (tm *Manager) Local() cipher.PubKey { + return tm.config.PubKey +} + +// Remote returns the key from the edges that is not equal to Manager.config.PubKey +// in case when both edges are different - returns (cipher.PubKey{}, false) +func (tm *Manager) Remote(edges [2]cipher.PubKey) (cipher.PubKey, bool) { + if tm.config.PubKey == edges[0] { + return edges[1], true + } + if tm.config.PubKey == edges[1] { + return edges[0], true + } + return cipher.PubKey{}, false +} + // CreateDefaultTransports created transports to DefaultNodes if they don't exist. func (tm *Manager) CreateDefaultTransports(ctx context.Context) { for _, pk := range tm.config.DefaultNodes { exist := false tm.WalkTransports(func(tr *ManagedTransport) bool { - if tr.Remote() == pk { + remote, ok := tm.Remote(tr.Edges()) + if ok && (remote == pk) { exist = true return false } @@ -196,9 +217,41 @@ func (tm *Manager) Serve(ctx context.Context) error { return nil } +// MakeTransportID generates uuid.UUID from pair of keys + type + public +// Generated uuid is: +// - always the same for a given pair +// - GenTransportUUID(keyA,keyB) == GenTransportUUID(keyB, keyA) +func MakeTransportID(keyA, keyB cipher.PubKey, tpType string, public bool) uuid.UUID { + keys := SortPubKeys(keyA, keyB) + if public { + return uuid.NewSHA1(uuid.UUID{}, + append(append(append(keys[0][:], keys[1][:]...), []byte(tpType)...), 1)) + } + return uuid.NewSHA1(uuid.UUID{}, + append(append(append(keys[0][:], keys[1][:]...), []byte(tpType)...), 0)) +} + +// SortPubKeys sorts keys so that least-significant comes first +func SortPubKeys(keyA, keyB cipher.PubKey) [2]cipher.PubKey { + for i := 0; i < 33; i++ { + if keyA[i] != keyB[i] { + if keyA[i] < keyB[i] { + return [2]cipher.PubKey{keyA, keyB} + } + return [2]cipher.PubKey{keyB, keyA} + } + } + return [2]cipher.PubKey{keyA, keyB} +} + +// SortEdges sorts edges so that list-significant comes firs +func SortEdges(edges [2]cipher.PubKey) [2]cipher.PubKey { + return SortPubKeys(edges[0], edges[1]) +} + // CreateTransport begins to attempt to establish transports to the given 'remote' node. func (tm *Manager) CreateTransport(ctx context.Context, remote cipher.PubKey, tpType string, public bool) (*ManagedTransport, error) { - return tm.createTransport(ctx, remote, tpType, uuid.UUID{}, public) + return tm.createTransport(ctx, remote, tpType, public) } // DeleteTransport disconnects and removes the Transport of Transport ID. @@ -247,13 +300,13 @@ func (tm *Manager) Close() error { return nil } -func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tpType string, id uuid.UUID, public bool) (*ManagedTransport, error) { +func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tpType string, public bool) (*ManagedTransport, error) { factory := tm.factories[tpType] if factory == nil { return nil, errors.New("unknown transport type") } - tr, entry, err := tm.dialTransport(ctx, factory, remote, id, public) + tr, entry, err := tm.dialTransport(ctx, factory, remote, public) if err != nil { return nil, err } @@ -279,7 +332,7 @@ func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tp return case err := <-managedTr.errChan: tm.Logger.Infof("Transport %s failed with error: %s. Re-dialing...", managedTr.ID, err) - tr, _, err := tm.dialTransport(ctx, factory, remote, managedTr.ID, public) + tr, _, err := tm.dialTransport(ctx, factory, remote, public) if err != nil { tm.Logger.Infof("Failed to re-dial Transport %s: %s", managedTr.ID, err) if err := tm.DeleteTransport(managedTr.ID); err != nil { @@ -296,13 +349,14 @@ func (tm *Manager) createTransport(ctx context.Context, remote cipher.PubKey, tp return managedTr, nil } -func (tm *Manager) dialTransport(ctx context.Context, factory Factory, remote cipher.PubKey, id uuid.UUID, public bool) (Transport, *Entry, error) { +func (tm *Manager) dialTransport(ctx context.Context, factory Factory, remote cipher.PubKey, public bool) (Transport, *Entry, error) { + tr, err := factory.Dial(ctx, remote) if err != nil { return nil, nil, err } - entry, err := settlementInitiatorHandshake(id, public).Do(tm, tr, time.Minute) + entry, err := settlementInitiatorHandshake(public).Do(tm, tr, time.Minute) if err != nil { tr.Close() return nil, nil, err @@ -318,15 +372,21 @@ func (tm *Manager) acceptTransport(ctx context.Context, factory Factory) (*Manag } var handshake settlementHandshake = settlementResponderHandshake - entry, err := handshake.Do(tm, tr, time.Minute) + entry, err := handshake.Do(tm, tr, 30*time.Second) if err != nil { tr.Close() return nil, err } - tm.Logger.Infof("Accepted new transport with type %s from %s. ID: %s", factory.Type(), tr.Remote(), entry.ID) + remote, ok := tm.Remote(tr.Edges()) + if !ok { + return nil, errors.New("remote pubkey not found in edges") + } + + tm.Logger.Infof("Accepted new transport with type %s from %s. ID: %s", factory.Type(), remote, entry.ID) managedTr := newManagedTransport(entry.ID, tr, entry.Public) tm.mu.Lock() + tm.transports[entry.ID] = managedTr select { case <-tm.doneChan: @@ -335,6 +395,7 @@ func (tm *Manager) acceptTransport(ctx context.Context, factory Factory) (*Manag } tm.mu.Unlock() + // go func(managedTr *ManagedTransport, tm *Manager) { go func() { select { case <-managedTr.doneChan: @@ -345,7 +406,7 @@ func (tm *Manager) acceptTransport(ctx context.Context, factory Factory) (*Manag return case err := <-managedTr.errChan: tm.Logger.Infof("Transport %s failed with error: %s. Re-dialing...", managedTr.ID, err) - if err := tm.DeleteTransport(entry.ID); err != nil { + if err := tm.DeleteTransport(managedTr.ID); err != nil { tm.Logger.Warnf("Failed to delete transport: %s", err) } } diff --git a/pkg/transport/manager_test.go b/pkg/transport/manager_test.go index c8e9bc1a5e..e07f545a0c 100644 --- a/pkg/transport/manager_test.go +++ b/pkg/transport/manager_test.go @@ -2,11 +2,13 @@ package transport import ( "context" + "fmt" "io" "sync" "testing" "time" + "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -23,7 +25,7 @@ func TestTransportManager(t *testing.T) { c1 := &ManagerConfig{pk1, sk1, client, logStore, nil} c2 := &ManagerConfig{pk2, sk2, client, logStore, nil} - f1, f2 := NewMockFactory(pk1, pk2) + f1, f2 := NewMockFactoryPair(pk1, pk2) m1, err := NewManager(c1, f1) require.NoError(t, err) @@ -66,7 +68,7 @@ func TestTransportManager(t *testing.T) { dEntry, err := client.GetTransportByID(context.TODO(), tr2.ID) require.NoError(t, err) - assert.Equal(t, [2]cipher.PubKey{pk2, pk1}, dEntry.Entry.Edges) + assert.Equal(t, SortPubKeys(pk2, pk1), dEntry.Entry.Edges()) assert.True(t, dEntry.IsUp) require.NoError(t, m1.DeleteTransport(tr1.ID)) @@ -111,13 +113,13 @@ func TestTransportManagerReEstablishTransports(t *testing.T) { c1 := &ManagerConfig{pk1, sk1, client, logStore, nil} c2 := &ManagerConfig{pk2, sk2, client, logStore, nil} - f1, f2 := NewMockFactory(pk1, pk2) + f1, f2 := NewMockFactoryPair(pk1, pk2) m1, err := NewManager(c1, f1) require.NoError(t, err) assert.Equal(t, []string{"mock"}, m1.Factories()) - errCh := make(chan error) + errCh := make(chan error, 2) go func() { errCh <- m1.Serve(context.TODO()) }() @@ -133,27 +135,32 @@ func TestTransportManagerReEstablishTransports(t *testing.T) { dEntry, err := client.GetTransportByID(context.TODO(), tr2.ID) require.NoError(t, err) - assert.Equal(t, [2]cipher.PubKey{pk2, pk1}, dEntry.Entry.Edges) + assert.Equal(t, SortPubKeys(pk2, pk1), dEntry.Entry.Edges()) assert.True(t, dEntry.IsUp) require.NoError(t, m2.Close()) - dEntry, err = client.GetTransportByID(context.TODO(), tr2.ID) + dEntry2, err := client.GetTransportByID(context.TODO(), tr2.ID) require.NoError(t, err) - assert.False(t, dEntry.IsUp) + assert.False(t, dEntry2.IsUp) m2, err = NewManager(c2, f2) require.NoError(t, err) - go m2.Serve(context.TODO()) // nolint - time.Sleep(time.Second) + go func() { + errCh <- m2.Serve(context.TODO()) // nolint + }() - dEntry, err = client.GetTransportByID(context.TODO(), tr2.ID) + time.Sleep(time.Second) // TODO: this time.Sleep looks fishy - figure out later + dEntry3, err := client.GetTransportByID(context.TODO(), tr2.ID) require.NoError(t, err) - assert.True(t, dEntry.IsUp) + + assert.True(t, dEntry3.IsUp) require.NoError(t, m2.Close()) require.NoError(t, m1.Close()) + + require.NoError(t, <-errCh) require.NoError(t, <-errCh) } @@ -168,7 +175,7 @@ func TestTransportManagerLogs(t *testing.T) { c1 := &ManagerConfig{pk1, sk1, client, logStore1, nil} c2 := &ManagerConfig{pk2, sk2, client, logStore2, nil} - f1, f2 := NewMockFactory(pk1, pk2) + f1, f2 := NewMockFactoryPair(pk1, pk2) m1, err := NewManager(c1, f1) require.NoError(t, err) @@ -211,3 +218,85 @@ func TestTransportManagerLogs(t *testing.T) { require.NoError(t, m1.Close()) require.NoError(t, <-errCh) } + +func ExampleSortPubKeys() { + keyA, _ := cipher.GenerateKeyPair() + keyB, _ := cipher.GenerateKeyPair() + + sortedKeysAB := SortPubKeys(keyA, keyB) + sortedKeysBA := SortPubKeys(keyB, keyA) + _ = SortPubKeys(keyA, keyA) + fmt.Println("SortPubKeys(keyA, keyA) is successful") + + if sortedKeysAB == sortedKeysBA { + fmt.Println("SortPubKeys(keyA, keyB) == SortPubKeys(keyB, keyA)") + } + + // Output: SortPubKeys(keyA, keyA) is successful + // SortPubKeys(keyA, keyB) == SortPubKeys(keyB, keyA) +} + +func ExampleMakeTransportID() { + keyA, _ := cipher.GenerateKeyPair() + keyB, _ := cipher.GenerateKeyPair() + + uuidAB := MakeTransportID(keyA, keyB, "type", true) + + for i := 0; i < 256; i++ { + if MakeTransportID(keyA, keyB, "type", true) != uuidAB { + fmt.Println("uuid is unstable") + break + } + } + fmt.Printf("uuid is stable\n") + + uuidBA := MakeTransportID(keyB, keyA, "type", true) + if uuidAB == uuidBA { + fmt.Println("uuid is bidirectional") + } else { + fmt.Printf("keyA = %v\n keyB=%v\n uuidAB=%v\n uuidBA=%v\n", keyA, keyB, uuidAB, uuidBA) + } + + _ = MakeTransportID(keyA, keyA, "type", true) // works for equal keys + fmt.Println("works for equal keys") + + if MakeTransportID(keyA, keyB, "type", true) != MakeTransportID(keyA, keyB, "another_type", true) { + fmt.Println("uuid is different for different types") + } + + if MakeTransportID(keyA, keyB, "type", true) != MakeTransportID(keyA, keyB, "type", false) { + fmt.Println("uuid is different for public and private transports") + } + + // Output: uuid is stable + // uuid is bidirectional + // works for equal keys + // uuid is different for different types + // uuid is different for public and private transports +} + +func ExampleManager_CreateTransport() { + // Repetition is required here to guarantee that correctness does not depends on order of edges + for i := 0; i < 4; i++ { + pkB, mgrA, err := MockTransportManager() + if err != nil { + fmt.Printf("MockTransportManager failed on iteration %v with: %v\n", i, err) + return + } + + mtrAB, err := mgrA.CreateTransport(context.TODO(), pkB, "mock", true) + if err != nil { + fmt.Printf("Manager.CreateTransport failed on iteration %v with: %v\n", i, err) + return + } + + if (mtrAB.ID == uuid.UUID{}) { + fmt.Printf("Manager.CreateTransport failed on iteration %v", i) + return + } + } + + fmt.Println("Manager.CreateTransport success") + + // Output: Manager.CreateTransport success +} diff --git a/pkg/transport/mock.go b/pkg/transport/mock.go index 7c6baf26bf..6098223aa3 100644 --- a/pkg/transport/mock.go +++ b/pkg/transport/mock.go @@ -26,8 +26,8 @@ type MockFactory struct { fType string } -// NewMockFactory constructs a pair of MockFactories. -func NewMockFactory(local, remote cipher.PubKey) (*MockFactory, *MockFactory) { +// NewMockFactoryPair constructs a pair of MockFactories. +func NewMockFactoryPair(local, remote cipher.PubKey) (*MockFactory, *MockFactory) { in := make(chan *fConn) out := make(chan *fConn) return &MockFactory{local, in, out, "mock"}, &MockFactory{remote, out, in, "mock"} @@ -78,15 +78,14 @@ func (f *MockFactory) Type() string { // operations type MockTransport struct { rw io.ReadWriteCloser - local cipher.PubKey - remote cipher.PubKey + edges [2]cipher.PubKey context context.Context } // NewMockTransport creates a transport with the given secret key and remote public key, taking a writer // and a reader that will be used in the Write and Read operation func NewMockTransport(rw io.ReadWriteCloser, local, remote cipher.PubKey) *MockTransport { - return &MockTransport{rw, local, remote, context.Background()} + return &MockTransport{rw, SortPubKeys(local, remote), context.Background()} } // Read implements reader for mock transport @@ -114,14 +113,9 @@ func (m *MockTransport) Close() error { return m.rw.Close() } -// Local returns the local static public key -func (m *MockTransport) Local() cipher.PubKey { - return m.local -} - -// Remote returns the remote public key fo the mock transport -func (m *MockTransport) Remote() cipher.PubKey { - return m.remote +// Edges returns edges of MockTransport +func (m *MockTransport) Edges() [2]cipher.PubKey { + return SortEdges(m.edges) } // SetDeadline sets a deadline for the write/read operations of the mock transport @@ -142,3 +136,37 @@ func (m *MockTransport) SetDeadline(t time.Time) error { func (m *MockTransport) Type() string { return "mock" } + +// MockTransportManagersPair constructs a pair of Transport Managers +func MockTransportManagersPair() (pk1, pk2 cipher.PubKey, m1, m2 *Manager, errCh chan error, err error) { + discovery := NewDiscoveryMock() + logs := InMemoryTransportLogStore() + + var sk1, sk2 cipher.SecKey + pk1, sk1 = cipher.GenerateKeyPair() + pk2, sk2 = cipher.GenerateKeyPair() + + c1 := &ManagerConfig{PubKey: pk1, SecKey: sk1, DiscoveryClient: discovery, LogStore: logs} + c2 := &ManagerConfig{PubKey: pk2, SecKey: sk2, DiscoveryClient: discovery, LogStore: logs} + + f1, f2 := NewMockFactoryPair(pk1, pk2) + + if m1, err = NewManager(c1, f1); err != nil { + return + } + if m2, err = NewManager(c2, f2); err != nil { + return + } + + errCh = make(chan error) + go func() { errCh <- m1.Serve(context.TODO()) }() + go func() { errCh <- m2.Serve(context.TODO()) }() + + return +} + +// MockTransportManager creates Manager +func MockTransportManager() (cipher.PubKey, *Manager, error) { + _, pkB, mgrA, _, _, err := MockTransportManagersPair() + return pkB, mgrA, err +} diff --git a/pkg/transport/tcp_transport.go b/pkg/transport/tcp_transport.go index 701b5e5185..b2b117e301 100644 --- a/pkg/transport/tcp_transport.go +++ b/pkg/transport/tcp_transport.go @@ -41,7 +41,7 @@ func (f *TCPFactory) Accept(ctx context.Context) (Transport, error) { return nil, ErrUnknownRemote } - return &TCPTransport{conn, f.lpk, rpk}, nil + return &TCPTransport{conn, [2]cipher.PubKey{f.lpk, rpk}}, nil } // Dial initiates a Transport with a remote node. @@ -56,7 +56,7 @@ func (f *TCPFactory) Dial(ctx context.Context, remote cipher.PubKey) (Transport, return nil, err } - return &TCPTransport{conn, f.lpk, remote}, nil + return &TCPTransport{conn, [2]cipher.PubKey{f.lpk, remote}}, nil } // Close implements io.Closer @@ -77,18 +77,12 @@ func (f *TCPFactory) Type() string { // TCPTransport implements Transport over TCP connection. type TCPTransport struct { *net.TCPConn - lpk cipher.PubKey - rpk cipher.PubKey -} - -// Local returns the local transport edge's public key. -func (tr *TCPTransport) Local() cipher.PubKey { - return tr.lpk + edges [2]cipher.PubKey } -// Remote returns the remote transport edge's public key. -func (tr *TCPTransport) Remote() cipher.PubKey { - return tr.rpk +// Edges returns the TCPTransport edges. +func (tr *TCPTransport) Edges() [2]cipher.PubKey { + return SortEdges(tr.edges) } // Type returns the string representation of the transport type. diff --git a/pkg/transport/tcp_transport_test.go b/pkg/transport/tcp_transport_test.go index bdb1d3ab62..0715a9c381 100644 --- a/pkg/transport/tcp_transport_test.go +++ b/pkg/transport/tcp_transport_test.go @@ -55,8 +55,6 @@ func TestTCPFactory(t *testing.T) { tr, err := f2.Dial(context.TODO(), pk1) require.NoError(t, err) assert.Equal(t, "tcp", tr.Type()) - assert.Equal(t, pk2, tr.Local()) - assert.Equal(t, pk1, tr.Remote()) buf := make([]byte, 3) _, err = tr.Read(buf) diff --git a/pkg/transport/transport.go b/pkg/transport/transport.go index e9e4981fdd..8e66224af6 100644 --- a/pkg/transport/transport.go +++ b/pkg/transport/transport.go @@ -21,11 +21,8 @@ type Transport interface { // Close implements io.Closer Close() error - // Local returns the local transport edge's public key. - Local() cipher.PubKey - - // Remote returns the remote transport edge's public key. - Remote() cipher.PubKey + // Edges returns sorted edges of transport + Edges() [2]cipher.PubKey // SetDeadline functions the same as that from net.Conn // With a Transport, we don't have a distinction between write and read timeouts.