Compare commits

..

6 Commits

Author SHA1 Message Date
Marko Baricevic
85f2e0bb14 remove slate 2019-09-04 23:03:34 +02:00
Marko Baricevic
1d7cdd6c76 .phony 2019-09-04 17:43:56 +02:00
Marko Baricevic
57523ce1b7 Update makefile
- these two files were taken from the sdk with adjustments for usage in TM

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-04 17:22:26 +02:00
Anton Kaliaev
acb874eaf6 docs: move dev sessions into docs (#3934)
* docs: move dev sessions into docs

and transform the list into a table

* fix formatting
2019-09-03 11:59:13 +04:00
Michelle-L
1802b60833 docs: add dev sessions from YouTube (#3929)
* Create development_sessions

* Rename development_sessions to videos_development_sessions

* Update videos_development_sessions

* Rename videos_development_sessions to videos_development_sessions_md

* Delete videos_development_sessions_md

* Add files via upload
2019-09-03 11:38:32 +04:00
Marko
72785a2e86 docs: switch the data in /unconfirmed_txs and num_unconfirmed_txs (#3933)
- switch the data that was represented in `/unconfirmed_txs` and `num_unconfirmed_txs`

Signed-off-by: Marko Baricevic <marbar3778@yahoo.com>
2019-09-02 22:23:02 +04:00
9 changed files with 177 additions and 229 deletions

View File

@@ -51,22 +51,6 @@ jobs:
paths:
- /go/src/github.com/tendermint/tendermint
build_slate:
<<: *defaults
steps:
- attach_workspace:
at: /tmp/workspace
- restore_cache:
key: v4-pkg-cache
- restore_cache:
key: v3-tree-{{ .Environment.CIRCLE_SHA1 }}
- run:
name: slate docs
command: |
set -ex
export PATH="$GOBIN:$PATH"
make build-slate
test_abci_apps:
<<: *defaults
steps:

View File

@@ -16,6 +16,9 @@ BUILD_FLAGS = -mod=readonly -ldflags "$(LD_FLAGS)"
all: check build test install
# The below include contains the tools.
include scripts/Makefile
check: check_tools
########################################
@@ -79,13 +82,13 @@ check_tools:
@echo "Found tools: $(foreach tool,$(notdir $(GOTOOLS)),\
$(if $(shell which $(tool)),$(tool),$(error "No $(tool) in PATH")))"
get_tools:
@echo "--> Installing tools"
./scripts/get_tools.sh
# get_tools:
# @echo "--> Installing tools"
# ./scripts/get_tools.sh
update_tools:
@echo "--> Updating tools"
./scripts/get_tools.sh
# update_tools:
# @echo "--> Updating tools"
# ./scripts/get_tools.sh
#For ABCI and libs
get_protoc:
@@ -304,10 +307,6 @@ sentry-stop:
@if [ -z "$(DO_API_TOKEN)" ]; then echo "DO_API_TOKEN environment variable not set." ; false ; fi
cd networks/remote/terraform && terraform destroy -var DO_API_TOKEN="$(DO_API_TOKEN)" -var SSH_KEY_FILE="$(HOME)/.ssh/id_rsa.pub"
# meant for the CI, inspect script & adapt accordingly
build-slate:
bash scripts/slate.sh
# Build hooks for dredd, to skip or add information on some steps
build-contract-tests-hooks:
ifeq ($(OS),Windows_NT)
@@ -327,4 +326,4 @@ contract-tests:
# To avoid unintended conflicts with file names, always add to .PHONY
# unless there is a reason not to.
# https://www.gnu.org/software/make/manual/html_node/Phony-Targets.html
.PHONY: check build build_race build_abci dist install install_abci check_tools get_tools update_tools draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop build-slate protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint build-contract-tests-hooks contract-tests
.PHONY: check build build_race build_abci dist install install_abci check_tools get_tools update_tools draw_deps get_protoc protoc_abci protoc_libs gen_certs clean_certs grpc_dbserver test_cover test_apps test_persistence test_p2p test test_race test_integrations test_release test100 vagrant_test fmt rpc-docs build-linux localnet-start localnet-stop build-docker build-docker-localnode sentry-start sentry-config sentry-stop protoc_grpc protoc_all build_c install_c test_with_deadlock cleanup_after_test_with_deadlock lint build-contract-tests-hooks contract-tests

View File

@@ -74,9 +74,8 @@ and the [contributing guidelines](CONTRIBUTING.md) when submitting code.
Join the larger community on the [forum](https://forum.cosmos.network/) and the [chat](https://riot.im/app/#/room/#tendermint:matrix.org).
To learn more about the structure of the software, watch the [Developer
Sessions](https://www.youtube.com/playlist?list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv)
and read some [Architectural
Decision Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
Sessions](/docs/DEV_SESSIONS.md) and read some [Architectural Decision
Records](https://github.com/tendermint/tendermint/tree/master/docs/architecture).
Learn more by reading the code and comparing it to the
[specification](https://github.com/tendermint/tendermint/tree/develop/docs/spec).

33
docs/DEV_SESSIONS.md Normal file
View File

@@ -0,0 +1,33 @@
# Developer Sessions
The Tendermint Core developer call is comprised of both [Interchain
Foundation](http://interchain.io/) and [All in Bits](https://tendermint.com/)
team members discussing the development of [Tendermint
BFT](https://github.com/tendermint/tendermint) and related research. The goal
of the Tendermint Core developer calls is to provide transparency into the
decision making process, technical information, update cycles etc.
## List
| Date | Topic | Link(s) |
| --------------- | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------- |
| August 2019 | Part Three: Tendermint Lite Client | [YouTube](https://www.youtube.com/watch?v=whyL6UrKe7I&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=5) |
| August 2019 | Fork Accountability | [YouTube](https://www.youtube.com/watch?v=Jph-4PGtdPo&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=4) |
| July 2019 | Part Two: Tendermint Lite Client | [YouTube](https://www.youtube.com/watch?v=gTjG7jNNdKQ&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=6) |
| July 2019 | Part One: Tendermint Lite Client | [YouTube](https://www.youtube.com/watch?v=C6fH_sgPJzA&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=7) |
| June 2019 | Testnet Deployments | [YouTube](https://www.youtube.com/watch?v=gYA6no7tRlM&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=10) |
| June 2019 | Blockchain Reactor Refactor | [YouTube](https://www.youtube.com/watch?v=JLBGH8yxABk&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=11) |
| June 2019 | Tendermint Rust Libraries | [YouTube](https://www.youtube.com/watch?v=-WXKdyoGHwA&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=9) |
| May 2019 | Merkle Tree Deep Dive | [YouTube](https://www.youtube.com/watch?v=L3bt2Uw8ICg&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=8) |
| May 2019 | Remote Signer Refactor | [YouTube](https://www.youtube.com/watch?v=eUyXXEEuBzQ&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=12) |
| May 2019 | Introduction to Ansible | [YouTube](https://www.youtube.com/watch?v=72clQLjzPg4&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=14&t=0s) | | |
| April 2019 | Tendermint State Sync Design Discussion | [YouTube](https://www.youtube.com/watch?v=4k23j2QHwrM&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=11) |
| April 2019 | ADR-036 - Blockchain Reactor Refactor | [YouTube](https://www.youtube.com/watch?v=TW2xC1LwEkE&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=10) |
| April 2019 | Verifying Distributed Algorithms | [YouTube](https://www.youtube.com/watch?v=tMd4lgPVBxE&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=9) |
| April 2019 | Byzantine Model Checker Presentation | [YouTube](https://www.youtube.com/watch?v=rdXl4VCQyow&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=8) |
| January 2019 | Proposer Selection in Idris | [YouTube](https://www.youtube.com/watch?v=hWZdc9c1aH8&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=7) |
| January 2019 | Current Mempool Design | [YouTube](https://www.youtube.com/watch?v=--iGIYYiLu4&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=6) |
| December 2018 | ABCI Proxy App | [YouTube](https://www.youtube.com/watch?v=s6sQ2HOVHdo&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=5) |
| October 2018 | DB Performance | [YouTube](https://www.youtube.com/watch?v=jVSNHi4l0fQ&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=4) |
| October 2018 | Alternative Mempool Algorithms | [YouTube](https://www.youtube.com/watch?v=XxH5ZtM4vMM&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv&index=2) |
| October 2018 | Tendermint Termination | [YouTube](https://www.youtube.com/watch?v=YBZjecfjeIk&list=PLdQIb0qr3pnBbG5ZG-0gr3zM86_s8Rpqv) |

View File

@@ -3,7 +3,6 @@
## Changelog
* 13-07-2019: Initial draft
* 14-08-2019: Address cwgoes comments
* 22-08-2019: Second version
## Context
@@ -54,25 +53,22 @@ network or when a light client that has been offline for longer than the
unbonding period connects to the network. Specifically, the node needs to
initialize the following structure before syncing from user input:
```go
```
type TrustOptions struct {
// Required: only trust commits up to this old.
// Should be equal to the unbonding period minus some delta for evidence reporting.
TrustPeriod time.Duration `json:"trust-period"`
// Required: only trust commits up to this old.
// Should be equal to the unbonding period minus some delta for evidence reporting.
TrustPeriod time.Duration `json:"trust-period"`
// Required: validator whom we've got the TrustHeight/Hash from
ValidatorAddress types.Address `json:"validator-address"`
// Option 1: TrustHeight and TrustHash can both be provided
// to force the trusting of a particular height and hash.
// If the latest trusted height/hash is more recent, then this option is
// ignored.
TrustHeight int64 `json:"trust-height"`
TrustHash []byte `json:"trust-hash"`
// Option 1: TrustHeight and TrustHash can both be provided
// to force the trusting of a particular height and hash.
// If the latest trusted height/hash is more recent, then this option is
// ignored.
TrustHeight int64 `json:"trust-height"`
TrustHash []byte `json:"trust-hash"`
// Option 2: Callback can be set to implement a confirmation
// step if the trust store is uninitialized, or expired.
Callback func(height int64, hash []byte) error
// Option 2: Callback can be set to implement a confirmation
// step if the trust store is uninitialized, or expired.
Callback func(height int64, hash []byte) error
}
```
@@ -125,182 +121,6 @@ network usage.
Check out the formal specification
[here](https://github.com/tendermint/tendermint/blob/master/docs/spec/consensus/light-client.md).
### Implementation
There are two primary modes of usage right now:
1) Trusted RPC proxy (wrapping multiple RPC clients + verification)
2) Part of the IBC light client (only verification bit, no RPC) [spec](https://github.com/cosmos/ics/tree/master/spec/ics-002-client-semantics)
First, we'll need something, which will provide us secure headers & validator sets.
```go
type Provider interface {
// 0 - latest
GetFullCommit(height int64) (FullCommit, error)
}
```
In case of the proxy it will be a `http` provider (wrapping RPC client). For
IBC, it will be a `ibc` provider, receiving information from IBC transactions.
Once we have the information, we need to verify it.
```go
type mode int
const (
sequential mode = iota
bisecting
)
// default mode - DefaultBisectingVerification
type Verifier struct {
chainID string
options TrustOptions
lastVerifiedHeight int64
logger log.Logger
mode mode
trustLevel float
// Source of new FullCommit(s).
source Provider
// Alternative sources for checking the primary for misbehavior by comparing data.
// If the primary misbehaves, we report the evidence to them.
verifiers []ProviderAndEvidenceReporter
// Where trusted FullCommit(s) are stored.
trusted PersistentProvider
}
```
Since providers themselves don't know when they have received a new header (or
may choose to do so upon a request), we must add a new function to `Verifier` -
`Verify(height int64) error` (0 - latest). It will try to fetch a new header &
validator set and verify it. nop if already verified.
**Sequential vs bisecting verifier**
Verifier should use bisection by default, but provide options to choose a
different mode OR tweak bisection.
```go
func SequentialVerification() Option {
return func(v *Verifier) {
v.mode = sequential
}
}
// trustLevel - maximum change between two not consequitive headers in terms of
// validators & their respective voting power, required to trust a new header
// (default: 1/3).
func BisectingVerification(trustLevel float) Option {
if trustLevel > 1 || trustLevel < 1/3 {
panic(fmt.Sprintf("trustLevel must be within [1/3, 1], given %v", trustLevel))
}
return func(v *Verifier) {
v.mode = bisecting
v.trustLevel = trustLevel
}
}
var DefaultBisectingVerification = func() Option {
return BisectingVerification(1/3)
}
```
Once we verified the header, we will need to store it somewhere.
```
type PersistentProvider interface {
Provider
SaveFullCommit(fc FullCommit) error
}
```
In case of the proxy it will be a `db` provider (levelDB + in-memory cache in
front). For IBC, it will be a `keeper` provider.
**Minimal test for (1)**
```go
c, err := lite.NewClient(
chainID,
lite.TrustOptions{TrustPeriod: 336 * time.Hour},
rpcclient.NewHTTP(remote1, "/websocket"),
)
require.NoError(t, err)
commit, err := c.Commit()
require.NoError(t, err)
assert.Equal(t, chainID, commit.ChainID)
```
`lite.Client` here is a `struct`, which uses `Verifier` and exposes
`rpcclient.Client` API.
```go
type Client struct {
verifier *Verifier
client rpcclient.Client
}
var rpcclient.Client = (*Client)(nil)
```
**Minimal test for (2)**
```go
c, err := lite.NewVerifier(
chainID,
lite.TrustOptions{TrustPeriod: 24 * time.Hour},
ibc.New(chainID),
Trusted(ibcKeeper{}),
)
require.NoError(t, err)
err = c.Verify(height)
require.NoError(t, err)
```
**Evidence Handling and Reporting**
light client should also be able to submit evidence of malfeasance and handle
evidence coming from a full node or another source.
We'll need to add evidence to `FullCommit`.
```go
type FullCommit struct {
SignedHeader types.SignedHeader `json:"signed_header"`
Validators *types.ValidatorSet `json:"validator_set"`
NextValidators *types.ValidatorSet `json:"next_validator_set"`
Evidence types.EvidenceList `json:"evidence"`
}
```
When/if evidence is received, client should check it and disconnect from the
node if `evidence.Address == TrustOptions.ValidatorAddress`. It's unwise to
think that a node will send an evidence of its misbehavior. That's why we
should also check `verifiers` sources in the background.
_Evidence handling can be implemented in the second version._
Submitting an evidence comes down to calling `ReportEvidence(ev types.Evidence)
error` on the `verifiers` sources.
```go
type ProviderAndEvidenceReporter interface {
Provider
ReportEvidence(ev types.Evidence) error
}
```
## Status
Accepted.

View File

@@ -2230,7 +2230,7 @@ definitions:
type: "object"
type: "object"
type: "object"
UnconfirmedTransactionsResponse:
NumUnconfirmedTransactionsResponse:
type: object
required:
- "jsonrpc"
@@ -2248,7 +2248,6 @@ definitions:
- "n_txs"
- "total"
- "total_bytes"
# - "txs"
properties:
n_txs:
type: "string"
@@ -2268,7 +2267,7 @@ definitions:
# example:
# - "gAPwYl3uCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUA75/FmYq9WymsOBJ0XSJ8yV8zmQKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhQbrvwbvlNiT+Yjr86G+YQNx7kRVgowjE1xDQoUjJyJG+WaWBwSiGannBRFdrbma+8SFK2m+1oxgILuQLO55n8mWfnbIzyPCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUQNGfkmhTNMis4j+dyMDIWXdIPiYKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhS8sL0D0wwgGCItQwVowak5YB38KRIUCg4KBXVhdG9tEgUxMDA1NBDoxRgaagom61rphyECn8x7emhhKdRCB2io7aS/6Cpuq5NbVqbODmqOT3jWw6kSQKUresk+d+Gw0BhjiggTsu8+1voW+VlDCQ1GRYnMaFOHXhyFv7BCLhFWxLxHSAYT8a5XqoMayosZf9mANKdXArA="
type: "object"
NumUnconfirmedTransactionsResponse:
UnconfirmedTransactionsResponse:
type: object
required:
- "jsonrpc"
@@ -2304,7 +2303,6 @@ definitions:
type: string
x-nullable: true
example:
- null
- "gAPwYl3uCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUA75/FmYq9WymsOBJ0XSJ8yV8zmQKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhQbrvwbvlNiT+Yjr86G+YQNx7kRVgowjE1xDQoUjJyJG+WaWBwSiGannBRFdrbma+8SFK2m+1oxgILuQLO55n8mWfnbIzyPCjCMTXENChSMnIkb5ZpYHBKIZqecFEV2tuZr7xIUQNGfkmhTNMis4j+dyMDIWXdIPiYKMIxNcQ0KFIyciRvlmlgcEohmp5wURXa25mvvEhS8sL0D0wwgGCItQwVowak5YB38KRIUCg4KBXVhdG9tEgUxMDA1NBDoxRgaagom61rphyECn8x7emhhKdRCB2io7aS/6Cpuq5NbVqbODmqOT3jWw6kSQKUresk+d+Gw0BhjiggTsu8+1voW+VlDCQ1GRYnMaFOHXhyFv7BCLhFWxLxHSAYT8a5XqoMayosZf9mANKdXArA="
type: "object"
TxSearchResponse:

View File

@@ -346,7 +346,7 @@ func UnconfirmedTxs(ctx *rpctypes.Context, limit int) (*ctypes.ResultUnconfirmed
// client := client.NewHTTP("tcp://0.0.0.0:26657", "/websocket")
// err := client.Start()
// if err != nil {
// // handle error
// // handle error
// }
// defer client.Stop()
// result, err := client.UnconfirmedTxs()
@@ -361,8 +361,8 @@ func UnconfirmedTxs(ctx *rpctypes.Context, limit int) (*ctypes.ResultUnconfirmed
// "result" : {
// "n_txs" : "0",
// "total_bytes" : "0",
// "txs" : null,
// "total" : "0"
// "txs" : null,
// }
// }
// ```

88
scripts/Makefile Normal file
View File

@@ -0,0 +1,88 @@
###
# Find OS and Go environment
# GO contains the Go binary
# FS contains the OS file separator
###
ifeq ($(OS),Windows_NT)
GO := $(shell where go.exe 2> NUL)
FS := "\\"
else
GO := $(shell command -v go 2> /dev/null)
FS := "/"
endif
ifeq ($(GO),)
$(error could not find go. Is it in PATH? $(GO))
endif
GOPATH ?= $(shell $(GO) env GOPATH)
GITHUBDIR := $(GOPATH)$(FS)src$(FS)github.com
GOLANGCI_LINT_VERSION := v1.17.1
GOLANGCI_LINT_HASHSUM := f5fa647a12f658924d9f7d6b9628d505ab118e8e049e43272de6526053ebe08d
###
# Functions
###
go_get = $(if $(findstring Windows_NT,$(OS)),\
IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS) ( mkdir $(GITHUBDIR)$(FS)$(1) ) else (cd .) &\
IF NOT EXIST $(GITHUBDIR)$(FS)$(1)$(FS)$(2)$(FS) ( cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2) ) else (cd .) &\
,\
mkdir -p $(GITHUBDIR)$(FS)$(1) &&\
(test ! -d $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && cd $(GITHUBDIR)$(FS)$(1) && git clone https://github.com/$(1)/$(2)) || true &&\
)\
cd $(GITHUBDIR)$(FS)$(1)$(FS)$(2) && git fetch origin && git checkout -q $(3)
mkfile_path := $(abspath $(lastword $(MAKEFILE_LIST)))
mkfile_dir := $(shell cd $(shell dirname $(mkfile_path)); pwd)
###
# tools
###
TOOLS_DESTDIR ?= $(GOPATH)/bin
GOLANGCI_LINT = $(TOOLS_DESTDIR)/golangci-lint
GOIMPORTS = $(TOOLS_DESTDIR)/goimports
CERTSTRAP = $(TOOLS_DESTDIR)/certstrap
PROTOBUF = $(TOOLS_DESTDIR)/protoc
GOX = $(TOOLS_DESTDIR)/gox
all: get_tools
get_tools: golangci-lint goimports certstrap protobuf gox
golangci-lint: $(GOLANGCI_LINT)
$(GOLANGCI_LINT): $(mkfile_dir)/install-golangci-lint.sh
bash $(mkfile_dir)/install-golangci-lint.sh $(TOOLS_DESTDIR) $(GOLANGCI_LINT_VERSION) $(GOLANGCI_LINT_HASHSUM)
goimports: $(GOIMPORTS)
$(GOIMPORTS):
@echo "Get goimports@v0.0.0-20190628034336-212fb13d595e"
@go get golang.org/x/tools/cmd/goimports@v0.0.0-20190628034336-212fb13d595e
certstrap: $(CERTSTRAP)
$(CERTSTRAP):
@echo "Get Certstrap"
@go get github.com/square/certstrap@338204a88c4349b1c135eac1e8c14c693ad007da
protobuf: $(PROTOBUF)
$(PROTOBUF):
@echo "Get Protobuf"
## protobuf v1.3.0
@go get github.com/gogo/protobuf/protoc-gen-gogo@0ca988a254f991240804bf9821f3450d87ccbb1b
gox: $(GOX)
$(GOX):
@echo "Get Gox"
# used to build tm-monitor & tm-bench binaries
## gox v1.0.1
@go get github.com/mitchellh/gox@d8caaff5a9dc98f4cfa1fcce6e7265a04689f641
tools-clean:
rm -f $(CERTSTRAP) $(GOIMPORTS) $(GOLANGCI_LINT) $(PROTOBUF) $(GOX)
rm -f tools-stamp
.PHONY: all get_tools tools-clean

View File

@@ -0,0 +1,27 @@
#!/bin/bash
set -euo pipefail
f_sha256() {
local l_file
l_file=$1
python -sBc "import hashlib;print(hashlib.sha256(open('$l_file','rb').read()).hexdigest())"
}
installer="$(mktemp)"
trap "rm -f ${installer}" EXIT
GOBIN="${1}"
VERSION="${2}"
HASHSUM="${3}"
CURL="$(which curl)"
echo "Downloading golangci-lint ${VERSION} installer ..." >&2
"${CURL}" -sfL "https://raw.githubusercontent.com/golangci/golangci-lint/${VERSION}/install.sh" > "${installer}"
echo "Checking hashsum ..." >&2
[ "${HASHSUM}" = "$(f_sha256 ${installer})" ]
chmod +x "${installer}"
echo "Launching installer ..." >&2
exec "${installer}" -d -b "${GOBIN}" "${VERSION}"