Merge branch 'master' of github.com:sikkatech/cosmos-sdk into gov_split_vote_weighted_vote
This commit is contained in:
commit
404dc4f356
1
.github/workflows/docker.yml
vendored
1
.github/workflows/docker.yml
vendored
@ -42,6 +42,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
|
||||
1
.github/workflows/proto-docker.yml
vendored
1
.github/workflows/proto-docker.yml
vendored
@ -31,6 +31,7 @@ jobs:
|
||||
uses: docker/setup-buildx-action@v1
|
||||
|
||||
- name: Login to DockerHub
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
uses: docker/login-action@v1
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUBTM_USERNAME }}
|
||||
|
||||
4
.github/workflows/release-sims.yml
vendored
4
.github/workflows/release-sims.yml
vendored
@ -30,7 +30,7 @@ jobs:
|
||||
- name: install runsim
|
||||
run: |
|
||||
export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
@ -40,7 +40,7 @@ jobs:
|
||||
needs: [build, install-runsim]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
|
||||
10
.github/workflows/sims.yml
vendored
10
.github/workflows/sims.yml
vendored
@ -39,7 +39,7 @@ jobs:
|
||||
run: go version
|
||||
- name: Install runsim
|
||||
run: export GO111MODULE="on" && go get github.com/cosmos/tools/cmd/runsim@v1.0.0
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
@ -60,7 +60,7 @@ jobs:
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
@ -88,7 +88,7 @@ jobs:
|
||||
go.sum
|
||||
SET_ENV_NAME_INSERTIONS: 1
|
||||
SET_ENV_NAME_LINES: 1
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
@ -116,7 +116,7 @@ jobs:
|
||||
go.sum
|
||||
SET_ENV_NAME_INSERTIONS: 1
|
||||
SET_ENV_NAME_LINES: 1
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
@ -144,7 +144,7 @@ jobs:
|
||||
go.sum
|
||||
SET_ENV_NAME_INSERTIONS: 1
|
||||
SET_ENV_NAME_LINES: 1
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-runsim-binary
|
||||
|
||||
41
.github/workflows/test.yml
vendored
41
.github/workflows/test.yml
vendored
@ -26,11 +26,31 @@ jobs:
|
||||
- name: install tparse
|
||||
run: |
|
||||
export GO111MODULE="on" && go get github.com/mfridman/tparse@v0.8.3
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tparse-binary
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
go-arch: ["amd64", "arm", "arm64"]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-go@v2.1.3
|
||||
with:
|
||||
go-version: 1.15
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Build
|
||||
run: GOARCH=${{ matrix.go-arch }} LEDGER_ENABLED=false make build
|
||||
|
||||
test-cosmovisor:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@ -188,6 +208,23 @@ jobs:
|
||||
name: "${{ github.sha }}-${{ matrix.part }}-race-output"
|
||||
path: ./${{ matrix.part }}-race-output.txt
|
||||
|
||||
test-rosetta:
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: technote-space/get-diff-action@v4
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: test rosetta
|
||||
run: |
|
||||
make test-rosetta
|
||||
# if: env.GIT_DIFF
|
||||
|
||||
race-detector-report:
|
||||
runs-on: ubuntu-latest
|
||||
needs: [test-race, install-tparse]
|
||||
@ -217,7 +254,7 @@ jobs:
|
||||
with:
|
||||
name: "${{ github.sha }}-03-race-output"
|
||||
if: env.GIT_DIFF
|
||||
- uses: actions/cache@v2.1.3
|
||||
- uses: actions/cache@v2.1.4
|
||||
with:
|
||||
path: ~/go/bin
|
||||
key: ${{ runner.os }}-go-tparse-binary
|
||||
|
||||
46
CHANGELOG.md
46
CHANGELOG.md
@ -36,19 +36,57 @@ Ref: https://keepachangelog.com/en/1.0.0/
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
### Client Breaking Changes
|
||||
|
||||
* [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Addresses no longer have a fixed 20-byte length. From the SDK modules' point of view, any 1-255 bytes-long byte array is a valid address.
|
||||
|
||||
### API Breaking Changes
|
||||
|
||||
* (client/keys) [\#8500](https://github.com/cosmos/cosmos-sdk/pull/8500) `InfoImporter` interface is removed from legacy keybase.
|
||||
|
||||
### State Machine Breaking
|
||||
|
||||
* (x/{bank,distrib,gov,slashing,staking}) [\#8363](https://github.com/cosmos/cosmos-sdk/issues/8363) Store keys have been modified to allow for variable-length addresses.
|
||||
* (x/ibc) [\#8266](https://github.com/cosmos/cosmos-sdk/issues/8266) Add amino JSON for IBC messages in order to support Ledger text signing.
|
||||
|
||||
### Improvements
|
||||
|
||||
* (x/ibc) [\#8458](https://github.com/cosmos/cosmos-sdk/pull/8458) Add `packet_connection` attribute to ibc events to enable relayer filtering
|
||||
* (x/bank) [\#8479](https://github.com/cosmos/cosmos-sdk/pull/8479) Adittional client denom metadata validation for `base` and `display` denoms.
|
||||
* (x/ibc) [\#8404](https://github.com/cosmos/cosmos-sdk/pull/8404) Reorder IBC `ChanOpenAck` and `ChanOpenConfirm` handler execution to perform core handler first, followed by application callbacks.
|
||||
* [\#8396](https://github.com/cosmos/cosmos-sdk/pull/8396) Add support for ARM platform
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (x/evidence) [#8461](https://github.com/cosmos/cosmos-sdk/pull/8461) Fix bech32 prefix in evidence validator address conversion
|
||||
* (x/slashing) [\#8427](https://github.com/cosmos/cosmos-sdk/pull/8427) Fix query signing infos command
|
||||
* (simapp) [\#8418](https://github.com/cosmos/cosmos-sdk/pull/8418) Add balance coin to supply when adding a new genesis account
|
||||
* (x/bank) [\#8417](https://github.com/cosmos/cosmos-sdk/pull/8417) Validate balances and coin denom metadata on genesis
|
||||
* (server) [\#8399](https://github.com/cosmos/cosmos-sdk/pull/8399) fix gRPC-web flag default value
|
||||
* (client/keys) [\#8436](https://github.com/cosmos/cosmos-sdk/pull/8436) Fix key migration issue
|
||||
* (server) [\#8481](https://github.com/cosmos/cosmos-sdk/pull/8481) Don't create
|
||||
files when running `{appd} tendermint show-*` subcommands
|
||||
|
||||
## [v0.40.1](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.1) - 2021-01-19
|
||||
|
||||
### Improvements
|
||||
|
||||
* (x/gov) [\#7733](https://github.com/cosmos/cosmos-sdk/pull/7733) ADR 037 Implementation: Governance Split Votes
|
||||
* (x/bank) [\#8302](https://github.com/cosmos/cosmos-sdk/issues/8302) Add gRPC and CLI queries for client denomination metadata.
|
||||
* (tendermint) [\#8316](https://github.com/cosmos/cosmos-sdk/pull/8316) Bump Tendermint version to [v0.34.2](https://github.com/tendermint/tendermint/releases/tag/v0.34.2)
|
||||
* (tendermint) Bump Tendermint version to [v0.34.3](https://github.com/tendermint/tendermint/releases/tag/v0.34.3).
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* (x/bank) [\#8317](https://github.com/cosmos/cosmos-sdk/pull/8317) Fix panic when querying for a not found client denomination metadata.
|
||||
* [\#8085](https://github.com/cosmos/cosmos-sdk/pull/8058) fix zero time checks
|
||||
* [\#8280](https://github.com/cosmos/cosmos-sdk/pull/8280) fix GET /upgrade/current query
|
||||
* (x/auth) [\#8287](https://github.com/cosmos/cosmos-sdk/pull/8287) Fix `tx sign --signature-only` to return correct sequence value in signature.
|
||||
* (x/ibc) [\#8341](https://github.com/cosmos/cosmos-sdk/pull/8341) Fix query latest consensus state.
|
||||
* (build) [\8300](https://github.com/cosmos/cosmos-sdk/pull/8300), [\8301](https://github.com/cosmos/cosmos-sdk/pull/8301) Fix reproducible builds
|
||||
* (types/errors) [\#8355][https://github.com/cosmos/cosmos-sdk/pull/8355] Fix errorWrap `Is` method.
|
||||
* (x/ibc) [\#8359](https://github.com/cosmos/cosmos-sdk/pull/8359) Add missing UnpackInterfaces functions to IBC Query Responses. Fixes 'cannot unpack Any' error for IBC types.
|
||||
* (x/ibc) [\#8341](https://github.com/cosmos/cosmos-sdk/pull/8341) Fix query latest consensus state.
|
||||
* (proto) [\#8350][https://github.com/cosmos/cosmos-sdk/pull/8350], [\#8361](https://github.com/cosmos/cosmos-sdk/pull/8361) Update gogo proto deps with v1.3.2 security fixes
|
||||
* (x/ibc) [\#8359](https://github.com/cosmos/cosmos-sdk/pull/8359) Add missing UnpackInterfaces functions to IBC Query Responses. Fixes 'cannot unpack Any' error for IBC types.
|
||||
* (x/bank) [\#8317](https://github.com/cosmos/cosmos-sdk/pull/8317) Fix panic when querying for a not found client denomination metadata.
|
||||
|
||||
|
||||
## [v0.40.0](https://github.com/cosmos/cosmos-sdk/releases/tag/v0.40.0) - 2021-01-08
|
||||
|
||||
|
||||
17
Makefile
17
Makefile
@ -309,6 +309,11 @@ test-cover:
|
||||
@export VERSION=$(VERSION); bash -x contrib/test_cover.sh
|
||||
.PHONY: test-cover
|
||||
|
||||
test-rosetta:
|
||||
docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile .
|
||||
docker-compose -f contrib/rosetta/docker-compose.yaml up --abort-on-container-exit --exit-code-from test_rosetta --build
|
||||
.PHONY: test-rosetta
|
||||
|
||||
benchmark:
|
||||
@go test -mod=readonly -bench=. $(PACKAGES_NOSIMULATION)
|
||||
.PHONY: benchmark
|
||||
@ -463,3 +468,15 @@ localnet-stop:
|
||||
docker-compose down
|
||||
|
||||
.PHONY: localnet-start localnet-stop
|
||||
|
||||
###############################################################################
|
||||
### rosetta ###
|
||||
###############################################################################
|
||||
# builds rosetta test data dir
|
||||
rosetta-data:
|
||||
-docker container rm data_dir_build
|
||||
docker build -t rosetta-ci:latest -f contrib/rosetta/node/Dockerfile .
|
||||
docker run --name data_dir_build -t rosetta-ci:latest sh /rosetta/data.sh
|
||||
docker cp data_dir_build:/tmp/data.tar.gz "$(CURDIR)/contrib/rosetta/node/data.tar.gz"
|
||||
docker container rm data_dir_build
|
||||
.PHONY: rosetta-data
|
||||
|
||||
@ -221,6 +221,19 @@ func readTxCommandFlags(clientCtx Context, flagSet *pflag.FlagSet) (Context, err
|
||||
clientCtx = clientCtx.WithSignModeStr(signModeStr)
|
||||
}
|
||||
|
||||
if clientCtx.FeeGranter == nil || flagSet.Changed(flags.FlagFeeAccount) {
|
||||
granter, _ := flagSet.GetString(flags.FlagFeeAccount)
|
||||
|
||||
if granter != "" {
|
||||
granterAcc, err := sdk.AccAddressFromBech32(granter)
|
||||
if err != nil {
|
||||
return clientCtx, err
|
||||
}
|
||||
|
||||
clientCtx = clientCtx.WithFeeGranterAddress(granterAcc)
|
||||
}
|
||||
}
|
||||
|
||||
if clientCtx.From == "" || flagSet.Changed(flags.FlagFrom) {
|
||||
from, _ := flagSet.GetString(flags.FlagFrom)
|
||||
fromAddr, fromName, keyType, err := GetFromFields(clientCtx.Keyring, from, clientCtx.GenerateOnly)
|
||||
|
||||
@ -97,8 +97,7 @@ func TestSetCmdClientContextHandler(t *testing.T) {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
ctx = context.WithValue(ctx, client.ClientContextKey, &client.Context{})
|
||||
ctx := context.WithValue(context.Background(), client.ClientContextKey, &client.Context{})
|
||||
|
||||
cmd := newCmd()
|
||||
_ = testutil.ApplyMockIODiscardOutErr(cmd)
|
||||
|
||||
@ -44,6 +44,7 @@ type Context struct {
|
||||
TxConfig TxConfig
|
||||
AccountRetriever AccountRetriever
|
||||
NodeURI string
|
||||
FeeGranter sdk.AccAddress
|
||||
|
||||
// TODO: Deprecated (remove).
|
||||
LegacyAmino *codec.LegacyAmino
|
||||
@ -166,6 +167,13 @@ func (ctx Context) WithFromAddress(addr sdk.AccAddress) Context {
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithFeeGranterAddress returns a copy of the context with an updated fee granter account
|
||||
// address.
|
||||
func (ctx Context) WithFeeGranterAddress(addr sdk.AccAddress) Context {
|
||||
ctx.FeeGranter = addr
|
||||
return ctx
|
||||
}
|
||||
|
||||
// WithBroadcastMode returns a copy of the context with an updated broadcast
|
||||
// mode.
|
||||
func (ctx Context) WithBroadcastMode(mode string) Context {
|
||||
|
||||
@ -70,6 +70,7 @@ const (
|
||||
FlagCountTotal = "count-total"
|
||||
FlagTimeoutHeight = "timeout-height"
|
||||
FlagKeyAlgorithm = "algo"
|
||||
FlagFeeAccount = "fee-account"
|
||||
|
||||
// Tendermint logging flags
|
||||
FlagLogLevel = "log_level"
|
||||
@ -112,6 +113,7 @@ func AddTxFlagsToCmd(cmd *cobra.Command) {
|
||||
cmd.Flags().String(FlagKeyringBackend, DefaultKeyringBackend, "Select keyring's backend (os|file|kwallet|pass|test)")
|
||||
cmd.Flags().String(FlagSignMode, "", "Choose sign mode (direct|amino-json), this is an advanced feature")
|
||||
cmd.Flags().Uint64(FlagTimeoutHeight, 0, "Set a block timeout height to prevent the tx from being committed past a certain height")
|
||||
cmd.Flags().String(FlagFeeAccount, "", "Fee account pays fees for the transaction instead of deducting from the signer")
|
||||
|
||||
// --gas can accept integers and "auto"
|
||||
cmd.Flags().String(FlagGas, "", fmt.Sprintf("gas limit to set per-transaction; set to %q to calculate sufficient gas automatically (default %d)", GasFlagAuto, DefaultGasLimit))
|
||||
|
||||
@ -8,12 +8,12 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getBlock(clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
func getBlock(ctx context.Context, clientCtx client.Context, height *int64) (*ctypes.ResultBlock, error) {
|
||||
// get the node
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return node.Block(context.Background(), height)
|
||||
return node.Block(ctx, height)
|
||||
}
|
||||
|
||||
@ -34,8 +34,8 @@ func NewQueryServer(clientCtx client.Context, interfaceRegistry codectypes.Inter
|
||||
}
|
||||
|
||||
// GetSyncing implements ServiceServer.GetSyncing
|
||||
func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
func (s queryServer) GetSyncing(ctx context.Context, _ *GetSyncingRequest) (*GetSyncingResponse, error) {
|
||||
status, err := getNodeStatus(ctx, s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -45,8 +45,8 @@ func (s queryServer) GetSyncing(_ context.Context, _ *GetSyncingRequest) (*GetSy
|
||||
}
|
||||
|
||||
// GetLatestBlock implements ServiceServer.GetLatestBlock
|
||||
func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*GetLatestBlockResponse, error) {
|
||||
status, err := getBlock(s.clientCtx, nil)
|
||||
func (s queryServer) GetLatestBlock(ctx context.Context, _ *GetLatestBlockRequest) (*GetLatestBlockResponse, error) {
|
||||
status, err := getBlock(ctx, s.clientCtx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -64,7 +64,7 @@ func (s queryServer) GetLatestBlock(context.Context, *GetLatestBlockRequest) (*G
|
||||
}
|
||||
|
||||
// GetBlockByHeight implements ServiceServer.GetBlockByHeight
|
||||
func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) {
|
||||
func (s queryServer) GetBlockByHeight(ctx context.Context, req *GetBlockByHeightRequest) (*GetBlockByHeightResponse, error) {
|
||||
chainHeight, err := rpc.GetChainHeight(s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -74,7 +74,7 @@ func (s queryServer) GetBlockByHeight(_ context.Context, req *GetBlockByHeightRe
|
||||
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
|
||||
}
|
||||
|
||||
res, err := getBlock(s.clientCtx, &req.Height)
|
||||
res, err := getBlock(ctx, s.clientCtx, &req.Height)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -96,7 +96,7 @@ func (s queryServer) GetLatestValidatorSet(ctx context.Context, req *GetLatestVa
|
||||
return nil, err
|
||||
}
|
||||
|
||||
validatorsRes, err := rpc.GetValidators(s.clientCtx, nil, &page, &limit)
|
||||
validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, nil, &page, &limit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -147,7 +147,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida
|
||||
return nil, status.Error(codes.InvalidArgument, "requested block height is bigger then the chain length")
|
||||
}
|
||||
|
||||
validatorsRes, err := rpc.GetValidators(s.clientCtx, &req.Height, &page, &limit)
|
||||
validatorsRes, err := rpc.GetValidators(ctx, s.clientCtx, &req.Height, &page, &limit)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -175,7 +175,7 @@ func (s queryServer) GetValidatorSetByHeight(ctx context.Context, req *GetValida
|
||||
|
||||
// GetNodeInfo implements ServiceServer.GetNodeInfo
|
||||
func (s queryServer) GetNodeInfo(ctx context.Context, req *GetNodeInfoRequest) (*GetNodeInfoResponse, error) {
|
||||
status, err := getNodeStatus(s.clientCtx)
|
||||
status, err := getNodeStatus(ctx, s.clientCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@ -8,10 +8,10 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
)
|
||||
|
||||
func getNodeStatus(clientCtx client.Context) (*ctypes.ResultStatus, error) {
|
||||
func getNodeStatus(ctx context.Context, clientCtx client.Context) (*ctypes.ResultStatus, error) {
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return &ctypes.ResultStatus{}, err
|
||||
}
|
||||
return node.Status(context.Background())
|
||||
return node.Status(ctx)
|
||||
}
|
||||
|
||||
@ -62,7 +62,7 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
var (
|
||||
tmpDir string
|
||||
migrator keyring.InfoImporter
|
||||
migrator keyring.Importer
|
||||
)
|
||||
|
||||
if dryRun, _ := cmd.Flags().GetBool(flags.FlagDryRun); dryRun {
|
||||
@ -73,10 +73,10 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||
|
||||
defer os.RemoveAll(tmpDir)
|
||||
|
||||
migrator, err = keyring.NewInfoImporter(keyringServiceName, "test", tmpDir, buf)
|
||||
migrator, err = keyring.New(keyringServiceName, keyring.BackendTest, tmpDir, buf)
|
||||
} else {
|
||||
backend, _ := cmd.Flags().GetString(flags.FlagKeyringBackend)
|
||||
migrator, err = keyring.NewInfoImporter(keyringServiceName, backend, rootDir, buf)
|
||||
migrator, err = keyring.New(keyringServiceName, backend, rootDir, buf)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
@ -86,12 +86,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||
))
|
||||
}
|
||||
|
||||
for _, key := range oldKeys {
|
||||
legKeyInfo, err := legacyKb.Export(key.GetName())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(oldKeys) == 0 {
|
||||
cmd.Print("Migration Aborted: no keys to migrate")
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, key := range oldKeys {
|
||||
keyName := key.GetName()
|
||||
keyType := key.GetType()
|
||||
|
||||
@ -107,7 +107,12 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
if keyType != keyring.TypeLocal {
|
||||
if err := migrator.Import(keyName, legKeyInfo); err != nil {
|
||||
pubkeyArmor, err := legacyKb.ExportPubKey(keyName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrator.ImportPubKey(keyName, pubkeyArmor); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -127,10 +132,11 @@ func runMigrateCmd(cmd *cobra.Command, args []string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := migrator.Import(keyName, armoredPriv); err != nil {
|
||||
if err := migrator.ImportPrivKey(keyName, armoredPriv, migratePassphrase); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
cmd.Print("Migration Complete")
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
@ -86,7 +85,7 @@ hexadecimal into bech32 cosmos prefixed format and vice versa.
|
||||
}
|
||||
|
||||
func parseKey(cmd *cobra.Command, args []string) error {
|
||||
config, _ := sdk.GetSealedConfig(context.Background())
|
||||
config, _ := sdk.GetSealedConfig(cmd.Context())
|
||||
return doParseKey(cmd, config, args)
|
||||
}
|
||||
|
||||
|
||||
BIN
client/keys/testdata/keys/keys.db/000002.ldb
vendored
BIN
client/keys/testdata/keys/keys.db/000002.ldb
vendored
Binary file not shown.
2
client/keys/testdata/keys/keys.db/CURRENT
vendored
2
client/keys/testdata/keys/keys.db/CURRENT
vendored
@ -1 +1 @@
|
||||
MANIFEST-000004
|
||||
MANIFEST-000005
|
||||
|
||||
@ -1 +1 @@
|
||||
MANIFEST-000000
|
||||
MANIFEST-000003
|
||||
|
||||
48
client/keys/testdata/keys/keys.db/LOG
vendored
48
client/keys/testdata/keys/keys.db/LOG
vendored
@ -1,18 +1,30 @@
|
||||
=============== Mar 30, 2020 (CEST) ===============
|
||||
02:07:34.137606 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
02:07:34.144547 db@open opening
|
||||
02:07:34.144770 version@stat F·[] S·0B[] Sc·[]
|
||||
02:07:34.145843 db@janitor F·2 G·0
|
||||
02:07:34.145875 db@open done T·1.315251ms
|
||||
02:07:34.335635 db@close closing
|
||||
02:07:34.335736 db@close done T·98.95µs
|
||||
=============== Mar 30, 2020 (CEST) ===============
|
||||
02:08:33.239115 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
02:08:33.239264 version@stat F·[] S·0B[] Sc·[]
|
||||
02:08:33.239281 db@open opening
|
||||
02:08:33.239310 journal@recovery F·1
|
||||
02:08:33.239398 journal@recovery recovering @1
|
||||
02:08:33.322008 memdb@flush created L0@2 N·4 S·391B "cos..ess,v4":"run..nfo,v3"
|
||||
02:08:33.323091 version@stat F·[1] S·391B[391B] Sc·[0.25]
|
||||
02:08:33.421979 db@janitor F·3 G·0
|
||||
02:08:33.422153 db@open done T·182.707962ms
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:25.348369 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:25.350695 db@open opening
|
||||
00:03:25.350888 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:25.351864 db@janitor F·2 G·0
|
||||
00:03:25.351881 db@open done T·1.169825ms
|
||||
00:03:25.351895 db@close closing
|
||||
00:03:25.351929 db@close done T·33.042µs
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:34.450638 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:34.450722 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:34.450737 db@open opening
|
||||
00:03:34.450765 journal@recovery F·1
|
||||
00:03:34.450851 journal@recovery recovering @1
|
||||
00:03:34.451173 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:34.454278 db@janitor F·2 G·0
|
||||
00:03:34.454298 db@open done T·3.548046ms
|
||||
00:03:34.454307 db@close closing
|
||||
00:03:34.454327 db@close done T·19.017µs
|
||||
=============== Feb 2, 2021 (IST) ===============
|
||||
00:03:42.025705 log@legend F·NumFile S·FileSize N·Entry C·BadEntry B·BadBlock Ke·KeyError D·DroppedEntry L·Level Q·SeqNum T·TimeElapsed
|
||||
00:03:42.025892 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:42.025907 db@open opening
|
||||
00:03:42.025943 journal@recovery F·1
|
||||
00:03:42.026790 journal@recovery recovering @2
|
||||
00:03:42.026946 version@stat F·[] S·0B[] Sc·[]
|
||||
00:03:42.031645 db@janitor F·2 G·0
|
||||
00:03:42.031661 db@open done T·5.750008ms
|
||||
00:03:42.283102 db@close closing
|
||||
00:03:42.283162 db@close done T·58.775µs
|
||||
|
||||
BIN
client/keys/testdata/keys/keys.db/MANIFEST-000004
vendored
BIN
client/keys/testdata/keys/keys.db/MANIFEST-000004
vendored
Binary file not shown.
BIN
client/keys/testdata/keys/keys.db/MANIFEST-000005
vendored
Normal file
BIN
client/keys/testdata/keys/keys.db/MANIFEST-000005
vendored
Normal file
Binary file not shown.
@ -57,6 +57,11 @@ func (ctx Context) GetFromAddress() sdk.AccAddress {
|
||||
return ctx.FromAddress
|
||||
}
|
||||
|
||||
// GetFeeGranterAddress returns the fee granter address from the context
|
||||
func (ctx Context) GetFeeGranterAddress() sdk.AccAddress {
|
||||
return ctx.FeeGranter
|
||||
}
|
||||
|
||||
// GetFromName returns the key name for the current context.
|
||||
func (ctx Context) GetFromName() string {
|
||||
return ctx.FromName
|
||||
|
||||
@ -50,7 +50,7 @@ func ValidatorCommand() *cobra.Command {
|
||||
page, _ := cmd.Flags().GetInt(flags.FlagPage)
|
||||
limit, _ := cmd.Flags().GetInt(flags.FlagLimit)
|
||||
|
||||
result, err := GetValidators(clientCtx, height, &page, &limit)
|
||||
result, err := GetValidators(cmd.Context(), clientCtx, height, &page, &limit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -117,14 +117,14 @@ func validatorOutput(validator *tmtypes.Validator) (ValidatorOutput, error) {
|
||||
}
|
||||
|
||||
// GetValidators from client
|
||||
func GetValidators(clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) {
|
||||
func GetValidators(ctx context.Context, clientCtx client.Context, height *int64, page, limit *int) (ResultValidatorsOutput, error) {
|
||||
// get the node
|
||||
node, err := clientCtx.GetNode()
|
||||
if err != nil {
|
||||
return ResultValidatorsOutput{}, err
|
||||
}
|
||||
|
||||
validatorsRes, err := node.Validators(context.Background(), height, page, limit)
|
||||
validatorsRes, err := node.Validators(ctx, height, page, limit)
|
||||
if err != nil {
|
||||
return ResultValidatorsOutput{}, err
|
||||
}
|
||||
@ -172,7 +172,7 @@ func ValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFunc {
|
||||
return
|
||||
}
|
||||
|
||||
output, err := GetValidators(clientCtx, &height, &page, &limit)
|
||||
output, err := GetValidators(r.Context(), clientCtx, &height, &page, &limit)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
@ -189,7 +189,7 @@ func LatestValidatorSetRequestHandlerFn(clientCtx client.Context) http.HandlerFu
|
||||
return
|
||||
}
|
||||
|
||||
output, err := GetValidators(clientCtx, nil, &page, &limit)
|
||||
output, err := GetValidators(r.Context(), clientCtx, nil, &page, &limit)
|
||||
if rest.CheckInternalServerError(w, err) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -117,6 +117,7 @@ func BroadcastTx(clientCtx client.Context, txf Factory, msgs ...sdk.Msg) error {
|
||||
}
|
||||
}
|
||||
|
||||
tx.SetFeeGranter(clientCtx.GetFeeGranterAddress())
|
||||
err = Sign(txf, clientCtx.GetFromName(), tx, true)
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@ -42,5 +42,6 @@ type (
|
||||
SetFeeAmount(amount sdk.Coins)
|
||||
SetGasLimit(limit uint64)
|
||||
SetTimeoutHeight(height uint64)
|
||||
SetFeeGranter(feeGranter sdk.AccAddress)
|
||||
}
|
||||
)
|
||||
|
||||
@ -113,8 +113,3 @@ func (any *Any) pack(x proto.Message) error {
|
||||
func (any *Any) GetCachedValue() interface{} {
|
||||
return any.cachedValue
|
||||
}
|
||||
|
||||
// ClearCachedValue clears the cached value from the Any
|
||||
func (any *Any) ClearCachedValue() {
|
||||
any.cachedValue = nil
|
||||
}
|
||||
|
||||
53
codec/types/any_internal_test.go
Normal file
53
codec/types/any_internal_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/gogo/protobuf/proto"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
type Dog struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=size,proto3" json:"size,omitempty"`
|
||||
}
|
||||
|
||||
func (d Dog) Greet() string { return d.Name }
|
||||
|
||||
// We implement a minimal proto.Message interface
|
||||
func (d *Dog) Reset() { d.Name = "" }
|
||||
func (d *Dog) String() string { return d.Name }
|
||||
func (d *Dog) ProtoMessage() {}
|
||||
func (d *Dog) XXX_MessageName() string { return "tests/dog" }
|
||||
|
||||
type Animal interface {
|
||||
Greet() string
|
||||
}
|
||||
|
||||
var _ Animal = (*Dog)(nil)
|
||||
var _ proto.Message = (*Dog)(nil)
|
||||
|
||||
func TestAnyPackUnpack(t *testing.T) {
|
||||
registry := NewInterfaceRegistry()
|
||||
registry.RegisterInterface("Animal", (*Animal)(nil))
|
||||
registry.RegisterImplementations(
|
||||
(*Animal)(nil),
|
||||
&Dog{},
|
||||
)
|
||||
|
||||
spot := &Dog{Name: "Spot"}
|
||||
var animal Animal
|
||||
|
||||
// with cache
|
||||
any, err := NewAnyWithValue(spot)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, any.GetCachedValue())
|
||||
err = registry.UnpackAny(any, &animal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, animal)
|
||||
|
||||
// without cache
|
||||
any.cachedValue = nil
|
||||
err = registry.UnpackAny(any, &animal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, animal)
|
||||
}
|
||||
@ -9,16 +9,15 @@ import (
|
||||
"github.com/gogo/protobuf/grpc"
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
"github.com/gogo/protobuf/proto"
|
||||
grpc2 "google.golang.org/grpc"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
grpc2 "google.golang.org/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
"github.com/cosmos/cosmos-sdk/codec/types"
|
||||
"github.com/cosmos/cosmos-sdk/testutil/testdata"
|
||||
)
|
||||
|
||||
func TestPackUnpack(t *testing.T) {
|
||||
func TestAnyPackUnpack(t *testing.T) {
|
||||
registry := testdata.NewTestInterfaceRegistry()
|
||||
|
||||
spot := &testdata.Dog{Name: "Spot"}
|
||||
@ -31,12 +30,6 @@ func TestPackUnpack(t *testing.T) {
|
||||
err = registry.UnpackAny(any, &animal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, animal)
|
||||
|
||||
// without cache
|
||||
any.ClearCachedValue()
|
||||
err = registry.UnpackAny(any, &animal)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, spot, animal)
|
||||
}
|
||||
|
||||
type TestI interface {
|
||||
|
||||
24
contrib/rosetta/README.md
Normal file
24
contrib/rosetta/README.md
Normal file
@ -0,0 +1,24 @@
|
||||
# rosetta
|
||||
|
||||
This directory contains the files required to run the rosetta CI. It builds `simapp` based on the current codebase.
|
||||
|
||||
## docker-compose.yaml
|
||||
|
||||
Builds:
|
||||
- cosmos-sdk simapp node, with prefixed data directory, keys etc. This is required to test historical balances.
|
||||
- faucet is required so we can test construction API, it was literally impossible to put there a deterministic address to request funds for
|
||||
- rosetta is the rosetta node used by rosetta-cli to interact with the cosmos-sdk app
|
||||
- test_rosetta runs the rosetta-cli test against construction API and data API
|
||||
|
||||
## configuration
|
||||
|
||||
Contains the required files to set up rosetta cli and make it work against its workflows
|
||||
|
||||
## node
|
||||
|
||||
Contains the files for a deterministic network, with fixed keys and some actions on there, to test parsing of msgs and historical balances.
|
||||
|
||||
## Notes
|
||||
|
||||
- Keyring password is 12345678
|
||||
- data.sh creates node data, it's required in case consensus breaking changes are made to quickly recreate replicable node data for rosetta
|
||||
12
contrib/rosetta/configuration/bootstrap.json
Normal file
12
contrib/rosetta/configuration/bootstrap.json
Normal file
@ -0,0 +1,12 @@
|
||||
[
|
||||
{
|
||||
"account_identifier": {
|
||||
"address":"cosmos158nkd0l9tyemv2crp579rmj8dg37qty8lzff88"
|
||||
},
|
||||
"currency":{
|
||||
"symbol":"stake",
|
||||
"decimals":0
|
||||
},
|
||||
"value": "999990000000"
|
||||
}
|
||||
]
|
||||
59
contrib/rosetta/configuration/data.sh
Normal file
59
contrib/rosetta/configuration/data.sh
Normal file
@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
wait_simd() {
|
||||
timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' localhost 9090
|
||||
}
|
||||
# this script is used to recreate the data dir
|
||||
echo clearing /root/.simapp
|
||||
rm -rf /root/.simapp
|
||||
echo initting new chain
|
||||
# init config files
|
||||
simd init simd --chain-id testing
|
||||
|
||||
# create accounts
|
||||
simd keys add fd --keyring-backend=test
|
||||
|
||||
addr=$(simd keys show fd -a --keyring-backend=test)
|
||||
val_addr=$(simd keys show fd --keyring-backend=test --bech val -a)
|
||||
|
||||
# give the accounts some money
|
||||
simd add-genesis-account "$addr" 1000000000000stake --keyring-backend=test
|
||||
|
||||
# save configs for the daemon
|
||||
simd gentx fd 10000000stake --chain-id testing --keyring-backend=test
|
||||
|
||||
# input genTx to the genesis file
|
||||
simd collect-gentxs
|
||||
# verify genesis file is fine
|
||||
simd validate-genesis
|
||||
echo changing network settings
|
||||
sed -i 's/127.0.0.1/0.0.0.0/g' /root/.simapp/config/config.toml
|
||||
|
||||
# start simd
|
||||
echo starting simd...
|
||||
simd start --pruning=nothing &
|
||||
pid=$!
|
||||
echo simd started with PID $pid
|
||||
|
||||
echo awaiting for simd to be ready
|
||||
wait_simd
|
||||
echo simd is ready
|
||||
sleep 10
|
||||
|
||||
|
||||
# send transaction to deterministic address
|
||||
echo sending transaction with addr $addr
|
||||
simd tx bank send "$addr" cosmos1wjmt63j4fv9nqda92nsrp2jp2vsukcke4va3pt 100stake --yes --keyring-backend=test --broadcast-mode=block --chain-id=testing
|
||||
|
||||
sleep 10
|
||||
|
||||
echo stopping simd...
|
||||
kill -9 $pid
|
||||
|
||||
echo zipping data dir and saving to /tmp/data.tar.gz
|
||||
|
||||
tar -czvf /tmp/data.tar.gz /root/.simapp
|
||||
|
||||
echo new address for bootstrap.json "$addr" "$val_addr"
|
||||
25
contrib/rosetta/configuration/faucet.py
Normal file
25
contrib/rosetta/configuration/faucet.py
Normal file
@ -0,0 +1,25 @@
|
||||
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||
import subprocess
|
||||
|
||||
import os
|
||||
|
||||
|
||||
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler):
|
||||
|
||||
def do_POST(self):
|
||||
try:
|
||||
content_len = int(self.headers.get('Content-Length'))
|
||||
addr = self.rfile.read(content_len).decode("utf-8")
|
||||
print("sending funds to " + addr)
|
||||
subprocess.call(['sh', './send_funds.sh', addr])
|
||||
self.send_response(200)
|
||||
self.end_headers()
|
||||
except Exception as e:
|
||||
print("failed " + str(e))
|
||||
os._exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
print("starting faucet server...")
|
||||
httpd = HTTPServer(('0.0.0.0', 8000), SimpleHTTPRequestHandler)
|
||||
httpd.serve_forever()
|
||||
51
contrib/rosetta/configuration/rosetta.json
Normal file
51
contrib/rosetta/configuration/rosetta.json
Normal file
@ -0,0 +1,51 @@
|
||||
{
|
||||
"network": {
|
||||
"blockchain": "app",
|
||||
"network": "network"
|
||||
},
|
||||
"online_url": "http://rosetta:8080",
|
||||
"data_directory": "",
|
||||
"http_timeout": 300,
|
||||
"max_retries": 5,
|
||||
"retry_elapsed_time": 0,
|
||||
"max_online_connections": 0,
|
||||
"max_sync_concurrency": 0,
|
||||
"tip_delay": 60,
|
||||
"log_configuration": true,
|
||||
"construction": {
|
||||
"offline_url": "http://rosetta:8080",
|
||||
"max_offline_connections": 0,
|
||||
"stale_depth": 0,
|
||||
"broadcast_limit": 0,
|
||||
"ignore_broadcast_failures": false,
|
||||
"clear_broadcasts": false,
|
||||
"broadcast_behind_tip": false,
|
||||
"block_broadcast_limit": 0,
|
||||
"rebroadcast_all": false,
|
||||
"constructor_dsl_file": "transfer.ros",
|
||||
"end_conditions": {
|
||||
"create_account": 1,
|
||||
"transfer": 3
|
||||
}
|
||||
},
|
||||
"data": {
|
||||
"active_reconciliation_concurrency": 0,
|
||||
"inactive_reconciliation_concurrency": 0,
|
||||
"inactive_reconciliation_frequency": 0,
|
||||
"log_blocks": false,
|
||||
"log_transactions": false,
|
||||
"log_balance_changes": false,
|
||||
"log_reconciliations": false,
|
||||
"ignore_reconciliation_error": false,
|
||||
"exempt_accounts": "",
|
||||
"bootstrap_balances": "bootstrap.json",
|
||||
"interesting_accounts": "",
|
||||
"reconciliation_disabled": false,
|
||||
"inactive_discrepency_search_disabled": false,
|
||||
"balance_tracking_disabled": false,
|
||||
"coin_tracking_disabled": false,
|
||||
"end_conditions": {
|
||||
"tip": true
|
||||
}
|
||||
}
|
||||
}
|
||||
29
contrib/rosetta/configuration/run_tests.sh
Executable file
29
contrib/rosetta/configuration/run_tests.sh
Executable file
@ -0,0 +1,29 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
|
||||
addr="abcd"
|
||||
|
||||
send_tx() {
|
||||
echo '12345678' | simd tx bank send $addr "$1" "$2"
|
||||
}
|
||||
|
||||
detect_account() {
|
||||
line=$1
|
||||
}
|
||||
|
||||
wait_for_rosetta() {
|
||||
timeout 30 sh -c 'until nc -z $0 $1; do sleep 1; done' rosetta 8080
|
||||
}
|
||||
|
||||
echo "waiting for rosetta instance to be up"
|
||||
wait_for_rosetta
|
||||
|
||||
echo "checking data API"
|
||||
rosetta-cli check:data --configuration-file ./config/rosetta.json
|
||||
|
||||
echo "checking construction API"
|
||||
rosetta-cli check:construction --configuration-file ./config/rosetta.json
|
||||
|
||||
echo "checking staking API"
|
||||
rosetta-cli check:construction --configuration-file ./config/staking.json
|
||||
5
contrib/rosetta/configuration/send_funds.sh
Normal file
5
contrib/rosetta/configuration/send_funds.sh
Normal file
@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
|
||||
set -e
|
||||
addr=$(simd keys show fd -a --keyring-backend=test)
|
||||
echo "12345678" | simd tx bank send "$addr" "$1" 100stake --chain-id="testing" --node tcp://cosmos:26657 --yes --keyring-backend=test
|
||||
30
contrib/rosetta/configuration/staking.json
Normal file
30
contrib/rosetta/configuration/staking.json
Normal file
@ -0,0 +1,30 @@
|
||||
{
|
||||
"network": {
|
||||
"blockchain": "app",
|
||||
"network": "network"
|
||||
},
|
||||
"online_url": "http://rosetta:8080",
|
||||
"data_directory": "",
|
||||
"http_timeout": 300,
|
||||
"max_retries": 5,
|
||||
"retry_elapsed_time": 0,
|
||||
"max_online_connections": 0,
|
||||
"max_sync_concurrency": 0,
|
||||
"tip_delay": 60,
|
||||
"log_configuration": true,
|
||||
"construction": {
|
||||
"offline_url": "http://rosetta:8080",
|
||||
"max_offline_connections": 0,
|
||||
"stale_depth": 0,
|
||||
"broadcast_limit": 0,
|
||||
"ignore_broadcast_failures": false,
|
||||
"clear_broadcasts": false,
|
||||
"broadcast_behind_tip": false,
|
||||
"block_broadcast_limit": 0,
|
||||
"rebroadcast_all": false,
|
||||
"constructor_dsl_file": "staking.ros",
|
||||
"end_conditions": {
|
||||
"staking": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
147
contrib/rosetta/configuration/staking.ros
Normal file
147
contrib/rosetta/configuration/staking.ros
Normal file
@ -0,0 +1,147 @@
|
||||
request_funds(1){
|
||||
find_account{
|
||||
currency = {"symbol":"stake", "decimals":0};
|
||||
random_account = find_balance({
|
||||
"minimum_balance":{
|
||||
"value": "0",
|
||||
"currency": {{currency}}
|
||||
},
|
||||
"create_limit":1
|
||||
});
|
||||
},
|
||||
send_funds{
|
||||
account_identifier = {{random_account.account_identifier}};
|
||||
address = {{account_identifier.address}};
|
||||
idk = http_request({
|
||||
"method": "POST",
|
||||
"url": "http:\/\/faucet:8000",
|
||||
"timeout": 10,
|
||||
"body": {{random_account.account_identifier.address}}
|
||||
});
|
||||
},
|
||||
// Create a separate scenario to request funds so that
|
||||
// the address we are using to request funds does not
|
||||
// get rolled back if funds do not yet exist.
|
||||
request{
|
||||
loaded_account = find_balance({
|
||||
"account_identifier": {{random_account.account_identifier}},
|
||||
"minimum_balance":{
|
||||
"value": "100",
|
||||
"currency": {{currency}}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
create_account(1){
|
||||
create{
|
||||
network = {"network":"network", "blockchain":"app"};
|
||||
key = generate_key({"curve_type": "secp256k1"});
|
||||
account = derive({
|
||||
"network_identifier": {{network}},
|
||||
"public_key": {{key.public_key}}
|
||||
});
|
||||
// If the account is not saved, the key will be lost!
|
||||
save_account({
|
||||
"account_identifier": {{account.account_identifier}},
|
||||
"keypair": {{key}}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
staking(1){
|
||||
stake{
|
||||
stake.network = {"network":"network", "blockchain":"app"};
|
||||
currency = {"symbol":"stake", "decimals":0};
|
||||
sender = find_balance({
|
||||
"minimum_balance":{
|
||||
"value": "100",
|
||||
"currency": {{currency}}
|
||||
}
|
||||
});
|
||||
// Set the recipient_amount as some value <= sender.balance-max_fee
|
||||
max_fee = "0";
|
||||
fee_amount = "1";
|
||||
fee_value = 0 - {{fee_amount}};
|
||||
available_amount = {{sender.balance.value}} - {{max_fee}};
|
||||
recipient_amount = "1";
|
||||
print_message({"recipient_amount":{{recipient_amount}}});
|
||||
// Find recipient and construct operations
|
||||
recipient = {{sender.account_identifier}};
|
||||
sender_amount = 0 - {{recipient_amount}};
|
||||
stake.confirmation_depth = "1";
|
||||
stake.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.staking.v1beta1.MsgDelegate",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.staking.v1beta1.MsgDelegate",
|
||||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
},
|
||||
undelegate{
|
||||
print_message({"undelegate":{{sender}}});
|
||||
|
||||
undelegate.network = {"network":"network", "blockchain":"app"};
|
||||
undelegate.confirmation_depth = "1";
|
||||
undelegate.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.staking.v1beta1.MsgUndelegate",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.staking.v1beta1.MsgUndelegate",
|
||||
"account": {
|
||||
"address": "staking_account",
|
||||
"sub_account": {
|
||||
"address" : "cosmosvaloper158nkd0l9tyemv2crp579rmj8dg37qty86kaut5"
|
||||
}
|
||||
},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
109
contrib/rosetta/configuration/transfer.ros
Normal file
109
contrib/rosetta/configuration/transfer.ros
Normal file
@ -0,0 +1,109 @@
|
||||
request_funds(1){
|
||||
find_account{
|
||||
currency = {"symbol":"stake", "decimals":0};
|
||||
random_account = find_balance({
|
||||
"minimum_balance":{
|
||||
"value": "0",
|
||||
"currency": {{currency}}
|
||||
},
|
||||
"create_limit":1
|
||||
});
|
||||
},
|
||||
send_funds{
|
||||
account_identifier = {{random_account.account_identifier}};
|
||||
address = {{account_identifier.address}};
|
||||
idk = http_request({
|
||||
"method": "POST",
|
||||
"url": "http:\/\/faucet:8000",
|
||||
"timeout": 10,
|
||||
"body": {{random_account.account_identifier.address}}
|
||||
});
|
||||
},
|
||||
// Create a separate scenario to request funds so that
|
||||
// the address we are using to request funds does not
|
||||
// get rolled back if funds do not yet exist.
|
||||
request{
|
||||
loaded_account = find_balance({
|
||||
"account_identifier": {{random_account.account_identifier}},
|
||||
"minimum_balance":{
|
||||
"value": "100",
|
||||
"currency": {{currency}}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
create_account(1){
|
||||
create{
|
||||
network = {"network":"network", "blockchain":"app"};
|
||||
key = generate_key({"curve_type": "secp256k1"});
|
||||
account = derive({
|
||||
"network_identifier": {{network}},
|
||||
"public_key": {{key.public_key}}
|
||||
});
|
||||
// If the account is not saved, the key will be lost!
|
||||
save_account({
|
||||
"account_identifier": {{account.account_identifier}},
|
||||
"keypair": {{key}}
|
||||
});
|
||||
}
|
||||
}
|
||||
transfer(3){
|
||||
transfer{
|
||||
transfer.network = {"network":"network", "blockchain":"app"};
|
||||
currency = {"symbol":"stake", "decimals":0};
|
||||
sender = find_balance({
|
||||
"minimum_balance":{
|
||||
"value": "100",
|
||||
"currency": {{currency}}
|
||||
}
|
||||
});
|
||||
// Set the recipient_amount as some value <= sender.balance-max_fee
|
||||
max_fee = "0";
|
||||
fee_amount = "1";
|
||||
fee_value = 0 - {{fee_amount}};
|
||||
available_amount = {{sender.balance.value}} - {{max_fee}};
|
||||
recipient_amount = random_number({"minimum": "1", "maximum": {{available_amount}}});
|
||||
print_message({"recipient_amount":{{recipient_amount}}});
|
||||
// Find recipient and construct operations
|
||||
sender_amount = 0 - {{recipient_amount}};
|
||||
recipient = find_balance({
|
||||
"not_account_identifier":[{{sender.account_identifier}}],
|
||||
"minimum_balance":{
|
||||
"value": "0",
|
||||
"currency": {{currency}}
|
||||
},
|
||||
"create_limit": 100,
|
||||
"create_probability": 50
|
||||
});
|
||||
transfer.confirmation_depth = "1";
|
||||
transfer.operations = [
|
||||
{
|
||||
"operation_identifier":{"index":0},
|
||||
"type":"fee",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{fee_value}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":1},
|
||||
"type":"cosmos.bank.v1beta1.MsgSend",
|
||||
"account":{{sender.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{sender_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
},
|
||||
{
|
||||
"operation_identifier":{"index":2},
|
||||
"type":"cosmos.bank.v1beta1.MsgSend",
|
||||
"account":{{recipient.account_identifier}},
|
||||
"amount":{
|
||||
"value":{{recipient_amount}},
|
||||
"currency":{{currency}}
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
39
contrib/rosetta/docker-compose.yaml
Normal file
39
contrib/rosetta/docker-compose.yaml
Normal file
@ -0,0 +1,39 @@
|
||||
version: "3"
|
||||
|
||||
services:
|
||||
cosmos:
|
||||
image: rosetta-ci:latest
|
||||
command: ["simd", "start", "--pruning", "nothing", "--grpc-web.enable", "true", "--grpc-web.address", "0.0.0.0:9091"]
|
||||
ports:
|
||||
- 9090:9090
|
||||
- 26657:26657
|
||||
logging:
|
||||
driver: "none"
|
||||
|
||||
rosetta:
|
||||
image: rosetta-ci:latest
|
||||
command: [
|
||||
"simd",
|
||||
"rosetta",
|
||||
"--blockchain", "app",
|
||||
"--network", "network",
|
||||
"--tendermint", "cosmos:26657",
|
||||
"--grpc", "cosmos:9090",
|
||||
"--addr", ":8080",
|
||||
]
|
||||
ports:
|
||||
- 8080:8080
|
||||
|
||||
faucet:
|
||||
image: rosetta-ci:latest
|
||||
working_dir: /rosetta
|
||||
command: ["python3", "faucet.py"]
|
||||
expose:
|
||||
- 8080
|
||||
|
||||
test_rosetta:
|
||||
image: tendermintdev/rosetta-cli:v0.6.6
|
||||
volumes:
|
||||
- ./configuration:/rosetta/config:z
|
||||
command: ["./config/run_tests.sh"]
|
||||
working_dir: /rosetta
|
||||
32
contrib/rosetta/node/Dockerfile
Normal file
32
contrib/rosetta/node/Dockerfile
Normal file
@ -0,0 +1,32 @@
|
||||
FROM golang:1.15-alpine as build
|
||||
|
||||
RUN apk add --no-cache tar
|
||||
|
||||
# prepare node data
|
||||
WORKDIR /node
|
||||
COPY ./contrib/rosetta/node/data.tar.gz data.tar.gz
|
||||
RUN tar -zxvf data.tar.gz -C .
|
||||
|
||||
# build simd
|
||||
WORKDIR /simd
|
||||
COPY . ./
|
||||
RUN go build -o simd ./simapp/simd/
|
||||
|
||||
FROM alpine
|
||||
RUN apk add gcc libc-dev python3 --no-cache
|
||||
|
||||
ENV PATH=$PATH:/bin
|
||||
|
||||
COPY --from=build /simd/simd /bin/simd
|
||||
|
||||
WORKDIR /rosetta
|
||||
COPY ./contrib/rosetta/configuration ./
|
||||
RUN chmod +x run_tests.sh
|
||||
RUN chmod +x send_funds.sh
|
||||
RUN chmod +x faucet.py
|
||||
|
||||
COPY --from=build /node/root /root/
|
||||
WORKDIR /root/.simapp
|
||||
|
||||
RUN chmod -R 0777 ./
|
||||
|
||||
BIN
contrib/rosetta/node/data.tar.gz
Normal file
BIN
contrib/rosetta/node/data.tar.gz
Normal file
Binary file not shown.
18
contrib/rosetta/rosetta-cli/Dockerfile
Normal file
18
contrib/rosetta/rosetta-cli/Dockerfile
Normal file
@ -0,0 +1,18 @@
|
||||
FROM golang:1.15-alpine as build
|
||||
|
||||
RUN apk add git gcc libc-dev --no-cache
|
||||
|
||||
ARG ROSETTA_VERSION="v0.5.23"
|
||||
|
||||
# build rosetta CLI
|
||||
WORKDIR /rosetta
|
||||
RUN git clone https://github.com/coinbase/rosetta-cli .
|
||||
RUN git checkout tags/$ROSETTA_VERSION
|
||||
RUN go build -o rosetta-cli ./main.go
|
||||
|
||||
FROM alpine
|
||||
RUN apk add gcc libc-dev python3 --no-cache
|
||||
|
||||
ENV PATH=$PATH:/bin
|
||||
|
||||
COPY --from=build /rosetta/rosetta-cli /bin/rosetta-cli
|
||||
@ -158,6 +158,8 @@ func TestUnarmorInfoBytesErrors(t *testing.T) {
|
||||
}
|
||||
|
||||
func BenchmarkBcryptGenerateFromPassword(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
|
||||
passphrase := []byte("passphrase")
|
||||
for securityParam := 9; securityParam < 16; securityParam++ {
|
||||
param := securityParam
|
||||
|
||||
@ -108,6 +108,7 @@ type Signer interface {
|
||||
type Importer interface {
|
||||
// ImportPrivKey imports ASCII armored passphrase-encrypted private keys.
|
||||
ImportPrivKey(uid, armor, passphrase string) error
|
||||
|
||||
// ImportPubKey imports ASCII armored public keys.
|
||||
ImportPubKey(uid string, armor string) error
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ package keyring
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@ -185,49 +184,6 @@ func (kb dbKeybase) Close() error { return kb.db.Close() }
|
||||
|
||||
func infoKey(name string) []byte { return []byte(fmt.Sprintf("%s.%s", name, infoSuffix)) }
|
||||
|
||||
// InfoImporter is implemented by those types that want to provide functions necessary
|
||||
// to migrate keys from LegacyKeybase types to Keyring types.
|
||||
type InfoImporter interface {
|
||||
// Import imports ASCII-armored private keys.
|
||||
Import(uid string, armor string) error
|
||||
}
|
||||
|
||||
type keyringMigrator struct {
|
||||
kr keystore
|
||||
}
|
||||
|
||||
func NewInfoImporter(
|
||||
appName, backend, rootDir string, userInput io.Reader, opts ...Option,
|
||||
) (InfoImporter, error) {
|
||||
keyring, err := New(appName, backend, rootDir, userInput, opts...)
|
||||
if err != nil {
|
||||
return keyringMigrator{}, err
|
||||
}
|
||||
|
||||
kr := keyring.(keystore)
|
||||
|
||||
return keyringMigrator{kr}, nil
|
||||
}
|
||||
|
||||
func (m keyringMigrator) Import(uid string, armor string) error {
|
||||
_, err := m.kr.Key(uid)
|
||||
if err == nil {
|
||||
return fmt.Errorf("cannot overwrite key %q", uid)
|
||||
}
|
||||
|
||||
infoBytes, err := crypto.UnarmorInfoBytes(armor)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := unmarshalInfo(infoBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return m.kr.writeInfo(info)
|
||||
}
|
||||
|
||||
// KeybaseOption overrides options for the db.
|
||||
type KeybaseOption func(*kbOptions)
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package keyring_test
|
||||
|
||||
import (
|
||||
"io"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
@ -43,15 +42,4 @@ func TestLegacyKeybase(t *testing.T) {
|
||||
armoredInfo, err := kb.Export(keys[0].GetName())
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, armoredInfo)
|
||||
|
||||
importer, err := keyring.NewInfoImporter("cosmos", "memory", "", nil)
|
||||
require.NoError(t, err)
|
||||
err = importer.Import("test", "")
|
||||
require.Error(t, err)
|
||||
require.Equal(t, io.EOF, err)
|
||||
require.NoError(t, importer.Import("test", armoredInfo))
|
||||
|
||||
err = importer.Import("test", armoredInfo)
|
||||
require.Error(t, err)
|
||||
require.Equal(t, `public key already exist in keybase`, err.Error())
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ func (zeroReader) Read(buf []byte) (int, error) {
|
||||
// BenchmarkKeyGeneration benchmarks the given key generation algorithm using
|
||||
// a dummy reader.
|
||||
func BenchmarkKeyGeneration(b *testing.B, generateKey func(reader io.Reader) types.PrivKey) {
|
||||
b.ReportAllocs()
|
||||
var zero zeroReader
|
||||
for i := 0; i < b.N; i++ {
|
||||
generateKey(zero)
|
||||
|
||||
@ -9,6 +9,7 @@ import (
|
||||
)
|
||||
|
||||
func BenchmarkKeyGeneration(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
benchmarkKeygenWrapper := func(reader io.Reader) types.PrivKey {
|
||||
priv := genPrivKey(reader)
|
||||
return &PrivKey{Key: priv}
|
||||
@ -17,11 +18,13 @@ func BenchmarkKeyGeneration(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkSigning(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkSigning(b, priv)
|
||||
}
|
||||
|
||||
func BenchmarkVerification(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
priv := GenPrivKey()
|
||||
benchmarking.BenchmarkVerification(b, priv)
|
||||
}
|
||||
|
||||
6
docs/.vuepress/enhanceApp.js
Normal file
6
docs/.vuepress/enhanceApp.js
Normal file
@ -0,0 +1,6 @@
|
||||
export default ({ router }) => {
|
||||
router.addRoutes([
|
||||
{ path: '/master/spec/*', redirect: '/master/modules/' },
|
||||
{ path: '/master/spec/governance/', redirect: '/master/modules/gov/' },
|
||||
])
|
||||
}
|
||||
@ -73,4 +73,5 @@ Read about the [PROCESS](./PROCESS.md).
|
||||
- [ADR 028: Public Key Addresses](./adr-028-public-key-addresses.md)
|
||||
- [ADR 032: Typed Events](./adr-032-typed-events.md)
|
||||
- [ADR 035: Rosetta API Support](./adr-035-rosetta-api-support.md)
|
||||
- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md)
|
||||
- [ADR 037: Governance Split Votes](./adr-037-gov-split-vote.md)
|
||||
- [ADR 038: State Listening](./adr-038-state-listening.md)
|
||||
@ -3,6 +3,7 @@
|
||||
## Changelog
|
||||
|
||||
- 2020/08/18: Initial version
|
||||
- 2021/01/15: Analysis and algorithm update
|
||||
|
||||
## Status
|
||||
|
||||
@ -10,42 +11,81 @@ Proposed
|
||||
|
||||
## Abstract
|
||||
|
||||
This ADR defines a canonical 20-byte address format for new public key algorithms, multisig public keys, and module
|
||||
accounts using string prefixes.
|
||||
This ADR defines an address format for all addressable SDK accounts. That includes: new public key algorithms, multisig public keys, and module accounts.
|
||||
|
||||
## Context
|
||||
|
||||
Issue [\#3685](https://github.com/cosmos/cosmos-sdk/issues/3685) identified that public key
|
||||
address spaces are currently overlapping. One initial proposal was extending the address length and
|
||||
adding prefixes for different types of addresses.
|
||||
address spaces are currently overlapping. We confirmed that it significantly decreases security of Cosmos SDK.
|
||||
|
||||
|
||||
### Problem
|
||||
|
||||
An attacker can control an input for an address generation function. This leads to a birthday attack, which significantly decreases the security space.
|
||||
To overcome this, we need to separate the inputs for different kind of account types:
|
||||
a security break of one account type shouldn't impact the security of other account types.
|
||||
|
||||
|
||||
### Initial proposals
|
||||
|
||||
One initial proposal was extending the address length and
|
||||
adding prefixes for different types of addresses.
|
||||
|
||||
@ethanfrey explained an alternate approach originally used in https://github.com/iov-one/weave:
|
||||
|
||||
> I spent quite a bit of time thinking about this issue while building weave... The other cosmos Sdk.
|
||||
|
||||
> Basically I define a condition to be a type and format as human readable string with some binary data appended. This condition is hashed into an Address (again at 20 bytes). The use of this prefix makes it impossible to find a preimage for a given address with a different condition (eg ed25519 vs secp256k1).
|
||||
|
||||
> This is explained in depth here https://weave.readthedocs.io/en/latest/design/permissions.html
|
||||
|
||||
> And the code is here, look mainly at the top where we process conditions. https://github.com/iov-one/weave/blob/master/conditions.go
|
||||
|
||||
And explained how this approach should be sufficiently collision resistant:
|
||||
|
||||
> Yeah, AFAIK, 20 bytes should be collision resistance when the preimages are unique and not malleable. A space of 2^160 would expect some collision to be likely around 2^80 elements (birthday paradox). And if you want to find a collision for some existing element in the database, it is still 2^160. 2^80 only is if all these elements are written to state.
|
||||
|
||||
> The good example you brought up was eg. a public key bytes being a valid public key on two algorithms supported by the codec. Meaning if either was broken, you would break accounts even if they were secured with the safer variant. This is only as the issue when no differentiating type info is present in the preimage (before hashing into an address).
|
||||
|
||||
> I would like to hear an argument if the 20 bytes space is an actual issue for security, as I would be happy to increase my address sizes in weave. I just figured cosmos and ethereum and bitcoin all use 20 bytes, it should be good enough. And the arguments above which made me feel it was secure. But I have not done a deeper analysis.
|
||||
|
||||
In discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694), we agreed to go with an
|
||||
approach similar to this where essentially we take the first 20 bytes of the `sha256` hash of
|
||||
the key type concatenated with the key bytes, summarized as `Sha256(KeyTypePrefix || Keybytes)[:20]`.
|
||||
This led to the first proposal (which we proved to be not good enough):
|
||||
we concatenate a key type with a public key, hash it and take the first 20 bytes of that hash, summarized as `sha256(keyTypePrefix || keybytes)[:20]`.
|
||||
|
||||
|
||||
### Review and Discussions
|
||||
|
||||
In [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694) we discussed various solutions.
|
||||
We agreed that 20 bytes it's not future proof, and extending the address length is the only way to allow addresses of different types, various signature types, etc.
|
||||
This disqualifies the initial proposal.
|
||||
|
||||
In the issue we discussed various modifications:
|
||||
+ Choice of the hash function.
|
||||
+ Move the prefix out of the hash function: `keyTypePrefix + sha256(keybytes)[:20]` [post-hash-prefix-proposal].
|
||||
+ Use double hashing: `sha256(keyTypePrefix + sha256(keybytes)[:20])`.
|
||||
+ Increase to keybytes hash slice from 20 byte to 32 or 40 bytes. We concluded that 32 bytes, produced by a good hash functions is future secure.
|
||||
|
||||
### Requirements
|
||||
|
||||
+ Support currently used tools - we don't want to break an ecosystem, or add a long adaptation period. Ref: https://github.com/cosmos/cosmos-sdk/issues/8041
|
||||
+ Try to keep the address length small - addresses are widely used in state, both as part of a key and object value.
|
||||
|
||||
|
||||
### Scope
|
||||
|
||||
This ADR only defines a process for the generation of address bytes. For end-user interactions with addresses (through the API, or CLI, etc.), we still use bech32 to format these addresses as strings. This ADR doesn't change that.
|
||||
Using bech32 for string encoding gives us support for checksum error codes and handling of user typos.
|
||||
|
||||
|
||||
## Decision
|
||||
|
||||
We define the following account types, for which we define the address function:
|
||||
|
||||
1. simple accounts: represented by a regular public key (ie: secp256k1, sr25519)
|
||||
2. naive multisig: accounts composed by other addressable objects (ie: naive multisig)
|
||||
3. composed accounts with a native address key (ie: bls, group module accounts)
|
||||
4. module accounts: basically any accounts which cannot sign transactions and which are managed internally by modules
|
||||
|
||||
|
||||
### Legacy Public Key Addresses Don't Change
|
||||
|
||||
`secp256k1` and multisig public keys are currently in use in existing Cosmos SDK zones. They use the following
|
||||
address formats:
|
||||
Currently (Jan 2021), the only officially supported SDK user accounts are `secp256k1` basic accounts and legacy amino multisig.
|
||||
They are used in existing Cosmos SDK zones. They use the following address formats:
|
||||
|
||||
- secp256k1: `ripemd160(sha256(pk_bytes))[:20]`
|
||||
- legacy amino multisig: `sha256(aminoCdc.Marshal(pk))[:20]`
|
||||
@ -56,42 +96,142 @@ The current multisig public keys use amino serialization to generate the address
|
||||
those public keys and their address formatting, and call them "legacy amino" multisig public keys
|
||||
in protobuf. We will also create multisig public keys without amino addresses to be described below.
|
||||
|
||||
### Hash Function Choice
|
||||
|
||||
### Canonical Address Format
|
||||
As in other parts of the Cosmos SDK, we will use `sha256`.
|
||||
|
||||
We have three types of accounts we would like to create addresses for in the future:
|
||||
- regular public key addresses for new signature algorithms (ex. `sr25519`).
|
||||
- public key addresses for multisig public keys that don't use amino encoding
|
||||
- module accounts: basically any accounts which cannot sign transactions and
|
||||
which are managed internally by modules
|
||||
### Basic Address
|
||||
|
||||
To address all of these use cases we propose the following basic `AddressHash` function,
|
||||
based on the discussions in [\#5694](https://github.com/cosmos/cosmos-sdk/issues/5694):
|
||||
We start with defining a base hash algorithm for generating addresses. Notably, it's used for accounts represented by a single key pair. For each public key schema we have to have an associated `typ` string, which we discuss in a section below. `hash` is the cryptographic hash function defined in the previous section.
|
||||
|
||||
```go
|
||||
func AddressHash(prefix string, contents []byte) []byte {
|
||||
preImage := []byte(prefix)
|
||||
if len(contents) != 0 {
|
||||
preImage = append(preImage, 0)
|
||||
preImage = append(preImage, contents...)
|
||||
}
|
||||
return sha256.Sum256(preImage)[:20]
|
||||
const A_LEN = 32
|
||||
|
||||
func Hash(typ string, key []byte) []byte {
|
||||
return hash(hash(typ) + key)[:A_LEN]
|
||||
}
|
||||
```
|
||||
|
||||
`AddressHash` always take a string `prefix` as a starting point which should represent the
|
||||
type of public key (ex. `sr25519`) or module account being used (ex. `staking` or `group`).
|
||||
For public keys, the `contents` parameter is used to specify the binary contents of the public
|
||||
key. For module accounts, `contents` can be left empty (for modules which don't manage "sub-accounts"),
|
||||
or can be some module-specific content to specify different pools (ex. `bonded` or `not-bonded` for `staking`)
|
||||
or managed accounts (ex. different accounts managed by the `group` module).
|
||||
The `+` is bytes concatenation, which doesn't use any separator.
|
||||
|
||||
In the `preImage`, the byte value `0` is used as the separator between `prefix` and `contents`. This is a logical
|
||||
choice given that `0` is an invalid value for a string character and is commonly used as a null terminator.
|
||||
This algorithm is the outcome of a consultation session with a professional cryptographer.
|
||||
Motivation: this algorithm keeps the address relatively small (length of the `typ` doesn't impact the length of the final address)
|
||||
and it's more secure than [post-hash-prefix-proposal] (which uses the first 20 bytes of a pubkey hash, significantly reducing the address space).
|
||||
Moreover the cryptographer motivated the choice of adding `typ` in the hash to protect against a switch table attack.
|
||||
|
||||
### Canonical Public Key Address Prefixes
|
||||
We use the `address.Hash` function for generating addresses for all accounts represented by a single key:
|
||||
* simple public keys: `address.Hash(keyType, pubkey)`
|
||||
+ aggregated keys (eg: BLS): `address.Hash(keyType, aggregatedPubKey)`
|
||||
+ modules: `address.Hash("module", moduleName)`
|
||||
|
||||
All public key types will have a unique protobuf message type such as:
|
||||
|
||||
### Composed Addresses
|
||||
|
||||
For simple composed accounts (like new naive multisig), we generalize the `address.Hash`. The address is constructed by recursively creating addresses for the sub accounts, sorting the addresses and composing them into a single address. It ensures that the ordering of keys doesn't impact the resulting address.
|
||||
|
||||
```go
|
||||
// We don't need a PubKey interface - we need anything which is addressable.
|
||||
type Addressable interface {
|
||||
Address() []byte
|
||||
}
|
||||
|
||||
func NewComposed(typ string, subaccounts []Addressable) []byte {
|
||||
addresses = map(subaccounts, \a -> LengthPrefix(a.Address()))
|
||||
addresses = sort(addresses)
|
||||
return address.Hash(typ, addresses[0] + ... + addresses[n])
|
||||
}
|
||||
```
|
||||
|
||||
The `typ` parameter should be a schema descriptor, containing all significant attributes with deterministic serialization (eg: utf8 string).
|
||||
`LengthPrefix` is a function which prepends 1 byte to the address. The value of that byte is the length of the address bits before prepending. The address must be at most 255 bits long.
|
||||
We are using `LengthPrefix` to eliminate conflicts - it assures, that for 2 lists of addresses: `as = {a1, a2, ..., an}` and `bs = {b1, b2, ..., bm}` such that every `bi` and `ai` is at most 255 long, `concatenate(map(as, \a -> LengthPrefix(a))) = map(bs, \b -> LengthPrefix(b))` iff `as = bs`.
|
||||
|
||||
Implementation Tip: account implementations should cache addresses.
|
||||
|
||||
#### Multisig Addresses
|
||||
|
||||
For new multisig public keys, we define the `typ` parameter not based on any encoding scheme (amino or protobuf). This avoids issues with non-determinism in the encoding scheme.
|
||||
|
||||
Example:
|
||||
|
||||
```proto
|
||||
package cosmos.crypto.multisig;
|
||||
|
||||
message PubKey {
|
||||
uint32 threshold = 1;
|
||||
repeated google.protobuf.Any pubkeys = 2;
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func (multisig PubKey) Address() {
|
||||
// first gather all nested pub keys
|
||||
var keys []address.Addressable // cryptotypes.PubKey implements Addressable
|
||||
for _, _key := range multisig.Pubkeys {
|
||||
keys = append(keys, key.GetCachedValue().(cryptotypes.PubKey))
|
||||
}
|
||||
|
||||
// form the type from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together
|
||||
prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold)
|
||||
|
||||
// use the Composed function defined above
|
||||
return address.NewComposed(prefix, keys)
|
||||
}
|
||||
```
|
||||
|
||||
#### Module Account Addresses
|
||||
|
||||
NOTE: this section is not finalize and it's in active discussion.
|
||||
|
||||
In Basic Address section we defined a module account address as:
|
||||
|
||||
```
|
||||
address.Hash("module", moduleName)
|
||||
```
|
||||
|
||||
We use `"module"` as a schema type for all module derived addresses. Module accounts can have sub accounts. The derivation process has a defined order: module name, submodule key, subsubmodule key.
|
||||
Module account addresses are heavily used in the SDK so it makes sense to optimize the derivation process: instead of using of using `LengthPrefix` for the module name, we use a null byte (`'\x00'`) as a separator. This works, because null byte is not a part of a valid module name.
|
||||
|
||||
```
|
||||
func Module(moduleName string, key []byte) []byte{
|
||||
return Hash("module", []byte(moduleName) + 0 + key)
|
||||
}
|
||||
```
|
||||
|
||||
**Example** A lending BTC pool address would be:
|
||||
```
|
||||
btcPool := address.Module("lending", btc.Addrress()})
|
||||
```
|
||||
|
||||
If we want to create an address for a module account depending on more than one key, we can concatenate them:
|
||||
```
|
||||
btcAtomAMM := address.Module("amm", btc.Addrress() + atom.Address()})
|
||||
```
|
||||
|
||||
We can continue the derivation process and can create an address for a submodule account.
|
||||
|
||||
```
|
||||
func Submodule(address []byte, derivationKey []byte) {
|
||||
return Hash("module", address + derivationKey)
|
||||
}
|
||||
```
|
||||
|
||||
NOTE: if `address` is not a hash based address (with `LEN` length) then we should use `LengthPrefix`. An alternative would be to use one `Module` function, which takes a slice of keys and mapped with `LengthPrefix`. For final version we need to validate what's the most common use.
|
||||
|
||||
|
||||
**Example** For a cosmwasm smart-contract address we could use the following construction:
|
||||
```
|
||||
smartContractAddr := Submodule(Module("cosmwasm", smartContractsNamespace), smartContractKey)
|
||||
```
|
||||
|
||||
|
||||
|
||||
### Schema Types
|
||||
|
||||
A `typ` parameter used in `Hash` function SHOULD be unique for each account type.
|
||||
Since all SDK account types are serialized in the state, we propose to use the protobuf message name string.
|
||||
|
||||
Example: all public key types have a unique protobuf message type similar to:
|
||||
|
||||
```proto
|
||||
package cosmos.crypto.sr25519;
|
||||
@ -100,69 +240,89 @@ message PubKey {
|
||||
bytes key = 1;
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
All protobuf messages have unique fully qualified names, in this example `cosmos.crypto.sr25519.PubKey`.
|
||||
These names are derived directly from .proto files in a standardized way and used
|
||||
in other places such as the type URL in `Any`s. Since there is an easy and obvious
|
||||
way to get this name for every protobuf type, we can use this message name as the
|
||||
key type `prefix` when creating addresses. For all basic public keys, `contents`
|
||||
should just be the raw unencoded public key bytes.
|
||||
in other places such as the type URL in `Any`s. We can easily obtain the name using
|
||||
`proto.MessageName(msg)`.
|
||||
|
||||
Thus the canonical address for new public key types would be `AddressHash(proto.MessageName(pk), pk.Bytes)`.
|
||||
|
||||
### Multisig Addresses
|
||||
|
||||
For new multisig public keys, we define a custom address format not based on any encoding scheme
|
||||
(amino or protobuf). This avoids issues with non-determinism in the encoding scheme. It also
|
||||
ensures that multisig public keys which differ simply in the ordering of keys have the same
|
||||
address by sorting child public keys first.
|
||||
|
||||
First we define a proto message for multisig public keys:
|
||||
```proto
|
||||
package cosmos.crypto.multisig;
|
||||
|
||||
message PubKey {
|
||||
uint32 threshold = 1;
|
||||
repeated google.protobuf.Any public_keys = 2;
|
||||
}
|
||||
```
|
||||
|
||||
We define the following `Address()` function for this public key:
|
||||
|
||||
```
|
||||
func (multisig PubKey) Address() {
|
||||
// first gather all the addresses of each nested public key
|
||||
var addresses [][]byte
|
||||
for key := range multisig.Keys {
|
||||
addresses = append(joinedAddresses, key.Address())
|
||||
}
|
||||
|
||||
// then sort them in ascending order
|
||||
addresses = Sort(addresses)
|
||||
|
||||
// then concatenate them together
|
||||
var joinedAddresses []byte
|
||||
for addr := range addresses {
|
||||
joinedAddresses := append(joinedAddresses, addr...)
|
||||
}
|
||||
|
||||
// form the string prefix from the message name (cosmos.crypto.multisig.PubKey) and the threshold joined together
|
||||
prefix := fmt.Sprintf("%s/%d", proto.MessageName(multisig), multisig.Threshold)
|
||||
|
||||
// use the standard AddressHash function
|
||||
return AddressHash(prefix, joinedAddresses)
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
This ADR is compatible with what was committed and directly supported in the SDK repository.
|
||||
|
||||
### Positive
|
||||
- a simple algorithm for generating addresses for new public keys and module accounts
|
||||
|
||||
- a simple algorithm for generating addresses for new public keys, complex accounts and modules
|
||||
- the algorithm generalizes _native composed keys_
|
||||
- increased security and collision resistance of addresses
|
||||
- the approach is extensible for future use-cases - one can use other address types, as long as they don't conflict with the address length specified here (20 or 32 bytes).
|
||||
- support new account types.
|
||||
|
||||
### Negative
|
||||
|
||||
- addresses do not communicate key type, a prefixed approach would have done this
|
||||
- addresses are 60% longer and will consume more storage space
|
||||
- requires a refactor of KVStore store keys to handle variable length addresses
|
||||
|
||||
### Neutral
|
||||
|
||||
- protobuf message names are used as key type prefixes
|
||||
|
||||
## References
|
||||
|
||||
## Further Discussions
|
||||
|
||||
Some accounts can have a fixed name or may be constructed in other way (eg: modules). We were discussing an idea of an account with a predefined name (eg: `me.regen`), which could be used by institutions.
|
||||
Without going into details, these kinds of addresses are compatible with the hash based addresses described here as long as they don't have the same length.
|
||||
More specifically, any special account address must not have a length equal to 20 or 32 bytes.
|
||||
|
||||
|
||||
## Appendix: Consulting session
|
||||
|
||||
End of Dec 2020 we had a session with [Alan Szepieniec](https://scholar.google.be/citations?user=4LyZn8oAAAAJ&hl=en) to consult the approach presented above.
|
||||
|
||||
Alan general observations:
|
||||
+ we don’t need 2-preimage resistance
|
||||
+ we need 32bytes address space for collision resistance
|
||||
+ when an attacker can control an input for object with an address then we have a problem with birthday attack
|
||||
+ there is an issue with smart-contracts for hashing
|
||||
+ sha2 mining can be use to breaking address pre-image
|
||||
|
||||
Hashing algorithm
|
||||
+ any attack breaking blake3 will break blake2
|
||||
+ Alan is pretty confident about the current security analysis of the blake hash algorithm. It was a finalist, and the author is well known in security analysis.
|
||||
|
||||
|
||||
Algorithm:
|
||||
+ Alan recommends to hash the prefix: `address(pub_key) = hash(hash(key_type) + pub_key)[:32]`, main benefits:
|
||||
+ we are free to user arbitrary long prefix names
|
||||
+ we still don’t risk collisions
|
||||
+ switch tables
|
||||
+ discussion about penalization -> about adding prefix post hash
|
||||
+ Aaron asked about post hash prefixes (`address(pub_key) = key_type + hash(pub_key)`) and differences. Alan noted that this approach has longer address space and it’s stronger.
|
||||
|
||||
Algorithm for complex / composed keys:
|
||||
+ merging tree like addresses with same algorithm are fine
|
||||
|
||||
Module addresses: Should module addresses have different size to differentiate it?
|
||||
+ we will need to set a pre-image prefix for module addresse to keept them in 32-byte space: `hash(hash('module') + module_key)`
|
||||
+ Aaron observation: we already need to deal with variable length (to not break secp256k1 keys).
|
||||
|
||||
Discssion about arithmetic hash function for ZKP
|
||||
+ Posseidon / Rescue
|
||||
+ Problem: much bigger risk because we don’t know much techniques and history of crypto-analysis of arithmetic constructions. It’s still a new ground and area of active research.
|
||||
|
||||
Post quantum signature size
|
||||
+ Alan suggestion: Falcon: speed / size ration - very good.
|
||||
+ Aaron - should we think about it?
|
||||
Alan: based on early extrapolation this thing will get able to break EC cryptography in 2050 . But that’s a lot of uncertainty. But there is magic happening with recurions / linking / simulation and that can speedup the progress.
|
||||
|
||||
Other ideas
|
||||
+ Let’s say we use same key and two different address algorithms for 2 different use cases. Is it still safe to use it? Alan: if we want to hide the public key (which is not our use case), then it’s less secure but there are fixes.
|
||||
|
||||
|
||||
### References
|
||||
+ [Notes](https://hackmd.io/_NGWI4xZSbKzj1BkCqyZMw)
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
- Jonathan Gimeno (@jgimeno)
|
||||
- David Grierson (@senormonito)
|
||||
- Alessio Treglia (@alessio)
|
||||
- Frojdy Dymylja (@fdymylja)
|
||||
|
||||
## Context
|
||||
|
||||
@ -35,9 +36,11 @@ We will achieve these delivering on these principles by the following:
|
||||
|
||||
1. There will be an external repo called [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway)
|
||||
for the implementation of the core Rosetta API features, particularly:
|
||||
a. The types and interfaces. This separates design from implementation detail.
|
||||
b. Some core implementations: specifically, the `Service` functionality as this is independent of the Cosmos SDK version.
|
||||
2. Due to differences between the Cosmos release series, each series will have its own specific API implementations of `Network` struct and `Adapter` interface.
|
||||
a. The types and interfaces (`Client`, `OfflineClient`...), this separates design from implementation detail.
|
||||
b. The `Server` functionality as this is independent of the Cosmos SDK version.
|
||||
c. The `Online/OfflineNetwork`, which is not exported, and implements the rosetta API using the `Client` interface to query the node, build tx and so on.
|
||||
d. The `errors` package to extend rosetta errors.
|
||||
2. Due to differences between the Cosmos release series, each series will have its own specific implementation of `Client` interface.
|
||||
3. There will be two options for starting an API service in applications:
|
||||
a. API shares the application process
|
||||
b. API-specific process.
|
||||
@ -49,143 +52,130 @@ We will achieve these delivering on these principles by the following:
|
||||
|
||||
As section will describe the proposed external library, including the service implementation, plus the defined types and interfaces.
|
||||
|
||||
#### Service
|
||||
#### Server
|
||||
|
||||
`Service` is a simple `struct` that is started and listens to the port specified in the options. This is meant to be used across all the Cosmos SDK versions that are actively supported.
|
||||
`Server` is a simple `struct` that is started and listens to the port specified in the settings. This is meant to be used across all the Cosmos SDK versions that are actively supported.
|
||||
|
||||
The constructor follows:
|
||||
|
||||
`func New(options Options, network Network) (*Service, error)`
|
||||
`func NewServer(settings Settings) (Server, error)`
|
||||
|
||||
`Settings`, which are used to construct a new server, are the following:
|
||||
```go
|
||||
// Settings define the rosetta server settings
|
||||
type Settings struct {
|
||||
// Network contains the information regarding the network
|
||||
Network *types.NetworkIdentifier
|
||||
// Client is the online API handler
|
||||
Client crgtypes.Client
|
||||
// Listen is the address the handler will listen at
|
||||
Listen string
|
||||
// Offline defines if the rosetta service should be exposed in offline mode
|
||||
Offline bool
|
||||
// Retries is the number of readiness checks that will be attempted when instantiating the handler
|
||||
// valid only for online API
|
||||
Retries int
|
||||
// RetryWait is the time that will be waited between retries
|
||||
RetryWait time.Duration
|
||||
}
|
||||
```
|
||||
|
||||
#### Types
|
||||
|
||||
`Service` accepts an `Options` `struct` that holds service configuration values, such as the port the service would be listening to:
|
||||
Package types uses a mixture of rosetta types and custom defined type wrappers, that the client must parse and return while executing operations.
|
||||
|
||||
```golang
|
||||
type Options struct {
|
||||
ListenAddress string
|
||||
|
||||
##### Interfaces
|
||||
|
||||
Every SDK version uses a different format to connect (rpc, gRPC, etc), query and build transactions, we have abstracted this in what is the `Client` interface.
|
||||
The client uses rosetta types, whilst the `Online/OfflineNetwork` takes care of returning correctly parsed rosetta responses and errors.
|
||||
|
||||
Each Cosmos SDK release series will have their own `Client` implementations.
|
||||
Developers can implement their own custom `Client`s as required.
|
||||
|
||||
```go
|
||||
// Client defines the API the client implementation should provide.
|
||||
type Client interface {
|
||||
// Needed if the client needs to perform some action before connecting.
|
||||
Bootstrap() error
|
||||
// Ready checks if the servicer constraints for queries are satisfied
|
||||
// for example the node might still not be ready, it's useful in process
|
||||
// when the rosetta instance might come up before the node itself
|
||||
// the servicer must return nil if the node is ready
|
||||
Ready() error
|
||||
|
||||
// Data API
|
||||
|
||||
// Balances fetches the balance of the given address
|
||||
// if height is not nil, then the balance will be displayed
|
||||
// at the provided height, otherwise last block balance will be returned
|
||||
Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error)
|
||||
// BlockByHashAlt gets a block and its transaction at the provided height
|
||||
BlockByHash(ctx context.Context, hash string) (BlockResponse, error)
|
||||
// BlockByHeightAlt gets a block given its height, if height is nil then last block is returned
|
||||
BlockByHeight(ctx context.Context, height *int64) (BlockResponse, error)
|
||||
// BlockTransactionsByHash gets the block, parent block and transactions
|
||||
// given the block hash.
|
||||
BlockTransactionsByHash(ctx context.Context, hash string) (BlockTransactionsResponse, error)
|
||||
// BlockTransactionsByHash gets the block, parent block and transactions
|
||||
// given the block hash.
|
||||
BlockTransactionsByHeight(ctx context.Context, height *int64) (BlockTransactionsResponse, error)
|
||||
// GetTx gets a transaction given its hash
|
||||
GetTx(ctx context.Context, hash string) (*types.Transaction, error)
|
||||
// GetUnconfirmedTx gets an unconfirmed Tx given its hash
|
||||
// NOTE(fdymylja): NOT IMPLEMENTED YET!
|
||||
GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error)
|
||||
// Mempool returns the list of the current non confirmed transactions
|
||||
Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error)
|
||||
// Peers gets the peers currently connected to the node
|
||||
Peers(ctx context.Context) ([]*types.Peer, error)
|
||||
// Status returns the node status, such as sync data, version etc
|
||||
Status(ctx context.Context) (*types.SyncStatus, error)
|
||||
|
||||
// Construction API
|
||||
|
||||
// PostTx posts txBytes to the node and returns the transaction identifier plus metadata related
|
||||
// to the transaction itself.
|
||||
PostTx(txBytes []byte) (res *types.TransactionIdentifier, meta map[string]interface{}, err error)
|
||||
// ConstructionMetadataFromOptions
|
||||
ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error)
|
||||
OfflineClient
|
||||
}
|
||||
|
||||
// OfflineClient defines the functionalities supported without having access to the node
|
||||
type OfflineClient interface {
|
||||
NetworkInformationProvider
|
||||
// SignedTx returns the signed transaction given the tx bytes (msgs) plus the signatures
|
||||
SignedTx(ctx context.Context, txBytes []byte, sigs []*types.Signature) (signedTxBytes []byte, err error)
|
||||
// TxOperationsAndSignersAccountIdentifiers returns the operations related to a transaction and the account
|
||||
// identifiers if the transaction is signed
|
||||
TxOperationsAndSignersAccountIdentifiers(signed bool, hexBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error)
|
||||
// ConstructionPayload returns the construction payload given the request
|
||||
ConstructionPayload(ctx context.Context, req *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error)
|
||||
// PreprocessOperationsToOptions returns the options given the preprocess operations
|
||||
PreprocessOperationsToOptions(ctx context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error)
|
||||
// AccountIdentifierFromPublicKey returns the account identifier given the public key
|
||||
AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error)
|
||||
}
|
||||
```
|
||||
|
||||
The `Network` type holds network-specific properties (i.e. configuration values) and adapters. Pre-configured concrete types will be available for each Cosmos SDK release. Applications can also create their own custom types.
|
||||
|
||||
```golang
|
||||
type Network struct {
|
||||
Properties rosetta.NetworkProperties
|
||||
Adapter rosetta.Adapter
|
||||
}
|
||||
```
|
||||
|
||||
A `NetworkProperties` `struct` comprises basic values that are required by a Rosetta API `Service`:
|
||||
|
||||
```golang
|
||||
type NetworkProperties struct {
|
||||
// Mandatory properties
|
||||
Blockchain string
|
||||
Network string
|
||||
SupportedOperations []string
|
||||
}
|
||||
```
|
||||
|
||||
Rosetta API services use `Blockchain` and `Network` as identifiers, e.g. the developers of _gaia_, the application that powers the Cosmos Hub, may want to set those to `Cosmos Hub` and `cosmos-hub-3` respectively.
|
||||
|
||||
`SupportedOperations` contains the transaction types that are supported by the library. At the present time,
|
||||
only `cosmos-sdk/MsgSend` is supported in Launchpad. Additional operations will be added in due time.
|
||||
|
||||
For Launchpad we will map the amino type name to the operation supported, in Stargate we will use the protoc one.
|
||||
|
||||
#### Interfaces
|
||||
|
||||
Every SDK version uses a different format to connect (rpc, gRpc, etc), we have abstracted this in what is called the
|
||||
Adapter. This is an interface that defines the methods an adapter implementation must provide in order to be used
|
||||
in the `Network` interface.
|
||||
|
||||
Each Cosmos SDK release series will have their own Adapter implementations.
|
||||
Developers can implement their own custom adapters as required.
|
||||
|
||||
```golang
|
||||
type Adapter interface {
|
||||
DataAPI
|
||||
ConstructionAPI
|
||||
}
|
||||
|
||||
type DataAPI interface {
|
||||
server.NetworkAPIServicer
|
||||
server.AccountAPIServicer
|
||||
server.MempoolAPIServicer
|
||||
server.BlockAPIServicer
|
||||
server.ConstructionAPIServicer
|
||||
}
|
||||
|
||||
type ConstructionAPI interface {
|
||||
server.ConstructionAPIServicer
|
||||
}
|
||||
```
|
||||
|
||||
Example in pseudo-code of an Adapter interface:
|
||||
|
||||
```golang
|
||||
type SomeAdapter struct {
|
||||
cosmosClient client
|
||||
tendermintClient client
|
||||
}
|
||||
|
||||
func NewSomeAdapter(cosmosClient client, tendermintClient client) rosetta.Adapter {
|
||||
return &SomeAdapter{cosmosClient: cosmosClient, tendermintClient: tendermintClient}
|
||||
}
|
||||
|
||||
func (s SomeAdapter) NetworkStatus(ctx context.Context, request *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
|
||||
resp := s.tendermintClient.CallStatus()
|
||||
// ... Parse status Response
|
||||
// build NetworkStatusResponse
|
||||
return networkStatusResp, nil
|
||||
}
|
||||
|
||||
func (s SomeAdapter) AccountBalance(ctx context.Context, request *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
|
||||
resp := s.cosmosClient.Account()
|
||||
// ... Parse cosmos specific account response
|
||||
// build AccountBalanceResponse
|
||||
return AccountBalanceResponse, nil
|
||||
}
|
||||
|
||||
// And we repeat for all the methods defined in the interface.
|
||||
```
|
||||
|
||||
For further information about the `Servicer` interfaces, please refer to the [Coinbase's rosetta-sdk-go's documentation](https://pkg.go.dev/github.com/coinbase/rosetta-sdk-go@v0.5.9/server).
|
||||
|
||||
### 2. Cosmos SDK Implementation
|
||||
|
||||
As described, each Cosmos SDK release series will have version specific implementations of `Network` and `Adapter`, as
|
||||
well as a `NewNetwork` constructor.
|
||||
The cosmos sdk implementation, based on version, takes care of satisfying the `Client` interface.
|
||||
In Stargate, Launchpad and 0.37, we have introduced the concept of rosetta.Msg, this message is not in the shared repository as the sdk.Msg type differs between cosmos-sdk versions.
|
||||
|
||||
Due to separation of interface and implementation, application developers have the option to override as needed,
|
||||
using this code as reference.
|
||||
The rosetta.Msg interface follows:
|
||||
|
||||
```golang
|
||||
// NewNetwork returns the default application configuration.
|
||||
func NewNetwork(options Options) service.Network {
|
||||
cosmosClient := cosmos.NewClient(fmt.Sprintf("http://%s", options.CosmosEndpoint))
|
||||
tendermintClient := tendermint.NewClient(fmt.Sprintf("http://%s", options.TendermintEndpoint))
|
||||
|
||||
return service.Network{
|
||||
Properties: rosetta.NetworkProperties{
|
||||
Blockchain: options.Blockchain,
|
||||
Network: options.Network,
|
||||
SupportedOperations: []string{OperationTransfer},
|
||||
},
|
||||
Adapter: newAdapter(
|
||||
cosmosClient,
|
||||
tendermintClient,
|
||||
properties{
|
||||
Blockchain: options.Blockchain,
|
||||
Network: options.Network,
|
||||
OfflineMode: options.OfflineMode,
|
||||
},
|
||||
),
|
||||
}
|
||||
```go
|
||||
// Msg represents a cosmos-sdk message that can be converted from and to a rosetta operation.
|
||||
type Msg interface {
|
||||
sdk.Msg
|
||||
ToOperations(withStatus, hasError bool) []*types.Operation
|
||||
FromOperations(ops []*types.Operation) (sdk.Msg, error)
|
||||
}
|
||||
```
|
||||
|
||||
Hence developers who want to extend the rosetta set of supported operations just need to extend their module's sdk.Msgs with the `ToOperations` and `FromOperations` methods.
|
||||
### 3. API service invocation
|
||||
|
||||
As stated at the start, application developers will have two methods for invocation of the Rosetta API service:
|
||||
@ -195,67 +185,13 @@ As stated at the start, application developers will have two methods for invocat
|
||||
|
||||
#### Shared Process (Only Stargate)
|
||||
|
||||
Rosetta API service could run within the same execution process as the application. New configuration option and
|
||||
command line flags would be provided to support this:
|
||||
Rosetta API service could run within the same execution process as the application. This would be enabled via app.toml settings, and if gRPC is not enabled the rosetta instance would be spinned in offline mode (tx building capabilities only).
|
||||
|
||||
```golang
|
||||
if config.Rosetta.Enable {
|
||||
....
|
||||
get contecxt, flags, etc
|
||||
...
|
||||
|
||||
h, err := service.New(
|
||||
service.Options{ListenAddress: config.Rosetta.ListenAddress},
|
||||
rosetta.NewNetwork(cdc, options),
|
||||
)
|
||||
if err != nil {
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
go func() {
|
||||
if err := h.Start(config); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
#### Separate API service
|
||||
|
||||
Client application developers can write a new command to launch a Rosetta API server as a separate process too:
|
||||
Client application developers can write a new command to launch a Rosetta API server as a separate process too, using the rosetta command contained in the `/server/rosetta` package. Construction of the command depends on cosmos sdk version. Examples can be found inside `simd` for stargate, and `contrib/rosetta/simapp` for other release series.
|
||||
|
||||
```golang
|
||||
func RosettaCommand(cdc *codec.Codec) *cobra.Command {
|
||||
|
||||
...
|
||||
cmd := &cobra.Command{
|
||||
Use: "rosetta",
|
||||
....
|
||||
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
....
|
||||
get contecxt, flags, etc
|
||||
...
|
||||
|
||||
h, err := service.New(
|
||||
service.Options{Endpoint: endpoint},
|
||||
rosetta.NewNetwork(cdc, options),
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
h.Start()
|
||||
}
|
||||
}
|
||||
...
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
## Status
|
||||
|
||||
|
||||
612
docs/architecture/adr-038-state-listening.md
Normal file
612
docs/architecture/adr-038-state-listening.md
Normal file
@ -0,0 +1,612 @@
|
||||
# ADR 038: KVStore state listening
|
||||
|
||||
## Changelog
|
||||
|
||||
- 11/23/2020: Initial draft
|
||||
|
||||
## Status
|
||||
|
||||
Proposed
|
||||
|
||||
## Abstract
|
||||
|
||||
This ADR defines a set of changes to enable listening to state changes of individual KVStores and exposing these data to consumers.
|
||||
|
||||
## Context
|
||||
|
||||
Currently, KVStore data can be remotely accessed through [Queries](https://github.com/cosmos/cosmos-sdk/blob/master/docs/building-modules/messages-and-queries.md#queries)
|
||||
which proceed either through Tendermint and the ABCI, or through the gRPC server.
|
||||
In addition to these request/response queries, it would be beneficial to have a means of listening to state changes as they occur in real time.
|
||||
|
||||
## Decision
|
||||
|
||||
We will modify the `MultiStore` interface and its concrete (`rootmulti` and `cachemulti`) implementations and introduce a new `listenkv.Store` to allow listening to state changes in underlying KVStores.
|
||||
We will also introduce the tooling for writing these state changes out to files and configuring this service.
|
||||
|
||||
### Listening interface
|
||||
|
||||
In a new file, `store/types/listening.go`, we will create a `WriteListener` interface for streaming out state changes from a KVStore.
|
||||
|
||||
```go
|
||||
// WriteListener interface for streaming data out from a listenkv.Store
|
||||
type WriteListener interface {
|
||||
// if value is nil then it was deleted
|
||||
// storeKey indicates the source KVStore, to facilitate using the the same WriteListener across separate KVStores
|
||||
// set bool indicates if it was a set; true: set, false: delete
|
||||
OnWrite(storeKey types.StoreKey, set bool, key []byte, value []byte)
|
||||
}
|
||||
```
|
||||
|
||||
### Listener type
|
||||
|
||||
We will create a concrete implementation of the `WriteListener` interface in `store/types/listening.go`, that writes out protobuf
|
||||
encoded KV pairs to an underlying `io.Writer`.
|
||||
|
||||
This will include defining a simple protobuf type for the KV pairs. In addition to the key and value fields this message
|
||||
will include the StoreKey for the originating KVStore so that we can write out from separate KVStores to the same stream/file
|
||||
and determine the source of each KV pair.
|
||||
|
||||
```protobuf
|
||||
message StoreKVPair {
|
||||
optional string store_key = 1; // the store key for the KVStore this pair originates from
|
||||
required bool set = 2; // true indicates a set operation, false indicates a delete operation
|
||||
required bytes key = 3;
|
||||
required bytes value = 4;
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
// StoreKVPairWriteListener is used to configure listening to a KVStore by writing out length-prefixed
|
||||
// protobuf encoded StoreKVPairs to an underlying io.Writer
|
||||
type StoreKVPairWriteListener struct {
|
||||
writer io.Writer
|
||||
marshaller codec.BinaryMarshaler
|
||||
}
|
||||
|
||||
// NewStoreKVPairWriteListener wraps creates a StoreKVPairWriteListener with a provdied io.Writer and codec.BinaryMarshaler
|
||||
func NewStoreKVPairWriteListener(w io.Writer, m codec.BinaryMarshaler) *StoreKVPairWriteListener {
|
||||
return &StoreKVPairWriteListener{
|
||||
writer: w,
|
||||
marshaller: m,
|
||||
}
|
||||
}
|
||||
|
||||
// OnWrite satisfies the WriteListener interface by writing length-prefixed protobuf encoded StoreKVPairs
|
||||
func (wl *StoreKVPairWriteListener) OnWrite(storeKey types.StoreKey, set bool, key []byte, value []byte) {
|
||||
kvPair := new(types.StoreKVPair)
|
||||
kvPair.StoreKey = storeKey.Name()
|
||||
kvPair.Set = set
|
||||
kvPair.Key = key
|
||||
kvPair.Value = value
|
||||
if by, err := wl.marshaller.MarshalBinaryLengthPrefixed(kvPair); err == nil {
|
||||
wl.writer.Write(by)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### ListenKVStore
|
||||
|
||||
We will create a new `Store` type `listenkv.Store` that the `MultiStore` wraps around a `KVStore` to enable state listening.
|
||||
We can configure the `Store` with a set of `WriteListener`s which stream the output to specific destinations.
|
||||
|
||||
```go
|
||||
// Store implements the KVStore interface with listening enabled.
|
||||
// Operations are traced on each core KVStore call and written to any of the
|
||||
// underlying listeners with the proper key and operation permissions
|
||||
type Store struct {
|
||||
parent types.KVStore
|
||||
listeners []types.WriteListener
|
||||
parentStoreKey types.StoreKey
|
||||
}
|
||||
|
||||
// NewStore returns a reference to a new traceKVStore given a parent
|
||||
// KVStore implementation and a buffered writer.
|
||||
func NewStore(parent types.KVStore, psk types.StoreKey, listeners []types.WriteListener) *Store {
|
||||
return &Store{parent: parent, listeners: listeners, parentStoreKey: psk}
|
||||
}
|
||||
|
||||
// Set implements the KVStore interface. It traces a write operation and
|
||||
// delegates the Set call to the parent KVStore.
|
||||
func (s *Store) Set(key []byte, value []byte) {
|
||||
types.AssertValidKey(key)
|
||||
s.parent.Set(key, value)
|
||||
s.onWrite(true, key, value)
|
||||
}
|
||||
|
||||
// Delete implements the KVStore interface. It traces a write operation and
|
||||
// delegates the Delete call to the parent KVStore.
|
||||
func (s *Store) Delete(key []byte) {
|
||||
s.parent.Delete(key)
|
||||
s.onWrite(false, key, nil)
|
||||
}
|
||||
|
||||
// onWrite writes a KVStore operation to all of the WriteListeners
|
||||
func (s *Store) onWrite(set bool, key, value []byte) {
|
||||
for _, l := range s.listeners {
|
||||
l.OnWrite(s.parentStoreKey, set, key, value)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### MultiStore interface updates
|
||||
|
||||
We will update the `MultiStore` interface to allow us to wrap a set of listeners around a specific `KVStore`.
|
||||
Additionally, we will update the `CacheWrap` and `CacheWrapper` interfaces to enable listening in the caching layer.
|
||||
|
||||
```go
|
||||
type MultiStore interface {
|
||||
...
|
||||
|
||||
// ListeningEnabled returns if listening is enabled for the KVStore belonging the provided StoreKey
|
||||
ListeningEnabled(key StoreKey) bool
|
||||
|
||||
// SetListeners sets the WriteListeners for the KVStore belonging to the provided StoreKey
|
||||
// It appends the listeners to a current set, if one already exists
|
||||
SetListeners(key StoreKey, listeners []WriteListener)
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
type CacheWrap interface {
|
||||
...
|
||||
|
||||
// CacheWrapWithListeners recursively wraps again with listening enabled
|
||||
CacheWrapWithListeners(storeKey types.StoreKey, listeners []WriteListener) CacheWrap
|
||||
}
|
||||
|
||||
type CacheWrapper interface {
|
||||
...
|
||||
|
||||
// CacheWrapWithListeners recursively wraps again with listening enabled
|
||||
CacheWrapWithListeners(storeKey types.StoreKey, listeners []WriteListener) CacheWrap
|
||||
}
|
||||
```
|
||||
|
||||
### MultiStore implementation updates
|
||||
|
||||
We will modify all of the `Store` and `MultiStore` implementations to satisfy these new interfaces, and adjust the `rootmulti` `GetKVStore` method
|
||||
to wrap the returned `KVStore` with a `listenkv.Store` if listening is turned on for that `Store`.
|
||||
|
||||
```go
|
||||
func (rs *Store) GetKVStore(key types.StoreKey) types.KVStore {
|
||||
store := rs.stores[key].(types.KVStore)
|
||||
|
||||
if rs.TracingEnabled() {
|
||||
store = tracekv.NewStore(store, rs.traceWriter, rs.traceContext)
|
||||
}
|
||||
if rs.ListeningEnabled(key) {
|
||||
store = listenkv.NewStore(key, store, rs.listeners[key])
|
||||
}
|
||||
|
||||
return store
|
||||
}
|
||||
```
|
||||
|
||||
We will also adjust the `cachemulti` constructor methods and the `rootmulti` `CacheMultiStore` method to forward the listeners
|
||||
to and enable listening in the cache layer.
|
||||
|
||||
```go
|
||||
func (rs *Store) CacheMultiStore() types.CacheMultiStore {
|
||||
stores := make(map[types.StoreKey]types.CacheWrapper)
|
||||
for k, v := range rs.stores {
|
||||
stores[k] = v
|
||||
}
|
||||
return cachemulti.NewStore(rs.db, stores, rs.keysByName, rs.traceWriter, rs.traceContext, rs.listeners)
|
||||
}
|
||||
```
|
||||
|
||||
### Exposing the data
|
||||
|
||||
We will introduce a new `StreamingService` interface for exposing `WriteListener` data streams to external consumers.
|
||||
|
||||
```go
|
||||
// Hook interface used to hook into the ABCI message processing of the BaseApp
|
||||
type Hook interface {
|
||||
ListenBeginBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) // update the streaming service with the latest BeginBlock messages
|
||||
ListenEndBlock(ctx sdk.Context, req abci.RequestEndBlock, res abci.ResponseEndBlock) // update the steaming service with the latest EndBlock messages
|
||||
ListenDeliverTx(ctx sdk.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) // update the steaming service with the latest DeliverTx messages
|
||||
}
|
||||
|
||||
// StreamingService interface for registering WriteListeners with the BaseApp and updating the service with the ABCI messages using the hooks
|
||||
type StreamingService interface {
|
||||
Stream(wg *sync.WaitGroup, quitChan <-chan struct{}) // streaming service loop, awaits kv pairs and writes them to some destination stream or file
|
||||
Listeners() map[sdk.StoreKey][]storeTypes.WriteListener // returns the streaming service's listeners for the BaseApp to register
|
||||
Hook
|
||||
}
|
||||
```
|
||||
|
||||
#### Writing state changes to files
|
||||
|
||||
We will introduce an implementation of `StreamingService` which writes state changes out to files as length-prefixed protobuf encoded `StoreKVPair`s.
|
||||
This service uses the same `StoreKVPairWriteListener` for every KVStore, writing all the KV pairs from every KVStore
|
||||
out to the same files, relying on the `StoreKey` field in the `StoreKVPair` protobuf message to later distinguish the source for each pair.
|
||||
|
||||
The file naming schema is as such:
|
||||
* After every `BeginBlock` request a new file is created with the name `block-{N}-begin`, where N is the block number. All
|
||||
subsequent state changes are written out to this file until the first `DeliverTx` request is received. At the head of these files,
|
||||
the length-prefixed protobuf encoded `BeginBlock` request is written, and the response is written at the tail.
|
||||
* After every `DeliverTx` request a new file is created with the name `block-{N}-tx-{M}` where N is the block number and M
|
||||
is the tx number in the block (i.e. 0, 1, 2...). All subsequent state changes are written out to this file until the next
|
||||
`DeliverTx` request is received or an `EndBlock` request is received. At the head of these files, the length-prefixed protobuf
|
||||
encoded `DeliverTx` request is written, and the response is written at the tail.
|
||||
* After every `EndBlock` request a new file is created with the name `block-{N}-end`, where N is the block number. All
|
||||
subsequent state changes are written out to this file until the next `BeginBlock` request is received. At the head of these files,
|
||||
the length-prefixed protobuf encoded `EndBlock` request is written, and the response is written at the tail.
|
||||
|
||||
```go
|
||||
// FileStreamingService is a concrete implementation of StreamingService that writes state changes out to a file
|
||||
type FileStreamingService struct {
|
||||
listeners map[sdk.StoreKey][]storeTypes.WriteListener // the listeners that will be initialized with BaseApp
|
||||
srcChan <-chan []byte // the channel that all of the WriteListeners write their data out to
|
||||
filePrefix string // optional prefix for each of the generated files
|
||||
writeDir string // directory to write files into
|
||||
dstFile *os.File // the current write output file
|
||||
marshaller codec.BinaryMarshaler // marshaller used for re-marshalling the ABCI messages to write them out to the destination files
|
||||
stateCache [][]byte // cache the protobuf binary encoded StoreKVPairs in the order they are received
|
||||
}
|
||||
```
|
||||
|
||||
This streaming service uses a single instance of a simple intermediate `io.Writer` as the underlying `io.Writer` for its single `StoreKVPairWriteListener`,
|
||||
It collects KV pairs from every KVStore synchronously off of the same channel, caching them in the order they are received, and then writing
|
||||
them out to a file generated in response to an ABCI message hook. Files are named as outlined above, with optional prefixes to avoid potential naming collisions
|
||||
across separate instances.
|
||||
|
||||
```go
|
||||
// intermediateWriter is used so that we do not need to update the underlying io.Writer inside the StoreKVPairWriteListener
|
||||
// everytime we begin writing to a new file
|
||||
type intermediateWriter struct {
|
||||
outChan chan <-[]byte
|
||||
}
|
||||
|
||||
// NewIntermediateWriter create an instance of an intermediateWriter that sends to the provided channel
|
||||
func NewIntermediateWriter(outChan chan <-[]byte) *intermediateWriter {
|
||||
return &intermediateWriter{
|
||||
outChan: outChan,
|
||||
}
|
||||
}
|
||||
|
||||
// Write satisfies io.Writer
|
||||
func (iw *intermediateWriter) Write(b []byte) (int, error) {
|
||||
iw.outChan <- b
|
||||
return len(b), nil
|
||||
}
|
||||
|
||||
// NewFileStreamingService creates a new FileStreamingService for the provided writeDir, (optional) filePrefix, and storeKeys
|
||||
func NewFileStreamingService(writeDir, filePrefix string, storeKeys []sdk.StoreKey, m codec.BinaryMarshaler) (*FileStreamingService, error) {
|
||||
listenChan := make(chan []byte, 0)
|
||||
iw := NewIntermediateWriter(listenChan)
|
||||
listener := listen.NewStoreKVPairWriteListener(iw, m)
|
||||
listners := make(map[sdk.StoreKey][]storeTypes.WriteListener, len(storeKeys))
|
||||
// in this case, we are using the same listener for each Store
|
||||
for _, key := range storeKeys {
|
||||
listeners[key] = listener
|
||||
}
|
||||
// check that the writeDir exists and is writeable so that we can catch the error here at initialization if it is not
|
||||
// we don't open a dstFile until we receive our first ABCI message
|
||||
if err := fileutil.IsDirWriteable(writeDir); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &FileStreamingService{
|
||||
listeners: listeners,
|
||||
srcChan: listenChan,
|
||||
filePrefix: filePrefix,
|
||||
writeDir: writeDir,
|
||||
marshaller: m,
|
||||
stateCache: make([][]byte, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Listeners returns the StreamingService's underlying WriteListeners, use for registering them with the BaseApp
|
||||
func (fss *FileStreamingService) Listeners() map[sdk.StoreKey][]storeTypes.WriteListener {
|
||||
return fss.listeners
|
||||
}
|
||||
|
||||
func (fss *FileStreamingService) ListenBeginBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) {
|
||||
// NOTE: this could either be done synchronously or asynchronously
|
||||
// create a new file with the req info according to naming schema
|
||||
// write req to file
|
||||
// write all state changes cached for this stage to file
|
||||
// reset cache
|
||||
// write res to file
|
||||
// close file
|
||||
}
|
||||
|
||||
func (fss *FileStreamingService) ListenEndBlock(ctx sdk.Context, req abci.RequestBeginBlock, res abci.ResponseBeginBlock) {
|
||||
// NOTE: this could either be done synchronously or asynchronously
|
||||
// create a new file with the req info according to naming schema
|
||||
// write req to file
|
||||
// write all state changes cached for this stage to file
|
||||
// reset cache
|
||||
// write res to file
|
||||
// close file
|
||||
}
|
||||
|
||||
func (fss *FileStreamingService) ListenDeliverTx(ctx sdk.Context, req abci.RequestDeliverTx, res abci.ResponseDeliverTx) {
|
||||
// NOTE: this could either be done synchronously or asynchronously
|
||||
// create a new file with the req info according to naming schema
|
||||
// NOTE: if the tx failed, handle accordingly
|
||||
// write req to file
|
||||
// write all state changes cached for this stage to file
|
||||
// reset cache
|
||||
// write res to file
|
||||
// close file
|
||||
}
|
||||
|
||||
// Stream spins up a goroutine select loop which awaits length-prefixed binary encoded KV pairs and caches them in the order they were received
|
||||
func (fss *FileStreamingService) Stream(wg *sync.WaitGroup, quitChan <-chan struct{}) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for {
|
||||
select {
|
||||
case <-quitChan:
|
||||
return
|
||||
case by := <-fss.srcChan:
|
||||
append(fss.stateCache, by)
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
```
|
||||
|
||||
Writing to a file is the simplest approach for streaming the data out to consumers.
|
||||
This approach also provides the advantages of being persistent and durable, and the files can be read directly,
|
||||
or an auxiliary streaming services can read from the files and serve the data over a remote interface.
|
||||
|
||||
#### Auxiliary streaming service
|
||||
|
||||
We will create a separate standalone process that reads and internally queues the state as it is written out to these files
|
||||
and serves the data over a gRPC API. This API will allow filtering of requested data, e.g. by block number, block/tx hash, ABCI message type,
|
||||
whether a DeliverTx message failed or succeeded, etc. In addition to unary RPC endpoints this service will expose `stream` RPC endpoints for realtime subscriptions.
|
||||
|
||||
#### File pruning
|
||||
|
||||
Without pruning the number of files can grow indefinitely, this may need to be managed by
|
||||
the developer in an application or even module-specific manner (e.g. log rotation).
|
||||
The file naming schema facilitates pruning by block number and/or ABCI message.
|
||||
The gRPC auxiliary streaming service introduced above will include an option to remove the files as it consumes their data.
|
||||
|
||||
### Configuration
|
||||
|
||||
We will provide detailed documentation on how to configure a `FileStreamingService` from within an app's `AppCreator`,
|
||||
using the provided `AppOptions` and TOML configuration fields.
|
||||
|
||||
#### BaseApp registration
|
||||
|
||||
We will add a new method to the `BaseApp` to enable the registration of `StreamingService`s:
|
||||
|
||||
```go
|
||||
// RegisterStreamingService is used to register a streaming service with the BaseApp
|
||||
func (app *BaseApp) RegisterHooks(s StreamingService) {
|
||||
// set the listeners for each StoreKey
|
||||
for key, lis := range s.Listeners() {
|
||||
app.cms.SetListeners(key, lis)
|
||||
}
|
||||
// register the streaming service hooks within the BaseApp
|
||||
// BaseApp will pass BeginBlock, DeliverTx, and EndBlock requests and responses to the streaming services to update their ABCI context using these hooks
|
||||
app.hooks = append(app.hooks, s)
|
||||
}
|
||||
```
|
||||
|
||||
We will also modify the `BeginBlock`, `EndBlock`, and `DeliverTx` methods to pass ABCI requests and responses to any streaming service hooks registered
|
||||
with the `BaseApp`.
|
||||
|
||||
|
||||
```go
|
||||
func (app *BaseApp) BeginBlock(req abci.RequestBeginBlock) (res abci.ResponseBeginBlock) {
|
||||
|
||||
...
|
||||
|
||||
// Call the streaming service hooks with the BeginBlock messages
|
||||
for _ hook := range app.hooks {
|
||||
hook.ListenBeginBlock(app.deliverState.ctx, req, res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func (app *BaseApp) EndBlock(req abci.RequestEndBlock) (res abci.ResponseEndBlock) {
|
||||
|
||||
...
|
||||
|
||||
// Call the streaming service hooks with the EndBlock messages
|
||||
for _, hook := range app.hooks {
|
||||
hook.ListenEndBlock(app.deliverState.ctx, req, res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
```go
|
||||
func (app *BaseApp) DeliverTx(req abci.RequestDeliverTx) abci.ResponseDeliverTx {
|
||||
|
||||
...
|
||||
|
||||
gInfo, result, err := app.runTx(runTxModeDeliver, req.Tx)
|
||||
if err != nil {
|
||||
resultStr = "failed"
|
||||
res := sdkerrors.ResponseDeliverTx(err, gInfo.GasWanted, gInfo.GasUsed, app.trace)
|
||||
// If we throw and error, be sure to still call the streaming service's hook
|
||||
for _, hook := range app.hooks {
|
||||
hook.ListenDeliverTx(app.deliverState.ctx, req, res)
|
||||
}
|
||||
return res
|
||||
}
|
||||
|
||||
res := abci.ResponseDeliverTx{
|
||||
GasWanted: int64(gInfo.GasWanted), // TODO: Should type accept unsigned ints?
|
||||
GasUsed: int64(gInfo.GasUsed), // TODO: Should type accept unsigned ints?
|
||||
Log: result.Log,
|
||||
Data: result.Data,
|
||||
Events: sdk.MarkEventsToIndex(result.Events, app.indexEvents),
|
||||
}
|
||||
|
||||
// Call the streaming service hooks with the DeliverTx messages
|
||||
for _, hook := range app.hook {
|
||||
hook.ListenDeliverTx(app.deliverState.ctx, req, res)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
```
|
||||
|
||||
#### TOML Configuration
|
||||
|
||||
We will provide standard TOML configuration options for configuring a `FileStreamingService` for specific `Store`s.
|
||||
Note: the actual namespace is TBD.
|
||||
|
||||
```toml
|
||||
[store]
|
||||
streamers = [ # if len(streamers) > 0 we are streaming
|
||||
"file",
|
||||
]
|
||||
|
||||
[streamers]
|
||||
[streamers.file]
|
||||
keys = ["list", "of", "store", "keys", "we", "want", "to", "expose", "for", "this", "streaming", "service"]
|
||||
writeDir = "path to the write directory"
|
||||
prefix = "optional prefix to prepend to the generated file names"
|
||||
```
|
||||
|
||||
We will also provide a mapping of the TOML `store.streamers` "file" configuration option to a helper functions for constructing the specified
|
||||
streaming service. In the future, as other streaming services are added, their constructors will be added here as well.
|
||||
|
||||
```go
|
||||
// StreamingServiceConstructor is used to construct a streaming service
|
||||
type StreamingServiceConstructor func(opts servertypes.AppOptions, keys []sdk.StoreKey) (StreamingService, error)
|
||||
|
||||
// StreamingServiceType enum for specifying the type of StreamingService
|
||||
type StreamingServiceType int
|
||||
|
||||
const (
|
||||
Unknown StreamingServiceType = iota
|
||||
File
|
||||
// add more in the future
|
||||
)
|
||||
|
||||
// NewStreamingServiceType returns the StreamingServiceType corresponding to the provided name
|
||||
func NewStreamingServiceType(name string) StreamingServiceType {
|
||||
switch strings.ToLower(name) {
|
||||
case "file", "f":
|
||||
return File
|
||||
default:
|
||||
return Unknown
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the string name of a StreamingServiceType
|
||||
func (sst StreamingServiceType) String() string {
|
||||
switch sst {
|
||||
case File:
|
||||
return "file"
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// StreamingServiceConstructorLookupTable is a mapping of StreamingServiceTypes to StreamingServiceConstructors
|
||||
var StreamingServiceConstructorLookupTable = map[StreamingServiceType]StreamingServiceConstructor{
|
||||
File: FileStreamingConstructor,
|
||||
}
|
||||
|
||||
// NewStreamingServiceConstructor returns the StreamingServiceConstructor corresponding to the provided name
|
||||
func NewStreamingServiceConstructor(name string) (StreamingServiceConstructor, error) {
|
||||
ssType := NewStreamingServiceType(name)
|
||||
if ssType == Unknown {
|
||||
return nil, fmt.Errorf("unrecognized streaming service name %s", name)
|
||||
}
|
||||
if constructor, ok := StreamingServiceConstructorLookupTable[ssType]; ok {
|
||||
return constructor, nil
|
||||
}
|
||||
return nil, fmt.Errorf("streaming service constructor of type %s not found", ssType.String())
|
||||
}
|
||||
|
||||
// FileStreamingConstructor is the StreamingServiceConstructor function for creating a FileStreamingService
|
||||
func FileStreamingConstructor(opts servertypes.AppOptions, keys []sdk.StoreKey) (StreamingService, error) {
|
||||
filePrefix := cast.ToString(opts.Get("streamers.file.prefix"))
|
||||
fileDir := cast.ToString(opts.Get("streamers.file.writeDir"))
|
||||
return streaming.NewFileStreamingService(fileDir, filePrefix, keys), nil
|
||||
}
|
||||
```
|
||||
|
||||
#### Example configuration
|
||||
|
||||
As a demonstration, we will implement the state watching features as part of SimApp.
|
||||
For example, the below is a very rudimentary integration of the state listening features into the SimApp `AppCreator` function:
|
||||
|
||||
|
||||
```go
|
||||
func NewSimApp(
|
||||
logger log.Logger, db dbm.DB, traceStore io.Writer, loadLatest bool, skipUpgradeHeights map[int64]bool,
|
||||
homePath string, invCheckPeriod uint, encodingConfig simappparams.EncodingConfig,
|
||||
appOpts servertypes.AppOptions, baseAppOptions ...func(*baseapp.BaseApp),
|
||||
) *SimApp {
|
||||
|
||||
...
|
||||
|
||||
keys := sdk.NewKVStoreKeys(
|
||||
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
|
||||
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
|
||||
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
|
||||
evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
|
||||
)
|
||||
|
||||
// configure state listening capabilities using AppOptions
|
||||
listeners := cast.ToStringSlice(appOpts.Get("store.streamers"))
|
||||
for _, listenerName := range listeners {
|
||||
// get the store keys allowed to be exposed for this streaming service/state listeners
|
||||
exposeKeyStrs := cast.ToStringSlice(appOpts.Get(fmt.Sprintf("streamers.%s.keys", listenerName))
|
||||
exposeStoreKeys = make([]storeTypes.StoreKey, 0, len(exposeKeyStrs))
|
||||
for _, keyStr := range exposeKeyStrs {
|
||||
if storeKey, ok := keys[keyStr]; ok {
|
||||
exposeStoreKeys = append(exposeStoreKeys, storeKey)
|
||||
}
|
||||
}
|
||||
// get the constructor for this listener name
|
||||
constructor, err := baseapp.NewStreamingServiceConstructor(listenerName)
|
||||
if err != nil {
|
||||
tmos.Exit(err.Error()) // or continue?
|
||||
}
|
||||
// generate the streaming service using the constructor, appOptions, and the StoreKeys we want to expose
|
||||
streamingService, err := constructor(appOpts, exposeStoreKeys)
|
||||
if err != nil {
|
||||
tmos.Exit(err.Error())
|
||||
}
|
||||
// register the streaming service with the BaseApp
|
||||
bApp.RegisterStreamingService(streamingService)
|
||||
// waitgroup and quit channel for optional shutdown coordination of the streaming service
|
||||
wg := new(sync.WaitGroup)
|
||||
quitChan := new(chan struct{}))
|
||||
// kick off the background streaming service loop
|
||||
streamingService.Stream(wg, quitChan) // maybe this should be done from inside BaseApp instead?
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
return app
|
||||
}
|
||||
```
|
||||
|
||||
## Consequences
|
||||
|
||||
These changes will provide a means of subscribing to KVStore state changes in real time.
|
||||
|
||||
### Backwards Compatibility
|
||||
|
||||
- This ADR changes the `MultiStore`, `CacheWrap`, and `CacheWrapper` interfaces, implementations supporting the previous version of these interfaces will not support the new ones
|
||||
|
||||
### Positive
|
||||
|
||||
- Ability to listen to KVStore state changes in real time and expose these events to external consumers
|
||||
|
||||
### Negative
|
||||
|
||||
- Changes `MultiStore`, `CacheWrap`, and `CacheWrapper` interfaces
|
||||
|
||||
### Neutral
|
||||
|
||||
- Introduces additional- but optional- complexity to configuring and running a cosmos application
|
||||
- If an application developer opts to use these features to expose data, they need to be aware of the ramifications/risks of that data exposure as it pertains to the specifics of their application
|
||||
@ -24,7 +24,7 @@ The actual implementation of `BeginBlocker` and `EndBlocker` in `./abci.go` are
|
||||
|
||||
A specificity of the `EndBlocker` is that it can return validator updates to the underlying consensus engine in the form of an [`[]abci.ValidatorUpdates`](https://tendermint.com/docs/app-dev/abci-spec.html#validatorupdate). This is the preferred way to implement custom validator changes.
|
||||
|
||||
It is possible for developers to defined the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager).
|
||||
It is possible for developers to define the order of execution between the `BeginBlocker`/`EndBlocker` functions of each of their application's modules via the module's manager `SetOrderBeginBlocker`/`SetOrderEndBlocker` methods. For more on the module manager, click [here](./module-manager.md#manager).
|
||||
|
||||
See an example implementation of `BeginBlocker` from the `distr` module:
|
||||
|
||||
|
||||
@ -74,7 +74,8 @@ Let us go through the methods of `AppModule`:
|
||||
- `LegacyQuerierHandler(*codec.LegacyAmino)` (deprecated): Returns a [`querier`](./query-services.md#legacy-queriers) given the query `path`, in order to process the `query`.
|
||||
- `RegisterServices(Configurator)`: Allows a module to register services.
|
||||
- `BeginBlock(sdk.Context, abci.RequestBeginBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the beginning of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the beginning of each block for this module.
|
||||
- `EndBlock(sdk.Context, abci.RequestEndBlock)`: This method gives module developers the option to implement logic that is automatically triggered at the end of each block. This is also where the module can inform the underlying consensus engine of validator set changes (e.g. the `staking` module). Implement empty if no logic needs to be triggered at the end of each block for this module.
|
||||
|
||||
|
||||
### Implementing the Application Module Interfaces
|
||||
|
||||
@ -132,7 +133,7 @@ The module manager is used throughout the application whenever an action on a co
|
||||
- `SetOrderInitGenesis(moduleNames ...string)`: Sets the order in which the [`InitGenesis`](./genesis.md#initgenesis) function of each module will be called when the application is first started. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderExportGenesis(moduleNames ...string)`: Sets the order in which the [`ExportGenesis`](./genesis.md#exportgenesis) function of each module will be called in case of an export. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderBeginBlockers(moduleNames ...string)`: Sets the order in which the `BeginBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the beginning of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `SetOrderEndBlockers(moduleNames ...string)`: Sets the order in which the `EndBlock()` function of each module will be called at the end of each block. This function is generally called from the application's main [constructor function](../basics/app-anatomy.md#constructor-function).
|
||||
- `RegisterInvariants(ir sdk.InvariantRegistry)`: Registers the [invariants](./invariants.md) of each module.
|
||||
- `RegisterRoutes(router sdk.Router, queryRouter sdk.QueryRouter, legacyQuerierCdc *codec.LegacyAmino)`: Registers legacy [`Msg`](./messages-and-queries.md#messages) and [`querier`](./query-services.md#legacy-queriers) routes.
|
||||
- `RegisterServices(cfg Configurator)`: Registers all module services.
|
||||
|
||||
@ -108,6 +108,24 @@ Flags are added to commands directly (generally in the [module's CLI file](../bu
|
||||
|
||||
+++ https://github.com/cosmos/cosmos-sdk/blob/v0.40.0/simapp/simd/cmd/root.go#L118
|
||||
|
||||
## Environment variables
|
||||
|
||||
Each flag is bound to it's respecteve named environment variable. Then name of the environment variable consist of two parts - capital case `basename` followed by flag name of the flag. `-` must be substituted with `_`. For example flag `--home` for application with basename `GAIA` is bound to `GAIA_HOME`. It allows to reduce amount of flags typed for routine operations. For example instead of:
|
||||
```sh
|
||||
gaia --home=./ --node=<node address> --chain-id="testchain-1" --keyring-backend=test tx ... --from=<key name>
|
||||
```
|
||||
this will be more convinient:
|
||||
```sh
|
||||
# define env variables in .env, .envrc etc
|
||||
GAIA_HOME=<path to home>
|
||||
GAIA_NODE=<node address>
|
||||
GAIA_CHAIN_ID="testchain-1"
|
||||
GAIA_KEYRING_BACKEND="test"
|
||||
|
||||
# and later just use
|
||||
gaia tx ... --from=<key name>
|
||||
```
|
||||
|
||||
## Configurations
|
||||
|
||||
It is vital that the root command of an application uses `PersistentPreRun()` cobra command property for executing the command, so all child commands have access to the server and client contexts. These contexts are set as their default values initially and maybe modified, scoped to the command, in their respective `PersistentPreRun()` functions. Note that the `client.Context` is typically pre-populated with "default" values that may be useful for all commands to inherit and override if necessary.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
14
docs/ibc/upgrades/README.md
Normal file
14
docs/ibc/upgrades/README.md
Normal file
@ -0,0 +1,14 @@
|
||||
<!--
|
||||
order: false
|
||||
parent:
|
||||
order: 3
|
||||
-->
|
||||
|
||||
### Upgrading IBC Chains Overview
|
||||
|
||||
This directory contains information on how to upgrade an IBC chain without breaking counterparty clients and connections.
|
||||
|
||||
IBC-connnected chains must be able to upgrade without breaking connections to other chains. Otherwise there would be a massive disincentive towards upgrading and disrupting high-value IBC connections, thus preventing chains in the IBC ecosystem from evolving and improving. Many chain upgrades may be irrelevant to IBC, however some upgrades could potentially break counterparty clients if not handled correctly. Thus, any IBC chain that wishes to perform a IBC-client-breaking upgrade must perform an IBC upgrade in order to allow counterparty clients to securely upgrade to the new light client.
|
||||
|
||||
1. The [quick-guide](./quick-guide.md) describes how IBC-connected chains can perform client-breaking upgrades and how relayers can securely upgrade counterparty clients using the SDK.
|
||||
2. The [developer-guide](./developer-guide.md) is a guide for developers intending to develop IBC client implementations with upgrade functionality.
|
||||
50
docs/ibc/upgrades/developer-guide.md
Normal file
50
docs/ibc/upgrades/developer-guide.md
Normal file
@ -0,0 +1,50 @@
|
||||
<!--
|
||||
order: 2
|
||||
-->
|
||||
|
||||
# IBC Client Developer Guide to Upgrades
|
||||
|
||||
Learn how to implement upgrade functionality for your custom IBC client. {synopsis}
|
||||
|
||||
As mentioned in the [README](./README.md), it is vital that high-value IBC clients can upgrade along with their underlying chains to avoid disruption to the IBC ecosystem. Thus, IBC client developers will want to implement upgrade functionality to enable clients to maintain connections and channels even across chain upgrades.
|
||||
|
||||
The IBC protocol allows client implementations to provide a path to upgrading clients given the upgraded client state, upgraded consensus state and proofs for each.
|
||||
|
||||
```go
|
||||
// Upgrade functions
|
||||
// NOTE: proof heights are not included as upgrade to a new revision is expected to pass only on the last
|
||||
// height committed by the current revision. Clients are responsible for ensuring that the planned last
|
||||
// height of the current revision is somehow encoded in the proof verification process.
|
||||
// This is to ensure that no premature upgrades occur, since upgrade plans committed to by the counterparty
|
||||
// may be cancelled or modified before the last planned height.
|
||||
VerifyUpgradeAndUpdateState(
|
||||
ctx sdk.Context,
|
||||
cdc codec.BinaryMarshaler,
|
||||
store sdk.KVStore,
|
||||
newClient ClientState,
|
||||
newConsState ConsensusState,
|
||||
proofUpgradeClient,
|
||||
proofUpgradeConsState []byte,
|
||||
) (upgradedClient ClientState, upgradedConsensus ConsensusState, err error)
|
||||
```
|
||||
|
||||
Note that the clients should have prior knowledge of the merkle path that the upgraded client and upgraded consensus states will use. The height at which the upgrade has occurred should also be encoded in the proof. The Tendermint client implementation accomplishes this by including an `UpgradePath` in the ClientState itself, which is used along with the upgrade height to construct the merkle path under which the client state and consensus state are committed.
|
||||
|
||||
Developers must ensure that the `UpgradeClientMsg` does not pass until the last height of the old chain has been committed, and after the chain upgrades, the `UpgradeClientMsg` should pass once and only once on all counterparty clients.
|
||||
|
||||
Developers must ensure that the new client adopts all of the new Client parameters that must be uniform across every valid light client of a chain (chain-chosen parameters), while maintaining the Client parameters that are customizable by each individual client (client-chosen parameters) from the previous version of the client.
|
||||
|
||||
Upgrades must adhere to the IBC Security Model. IBC does not rely on the assumption of honest relayers for correctness. Thus users should not have to rely on relayers to maintain client correctness and security (though honest relayers must exist to maintain relayer liveness). While relayers may choose any set of client parameters while creating a new `ClientState`, this still holds under the security model since users can always choose a relayer-created client that suits their security and correctness needs or create a Client with their desired parameters if no such client exists.
|
||||
|
||||
However, when upgrading an existing client, one must keep in mind that there are already many users who depend on this client's particular parameters. We cannot give the upgrading relayer free choice over these parameters once they have already been chosen. This would violate the security model since users who rely on the client would have to rely on the upgrading relayer to maintain the same level of security. Thus, developers must make sure that their upgrade mechanism allows clients to upgrade the chain-specified parameters whenever a chain upgrade changes these parameters (examples in the Tendermint client include `UnbondingPeriod`, `ChainID`, `UpgradePath`, etc.), while ensuring that the relayer submitting the `UpgradeClientMsg` cannot alter the client-chosen parameters that the users are relying upon (examples in Tendermint client include `TrustingPeriod`, `TrustLevel`, `MaxClockDrift`, etc).
|
||||
|
||||
Developers should maintain the distinction between Client parameters that are uniform across every valid light client of a chain (chain-chosen parameters), and Client parameters that are customizable by each individual client (client-chosen parameters); since this distinction is necessary to implement the `ZeroCustomFields` method in the `ClientState` interface:
|
||||
|
||||
```go
|
||||
// Utility function that zeroes out any client customizable fields in client state
|
||||
// Ledger enforced fields are maintained while all custom fields are zero values
|
||||
// Used to verify upgrades
|
||||
ZeroCustomFields() ClientState
|
||||
```
|
||||
|
||||
Counterparty clients can upgrade securely by using all of the chain-chosen parameters from the chain-committed `UpgradedClient` and preserving all of the old client-chosen parameters. This enables chains to securely upgrade without relying on an honest relayer, however it can in some cases lead to an invalid final `ClientState` if the new chain-chosen parameters clash with the old client-chosen parameter. This can happen in the Tendermint client case if the upgrading chain lowers the `UnbondingPeriod` (chain-chosen) to a duration below that of a counterparty client's `TrustingPeriod` (client-chosen). Such cases should be clearly documented by developers, so that chains know which upgrades should be avoided to prevent this problem. The final upgraded client should also be validated in `VerifyUpgradeAndUpdateState` before returning to ensure that the client does not upgrade to an invalid `ClientState`.
|
||||
54
docs/ibc/upgrades/quick-guide.md
Normal file
54
docs/ibc/upgrades/quick-guide.md
Normal file
@ -0,0 +1,54 @@
|
||||
<!--
|
||||
order: 1
|
||||
-->
|
||||
|
||||
# How to Upgrade IBC Chains and their Clients
|
||||
|
||||
Learn how to upgrade your chain and counterparty clients. {synopsis}
|
||||
|
||||
The information in this doc for upgrading chains is relevant to SDK chains. However, the guide for counterparty clients is relevant to any Tendermint client that enables upgrades.
|
||||
|
||||
### IBC Client Breaking Upgrades
|
||||
|
||||
IBC-connected chains must perform an IBC upgrade if their upgrade will break counterparty IBC clients. The current IBC protocol supports upgrading tendermint chains for a specific subset of IBC-client-breaking upgrades. Here is the exhaustive list of IBC client-breaking upgrades and whether the IBC protocol currently supports such upgrades.
|
||||
|
||||
IBC currently does **NOT** support unplanned upgrades. All of the following upgrades must be planned and committed to in advance by the upgrading chain, in order for counterparty clients to maintain their connections securely.
|
||||
|
||||
Note: Since upgrades are only implemented for Tendermint clients, this doc only discusses upgrades on Tendermint chains that would break counterparty IBC Tendermint Clients.
|
||||
|
||||
1. Changing the Chain-ID: **Supported**
|
||||
2. Changing the UnbondingPeriod: **Partially Supported**, chains may increase the unbonding period with no issues. However, decreasing the unbonding period may irreversibly break some counterparty clients. Thus, it is **not recommended** that chains reduce the unbonding period.
|
||||
3. Changing the height (resetting to 0): **Supported**, so long as chains remember to increment the revision number in their chain-id.
|
||||
4. Changing the ProofSpecs: **Supported**, this should be changed if the proof structure needed to verify IBC proofs is changed across the upgrade. Ex: Switching from an IAVL store, to a SimpleTree Store
|
||||
5. Changing the UpgradePath: **Supported**, this might involve changing the key under which upgraded clients and consensus states are stored in the upgrade store, or even migrating the upgrade store itself.
|
||||
6. Migrating the IBC store: **Unsupported**, as the IBC store location is negotiated by the connection.
|
||||
7. Upgrading to a backwards compatible version of IBC: Supported
|
||||
8. Upgrading to a non-backwards compatible version of IBC: **Unsupported**, as IBC version is negotiated on connection handshake.
|
||||
9. Changing the Tendermint LightClient algorithm: **Partially Supported**. Changes to the light client algorithm that do not change the ClientState or ConsensusState struct may be supported, provided that the counterparty is also upgraded to support the new light client algorithm. Changes that require updating the ClientState and ConsensusState structs themselves are theoretically possible by providing a path to translate an older ClientState struct into the new ClientState struct; however this is not currently implemented.
|
||||
|
||||
### Step-by-Step Upgrade Process for SDK chains
|
||||
|
||||
If the IBC-connected chain is conducting an upgrade that will break counterparty clients, it must ensure that the upgrade is first supported by IBC using the list above and then execute the upgrade process described below in order to prevent counterparty clients from breaking.
|
||||
|
||||
1. Create a `SoftwareUpgradeProposal` with an `UpgradePlan` that includes the new IBC ClientState in the `UpgradedClientState`. Note that the `UpgradePlan` must specify an upgrade height **only** (no upgrade time), and the `ClientState` should only include the fields common to all valid clients and zero out any client-customizable fields (such as TrustingPeriod).
|
||||
2. Vote on and pass the `SoftwareUpgradeProposal`
|
||||
|
||||
Upon the `SoftwareUpgradeProposal` passing, the upgrade module will commit the UpgradedClient under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedClient`. On the block right before the upgrade height, the upgrade module will also commit an initial consensus state for the next chain under the key: `upgrade/UpgradedIBCState/{upgradeHeight}/upgradedConsState`.
|
||||
|
||||
Once the chain reaches the upgrade height and halts, a relayer can upgrade the counterparty clients to the last block of the old chain. They can then submit the proofs of the `UpgradedClient` and `UpgradedConsensusState` against this last block and upgrade the counterparty client.
|
||||
|
||||
### Step-by-Step Upgrade Process for Relayers Upgrading Counterparty Clients
|
||||
|
||||
Once the upgrading chain has committed to upgrading, relayers must wait till the chain halts at the upgrade height before upgrading counterparty clients. This is because chains may reschedule or cancel upgrade plans before they occur. Thus, relayers must wait till the chain reaches the upgrade height and halts before they can be sure the upgrade will take place.
|
||||
|
||||
Thus, the upgrade process for relayers trying to upgrade the counterparty clients is as follows:
|
||||
|
||||
1. Wait for the upgrading chain to reach the upgrade height and halt
|
||||
2. Query a full node for the proofs of `UpgradedClient` and `UpgradedConsensusState` at the last height of the old chain.
|
||||
3. Update the counterparty client to the last height of the old chain using the `UpdateClient` msg.
|
||||
4. Submit an `UpgradeClient` msg to the counterparty chain with the `UpgradedClient`, `UpgradedConsensusState` and their respective proofs.
|
||||
5. Submit an `UpdateClient` msg to the counterparty chain with a header from the new upgraded chain.
|
||||
|
||||
The Tendermint client on the counterparty chain will verify that the upgrading chain did indeed commit to the upgraded client and upgraded consensus state at the upgrade height (since the upgrade height is included in the key). If the proofs are verified against the upgrade height, then the client will upgrade to the new client while retaining all of its client-customized fields. Thus, it will retain its old TrustingPeriod, TrustLevel, MaxClockDrift, etc; while adopting the new chain-specified fields such as UnbondingPeriod, ChainId, UpgradePath, etc. Note, this can lead to an invalid client since the old client-chosen fields may no longer be valid given the new chain-chosen fields. Upgrading chains should try to avoid these situations by not altering parameters that can break old clients. For an example, see the UnbondingPeriod example in the supported upgrades section.
|
||||
|
||||
The upgraded consensus state will serve purely as a basis of trust for future `UpdateClientMsgs` and will not contain a consensus root to perform proof verification against. Thus, relayers must submit an `UpdateClientMsg` with a header from the new chain so that the connection can be used for proof verification again.
|
||||
@ -13,3 +13,4 @@ This folder contains documentation on how to run a node and interact with it.
|
||||
1. [Interacting with a Node](./interact-node.md)
|
||||
1. [Generating, Signing and Broadcasting Transactions](./txs.md)
|
||||
1. [Cosmos Upgrade Manager](./cosmovisor.md)
|
||||
1. [Rosetta API](./rosetta.md)
|
||||
|
||||
@ -16,7 +16,7 @@ There are multiple ways to interact with a node: using the CLI, using gRPC or us
|
||||
Now that your chain is running, it is time to try sending tokens from the first account you created to a second account. In a new terminal window, start by running the following query command:
|
||||
|
||||
```bash
|
||||
simd query account $MY_VALIDATOR_ADDRESS --chain-id my-test-chain
|
||||
simd query bank balances $MY_VALIDATOR_ADDRESS --chain-id my-test-chain
|
||||
```
|
||||
|
||||
You should see the current balance of the account you created, equal to the original balance of `stake` you granted it minus the amount you delegated via the `gentx`. Now, create a second account:
|
||||
@ -31,16 +31,16 @@ RECIPIENT=$(simd keys show recipient -a --keyring-backend test)
|
||||
The command above creates a local key-pair that is not yet registered on the chain. An account is created the first time it receives tokens from another account. Now, run the following command to send tokens to the `recipient` account:
|
||||
|
||||
```bash
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000000stake --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Check that the recipient account did receive the tokens.
|
||||
simd query account $RECIPIENT --chain-id my-test-chain
|
||||
simd query bank balances $RECIPIENT --chain-id my-test-chain
|
||||
```
|
||||
|
||||
Finally, delegate some of the stake tokens sent to the `recipient` account to the validator:
|
||||
|
||||
```bash
|
||||
simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain
|
||||
simd tx staking delegate $(simd keys show my_validator --bech val -a --keyring-backend test) 500stake --from recipient --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Query the total delegations to `validator`.
|
||||
simd query staking delegations-to $(simd keys show my_validator --bech val -a --keyring-backend test) --chain-id my-test-chain
|
||||
|
||||
@ -31,7 +31,7 @@ is a list of the most popular operating systems and their respective passwords m
|
||||
GNU/Linux distributions that use GNOME as default desktop environment typically come with
|
||||
[Seahorse](https://wiki.gnome.org/Apps/Seahorse). Users of KDE based distributions are
|
||||
commonly provided with [KDE Wallet Manager](https://userbase.kde.org/KDE_Wallet_Manager).
|
||||
Whilst the former is in fact a `libsecret` convenient frontend, the former is a `kwallet`
|
||||
Whilst the former is in fact a `libsecret` convenient frontend, the latter is a `kwallet`
|
||||
client.
|
||||
|
||||
`os` is the default option since operating system's default credentials managers are
|
||||
|
||||
83
docs/run-node/rosetta.md
Normal file
83
docs/run-node/rosetta.md
Normal file
@ -0,0 +1,83 @@
|
||||
# Rosetta
|
||||
|
||||
Package rosetta implements the rosetta API for the current cosmos sdk release series.
|
||||
|
||||
The client satisfies [cosmos-rosetta-gateway](https://github.com/tendermint/cosmos-rosetta-gateway) `Client` interface implementation.
|
||||
|
||||
## Extension
|
||||
|
||||
There are two ways in which you can customize and extend the implementation with your custom settings.
|
||||
|
||||
### Message extension
|
||||
|
||||
In order to make an `sdk.Msg` understandable by rosetta the only thing which is required is adding the methods to your message that satisfy the `rosetta.Msg` interface.
|
||||
Examples on how to do so can be found in the staking types such as `MsgDelegate`, or in bank types such as `MsgSend`.
|
||||
|
||||
### Client interface override
|
||||
|
||||
In case more customization is required, it's possible to embed the Client type and override the methods which require customizations.
|
||||
|
||||
Example:
|
||||
```go
|
||||
package custom_client
|
||||
import (
|
||||
|
||||
"context"
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
)
|
||||
|
||||
// CustomClient embeds the standard cosmos client
|
||||
// which means that it implements the cosmos-rosetta-gateway Client
|
||||
// interface while at the same time allowing to customize certain methods
|
||||
type CustomClient struct {
|
||||
*rosetta.Client
|
||||
}
|
||||
|
||||
func (c *CustomClient) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) {
|
||||
// provide custom signature bytes
|
||||
panic("implement me")
|
||||
}
|
||||
```
|
||||
|
||||
### Error extension
|
||||
|
||||
Since rosetta requires to provide 'returned' errors to network options. In order to declare a new rosetta error, we use the `errors` package in cosmos-rosetta-gateway.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
package custom_errors
|
||||
import crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
|
||||
var customErrRetriable = true
|
||||
var CustomError = crgerrs.RegisterError(100, "custom message", customErrRetriable, "description")
|
||||
```
|
||||
|
||||
Note: errors must be registered before cosmos-rosetta-gateway's `Server`.`Start` method is called. Otherwise the registration will be ignored. Errors with same code will be ignored too.
|
||||
|
||||
## Integration in app.go
|
||||
|
||||
To integrate rosetta as a command in your application, in app.go, in your root command simply use the `server.RosettaCommand` method.
|
||||
|
||||
Example:
|
||||
|
||||
```go
|
||||
package app
|
||||
import (
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func buildAppCommand(rootCmd *cobra.Command) {
|
||||
// more app.go init stuff
|
||||
// ...
|
||||
// add rosetta command
|
||||
rootCmd.AddCommand(server.RosettaCommand(encodingConfig.InterfaceRegistry, encodingConfig.Marshaler))
|
||||
}
|
||||
```
|
||||
|
||||
A full implementation example can be found in `simapp` package.
|
||||
|
||||
NOTE: when using a customized client, the command cannot be used as the constructors required **may** differ, so it's required to create a new one. We intend to provide a way to init a customized client without writing extra code in the future.
|
||||
@ -44,7 +44,7 @@ Before starting the chain, you need to populate the state with at least one acco
|
||||
Now that you have created a local account, go ahead and grant it some `stake` tokens in your chain's genesis file. Doing so will also make sure your chain is aware of this account's existence:
|
||||
|
||||
```bash
|
||||
simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000stake
|
||||
simd add-genesis-account $MY_VALIDATOR_ADDRESS 100000000000stake
|
||||
```
|
||||
|
||||
Recall that `$MY_VALIDATOR_ADDRESS` is a variable that holds the address of the `my_validator` key in the [keyring](./keyring.md#adding-keys-to-the-keyring). Also note that the tokens in the SDK have the `{amount}{denom}` format: `amount` is is a 18-digit-precision decimal number, and `denom` is the unique token identifier with its denomination key (e.g. `atom` or `uatom`). Here, we are granting `stake` tokens, as `stake` is the token identifier used for staking in [`simapp`](https://github.com/cosmos/cosmos-sdk/tree/v0.40.0-rc3/simapp). For your own chain with its own staking denom, that token identifier should be used instead.
|
||||
@ -53,7 +53,7 @@ Now that your account has some tokens, you need to add a validator to your chain
|
||||
|
||||
```bash
|
||||
# Create a gentx.
|
||||
simd gentx my_validator 100000stake --chain-id my-test-chain --keyring-backend test
|
||||
simd gentx my_validator 100000000stake --chain-id my-test-chain --keyring-backend test
|
||||
|
||||
# Add the gentx to the genesis file.
|
||||
simd collect-gentxs
|
||||
|
||||
@ -11,7 +11,7 @@ This document describes how to generate an (unsigned) transaction, signing it (w
|
||||
The easiest way to send transactions is using the CLI, as we have seen in the previous page when [interacting with a node](./interact-node.md#using-the-cli). For example, running the following command
|
||||
|
||||
```bash
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain
|
||||
simd tx bank send $MY_VALIDATOR_ADDRESS $RECIPIENT 1000stake --chain-id my-test-chain --keyring-backend test
|
||||
```
|
||||
|
||||
will run the following steps:
|
||||
|
||||
10
go.mod
10
go.mod
@ -4,18 +4,16 @@ module github.com/cosmos/cosmos-sdk
|
||||
|
||||
require (
|
||||
github.com/99designs/keyring v1.1.6
|
||||
github.com/DataDog/zstd v1.4.5 // indirect
|
||||
github.com/armon/go-metrics v0.3.6
|
||||
github.com/bgentry/speakeasy v0.1.0
|
||||
github.com/btcsuite/btcd v0.21.0-beta
|
||||
github.com/btcsuite/btcutil v1.0.2
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9
|
||||
github.com/confio/ics23/go v0.6.3
|
||||
github.com/cosmos/go-bip39 v1.0.0
|
||||
github.com/cosmos/iavl v0.15.3
|
||||
github.com/cosmos/ledger-cosmos-go v0.11.1
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
|
||||
github.com/dgraph-io/ristretto v0.0.3 // indirect
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25
|
||||
github.com/gogo/gateway v1.1.0
|
||||
github.com/gogo/protobuf v1.3.3
|
||||
@ -31,7 +29,7 @@ require (
|
||||
github.com/magiconair/properties v1.8.4
|
||||
github.com/mattn/go-isatty v0.0.12
|
||||
github.com/otiai10/copy v1.4.2
|
||||
github.com/pelletier/go-toml v1.8.0 // indirect
|
||||
github.com/pelletier/go-toml v1.8.1 // indirect
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/prometheus/client_golang v1.8.0
|
||||
github.com/prometheus/common v0.15.0
|
||||
@ -41,11 +39,12 @@ require (
|
||||
github.com/spf13/afero v1.3.4 // indirect
|
||||
github.com/spf13/cast v1.3.1
|
||||
github.com/spf13/cobra v1.1.1
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect; indirects
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5
|
||||
github.com/spf13/viper v1.7.1
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/tendermint/btcd v0.1.1
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15
|
||||
github.com/tendermint/go-amino v0.16.0
|
||||
github.com/tendermint/tendermint v0.34.3
|
||||
@ -54,6 +53,7 @@ require (
|
||||
google.golang.org/genproto v0.0.0-20210114201628-6edceaf6022f
|
||||
google.golang.org/grpc v1.35.0
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/ini.v1 v1.61.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
|
||||
116
go.sum
116
go.sum
@ -12,6 +12,19 @@ cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiy
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/99designs/keyring v1.1.6 h1:kVDC2uCgVwecxCk+9zoCt2uEL6dt+dfVzMvGgnVcIuM=
|
||||
github.com/99designs/keyring v1.1.6/go.mod h1:16e0ds7LGQQcT59QqkTg72Hh5ShM51Byv5PEmW6uoRU=
|
||||
github.com/Azure/azure-pipeline-go v0.2.1/go.mod h1:UGSo8XybXnIGZ3epmeBw7Jdz+HiUVpqIlpz/HKHylF4=
|
||||
github.com/Azure/azure-pipeline-go v0.2.2/go.mod h1:4rQ/NZncSvGqNkkOsNpOU1tgoNuIlp9AfUH5G1tvCHc=
|
||||
github.com/Azure/azure-storage-blob-go v0.7.0/go.mod h1:f9YQKtsG1nMisotuTPpO0tjNuEjKRYAcJU8/ydDI++4=
|
||||
github.com/Azure/go-autorest/autorest v0.9.0/go.mod h1:xyHB1BMZT0cuDHU7I0+g046+BFDTQ8rEZB0s4Yfa6bI=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.5.0/go.mod h1:8Z9fGy2MpX0PvDjB1pEgQTmVqjGhiHBW7RJJEciWzS0=
|
||||
github.com/Azure/go-autorest/autorest/adal v0.8.0/go.mod h1:Z6vX6WXXuyieHAXwMj0S6HY6e6wcHn37qQMBQlvY3lc=
|
||||
github.com/Azure/go-autorest/autorest/date v0.1.0/go.mod h1:plvfp3oPSKwf2DNjlBjWF/7vwR+cUD/ELuzDCXwHUVA=
|
||||
github.com/Azure/go-autorest/autorest/date v0.2.0/go.mod h1:vcORJHLJEh643/Ioh9+vPmf1Ij9AEBM5FuBIXLmIy0g=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.1.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.2.0/go.mod h1:OTyCOPRA2IgIlWxVYxBee2F5Gr4kF2zd2J5cFRaIDN0=
|
||||
github.com/Azure/go-autorest/autorest/mocks v0.3.0/go.mod h1:a8FDP3DYzQ4RYfVAxAN3SVSiiO77gL2j2ronKKP0syM=
|
||||
github.com/Azure/go-autorest/logger v0.1.0/go.mod h1:oExouG+K6PryycPJfVSxi/koC6LSNgds39diKLz7Vrc=
|
||||
github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
@ -26,6 +39,8 @@ github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/VictoriaMetrics/fastcache v1.5.7/go.mod h1:ptDBkNMQI4RtmVo8VS/XwRY6RoTu1dAWCbrk+6WsEM8=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/Workiva/go-datastructures v1.0.52 h1:PLSK6pwn8mYdaoaCZEMsXBpBotr4HHn9abU0yMQt0NI=
|
||||
@ -37,9 +52,11 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
|
||||
github.com/aristanetworks/goarista v0.0.0-20170210015632-ea17b1a17847/go.mod h1:D/tb0zPVXnP7fmsLZjtdUhSsumbK/ij54UXjjVgMGxQ=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
@ -48,6 +65,7 @@ github.com/armon/go-metrics v0.3.6/go.mod h1:4O98XIr/9W0sxpJ8UaYkvjk10Iff7SnFrb4
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.25.48/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
@ -57,6 +75,7 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
|
||||
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/btcsuite/btcd v0.0.0-20171128150713-2e60448ffcc6/go.mod h1:Dmm/EzmjnCiweXmzRIAiUWCInVmPgjkzgv5k4tVyXiQ=
|
||||
github.com/btcsuite/btcd v0.0.0-20190115013929-ed77733ec07d/go.mod h1:d3C0AkH6BRcvO8T0UEPu53cnw4IbV63x1bEjildYhO0=
|
||||
github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ=
|
||||
github.com/btcsuite/btcd v0.21.0-beta h1:At9hIZdJW0s9E/fAz28nrz6AmcNlSVucCH796ZteX1M=
|
||||
@ -76,6 +95,7 @@ github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46f
|
||||
github.com/casbin/casbin/v2 v2.1.2/go.mod h1:YcPU1XXisHhLzuxH9coDNf2FbKpjGlbCg3n9yuLkIJQ=
|
||||
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s=
|
||||
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY=
|
||||
@ -83,9 +103,13 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
|
||||
github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
github.com/cloudflare/cloudflare-go v0.10.2-0.20190916151808-a80f83b9add9/go.mod h1:1MxXX1Ux4x6mqPmjkUgTP1CdXIBXKX7T+Jk9Gxrmx+U=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
|
||||
github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.8/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9 h1:CuGQE3HFmYwdEACJnuOtVI9cofqPsGvq6FdFIzaOPKI=
|
||||
github.com/coinbase/rosetta-sdk-go v0.5.9/go.mod h1:xd4wYUhV3LkY78SPH8BUhc88rXfn2jYgN9BfiSjbcvM=
|
||||
github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
github.com/confio/ics23/go v0.6.3 h1:PuGK2V1NJWZ8sSkNDq91jgT/cahFEW9RGp4Y5jxulf0=
|
||||
github.com/confio/ics23/go v0.6.3/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
|
||||
@ -120,6 +144,7 @@ github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2
|
||||
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/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea/go.mod h1:93vsz/8Wt4joVM7c2AVqh+YRMiUSc14yDtF28KmMOgQ=
|
||||
github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
|
||||
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
|
||||
@ -134,29 +159,39 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
|
||||
github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
|
||||
github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
|
||||
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b h1:HBah4D48ypg3J7Np4N+HY/ZR76fx3HEUGxDU6Uk39oQ=
|
||||
github.com/dvsekhvalnov/jose2go v0.0.0-20200901110807-248326c1351b/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20200318091601-be3528f3a813/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/eapache/go-resiliency v1.1.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20160512033002-935e0e8a636c/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25 h1:2vLKys4RBU4pn2T/hjXMbvwTr1Cvy5THHrQkbeY9HRk=
|
||||
github.com/enigmampc/btcutil v1.0.3-0.20200723161021-e2fb6adb2a25/go.mod h1:hTr8+TLQmkUkgcuh3mcr5fjrT9c64ZzsBCdCEC6UppY=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0 h1:EQciDnbrYxy13PgWoY8AqoxGiPrpgBZ1R8UNe3ddc+A=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ethereum/go-ethereum v1.9.23 h1:SIKhg/z4Q7AbvqcxuPYvMxf36che/Rq/Pp0IdYEkbtw=
|
||||
github.com/ethereum/go-ethereum v1.9.23/go.mod h1:JIfVb6esrqALTExdz9hRYvrP0xBDf6wCncIu1hNwHpM=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51 h1:0JZ+dUmQeA8IIVUMzysrX4/AKuQwWhV2dYQuPZdvdSQ=
|
||||
github.com/facebookgo/ensure v0.0.0-20160127193407-b4ab57deab51/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
|
||||
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052/go.mod h1:UbMTZqLaRiH3MsBH8va0n7s1pQYcu3uTb8G4tygF4Zg=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870 h1:E2s37DuLxFhQDg5gKsWoLBOB0n+ZW8s599zru8FJ2/Y=
|
||||
github.com/facebookgo/subset v0.0.0-20150612182917-8dac2c3c4870/go.mod h1:5tD+neXqOorC30/tWg0LCSkrqj/AR6gu8yY8/fpw1q0=
|
||||
github.com/fatih/color v1.3.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
|
||||
github.com/felixge/httpsnoop v1.0.1 h1:lvB5Jl89CsZtGIWuTcDM1E/vkVs49/Ml7JJe07l8SPQ=
|
||||
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/fjl/memsize v0.0.0-20180418122429-ca190fb6ffbc/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
@ -164,6 +199,7 @@ github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
@ -175,6 +211,8 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0 h1:TrB8swr/68K7m9CcGut2g3UOihhbcbiMAYiuTXdEih4=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
|
||||
github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
|
||||
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
@ -207,6 +245,7 @@ github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2-0.20200707131729-196ae77b8a26/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2 h1:aeE13tS0IiQgFjYdoL8qN3K1N2bXXtI6Vi51/y7BpMw=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
@ -218,9 +257,13 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2 h1:X2ev0eStA3AbceY54o37/0PQ/UWqKEiiO2dKL5OPaFM=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
|
||||
github.com/google/gofuzz v1.0.0 h1:A8PeW59pxE9IoFRqBp37U+mSNaQoZ46F1f0f863XSXw=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa h1:Q75Upo5UN4JbPFURXZ8nLKYUvF85dyFRop/vQ0Rv+64=
|
||||
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
@ -241,8 +284,10 @@ github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.1-0.20190629185528-ae1634f6a989/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.2.1/go.mod h1:EaizFBKfUKtMIF5iaDEhniwNedqGo9FuLFzppDr3uwI=
|
||||
@ -293,13 +338,18 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/holiman/uint256 v1.1.1/go.mod h1:y4ga/t+u+Xwd7CpDgZESaRcWy0I7XMlTMA25ApIH5Jw=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc=
|
||||
github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o=
|
||||
github.com/improbable-eng/grpc-web v0.13.0 h1:7XqtaBWaOCH0cVGKHyvhtcuo6fgW32Y10yRKrDHFHOc=
|
||||
github.com/improbable-eng/grpc-web v0.13.0/go.mod h1:6hRR09jOEG81ADP5wCQju1z71g6OL4eEvELdran/3cs=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/influxdata/influxdb v1.2.3-0.20180221223340-01288bdb0883/go.mod h1:qZna6X/4elxqT3yI9iZYdZrWWdeFOOprn86kgg4+IzY=
|
||||
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
|
||||
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc=
|
||||
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||
@ -317,8 +367,10 @@ github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.1.1-0.20170430222011-975b5c4c7c21/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d h1:Z+RDyXzjKE0i2sTjZ/b1uxiGtPhFy34Ou/Tk0qwN0kM=
|
||||
github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d/go.mod h1:JJNrCn9otv/2QP4D7SMJBgaleKpOf66PnW6F5WGNRIc=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@ -331,23 +383,36 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxv
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/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/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs=
|
||||
github.com/libp2p/go-buffer-pool v0.0.2/go.mod h1:MvaB6xw5vOrDl8rYZGLFdKAuk/hRoRZd1Vi32+RXyFM=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4=
|
||||
github.com/lucasjones/reggen v0.0.0-20180717132126-cdb49ff09d77/go.mod h1:5ELEyG+X8f+meRWHuqUOewBOhvHkl7M76pdGEansxW4=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.4 h1:8KGKTcQQGm0Kv7vEbKFErAoAOFyyacLStRtQSeYtvkY=
|
||||
github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.0/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190610004146-91bb50d98149/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-ieproxy v0.0.0-20190702010315-6dee0af9227d/go.mod h1:31jz6HNzdxOmlERGGEc4v/dMssOfmp2p5bT/okiKFFc=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.5-0.20180830101745-3fb116b82035/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
|
||||
github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE=
|
||||
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
|
||||
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.3/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@ -365,6 +430,8 @@ github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0Qu
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.3 h1:SzB1nHZ2Xi+17FP0zVQBHIZqvwRN9408fJO8h+eeNA8=
|
||||
github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
@ -374,6 +441,8 @@ github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ib
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/naoina/go-stringutil v0.1.0/go.mod h1:XJ2SJL9jCtBh+P9q5btrd/Ylo8XwT/h1USek5+NqSA0=
|
||||
github.com/naoina/toml v0.1.2-0.20170918210437-9fafd6967416/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4i1E=
|
||||
github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
|
||||
github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
|
||||
@ -389,6 +458,8 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb
|
||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/olekukonko/tablewriter v0.0.2-0.20190409134802-7e037d187b0c/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
@ -420,11 +491,13 @@ github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIw
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pborman/uuid v0.0.0-20170112150404-1b00554d8222/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
|
||||
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pelletier/go-toml v1.8.1 h1:1Nf83orprkJyknT6h7zbuEGUEjcyVlCxSUGTENmNCRM=
|
||||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
|
||||
github.com/peterh/liner v1.1.1-0.20190123174540-a2c9a5303de7/go.mod h1:CRroGNssyjTd/qIG2FyxByd2S8JEAZXBl4qUrZf8GS0=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 h1:q2e307iGHPdTGp0hoxKjt1H5pDo6utceo3dQVK3I5XQ=
|
||||
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
|
||||
github.com/pierrec/lz4 v1.0.2-0.20190131084431-473cd7ce01a1/go.mod h1:3/3N9NVKO0jef7pBehbT1qWhCMrIgbYNnFAZCqQ5LRc=
|
||||
@ -472,6 +545,7 @@ github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+Gx
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.2.0 h1:wH4vA7pcjKuZzjF7lM8awk4fnuJO6idemZXoKnULUx4=
|
||||
github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/tsdb v0.6.2-0.20190402121629-4f204dcbc150/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
|
||||
github.com/rakyll/statik v0.1.7/go.mod h1:AlZONWzMtEnMs7W4e/1LURLiI49pIMmp6V9Unghqrcc=
|
||||
@ -482,11 +556,14 @@ github.com/regen-network/cosmos-proto v0.3.1 h1:rV7iM4SSFAagvy8RiyhiACbWEGotmqzy
|
||||
github.com/regen-network/cosmos-proto v0.3.1/go.mod h1:jO0sVX6a1B36nmE8C9xBFXpNwWejXC7QqCOnH3O0+YM=
|
||||
github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4=
|
||||
github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI=
|
||||
github.com/rjeczalik/notify v0.9.1/go.mod h1:rKwnCoCGeuQnwBtTSPL9Dad03Vh2n40ePRrjvIXnJho=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
|
||||
github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
|
||||
github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ=
|
||||
github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
|
||||
github.com/rs/zerolog v1.20.0 h1:38k9hgtUBdxFwE34yS8rTHmHBa4eN16E4DJlv177LNs=
|
||||
github.com/rs/zerolog v1.20.0/go.mod h1:IzD0RJ65iWH0w97OQQebJEvTZYvsCUm9WVLWBQrJRjo=
|
||||
@ -497,6 +574,7 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
|
||||
github.com/sasha-s/go-deadlock v0.2.0 h1:lMqc+fUb7RrFS3gQLtoQsJ7/6TV/pAIFvBsqX73DK8Y=
|
||||
github.com/sasha-s/go-deadlock v0.2.0/go.mod h1:StQn567HiB1fF2yJ44N9au7wOhrPS3iZqiDbRupzT10=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shirou/gopsutil v2.20.5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
@ -535,6 +613,9 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4/go.mod h1:RZLeN1LMWmRsyYjvAu+I6Dm9QmlDaIIt+Y+4Kd7Tp+Q=
|
||||
github.com/steakknife/bloomfilter v0.0.0-20180922174646-6819c0d2a570/go.mod h1:8OR4w3TdeIHIh1g6EMY5p0gVNOovcWC+1vpc7naMuAw=
|
||||
github.com/steakknife/hamming v0.0.0-20180906055917-c99c65617cd3/go.mod h1:hpGUWaI9xL8pRQCTXQgocU38Qw1g0Us7n5PxxTwTCYU=
|
||||
github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw=
|
||||
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
|
||||
@ -545,6 +626,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
@ -556,6 +638,8 @@ github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c h1:g+WoO5jjkqGAzH
|
||||
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8=
|
||||
github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s=
|
||||
github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U=
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2 h1:crekJuQ57yIBDuKd3/dMJ00ZvOHURuv9RGJSi2hWTW4=
|
||||
github.com/tendermint/cosmos-rosetta-gateway v0.3.0-rc2/go.mod h1:gBPw8WV2Erm4UGHlBRiM3zaEBst4bsuihmMCNQdgP/s=
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI=
|
||||
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk=
|
||||
github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E=
|
||||
@ -568,13 +652,21 @@ github.com/tendermint/tendermint v0.34.3/go.mod h1:h57vnXeOlrdvvNFCqPBSaOrpOivl+
|
||||
github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI=
|
||||
github.com/tendermint/tm-db v0.6.3 h1:ZkhQcKnB8/2jr5EaZwGndN4owkPsGezW2fSisS9zGbg=
|
||||
github.com/tendermint/tm-db v0.6.3/go.mod h1:lfA1dL9/Y/Y8wwyPp2NMLyn5P5Ptr/gvDFNWtrCWSf8=
|
||||
github.com/tidwall/gjson v1.6.1/go.mod h1:BaHyNc5bjzYkPqgLq7mdVzeiRtULKULXLgZFKsxEHI0=
|
||||
github.com/tidwall/match v1.0.1/go.mod h1:LujAq0jyVjBy028G1WhWfIzbpQfMO8bBZ6Tyb0+pL9E=
|
||||
github.com/tidwall/pretty v1.0.2/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
|
||||
github.com/tidwall/sjson v1.1.2/go.mod h1:SEzaDwxiPzKzNfUEO4HbYF/m4UCSJDsGgNqsS1LvdoY=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
|
||||
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef/go.mod h1:sJ5fKU0s6JVwZjjcUEX2zFOnvq0ASQ2K9Zr6cf67kNs=
|
||||
github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/vmihailenco/msgpack/v5 v5.0.0-beta.9/go.mod h1:HVxBVPUK/+fZMonk4bi1islLa8V3cfnBug0+4dykPzo=
|
||||
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
|
||||
github.com/wsddn/go-ecdh v0.0.0-20161211032359-48726bab9208/go.mod h1:IotVbo4F+mw0EzQ08zFqg7pK3FebNXpaMsRy2RT+Ees=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
@ -623,6 +715,7 @@ golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWP
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw=
|
||||
@ -636,15 +729,18 @@ golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHl
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20200801112145-973feb4309de/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@ -669,6 +765,7 @@ golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -681,6 +778,7 @@ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@ -692,6 +790,7 @@ golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190130150945-aca44879d564/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -713,12 +812,16 @@ golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200420163511-1957bb5e6d1f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200824131525-c12d262b63d8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
|
||||
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -754,6 +857,7 @@ golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtn
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@ -818,9 +922,14 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy
|
||||
gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o=
|
||||
gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.61.0 h1:LBCdW4FmFYL4s/vDZD1RQYX7oAR6IjujCYgMdbHBR10=
|
||||
gopkg.in/ini.v1 v1.61.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
|
||||
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6/go.mod h1:uAJfkITjFhyEEuUfm7bsmCZRbW5WRq8s9EY8HZ6hCns=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/urfave/cli.v1 v1.20.0/go.mod h1:vuBzUtMdQeixQj8LVd+/98pzhxNGQoyuPBlsXHOQNO0=
|
||||
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
@ -834,6 +943,9 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
137
package-lock.json
generated
137
package-lock.json
generated
@ -1,137 +0,0 @@
|
||||
{
|
||||
"requires": true,
|
||||
"lockfileVersion": 1,
|
||||
"dependencies": {
|
||||
"@types/body-parser": {
|
||||
"version": "1.19.0",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.0.tgz",
|
||||
"integrity": "sha512-W98JrE0j2K78swW4ukqMleo8R7h/pFETjM2DQ90MF6XK2i4LO4W3gQ71Lt4w3bfm2EvVSyWHplECvB5sK22yFQ==",
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "4.17.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.6.tgz",
|
||||
"integrity": "sha512-n/mr9tZI83kd4azlPG5y997C/M4DNABK9yErhFM6hKdym4kkmd9j0vtsJyjFIwfRBxtrxZtAfGZCNRIBMFLK5w==",
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.8.tgz",
|
||||
"integrity": "sha512-1SJZ+R3Q/7mLkOD9ewCBDYD2k0WyZQtWYqF/2VvoNN2/uhI49J9CDN4OAm+wGMA0DbArA4ef27xl4+JwMtGggw==",
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.2.tgz",
|
||||
"integrity": "sha512-4kPlzbljFcsttWEq6aBW0OZe6BDajAmyvr2xknBG92tejQnvdGtT9+kXSZ580DqpxY9qG2xeQVF9Dq0ymUTo5Q=="
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "14.0.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-14.0.14.tgz",
|
||||
"integrity": "sha512-syUgf67ZQpaJj01/tRTknkMNoBBLWJOBODF0Zm4NrXmiSuxjymFrxnTu1QVYRubhVkRcZLYZG8STTwJRdVm/WQ=="
|
||||
},
|
||||
"@types/passport": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.3.tgz",
|
||||
"integrity": "sha512-nyztuxtDPQv9utCzU0qW7Gl8BY2Dn8BKlYAFFyxKipFxjaVd96celbkLCV/tRqqBUZ+JB8If3UfgV8347DTo3Q==",
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/passport-twitter": {
|
||||
"version": "1.0.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-twitter/-/passport-twitter-1.0.35.tgz",
|
||||
"integrity": "sha512-7ceE/w7bvIqDPdOkPuXSDHTwwCnBW/wpn4vB98AlXieJ31nuCzjWZKWjxtyNpie8/StigN1tzF/YCRGgEh2Seg==",
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*"
|
||||
}
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.3.tgz",
|
||||
"integrity": "sha512-7s9EQWupR1fTc2pSMtXRQ9w9gLOcrJn+h7HOXw4evxyvVqMi4f+q7d2tnFe3ng3SNHjtK+0EzGMGFUQX4/AQRA=="
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.4.tgz",
|
||||
"integrity": "sha512-jTDt0o/YbpNwZbQmE/+2e+lfjJEJJR0I3OFaKQKPWkASkCoW3i6fsUnqudSMcNAfbtmADGu8f4MV4q+GqULmug==",
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/mime": "*"
|
||||
}
|
||||
},
|
||||
"oauth": {
|
||||
"version": "0.9.15",
|
||||
"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz",
|
||||
"integrity": "sha1-vR/vr2hslrdUda7VGWQS/2DPucE="
|
||||
},
|
||||
"passport-oauth1": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-oauth1/-/passport-oauth1-1.1.0.tgz",
|
||||
"integrity": "sha1-p96YiiEfnPRoc3cTDqdN8ycwyRg=",
|
||||
"requires": {
|
||||
"oauth": "0.9.x",
|
||||
"passport-strategy": "1.x.x",
|
||||
"utils-merge": "1.x.x"
|
||||
}
|
||||
},
|
||||
"passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
|
||||
},
|
||||
"passport-twitter": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/passport-twitter/-/passport-twitter-1.0.4.tgz",
|
||||
"integrity": "sha1-AaeZ4fdgvy3knyul+6MigvGJMtc=",
|
||||
"requires": {
|
||||
"passport-oauth1": "1.x.x",
|
||||
"xtraverse": "0.1.x"
|
||||
}
|
||||
},
|
||||
"utils-merge": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
|
||||
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
|
||||
},
|
||||
"xmldom": {
|
||||
"version": "0.1.31",
|
||||
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.31.tgz",
|
||||
"integrity": "sha512-yS2uJflVQs6n+CyjHoaBmVSqIDevTAWrzMmjG1Gc7h1qQ7uVozNhEPJAwZXWyGQ/Gafo3fCwrcaokezLPupVyQ=="
|
||||
},
|
||||
"xtraverse": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/xtraverse/-/xtraverse-0.1.0.tgz",
|
||||
"integrity": "sha1-t0G60BjveNip0ug63gB7P3lZxzI=",
|
||||
"requires": {
|
||||
"xmldom": "0.1.x"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
proto/cosmos/authz/v1beta1/authz.proto
Normal file
37
proto/cosmos/authz/v1beta1/authz.proto
Normal file
@ -0,0 +1,37 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.authz.v1beta1;
|
||||
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types";
|
||||
|
||||
// SendAuthorization allows the grantee to spend up to spend_limit coins from
|
||||
// the granter's account.
|
||||
message SendAuthorization {
|
||||
option (cosmos_proto.implements_interface) = "Authorization";
|
||||
|
||||
repeated cosmos.base.v1beta1.Coin spend_limit = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
}
|
||||
|
||||
// GenericAuthorization gives the grantee unrestricted permissions to execute
|
||||
// the provided method on behalf of the granter's account.
|
||||
message GenericAuthorization {
|
||||
option (cosmos_proto.implements_interface) = "Authorization";
|
||||
|
||||
// method name to grant unrestricted permissions to execute
|
||||
// Note: MethodName() is already a method on `GenericAuthorization` type,
|
||||
// we need some custom naming here so using `MessageName`
|
||||
string method_name = 1 [(gogoproto.customname) = "MessageName"];
|
||||
}
|
||||
|
||||
// AuthorizationGrant gives permissions to execute
|
||||
// the provide method with expiration time.
|
||||
message AuthorizationGrant {
|
||||
google.protobuf.Any authorization = 1 [(cosmos_proto.accepts_interface) = "Authorization"];
|
||||
google.protobuf.Timestamp expiration = 2 [(gogoproto.stdtime) = true, (gogoproto.nullable) = false];
|
||||
}
|
||||
24
proto/cosmos/authz/v1beta1/genesis.proto
Normal file
24
proto/cosmos/authz/v1beta1/genesis.proto
Normal file
@ -0,0 +1,24 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.authz.v1beta1;
|
||||
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "cosmos/authz/v1beta1/tx.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types";
|
||||
|
||||
// GenesisState defines the authz module's genesis state.
|
||||
message GenesisState {
|
||||
repeated GrantAuthorization authorization = 1 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// GrantAuthorization defines the GenesisState/GrantAuthorization type.
|
||||
message GrantAuthorization {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
|
||||
google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"];
|
||||
google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
}
|
||||
54
proto/cosmos/authz/v1beta1/query.proto
Normal file
54
proto/cosmos/authz/v1beta1/query.proto
Normal file
@ -0,0 +1,54 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.authz.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "google/api/annotations.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "cosmos/base/query/v1beta1/pagination.proto";
|
||||
import "cosmos/authz/v1beta1/authz.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types";
|
||||
|
||||
// Query defines the gRPC querier service.
|
||||
service Query {
|
||||
// Returns any `Authorization` (or `nil`), with the expiration time, granted to the grantee by the granter for the
|
||||
// provided msg type.
|
||||
rpc Authorization(QueryAuthorizationRequest) returns (QueryAuthorizationResponse) {
|
||||
option (google.api.http).get = "/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grant";
|
||||
}
|
||||
|
||||
// Returns list of `Authorization`, granted to the grantee by the granter.
|
||||
rpc Authorizations(QueryAuthorizationsRequest) returns (QueryAuthorizationsResponse) {
|
||||
option (google.api.http).get = "/cosmos/authz/v1beta1/granters/{granter}/grantees/{grantee}/grants";
|
||||
}
|
||||
}
|
||||
|
||||
// QueryAuthorizationRequest is the request type for the Query/Authorization RPC method.
|
||||
message QueryAuthorizationRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
string method_name = 3;
|
||||
}
|
||||
|
||||
// QueryAuthorizationResponse is the response type for the Query/Authorization RPC method.
|
||||
message QueryAuthorizationResponse {
|
||||
// authorization is a authorization granted for grantee by granter.
|
||||
cosmos.authz.v1beta1.AuthorizationGrant authorization = 1;
|
||||
}
|
||||
|
||||
// QueryAuthorizationsRequest is the request type for the Query/Authorizations RPC method.
|
||||
message QueryAuthorizationsRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
// pagination defines an pagination for the request.
|
||||
cosmos.base.query.v1beta1.PageRequest pagination = 3;
|
||||
}
|
||||
|
||||
// QueryAuthorizationsResponse is the response type for the Query/Authorizations RPC method.
|
||||
message QueryAuthorizationsResponse {
|
||||
// authorizations is a list of grants granted for grantee by granter.
|
||||
repeated cosmos.authz.v1beta1.AuthorizationGrant authorizations = 1;
|
||||
// pagination defines an pagination for the response.
|
||||
cosmos.base.query.v1beta1.PageResponse pagination = 2;
|
||||
}
|
||||
62
proto/cosmos/authz/v1beta1/tx.proto
Normal file
62
proto/cosmos/authz/v1beta1/tx.proto
Normal file
@ -0,0 +1,62 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.authz.v1beta1;
|
||||
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos/base/abci/v1beta1/abci.proto";
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/authz/types";
|
||||
|
||||
// Msg defines the authz Msg service.
|
||||
service Msg {
|
||||
// GrantAuthorization grants the provided authorization to the grantee on the granter's
|
||||
// account with the provided expiration time.
|
||||
rpc GrantAuthorization(MsgGrantAuthorizationRequest) returns (MsgGrantAuthorizationResponse);
|
||||
|
||||
// ExecAuthorized attempts to execute the provided messages using
|
||||
// authorizations granted to the grantee. Each message should have only
|
||||
// one signer corresponding to the granter of the authorization.
|
||||
rpc ExecAuthorized(MsgExecAuthorizedRequest) returns (MsgExecAuthorizedResponse);
|
||||
|
||||
// RevokeAuthorization revokes any authorization corresponding to the provided method name on the
|
||||
// granter's account that has been granted to the grantee.
|
||||
rpc RevokeAuthorization(MsgRevokeAuthorizationRequest) returns (MsgRevokeAuthorizationResponse);
|
||||
}
|
||||
|
||||
// MsgGrantAuthorizationRequest grants the provided authorization to the grantee on the granter's
|
||||
// account with the provided expiration time.
|
||||
message MsgGrantAuthorizationRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
|
||||
google.protobuf.Any authorization = 3 [(cosmos_proto.accepts_interface) = "Authorization"];
|
||||
google.protobuf.Timestamp expiration = 4 [(gogoproto.nullable) = false, (gogoproto.stdtime) = true];
|
||||
}
|
||||
|
||||
// MsgExecAuthorizedResponse defines the Msg/MsgExecAuthorizedResponse response type.
|
||||
message MsgExecAuthorizedResponse {
|
||||
cosmos.base.abci.v1beta1.Result result = 1;
|
||||
}
|
||||
|
||||
// MsgExecAuthorizedRequest attempts to execute the provided messages using
|
||||
// authorizations granted to the grantee. Each message should have only
|
||||
// one signer corresponding to the granter of the authorization.
|
||||
message MsgExecAuthorizedRequest {
|
||||
string grantee = 1;
|
||||
repeated google.protobuf.Any msgs = 2;
|
||||
}
|
||||
|
||||
// MsgGrantAuthorizationResponse defines the Msg/MsgGrantAuthorization response type.
|
||||
message MsgGrantAuthorizationResponse {}
|
||||
|
||||
// MsgRevokeAuthorizationRequest revokes any authorization with the provided sdk.Msg type on the
|
||||
// granter's account with that has been granted to the grantee.
|
||||
message MsgRevokeAuthorizationRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
string method_name = 3;
|
||||
}
|
||||
|
||||
// MsgRevokeAuthorizationResponse defines the Msg/MsgRevokeAuthorizationResponse response type.
|
||||
message MsgRevokeAuthorizationResponse {}
|
||||
81
proto/cosmos/feegrant/v1beta1/feegrant.proto
Normal file
81
proto/cosmos/feegrant/v1beta1/feegrant.proto
Normal file
@ -0,0 +1,81 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
import "cosmos/base/v1beta1/coin.proto";
|
||||
import "google/protobuf/timestamp.proto";
|
||||
import "google/protobuf/duration.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// BasicFeeAllowance implements FeeAllowance with a one-time grant of tokens
|
||||
// that optionally expires. The delegatee can use up to SpendLimit to cover fees.
|
||||
message BasicFeeAllowance {
|
||||
option (cosmos_proto.implements_interface) = "FeeAllowanceI";
|
||||
|
||||
// spend_limit specifies the maximum amount of tokens that can be spent
|
||||
// by this allowance and will be updated as tokens are spent. If it is
|
||||
// empty, there is no spend limit and any amount of coins can be spent.
|
||||
repeated cosmos.base.v1beta1.Coin spend_limit = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// expiration specifies an optional time when this allowance expires
|
||||
ExpiresAt expiration = 2 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// PeriodicFeeAllowance extends FeeAllowance to allow for both a maximum cap,
|
||||
// as well as a limit per time period.
|
||||
message PeriodicFeeAllowance {
|
||||
option (cosmos_proto.implements_interface) = "FeeAllowanceI";
|
||||
|
||||
// basic specifies a struct of `BasicFeeAllowance`
|
||||
BasicFeeAllowance basic = 1 [(gogoproto.nullable) = false];
|
||||
|
||||
// period specifies the time duration in which period_spend_limit coins can
|
||||
// be spent before that allowance is reset
|
||||
Duration period = 2 [(gogoproto.nullable) = false];
|
||||
|
||||
// period_spend_limit specifies the maximum number of coins that can be spent
|
||||
// in the period
|
||||
repeated cosmos.base.v1beta1.Coin period_spend_limit = 3
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// period_can_spend is the number of coins left to be spent before the period_reset time
|
||||
repeated cosmos.base.v1beta1.Coin period_can_spend = 4
|
||||
[(gogoproto.nullable) = false, (gogoproto.castrepeated) = "github.com/cosmos/cosmos-sdk/types.Coins"];
|
||||
|
||||
// period_reset is the time at which this period resets and a new one begins,
|
||||
// it is calculated from the start time of the first transaction after the
|
||||
// last period ended
|
||||
ExpiresAt period_reset = 5 [(gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
// Duration is a span of a clock time or number of blocks.
|
||||
// This is designed to be added to an ExpiresAt struct.
|
||||
message Duration {
|
||||
// sum is the oneof that represents either duration or block
|
||||
oneof sum {
|
||||
google.protobuf.Duration duration = 1 [(gogoproto.stdduration) = true];
|
||||
uint64 blocks = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// ExpiresAt is a point in time where something expires.
|
||||
// It may be *either* block time or block height
|
||||
message ExpiresAt {
|
||||
// sum is the oneof that represents either time or height
|
||||
oneof sum {
|
||||
google.protobuf.Timestamp time = 1 [(gogoproto.stdtime) = true];
|
||||
int64 height = 2;
|
||||
}
|
||||
}
|
||||
|
||||
// FeeAllowanceGrant is stored in the KVStore to record a grant with full context
|
||||
message FeeAllowanceGrant {
|
||||
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];
|
||||
}
|
||||
12
proto/cosmos/feegrant/v1beta1/genesis.proto
Normal file
12
proto/cosmos/feegrant/v1beta1/genesis.proto
Normal file
@ -0,0 +1,12 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/feegrant/v1beta1/feegrant.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// GenesisState contains a set of fee allowances, persisted from the store
|
||||
message GenesisState {
|
||||
repeated FeeAllowanceGrant fee_allowances = 1 [(gogoproto.nullable) = false];
|
||||
}
|
||||
52
proto/cosmos/feegrant/v1beta1/query.proto
Normal file
52
proto/cosmos/feegrant/v1beta1/query.proto
Normal file
@ -0,0 +1,52 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "cosmos/feegrant/v1beta1/feegrant.proto";
|
||||
import "cosmos/base/query/v1beta1/pagination.proto";
|
||||
import "google/api/annotations.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// Query defines the gRPC querier service.
|
||||
service Query {
|
||||
|
||||
// FeeAllowance returns fee granted to the grantee by the granter.
|
||||
rpc FeeAllowance(QueryFeeAllowanceRequest) returns (QueryFeeAllowanceResponse) {
|
||||
option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowance/{granter}/{grantee}";
|
||||
}
|
||||
|
||||
// FeeAllowances returns all the grants for address.
|
||||
rpc FeeAllowances(QueryFeeAllowancesRequest) returns (QueryFeeAllowancesResponse) {
|
||||
option (google.api.http).get = "/cosmos/feegrant/v1beta1/fee_allowances/{grantee}";
|
||||
}
|
||||
}
|
||||
|
||||
// QueryFeeAllowanceRequest is the request type for the Query/FeeAllowance RPC method.
|
||||
message QueryFeeAllowanceRequest {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
}
|
||||
|
||||
// QueryFeeAllowanceResponse is the response type for the Query/FeeAllowance RPC method.
|
||||
message QueryFeeAllowanceResponse {
|
||||
// fee_allowance is a fee_allowance granted for grantee by granter.
|
||||
cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowance = 1;
|
||||
}
|
||||
|
||||
// QueryFeeAllowancesRequest is the request type for the Query/FeeAllowances RPC method.
|
||||
message QueryFeeAllowancesRequest {
|
||||
string grantee = 1;
|
||||
|
||||
// pagination defines an pagination for the request.
|
||||
cosmos.base.query.v1beta1.PageRequest pagination = 2;
|
||||
}
|
||||
|
||||
// QueryFeeAllowancesResponse is the response type for the Query/FeeAllowances RPC method.
|
||||
message QueryFeeAllowancesResponse {
|
||||
// fee_allowances are fee_allowance's granted for grantee by granter.
|
||||
repeated cosmos.feegrant.v1beta1.FeeAllowanceGrant fee_allowances = 1;
|
||||
|
||||
// pagination defines an pagination for the response.
|
||||
cosmos.base.query.v1beta1.PageResponse pagination = 2;
|
||||
}
|
||||
40
proto/cosmos/feegrant/v1beta1/tx.proto
Normal file
40
proto/cosmos/feegrant/v1beta1/tx.proto
Normal file
@ -0,0 +1,40 @@
|
||||
syntax = "proto3";
|
||||
package cosmos.feegrant.v1beta1;
|
||||
|
||||
import "gogoproto/gogo.proto";
|
||||
import "google/protobuf/any.proto";
|
||||
import "cosmos_proto/cosmos.proto";
|
||||
|
||||
option go_package = "github.com/cosmos/cosmos-sdk/x/feegrant/types";
|
||||
|
||||
// Msg defines the feegrant msg service.
|
||||
service Msg {
|
||||
|
||||
// GrantFeeAllowance grants fee allowance to the grantee on the granter's
|
||||
// account with the provided expiration time.
|
||||
rpc GrantFeeAllowance(MsgGrantFeeAllowance) returns (MsgGrantFeeAllowanceResponse);
|
||||
|
||||
// RevokeFeeAllowance revokes any fee allowance of granter's account that
|
||||
// has been granted to the grantee.
|
||||
rpc RevokeFeeAllowance(MsgRevokeFeeAllowance) returns (MsgRevokeFeeAllowanceResponse);
|
||||
}
|
||||
|
||||
// MsgGrantFeeAllowance adds permission for Grantee to spend up to Allowance
|
||||
// of fees from the account of Granter.
|
||||
message MsgGrantFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
google.protobuf.Any allowance = 3 [(cosmos_proto.accepts_interface) = "FeeAllowanceI"];
|
||||
}
|
||||
|
||||
// MsgGrantFeeAllowanceResponse defines the Msg/GrantFeeAllowanceResponse response type.
|
||||
message MsgGrantFeeAllowanceResponse {}
|
||||
|
||||
// MsgRevokeFeeAllowance removes any existing FeeAllowance from Granter to Grantee.
|
||||
message MsgRevokeFeeAllowance {
|
||||
string granter = 1;
|
||||
string grantee = 2;
|
||||
}
|
||||
|
||||
// MsgRevokeFeeAllowanceResponse defines the Msg/RevokeFeeAllowanceResponse response type.
|
||||
message MsgRevokeFeeAllowanceResponse {}
|
||||
@ -27,12 +27,15 @@ message CommissionRates {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// rate is the commission rate charged to delegators, as a fraction.
|
||||
string rate = 1 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
// max_rate defines the maximum commission rate which validator can ever charge, as a fraction.
|
||||
string max_rate = 2 [
|
||||
(gogoproto.moretags) = "yaml:\"max_rate\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
// max_change_rate defines the maximum daily increase of the validator commission, as a fraction.
|
||||
string max_change_rate = 3 [
|
||||
(gogoproto.moretags) = "yaml:\"max_change_rate\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
@ -45,7 +48,9 @@ message Commission {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// commission_rates defines the initial commission rates to be used for creating a validator.
|
||||
CommissionRates commission_rates = 1 [(gogoproto.embed) = true, (gogoproto.nullable) = false];
|
||||
// update_time is the last time the commission rate was changed.
|
||||
google.protobuf.Timestamp update_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"update_time\""];
|
||||
}
|
||||
@ -55,10 +60,15 @@ message Description {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// moniker defines a human-readable name for the validator.
|
||||
string moniker = 1;
|
||||
// identity defines an optional identity signature (ex. UPort or Keybase).
|
||||
string identity = 2;
|
||||
// website defines an optional website link.
|
||||
string website = 3;
|
||||
// security_contact defines an optional email for security contact.
|
||||
string security_contact = 4 [(gogoproto.moretags) = "yaml:\"security_contact\""];
|
||||
// details define other optional details.
|
||||
string details = 5;
|
||||
}
|
||||
|
||||
@ -75,22 +85,33 @@ message Validator {
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
|
||||
// operator_address defines the address of the validator's operator; bech encoded in JSON.
|
||||
string operator_address = 1 [(gogoproto.moretags) = "yaml:\"operator_address\""];
|
||||
// consensus_pubkey is the consensus public key of the validator, as a Protobuf Any.
|
||||
google.protobuf.Any consensus_pubkey = 2
|
||||
[(cosmos_proto.accepts_interface) = "cosmos.crypto.PubKey", (gogoproto.moretags) = "yaml:\"consensus_pubkey\""];
|
||||
// jailed defined whether the validator has been jailed from bonded status or not.
|
||||
bool jailed = 3;
|
||||
// status is the validator status (bonded/unbonding/unbonded).
|
||||
BondStatus status = 4;
|
||||
// tokens define the delegated tokens (incl. self-delegation).
|
||||
string tokens = 5 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false];
|
||||
// delegator_shares defines total shares issued to a validator's delegators.
|
||||
string delegator_shares = 6 [
|
||||
(gogoproto.moretags) = "yaml:\"delegator_shares\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec",
|
||||
(gogoproto.nullable) = false
|
||||
];
|
||||
// description defines the description terms for the validator.
|
||||
Description description = 7 [(gogoproto.nullable) = false];
|
||||
// unbonding_height defines, if unbonding, the height at which this validator has begun unbonding.
|
||||
int64 unbonding_height = 8 [(gogoproto.moretags) = "yaml:\"unbonding_height\""];
|
||||
// unbonding_time defines, if unbonding, the min time for the validator to complete unbonding.
|
||||
google.protobuf.Timestamp unbonding_time = 9
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
|
||||
// commission defines the commission parameters.
|
||||
Commission commission = 10 [(gogoproto.nullable) = false];
|
||||
// min_self_delegation is the validator's self declared minimum self delegation.
|
||||
string min_self_delegation = 11 [
|
||||
(gogoproto.moretags) = "yaml:\"min_self_delegation\"",
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
@ -164,8 +185,11 @@ message Delegation {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_address is the bech32-encoded address of the validator.
|
||||
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
|
||||
// shares define the delegation shares received.
|
||||
string shares = 3 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
@ -176,8 +200,11 @@ message UnbondingDelegation {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_address is the bech32-encoded address of the validator.
|
||||
string validator_address = 2 [(gogoproto.moretags) = "yaml:\"validator_address\""];
|
||||
// entries are the unbonding delegation entries.
|
||||
repeated UnbondingDelegationEntry entries = 3 [(gogoproto.nullable) = false]; // unbonding delegation entries
|
||||
}
|
||||
|
||||
@ -186,14 +213,18 @@ message UnbondingDelegationEntry {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// creation_height is the height which the unbonding took place.
|
||||
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
|
||||
// completion_time is the unix time for unbonding completion.
|
||||
google.protobuf.Timestamp completion_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
|
||||
// initial_balance defines the tokens initially scheduled to receive at completion.
|
||||
string initial_balance = 3 [
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.moretags) = "yaml:\"initial_balance\""
|
||||
];
|
||||
// balance defines the tokens to receive at completion.
|
||||
string balance = 4 [(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int", (gogoproto.nullable) = false];
|
||||
}
|
||||
|
||||
@ -202,14 +233,18 @@ message RedelegationEntry {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// creation_height defines the height which the redelegation took place.
|
||||
int64 creation_height = 1 [(gogoproto.moretags) = "yaml:\"creation_height\""];
|
||||
// completion_time defines the unix time for redelegation completion.
|
||||
google.protobuf.Timestamp completion_time = 2
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"completion_time\""];
|
||||
// initial_balance defines the initial balance when redelegation started.
|
||||
string initial_balance = 3 [
|
||||
(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Int",
|
||||
(gogoproto.nullable) = false,
|
||||
(gogoproto.moretags) = "yaml:\"initial_balance\""
|
||||
];
|
||||
// shares_dst is the amount of destination-validator shares created by redelegation.
|
||||
string shares_dst = 4
|
||||
[(gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.nullable) = false];
|
||||
}
|
||||
@ -221,9 +256,13 @@ message Redelegation {
|
||||
option (gogoproto.goproto_getters) = false;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// delegator_address is the bech32-encoded address of the delegator.
|
||||
string delegator_address = 1 [(gogoproto.moretags) = "yaml:\"delegator_address\""];
|
||||
// validator_src_address is the validator redelegation source operator address.
|
||||
string validator_src_address = 2 [(gogoproto.moretags) = "yaml:\"validator_src_address\""];
|
||||
// validator_dst_address is the validator redelegation destination operator address.
|
||||
string validator_dst_address = 3 [(gogoproto.moretags) = "yaml:\"validator_dst_address\""];
|
||||
// entries are the redelegation entries.
|
||||
repeated RedelegationEntry entries = 4 [(gogoproto.nullable) = false]; // redelegation entries
|
||||
}
|
||||
|
||||
@ -232,11 +271,16 @@ message Params {
|
||||
option (gogoproto.equal) = true;
|
||||
option (gogoproto.goproto_stringer) = false;
|
||||
|
||||
// unbonding_time is the time duration of unbonding.
|
||||
google.protobuf.Duration unbonding_time = 1
|
||||
[(gogoproto.nullable) = false, (gogoproto.stdduration) = true, (gogoproto.moretags) = "yaml:\"unbonding_time\""];
|
||||
// max_validators is the maximum number of validators.
|
||||
uint32 max_validators = 2 [(gogoproto.moretags) = "yaml:\"max_validators\""];
|
||||
// max_entries is the max entries for either unbonding delegation or redelegation (per pair/trio).
|
||||
uint32 max_entries = 3 [(gogoproto.moretags) = "yaml:\"max_entries\""];
|
||||
// historical_entries is the number of historical entries to persist.
|
||||
uint32 historical_entries = 4 [(gogoproto.moretags) = "yaml:\"historical_entries\""];
|
||||
// bond_denom defines the bondable coin denomination.
|
||||
string bond_denom = 5 [(gogoproto.moretags) = "yaml:\"bond_denom\""];
|
||||
}
|
||||
|
||||
|
||||
@ -101,6 +101,29 @@ type APIConfig struct {
|
||||
// Ref: https://github.com/cosmos/cosmos-sdk/issues/6420
|
||||
}
|
||||
|
||||
// RosettaConfig defines the Rosetta API listener configuration.
|
||||
type RosettaConfig struct {
|
||||
// Address defines the API server to listen on
|
||||
Address string `mapstructure:"address"`
|
||||
|
||||
// Blockchain defines the blockchain name
|
||||
// defaults to DefaultBlockchain
|
||||
Blockchain string `mapstructure:"blockchain"`
|
||||
|
||||
// Network defines the network name
|
||||
Network string `mapstructure:"network"`
|
||||
|
||||
// Retries defines the maximum number of retries
|
||||
// rosetta will do before quitting
|
||||
Retries int `mapstructure:"retries"`
|
||||
|
||||
// Enable defines if the API server should be enabled.
|
||||
Enable bool `mapstructure:"enable"`
|
||||
|
||||
// Offline defines if the server must be run in offline mode
|
||||
Offline bool `mapstructure:"offline"`
|
||||
}
|
||||
|
||||
// GRPCConfig defines configuration for the gRPC server.
|
||||
type GRPCConfig struct {
|
||||
// Enable defines if the gRPC server should be enabled.
|
||||
@ -138,6 +161,7 @@ type Config struct {
|
||||
Telemetry telemetry.Config `mapstructure:"telemetry"`
|
||||
API APIConfig `mapstructure:"api"`
|
||||
GRPC GRPCConfig `mapstructure:"grpc"`
|
||||
Rosetta RosettaConfig `mapstructure:"rosetta"`
|
||||
GRPCWeb GRPCWebConfig `mapstructure:"grpc-web"`
|
||||
StateSync StateSyncConfig `mapstructure:"state-sync"`
|
||||
}
|
||||
@ -198,6 +222,14 @@ func DefaultConfig() *Config {
|
||||
Enable: true,
|
||||
Address: DefaultGRPCAddress,
|
||||
},
|
||||
Rosetta: RosettaConfig{
|
||||
Enable: false,
|
||||
Address: ":8080",
|
||||
Blockchain: "app",
|
||||
Network: "network",
|
||||
Retries: 3,
|
||||
Offline: false,
|
||||
},
|
||||
GRPCWeb: GRPCWebConfig{
|
||||
Enable: true,
|
||||
Address: DefaultGRPCWebAddress,
|
||||
@ -252,6 +284,14 @@ func GetConfig(v *viper.Viper) Config {
|
||||
RPCMaxBodyBytes: v.GetUint("api.rpc-max-body-bytes"),
|
||||
EnableUnsafeCORS: v.GetBool("api.enabled-unsafe-cors"),
|
||||
},
|
||||
Rosetta: RosettaConfig{
|
||||
Enable: v.GetBool("rosetta.enable"),
|
||||
Address: v.GetString("rosetta.address"),
|
||||
Blockchain: v.GetString("rosetta.blockchain"),
|
||||
Network: v.GetString("rosetta.network"),
|
||||
Retries: v.GetInt("rosetta.retries"),
|
||||
Offline: v.GetBool("rosetta.offline"),
|
||||
},
|
||||
GRPC: GRPCConfig{
|
||||
Enable: v.GetBool("grpc.enable"),
|
||||
Address: v.GetString("grpc.address"),
|
||||
|
||||
@ -135,6 +135,30 @@ rpc-max-body-bytes = {{ .API.RPCMaxBodyBytes }}
|
||||
# EnableUnsafeCORS defines if CORS should be enabled (unsafe - use it at your own risk).
|
||||
enabled-unsafe-cors = {{ .API.EnableUnsafeCORS }}
|
||||
|
||||
###############################################################################
|
||||
### Rosetta Configuration ###
|
||||
###############################################################################
|
||||
|
||||
[rosetta]
|
||||
|
||||
# Enable defines if the Rosetta API server should be enabled.
|
||||
enable = {{ .Rosetta.Enable }}
|
||||
|
||||
# Address defines the Rosetta API server to listen on.
|
||||
address = "{{ .Rosetta.Address }}"
|
||||
|
||||
# Network defines the name of the blockchain that will be returned by Rosetta.
|
||||
blockchain = "{{ .Rosetta.Blockchain }}"
|
||||
|
||||
# Network defines the name of the network that will be returned by Rosetta.
|
||||
network = "{{ .Rosetta.Network }}"
|
||||
|
||||
# Retries defines the number of retries when connecting to the node before failing.
|
||||
retries = {{ .Rosetta.Retries }}
|
||||
|
||||
# Offline defines if Rosetta server should run in offline mode.
|
||||
offline = {{ .Rosetta.Offline }}
|
||||
|
||||
###############################################################################
|
||||
### gRPC Configuration ###
|
||||
###############################################################################
|
||||
|
||||
@ -3,9 +3,10 @@ package grpc
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
"github.com/improbable-eng/grpc-web/go/grpcweb"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/config"
|
||||
)
|
||||
|
||||
// StartGRPCWeb starts a gRPC-Web server on the given address.
|
||||
|
||||
42
server/rosetta.go
Normal file
42
server/rosetta.go
Normal file
@ -0,0 +1,42 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
// RosettaCommand builds the rosetta root command given
|
||||
// a protocol buffers serializer/deserializer
|
||||
func RosettaCommand(ir codectypes.InterfaceRegistry, cdc codec.Marshaler) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "rosetta",
|
||||
Short: "spin up a rosetta server",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
conf, err := rosetta.FromFlags(cmd.Flags())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
protoCodec, ok := cdc.(*codec.ProtoCodec)
|
||||
if !ok {
|
||||
return fmt.Errorf("exoected *codec.ProtoMarshaler, got: %T", cdc)
|
||||
}
|
||||
conf.WithCodec(ir, protoCodec)
|
||||
|
||||
rosettaSrv, err := rosetta.ServerFromConfig(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return rosettaSrv.Start()
|
||||
},
|
||||
}
|
||||
rosetta.SetFlags(cmd.Flags())
|
||||
|
||||
return cmd
|
||||
}
|
||||
221
server/rosetta/client_offline.go
Normal file
221
server/rosetta/client_offline.go
Normal file
@ -0,0 +1,221 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"strings"
|
||||
|
||||
"github.com/btcsuite/btcd/btcec"
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
"github.com/tendermint/tendermint/crypto"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client/tx"
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
"github.com/cosmos/cosmos-sdk/types/tx/signing"
|
||||
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
|
||||
)
|
||||
|
||||
func (c *Client) OperationStatuses() []*types.OperationStatus {
|
||||
return []*types.OperationStatus{
|
||||
{
|
||||
Status: StatusSuccess,
|
||||
Successful: true,
|
||||
},
|
||||
{
|
||||
Status: StatusReverted,
|
||||
Successful: false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Version() string {
|
||||
return c.version
|
||||
}
|
||||
|
||||
func (c *Client) SupportedOperations() []string {
|
||||
var supportedOperations []string
|
||||
for _, ii := range c.ir.ListImplementations("cosmos.base.v1beta1.Msg") {
|
||||
resolve, err := c.ir.Resolve(ii)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if _, ok := resolve.(Msg); ok {
|
||||
supportedOperations = append(supportedOperations, strings.TrimLeft(ii, "/"))
|
||||
}
|
||||
}
|
||||
|
||||
supportedOperations = append(supportedOperations, OperationFee)
|
||||
|
||||
return supportedOperations
|
||||
}
|
||||
|
||||
func (c *Client) SignedTx(ctx context.Context, txBytes []byte, signatures []*types.Signature) (signedTxBytes []byte, err error) {
|
||||
TxConfig := c.getTxConfig()
|
||||
rawTx, err := TxConfig.TxDecoder()(txBytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBldr, err := TxConfig.WrapTxBuilder(rawTx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var sigs = make([]signing.SignatureV2, len(signatures))
|
||||
for i, signature := range signatures {
|
||||
if signature.PublicKey.CurveType != types.Secp256k1 {
|
||||
return nil, crgerrs.ErrUnsupportedCurve
|
||||
}
|
||||
|
||||
cmp, err := btcec.ParsePubKey(signature.PublicKey.Bytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
compressedPublicKey := make([]byte, secp256k1.PubKeySize)
|
||||
copy(compressedPublicKey, cmp.SerializeCompressed())
|
||||
pubKey := &secp256k1.PubKey{Key: compressedPublicKey}
|
||||
|
||||
accountInfo, err := c.accountInfo(ctx, sdk.AccAddress(pubKey.Address()).String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
sig := signing.SignatureV2{
|
||||
PubKey: pubKey,
|
||||
Data: &signing.SingleSignatureData{
|
||||
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
|
||||
Signature: signature.Bytes,
|
||||
},
|
||||
Sequence: accountInfo.GetSequence(),
|
||||
}
|
||||
sigs[i] = sig
|
||||
}
|
||||
|
||||
if err = txBldr.SetSignatures(sigs...); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err = c.getTxConfig().TxEncoder()(txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return txBytes, nil
|
||||
}
|
||||
|
||||
func (c *Client) ConstructionPayload(_ context.Context, request *types.ConstructionPayloadsRequest) (resp *types.ConstructionPayloadsResponse, err error) {
|
||||
// check if there is at least one operation
|
||||
if len(request.Operations) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "expected at least one operation")
|
||||
}
|
||||
|
||||
// convert rosetta operations to sdk msgs and fees (if present)
|
||||
msgs, fee, err := opsToMsgsAndFees(c.ir, request.Operations)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error())
|
||||
}
|
||||
|
||||
metadata, err := getMetadataFromPayloadReq(request)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
|
||||
}
|
||||
|
||||
txFactory := tx.Factory{}.WithAccountNumber(metadata.AccountNumber).WithChainID(metadata.ChainID).
|
||||
WithGas(metadata.Gas).WithSequence(metadata.Sequence).WithMemo(metadata.Memo).WithFees(fee.String())
|
||||
|
||||
TxConfig := c.getTxConfig()
|
||||
txFactory = txFactory.WithTxConfig(TxConfig)
|
||||
|
||||
txBldr, err := tx.BuildUnsignedTx(txFactory, msgs...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Sign_mode_legacy_amino is being used as default here, as sign_mode_direct
|
||||
// needs the signer infos to be set before hand but rosetta doesn't have a way
|
||||
// to do this yet. To be revisited in future versions of sdk and rosetta
|
||||
if txFactory.SignMode() == signing.SignMode_SIGN_MODE_UNSPECIFIED {
|
||||
txFactory = txFactory.WithSignMode(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON)
|
||||
}
|
||||
|
||||
signerData := authsigning.SignerData{
|
||||
ChainID: txFactory.ChainID(),
|
||||
AccountNumber: txFactory.AccountNumber(),
|
||||
Sequence: txFactory.Sequence(),
|
||||
}
|
||||
|
||||
signBytes, err := TxConfig.SignModeHandler().GetSignBytes(txFactory.SignMode(), signerData, txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
txBytes, err := TxConfig.TxEncoder()(txBldr.GetTx())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
accIdentifiers := getAccountIdentifiersByMsgs(msgs)
|
||||
|
||||
payloads := make([]*types.SigningPayload, len(accIdentifiers))
|
||||
for i, accID := range accIdentifiers {
|
||||
payloads[i] = &types.SigningPayload{
|
||||
AccountIdentifier: accID,
|
||||
Bytes: crypto.Sha256(signBytes),
|
||||
SignatureType: types.Ecdsa,
|
||||
}
|
||||
}
|
||||
|
||||
return &types.ConstructionPayloadsResponse{
|
||||
UnsignedTransaction: hex.EncodeToString(txBytes),
|
||||
Payloads: payloads,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getAccountIdentifiersByMsgs(msgs []sdk.Msg) []*types.AccountIdentifier {
|
||||
var accIdentifiers []*types.AccountIdentifier
|
||||
for _, msg := range msgs {
|
||||
for _, signer := range msg.GetSigners() {
|
||||
accIdentifiers = append(accIdentifiers, &types.AccountIdentifier{Address: signer.String()})
|
||||
}
|
||||
}
|
||||
|
||||
return accIdentifiers
|
||||
}
|
||||
|
||||
func (c *Client) PreprocessOperationsToOptions(_ context.Context, req *types.ConstructionPreprocessRequest) (options map[string]interface{}, err error) {
|
||||
operations := req.Operations
|
||||
if len(operations) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "invalid number of operations")
|
||||
}
|
||||
|
||||
msgs, err := opsToMsgs(c.ir, operations)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error())
|
||||
}
|
||||
|
||||
if len(msgs) < 1 || len(msgs[0].GetSigners()) < 1 {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, "operation produced no msg or signers")
|
||||
}
|
||||
|
||||
memo, ok := req.Metadata["memo"]
|
||||
if !ok {
|
||||
memo = ""
|
||||
}
|
||||
|
||||
defaultGas := float64(200000)
|
||||
|
||||
gas := req.SuggestedFeeMultiplier
|
||||
if gas == nil {
|
||||
gas = &defaultGas
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
OptionAddress: msgs[0].GetSigners()[0],
|
||||
OptionMemo: memo,
|
||||
OptionGas: gas,
|
||||
}, nil
|
||||
}
|
||||
447
server/rosetta/client_online.go
Normal file
447
server/rosetta/client_online.go
Normal file
@ -0,0 +1,447 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/version"
|
||||
|
||||
abcitypes "github.com/tendermint/tendermint/abci/types"
|
||||
|
||||
"github.com/tendermint/btcd/btcec"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"google.golang.org/grpc/metadata"
|
||||
|
||||
"github.com/tendermint/tendermint/rpc/client/http"
|
||||
tmtypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
|
||||
crgtypes "github.com/tendermint/cosmos-rosetta-gateway/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
|
||||
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
|
||||
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
|
||||
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
// interface assertion
|
||||
var _ crgtypes.Client = (*Client)(nil)
|
||||
|
||||
const tmWebsocketPath = "/websocket"
|
||||
const defaultNodeTimeout = 15 * time.Second
|
||||
|
||||
// Client implements a single network client to interact with cosmos based chains
|
||||
type Client struct {
|
||||
config *Config
|
||||
|
||||
auth auth.QueryClient
|
||||
bank bank.QueryClient
|
||||
|
||||
ir codectypes.InterfaceRegistry
|
||||
|
||||
clientCtx client.Context
|
||||
|
||||
version string
|
||||
}
|
||||
|
||||
func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) {
|
||||
if pubKey.CurveType != "secp256k1" {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported")
|
||||
}
|
||||
|
||||
cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256())
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
|
||||
}
|
||||
|
||||
compressedPublicKey := make([]byte, secp256k1.PubKeySize)
|
||||
copy(compressedPublicKey, cmp.SerializeCompressed())
|
||||
|
||||
pk := secp256k1.PubKey{Key: compressedPublicKey}
|
||||
|
||||
return &types.AccountIdentifier{
|
||||
Address: sdk.AccAddress(pk.Address()).String(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewClient instantiates a new online servicer
|
||||
func NewClient(cfg *Config) (*Client, error) {
|
||||
info := version.NewInfo()
|
||||
|
||||
v := info.Version
|
||||
if v == "" {
|
||||
v = "unknown"
|
||||
}
|
||||
|
||||
return &Client{
|
||||
config: cfg,
|
||||
ir: cfg.InterfaceRegistry,
|
||||
version: fmt.Sprintf("%s/%s", info.AppName, v),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) accountInfo(ctx context.Context, addr string, height *int64) (auth.AccountI, error) {
|
||||
if height != nil {
|
||||
strHeight := strconv.FormatInt(*height, 10)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight)
|
||||
}
|
||||
|
||||
accountInfo, err := c.auth.Account(ctx, &auth.QueryAccountRequest{
|
||||
Address: addr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
|
||||
var account auth.AccountI
|
||||
err = c.ir.UnpackAny(accountInfo.Account, &account)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
|
||||
}
|
||||
|
||||
return account, nil
|
||||
}
|
||||
|
||||
func (c *Client) Balances(ctx context.Context, addr string, height *int64) ([]*types.Amount, error) {
|
||||
if height != nil {
|
||||
strHeight := strconv.FormatInt(*height, 10)
|
||||
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight)
|
||||
}
|
||||
|
||||
balance, err := c.bank.AllBalances(ctx, &bank.QueryAllBalancesRequest{
|
||||
Address: addr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
|
||||
availableCoins, err := c.coins(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sdkCoinsToRosettaAmounts(balance.Balances, availableCoins), nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockByHash(ctx context.Context, hash string) (crgtypes.BlockResponse, error) {
|
||||
bHash, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, fmt.Errorf("invalid block hash: %s", err)
|
||||
}
|
||||
|
||||
block, err := c.clientCtx.Client.BlockByHash(ctx, bHash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, err
|
||||
}
|
||||
|
||||
return buildBlockResponse(block), nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.BlockResponse, error) {
|
||||
block, err := c.clientCtx.Client.Block(ctx, height)
|
||||
if err != nil {
|
||||
return crgtypes.BlockResponse{}, err
|
||||
}
|
||||
|
||||
return buildBlockResponse(block), nil
|
||||
}
|
||||
|
||||
func buildBlockResponse(block *tmtypes.ResultBlock) crgtypes.BlockResponse {
|
||||
return crgtypes.BlockResponse{
|
||||
Block: TMBlockToRosettaBlockIdentifier(block),
|
||||
ParentBlock: TMBlockToRosettaParentBlockIdentifier(block),
|
||||
MillisecondTimestamp: timeToMilliseconds(block.Block.Time),
|
||||
TxCount: int64(len(block.Block.Txs)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) BlockTransactionsByHash(ctx context.Context, hash string) (crgtypes.BlockTransactionsResponse, error) {
|
||||
blockResp, err := c.BlockByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
return crgtypes.BlockTransactionsResponse{
|
||||
BlockResponse: blockResp,
|
||||
Transactions: sdkTxsWithHashToRosettaTxs(txs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) BlockTransactionsByHeight(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) {
|
||||
blockResp, err := c.BlockByHeight(ctx, height)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
|
||||
if err != nil {
|
||||
return crgtypes.BlockTransactionsResponse{}, err
|
||||
}
|
||||
|
||||
return crgtypes.BlockTransactionsResponse{
|
||||
BlockResponse: blockResp,
|
||||
Transactions: sdkTxsWithHashToRosettaTxs(txs),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Coins fetches the existing coins in the application
|
||||
func (c *Client) coins(ctx context.Context) (sdk.Coins, error) {
|
||||
supply, err := c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
|
||||
if err != nil {
|
||||
return nil, crgerrs.FromGRPCToRosettaError(err)
|
||||
}
|
||||
return supply.Supply, nil
|
||||
}
|
||||
|
||||
// listTransactionsInBlock returns the list of the transactions in a block given its height
|
||||
func (c *Client) listTransactionsInBlock(ctx context.Context, height int64) ([]*sdkTxWithHash, error) {
|
||||
txQuery := fmt.Sprintf(`tx.height=%d`, height)
|
||||
txList, err := c.clientCtx.Client.TxSearch(ctx, txQuery, true, nil, nil, "")
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
|
||||
sdkTxs, err := tmResultTxsToSdkTxsWithHash(c.clientCtx.TxConfig.TxDecoder(), txList.Txs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return sdkTxs, nil
|
||||
}
|
||||
|
||||
func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) {
|
||||
txConfig := c.getTxConfig()
|
||||
rawTx, err := txConfig.TxDecoder()(txBytes)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
txBldr, err := txConfig.WrapTxBuilder(rawTx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
var accountIdentifierSigners []*types.AccountIdentifier
|
||||
if signed {
|
||||
addrs := txBldr.GetTx().GetSigners()
|
||||
for _, addr := range addrs {
|
||||
signer := &types.AccountIdentifier{
|
||||
Address: addr.String(),
|
||||
}
|
||||
accountIdentifierSigners = append(accountIdentifierSigners, signer)
|
||||
}
|
||||
}
|
||||
|
||||
return sdkTxToOperations(txBldr.GetTx(), false, false), accountIdentifierSigners, nil
|
||||
}
|
||||
|
||||
// GetTx returns a transaction given its hash
|
||||
func (c *Client) GetTx(_ context.Context, hash string) (*types.Transaction, error) {
|
||||
txResp, err := authclient.QueryTx(c.clientCtx, hash)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
var sdkTx sdk.Tx
|
||||
err = c.ir.UnpackAny(txResp.Tx, &sdkTx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
|
||||
}
|
||||
return sdkTxWithHashToOperations(&sdkTxWithHash{
|
||||
HexHash: txResp.TxHash,
|
||||
Code: txResp.Code,
|
||||
Log: txResp.RawLog,
|
||||
Tx: sdkTx,
|
||||
}), nil
|
||||
}
|
||||
|
||||
// GetUnconfirmedTx gets an unconfirmed transaction given its hash
|
||||
func (c *Client) GetUnconfirmedTx(ctx context.Context, hash string) (*types.Transaction, error) {
|
||||
res, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "unconfirmed tx not found")
|
||||
}
|
||||
|
||||
hashAsBytes, err := hex.DecodeString(hash)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInterpreting, "invalid hash")
|
||||
}
|
||||
|
||||
for _, tx := range res.Txs {
|
||||
if bytes.Equal(tx.Hash(), hashAsBytes) {
|
||||
sdkTx, err := tmTxToSdkTx(c.clientCtx.TxConfig.TxDecoder(), tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &types.Transaction{
|
||||
TransactionIdentifier: TmTxToRosettaTxsIdentifier(tx),
|
||||
Operations: sdkTxToOperations(sdkTx, false, false),
|
||||
Metadata: nil,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool")
|
||||
}
|
||||
|
||||
// Mempool returns the unconfirmed transactions in the mempool
|
||||
func (c *Client) Mempool(ctx context.Context) ([]*types.TransactionIdentifier, error) {
|
||||
txs, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return TMTxsToRosettaTxsIdentifiers(txs.Txs), nil
|
||||
}
|
||||
|
||||
// Peers gets the number of peers
|
||||
func (c *Client) Peers(ctx context.Context) ([]*types.Peer, error) {
|
||||
netInfo, err := c.clientCtx.Client.NetInfo(ctx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
return TmPeersToRosettaPeers(netInfo.Peers), nil
|
||||
}
|
||||
|
||||
func (c *Client) Status(ctx context.Context) (*types.SyncStatus, error) {
|
||||
status, err := c.clientCtx.Client.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
return TMStatusToRosettaSyncStatus(status), err
|
||||
}
|
||||
|
||||
func (c *Client) getTxConfig() client.TxConfig {
|
||||
return c.clientCtx.TxConfig
|
||||
}
|
||||
|
||||
func (c *Client) PostTx(txBytes []byte) (*types.TransactionIdentifier, map[string]interface{}, error) {
|
||||
// sync ensures it will go through checkTx
|
||||
res, err := c.clientCtx.BroadcastTxSync(txBytes)
|
||||
if err != nil {
|
||||
return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
|
||||
}
|
||||
// check if tx was broadcast successfully
|
||||
if res.Code != abcitypes.CodeTypeOK {
|
||||
return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, fmt.Sprintf("transaction broadcast failure: (%d) %s ", res.Code, res.RawLog))
|
||||
}
|
||||
|
||||
return &types.TransactionIdentifier{
|
||||
Hash: res.TxHash,
|
||||
},
|
||||
map[string]interface{}{
|
||||
Log: res.RawLog,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) ConstructionMetadataFromOptions(ctx context.Context, options map[string]interface{}) (meta map[string]interface{}, err error) {
|
||||
if len(options) == 0 {
|
||||
return nil, crgerrs.ErrBadArgument
|
||||
}
|
||||
|
||||
addr, ok := options[OptionAddress]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "no address provided")
|
||||
}
|
||||
|
||||
addrString, ok := addr.(string)
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "address is not a string")
|
||||
}
|
||||
|
||||
accountInfo, err := c.accountInfo(ctx, addrString, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gas, ok := options[OptionGas]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidAddress, "gas not set")
|
||||
}
|
||||
|
||||
memo, ok := options[OptionMemo]
|
||||
if !ok {
|
||||
return nil, crgerrs.WrapError(crgerrs.ErrInvalidMemo, "memo not set")
|
||||
}
|
||||
|
||||
status, err := c.clientCtx.Client.Status(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
OptionAccountNumber: accountInfo.GetAccountNumber(),
|
||||
OptionSequence: accountInfo.GetSequence(),
|
||||
OptionChainID: status.NodeInfo.Network,
|
||||
OptionGas: gas,
|
||||
OptionMemo: memo,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *Client) Ready() error {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), defaultNodeTimeout)
|
||||
defer cancel()
|
||||
_, err := c.clientCtx.Client.Health(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Bootstrap() error {
|
||||
grpcConn, err := grpc.Dial(c.config.GRPCEndpoint, grpc.WithInsecure())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tmRPC, err := http.New(c.config.TendermintRPC, tmWebsocketPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
authClient := auth.NewQueryClient(grpcConn)
|
||||
bankClient := bank.NewQueryClient(grpcConn)
|
||||
|
||||
// NodeURI and Client are set from here otherwise
|
||||
// WitNodeURI will require to create a new client
|
||||
// it's done here because WithNodeURI panics if
|
||||
// connection to tendermint node fails
|
||||
clientCtx := client.Context{
|
||||
Client: tmRPC,
|
||||
NodeURI: c.config.TendermintRPC,
|
||||
}
|
||||
clientCtx = clientCtx.
|
||||
WithJSONMarshaler(c.config.Codec).
|
||||
WithInterfaceRegistry(c.config.InterfaceRegistry).
|
||||
WithTxConfig(authtx.NewTxConfig(c.config.Codec, authtx.DefaultSignModes)).
|
||||
WithAccountRetriever(auth.AccountRetriever{}).
|
||||
WithBroadcastMode(flags.BroadcastBlock)
|
||||
|
||||
c.auth = authClient
|
||||
c.bank = bankClient
|
||||
c.clientCtx = clientCtx
|
||||
c.ir = c.config.InterfaceRegistry
|
||||
|
||||
return nil
|
||||
}
|
||||
22
server/rosetta/codec.go
Normal file
22
server/rosetta/codec.go
Normal file
@ -0,0 +1,22 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
|
||||
authcodec "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
bankcodec "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
)
|
||||
|
||||
// MakeCodec generates the codec required to interact
|
||||
// with the cosmos APIs used by the rosetta gateway
|
||||
func MakeCodec() (*codec.ProtoCodec, codectypes.InterfaceRegistry) {
|
||||
ir := codectypes.NewInterfaceRegistry()
|
||||
cdc := codec.NewProtoCodec(ir)
|
||||
|
||||
authcodec.RegisterInterfaces(ir)
|
||||
bankcodec.RegisterInterfaces(ir)
|
||||
cryptocodec.RegisterInterfaces(ir)
|
||||
|
||||
return cdc, ir
|
||||
}
|
||||
203
server/rosetta/config.go
Normal file
203
server/rosetta/config.go
Normal file
@ -0,0 +1,203 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
"github.com/spf13/pflag"
|
||||
crg "github.com/tendermint/cosmos-rosetta-gateway/server"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
|
||||
)
|
||||
|
||||
// configuration defaults constants
|
||||
const (
|
||||
// DefaultBlockchain defines the default blockchain identifier name
|
||||
DefaultBlockchain = "app"
|
||||
// DefaultAddr defines the default rosetta binding address
|
||||
DefaultAddr = ":8080"
|
||||
// DefaultRetries is the default number of retries
|
||||
DefaultRetries = 5
|
||||
// DefaultTendermintEndpoint is the default value for the tendermint endpoint
|
||||
DefaultTendermintEndpoint = "localhost:26657"
|
||||
// DefaultGRPCEndpoint is the default value for the gRPC endpoint
|
||||
DefaultGRPCEndpoint = "localhost:9090"
|
||||
// DefaultNetwork defines the default network name
|
||||
DefaultNetwork = "network"
|
||||
// DefaultOffline defines the default offline value
|
||||
DefaultOffline = false
|
||||
)
|
||||
|
||||
// configuration flags
|
||||
const (
|
||||
FlagBlockchain = "blockchain"
|
||||
FlagNetwork = "network"
|
||||
FlagTendermintEndpoint = "tendermint"
|
||||
FlagGRPCEndpoint = "grpc"
|
||||
FlagAddr = "addr"
|
||||
FlagRetries = "retries"
|
||||
FlagOffline = "offline"
|
||||
)
|
||||
|
||||
// Config defines the configuration of the rosetta server
|
||||
type Config struct {
|
||||
// Blockchain defines the blockchain name
|
||||
// defaults to DefaultBlockchain
|
||||
Blockchain string
|
||||
// Network defines the network name
|
||||
Network string
|
||||
// TendermintRPC defines the endpoint to connect to
|
||||
// tendermint RPC, specifying 'tcp://' before is not
|
||||
// required, usually it's at port 26657 of the
|
||||
TendermintRPC string
|
||||
// GRPCEndpoint defines the cosmos application gRPC endpoint
|
||||
// usually it is located at 9090 port
|
||||
GRPCEndpoint string
|
||||
// Addr defines the default address to bind the rosetta server to
|
||||
// defaults to DefaultAddr
|
||||
Addr string
|
||||
// Retries defines the maximum number of retries
|
||||
// rosetta will do before quitting
|
||||
Retries int
|
||||
// Offline defines if the server must be run in offline mode
|
||||
Offline bool
|
||||
// Codec overrides the default data and construction api client codecs
|
||||
Codec *codec.ProtoCodec
|
||||
// InterfaceRegistry overrides the default data and construction api interface registry
|
||||
InterfaceRegistry codectypes.InterfaceRegistry
|
||||
}
|
||||
|
||||
// NetworkIdentifier returns the network identifier given the configuration
|
||||
func (c *Config) NetworkIdentifier() *types.NetworkIdentifier {
|
||||
return &types.NetworkIdentifier{
|
||||
Blockchain: c.Blockchain,
|
||||
Network: c.Network,
|
||||
}
|
||||
}
|
||||
|
||||
// validate validates a configuration and sets
|
||||
// its defaults in case they were not provided
|
||||
func (c *Config) validate() error {
|
||||
if (c.Codec == nil) != (c.InterfaceRegistry == nil) {
|
||||
return fmt.Errorf("codec and interface registry must be both different from nil or nil")
|
||||
}
|
||||
|
||||
if c.Addr == "" {
|
||||
c.Addr = DefaultAddr
|
||||
}
|
||||
if c.Blockchain == "" {
|
||||
c.Blockchain = DefaultBlockchain
|
||||
}
|
||||
if c.Retries == 0 {
|
||||
c.Retries = DefaultRetries
|
||||
}
|
||||
// these are must
|
||||
if c.Network == "" {
|
||||
return fmt.Errorf("network not provided")
|
||||
}
|
||||
if c.Offline {
|
||||
return fmt.Errorf("offline mode is not supported for stargate implementation due to how sigv2 works")
|
||||
}
|
||||
|
||||
// these are optional but it must be online
|
||||
if c.GRPCEndpoint == "" {
|
||||
return fmt.Errorf("grpc endpoint not provided")
|
||||
}
|
||||
if c.TendermintRPC == "" {
|
||||
return fmt.Errorf("tendermint rpc not provided")
|
||||
}
|
||||
if !strings.HasPrefix(c.TendermintRPC, "tcp://") {
|
||||
c.TendermintRPC = fmt.Sprintf("tcp://%s", c.TendermintRPC)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WithCodec extends the configuration with a predefined Codec
|
||||
func (c *Config) WithCodec(ir codectypes.InterfaceRegistry, cdc *codec.ProtoCodec) {
|
||||
c.Codec = cdc
|
||||
c.InterfaceRegistry = ir
|
||||
}
|
||||
|
||||
// FromFlags gets the configuration from flags
|
||||
func FromFlags(flags *pflag.FlagSet) (*Config, error) {
|
||||
blockchain, err := flags.GetString(FlagBlockchain)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
network, err := flags.GetString(FlagNetwork)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tendermintRPC, err := flags.GetString(FlagTendermintEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gRPCEndpoint, err := flags.GetString(FlagGRPCEndpoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
addr, err := flags.GetString(FlagAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
retries, err := flags.GetInt(FlagRetries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
offline, err := flags.GetBool(FlagOffline)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf := &Config{
|
||||
Blockchain: blockchain,
|
||||
Network: network,
|
||||
TendermintRPC: tendermintRPC,
|
||||
GRPCEndpoint: gRPCEndpoint,
|
||||
Addr: addr,
|
||||
Retries: retries,
|
||||
Offline: offline,
|
||||
}
|
||||
err = conf.validate()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return conf, nil
|
||||
}
|
||||
|
||||
func ServerFromConfig(conf *Config) (crg.Server, error) {
|
||||
err := conf.validate()
|
||||
if err != nil {
|
||||
return crg.Server{}, err
|
||||
}
|
||||
client, err := NewClient(conf)
|
||||
if err != nil {
|
||||
return crg.Server{}, err
|
||||
}
|
||||
return crg.NewServer(
|
||||
crg.Settings{
|
||||
Network: &types.NetworkIdentifier{
|
||||
Blockchain: conf.Blockchain,
|
||||
Network: conf.Network,
|
||||
},
|
||||
Client: client,
|
||||
Listen: conf.Addr,
|
||||
Offline: conf.Offline,
|
||||
Retries: conf.Retries,
|
||||
RetryWait: 15 * time.Second,
|
||||
})
|
||||
}
|
||||
|
||||
// SetFlags sets the configuration flags to the given flagset
|
||||
func SetFlags(flags *pflag.FlagSet) {
|
||||
flags.String(FlagBlockchain, DefaultBlockchain, "the blockchain type")
|
||||
flags.String(FlagNetwork, DefaultNetwork, "the network name")
|
||||
flags.String(FlagTendermintEndpoint, DefaultTendermintEndpoint, "the tendermint rpc endpoint, without tcp://")
|
||||
flags.String(FlagGRPCEndpoint, DefaultGRPCEndpoint, "the app gRPC endpoint")
|
||||
flags.String(FlagAddr, DefaultAddr, "the address rosetta will bind to")
|
||||
flags.Int(FlagRetries, DefaultRetries, "the number of retries that will be done before quitting")
|
||||
flags.Bool(FlagOffline, DefaultOffline, "run rosetta only with construction API")
|
||||
}
|
||||
211
server/rosetta/conv_from_rosetta.go
Normal file
211
server/rosetta/conv_from_rosetta.go
Normal file
@ -0,0 +1,211 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// timeToMilliseconds converts time to milliseconds timestamp
|
||||
func timeToMilliseconds(t time.Time) int64 {
|
||||
return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
|
||||
}
|
||||
|
||||
// sdkCoinsToRosettaAmounts converts []sdk.Coin to rosetta amounts
|
||||
// availableCoins keeps track of current available coins vs the coins
|
||||
// owned by an address. This is required to support historical balances
|
||||
// as rosetta expects them to be set to 0, if an address does not own them
|
||||
func sdkCoinsToRosettaAmounts(ownedCoins []sdk.Coin, availableCoins sdk.Coins) []*types.Amount {
|
||||
amounts := make([]*types.Amount, len(availableCoins))
|
||||
ownedCoinsMap := make(map[string]sdk.Int, len(availableCoins))
|
||||
|
||||
for _, ownedCoin := range ownedCoins {
|
||||
ownedCoinsMap[ownedCoin.Denom] = ownedCoin.Amount
|
||||
}
|
||||
|
||||
for i, coin := range availableCoins {
|
||||
value, owned := ownedCoinsMap[coin.Denom]
|
||||
if !owned {
|
||||
amounts[i] = &types.Amount{
|
||||
Value: sdk.NewInt(0).String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
}
|
||||
continue
|
||||
}
|
||||
amounts[i] = &types.Amount{
|
||||
Value: value.String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return amounts
|
||||
}
|
||||
|
||||
// sdkTxsWithHashToRosettaTxs converts sdk transactions wrapped with their hash to rosetta transactions
|
||||
func sdkTxsWithHashToRosettaTxs(txs []*sdkTxWithHash) []*types.Transaction {
|
||||
converted := make([]*types.Transaction, len(txs))
|
||||
for i, tx := range txs {
|
||||
converted[i] = sdkTxWithHashToOperations(tx)
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
func sdkTxWithHashToOperations(tx *sdkTxWithHash) *types.Transaction {
|
||||
hasError := tx.Code != 0
|
||||
return &types.Transaction{
|
||||
TransactionIdentifier: &types.TransactionIdentifier{Hash: tx.HexHash},
|
||||
Operations: sdkTxToOperations(tx.Tx, true, hasError),
|
||||
Metadata: map[string]interface{}{
|
||||
Log: tx.Log,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// sdkTxToOperations converts an sdk.Tx to rosetta operations
|
||||
func sdkTxToOperations(tx sdk.Tx, withStatus, hasError bool) []*types.Operation {
|
||||
var operations []*types.Operation
|
||||
|
||||
msgOps := sdkMsgsToRosettaOperations(tx.GetMsgs(), withStatus, hasError)
|
||||
operations = append(operations, msgOps...)
|
||||
|
||||
feeTx := tx.(sdk.FeeTx)
|
||||
feeOps := sdkFeeTxToOperations(feeTx, withStatus, len(msgOps))
|
||||
operations = append(operations, feeOps...)
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
// sdkFeeTxToOperations converts sdk.FeeTx to rosetta operations
|
||||
func sdkFeeTxToOperations(feeTx sdk.FeeTx, withStatus bool, previousOps int) []*types.Operation {
|
||||
feeCoins := feeTx.GetFee()
|
||||
var ops []*types.Operation
|
||||
if feeCoins != nil {
|
||||
var feeOps = rosettaFeeOperationsFromCoins(feeCoins, feeTx.FeePayer().String(), withStatus, previousOps)
|
||||
ops = append(ops, feeOps...)
|
||||
}
|
||||
|
||||
return ops
|
||||
}
|
||||
|
||||
// rosettaFeeOperationsFromCoins returns the list of rosetta fee operations given sdk coins
|
||||
func rosettaFeeOperationsFromCoins(coins sdk.Coins, account string, withStatus bool, previousOps int) []*types.Operation {
|
||||
feeOps := make([]*types.Operation, 0)
|
||||
var status string
|
||||
if withStatus {
|
||||
status = StatusSuccess
|
||||
}
|
||||
|
||||
for i, coin := range coins {
|
||||
op := &types.Operation{
|
||||
OperationIdentifier: &types.OperationIdentifier{
|
||||
Index: int64(previousOps + i),
|
||||
},
|
||||
Type: OperationFee,
|
||||
Status: status,
|
||||
Account: &types.AccountIdentifier{
|
||||
Address: account,
|
||||
},
|
||||
Amount: &types.Amount{
|
||||
Value: "-" + coin.Amount.String(),
|
||||
Currency: &types.Currency{
|
||||
Symbol: coin.Denom,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
feeOps = append(feeOps, op)
|
||||
}
|
||||
|
||||
return feeOps
|
||||
}
|
||||
|
||||
// sdkMsgsToRosettaOperations converts sdk messages to rosetta operations
|
||||
func sdkMsgsToRosettaOperations(msgs []sdk.Msg, withStatus bool, hasError bool) []*types.Operation {
|
||||
var operations []*types.Operation
|
||||
for _, msg := range msgs {
|
||||
if rosettaMsg, ok := msg.(Msg); ok {
|
||||
operations = append(operations, rosettaMsg.ToOperations(withStatus, hasError)...)
|
||||
}
|
||||
}
|
||||
|
||||
return operations
|
||||
}
|
||||
|
||||
// TMTxsToRosettaTxsIdentifiers converts a tendermint raw transactions into an array of rosetta tx identifiers
|
||||
func TMTxsToRosettaTxsIdentifiers(txs []tmtypes.Tx) []*types.TransactionIdentifier {
|
||||
converted := make([]*types.TransactionIdentifier, len(txs))
|
||||
for i, tx := range txs {
|
||||
converted[i] = TmTxToRosettaTxsIdentifier(tx)
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
// TmTxToRosettaTxsIdentifier converts a tendermint raw transaction into a rosetta tx identifier
|
||||
func TmTxToRosettaTxsIdentifier(tx tmtypes.Tx) *types.TransactionIdentifier {
|
||||
return &types.TransactionIdentifier{Hash: fmt.Sprintf("%x", tx.Hash())}
|
||||
}
|
||||
|
||||
// TMBlockToRosettaBlockIdentifier converts a tendermint result block to a rosetta block identifier
|
||||
func TMBlockToRosettaBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier {
|
||||
return &types.BlockIdentifier{
|
||||
Index: block.Block.Height,
|
||||
Hash: block.Block.Hash().String(),
|
||||
}
|
||||
}
|
||||
|
||||
// TmPeersToRosettaPeers converts tendermint peers to rosetta ones
|
||||
func TmPeersToRosettaPeers(peers []tmcoretypes.Peer) []*types.Peer {
|
||||
converted := make([]*types.Peer, len(peers))
|
||||
|
||||
for i, peer := range peers {
|
||||
converted[i] = &types.Peer{
|
||||
PeerID: peer.NodeInfo.Moniker,
|
||||
Metadata: map[string]interface{}{
|
||||
"addr": peer.NodeInfo.ListenAddr,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return converted
|
||||
}
|
||||
|
||||
// TMStatusToRosettaSyncStatus converts a tendermint status to rosetta sync status
|
||||
func TMStatusToRosettaSyncStatus(status *tmcoretypes.ResultStatus) *types.SyncStatus {
|
||||
// determine sync status
|
||||
var stage = StageSynced
|
||||
if status.SyncInfo.CatchingUp {
|
||||
stage = StageSyncing
|
||||
}
|
||||
|
||||
return &types.SyncStatus{
|
||||
CurrentIndex: status.SyncInfo.LatestBlockHeight,
|
||||
TargetIndex: nil, // sync info does not allow us to get target height
|
||||
Stage: &stage,
|
||||
}
|
||||
}
|
||||
|
||||
// TMBlockToRosettaParentBlockIdentifier returns the parent block identifier from the last block
|
||||
func TMBlockToRosettaParentBlockIdentifier(block *tmcoretypes.ResultBlock) *types.BlockIdentifier {
|
||||
if block.Block.Height == 1 {
|
||||
return &types.BlockIdentifier{
|
||||
Index: 1,
|
||||
Hash: fmt.Sprintf("%X", block.BlockID.Hash.Bytes()),
|
||||
}
|
||||
}
|
||||
|
||||
return &types.BlockIdentifier{
|
||||
Index: block.Block.Height - 1,
|
||||
Hash: fmt.Sprintf("%X", block.Block.LastBlockID.Hash.Bytes()),
|
||||
}
|
||||
}
|
||||
95
server/rosetta/conv_to_rosetta.go
Normal file
95
server/rosetta/conv_to_rosetta.go
Normal file
@ -0,0 +1,95 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/gogo/protobuf/jsonpb"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// opsToMsgsAndFees converts rosetta operations to sdk.Msg and fees represented as sdk.Coins
|
||||
func opsToMsgsAndFees(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, sdk.Coins, error) {
|
||||
var feeAmnt []*types.Amount
|
||||
var newOps []*types.Operation
|
||||
var msgType string
|
||||
// find the fee operation and put it aside
|
||||
for _, op := range ops {
|
||||
switch op.Type {
|
||||
case OperationFee:
|
||||
amount := op.Amount
|
||||
feeAmnt = append(feeAmnt, amount)
|
||||
default:
|
||||
// check if operation matches the one already used
|
||||
// as, at the moment, we only support operations
|
||||
// that represent a single cosmos-sdk message
|
||||
switch {
|
||||
// if msgType was not set then set it
|
||||
case msgType == "":
|
||||
msgType = op.Type
|
||||
// if msgType does not match op.Type then it means we're trying to send multiple messages in a single tx
|
||||
case msgType != op.Type:
|
||||
return nil, nil, fmt.Errorf("only single message operations are supported: %s - %s", msgType, op.Type)
|
||||
}
|
||||
// append operation to new ops list
|
||||
newOps = append(newOps, op)
|
||||
}
|
||||
}
|
||||
// convert all operations, except fee op to sdk.Msgs
|
||||
msgs, err := opsToMsgs(interfaceRegistry, newOps)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return msgs, amountsToCoins(feeAmnt), nil
|
||||
}
|
||||
|
||||
// amountsToCoins converts rosetta amounts to sdk coins
|
||||
func amountsToCoins(amounts []*types.Amount) sdk.Coins {
|
||||
var feeCoins sdk.Coins
|
||||
|
||||
for _, amount := range amounts {
|
||||
absValue := strings.Trim(amount.Value, "-")
|
||||
value, err := strconv.ParseInt(absValue, 10, 64)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
coin := sdk.NewCoin(amount.Currency.Symbol, sdk.NewInt(value))
|
||||
feeCoins = append(feeCoins, coin)
|
||||
}
|
||||
|
||||
return feeCoins
|
||||
}
|
||||
|
||||
func opsToMsgs(interfaceRegistry jsonpb.AnyResolver, ops []*types.Operation) ([]sdk.Msg, error) {
|
||||
var msgs []sdk.Msg
|
||||
var operationsByType = make(map[string][]*types.Operation)
|
||||
for _, op := range ops {
|
||||
operationsByType[op.Type] = append(operationsByType[op.Type], op)
|
||||
}
|
||||
|
||||
for opName, operations := range operationsByType {
|
||||
if opName == OperationFee {
|
||||
continue
|
||||
}
|
||||
|
||||
msgType, err := interfaceRegistry.Resolve("/" + opName) // Types are registered as /proto-name in the interface registry.
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if rosettaMsg, ok := msgType.(Msg); ok {
|
||||
m, err := rosettaMsg.FromOperations(operations)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
msgs = append(msgs, m)
|
||||
}
|
||||
}
|
||||
|
||||
return msgs, nil
|
||||
}
|
||||
41
server/rosetta/types.go
Normal file
41
server/rosetta/types.go
Normal file
@ -0,0 +1,41 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// statuses
|
||||
const (
|
||||
StatusSuccess = "Success"
|
||||
StatusReverted = "Reverted"
|
||||
StageSynced = "synced"
|
||||
StageSyncing = "syncing"
|
||||
)
|
||||
|
||||
// misc
|
||||
const (
|
||||
Log = "log"
|
||||
)
|
||||
|
||||
// operations
|
||||
const (
|
||||
OperationFee = "fee"
|
||||
)
|
||||
|
||||
// options
|
||||
const (
|
||||
OptionAccountNumber = "account_number"
|
||||
OptionAddress = "address"
|
||||
OptionChainID = "chain_id"
|
||||
OptionSequence = "sequence"
|
||||
OptionMemo = "memo"
|
||||
OptionGas = "gas"
|
||||
)
|
||||
|
||||
type Msg interface {
|
||||
sdk.Msg
|
||||
ToOperations(withStatus, hasError bool) []*types.Operation
|
||||
FromOperations(ops []*types.Operation) (sdk.Msg, error)
|
||||
}
|
||||
112
server/rosetta/util.go
Normal file
112
server/rosetta/util.go
Normal file
@ -0,0 +1,112 @@
|
||||
package rosetta
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/coinbase/rosetta-sdk-go/types"
|
||||
|
||||
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
|
||||
tmtypes "github.com/tendermint/tendermint/types"
|
||||
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
)
|
||||
|
||||
// tmResultTxsToSdkTxsWithHash converts tendermint result txs to cosmos sdk.Tx
|
||||
func tmResultTxsToSdkTxsWithHash(decode sdk.TxDecoder, txs []*tmcoretypes.ResultTx) ([]*sdkTxWithHash, error) {
|
||||
converted := make([]*sdkTxWithHash, len(txs))
|
||||
for i, tx := range txs {
|
||||
sdkTx, err := decode(tx.Tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
converted[i] = &sdkTxWithHash{
|
||||
HexHash: fmt.Sprintf("%X", tx.Tx.Hash()),
|
||||
Code: tx.TxResult.Code,
|
||||
Log: tx.TxResult.Log,
|
||||
Tx: sdkTx,
|
||||
}
|
||||
}
|
||||
|
||||
return converted, nil
|
||||
}
|
||||
|
||||
func tmTxToSdkTx(decode sdk.TxDecoder, tx tmtypes.Tx) (sdk.Tx, error) {
|
||||
sdkTx, err := decode(tx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return sdkTx, err
|
||||
}
|
||||
|
||||
type sdkTxWithHash struct {
|
||||
HexHash string
|
||||
Code uint32
|
||||
Log string
|
||||
Tx sdk.Tx
|
||||
}
|
||||
|
||||
type PayloadReqMetadata struct {
|
||||
ChainID string
|
||||
Sequence uint64
|
||||
AccountNumber uint64
|
||||
Gas uint64
|
||||
Memo string
|
||||
}
|
||||
|
||||
// getMetadataFromPayloadReq obtains the metadata from the request to /construction/payloads endpoint.
|
||||
func getMetadataFromPayloadReq(req *types.ConstructionPayloadsRequest) (*PayloadReqMetadata, error) {
|
||||
chainID, ok := req.Metadata[OptionChainID].(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("chain_id metadata was not provided")
|
||||
}
|
||||
|
||||
sequence, ok := req.Metadata[OptionSequence]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("sequence metadata was not provided")
|
||||
}
|
||||
|
||||
seqNum, ok := sequence.(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid sequence value")
|
||||
}
|
||||
|
||||
accountNum, ok := req.Metadata[OptionAccountNumber]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("account_number metadata was not provided")
|
||||
}
|
||||
|
||||
accNum, ok := accountNum.(float64)
|
||||
if !ok {
|
||||
fmt.Printf("this is type %T", accountNum)
|
||||
return nil, fmt.Errorf("invalid account_number value")
|
||||
}
|
||||
|
||||
gasNum, ok := req.Metadata[OptionGas]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("gas metadata was not provided")
|
||||
}
|
||||
|
||||
gasF64, ok := gasNum.(float64)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid gas value")
|
||||
}
|
||||
|
||||
memo, ok := req.Metadata[OptionMemo]
|
||||
if !ok {
|
||||
memo = ""
|
||||
}
|
||||
|
||||
memoStr, ok := memo.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("invalid memo")
|
||||
}
|
||||
|
||||
return &PayloadReqMetadata{
|
||||
ChainID: chainID,
|
||||
Sequence: uint64(seqNum),
|
||||
AccountNumber: uint64(accNum),
|
||||
Gas: uint64(gasF64),
|
||||
Memo: memoStr,
|
||||
}, nil
|
||||
}
|
||||
@ -9,7 +9,14 @@ import (
|
||||
"runtime/pprof"
|
||||
"time"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/server/rosetta"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/codec"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
crgserver "github.com/tendermint/cosmos-rosetta-gateway/server"
|
||||
"github.com/tendermint/tendermint/abci/server"
|
||||
tcmd "github.com/tendermint/tendermint/cmd/tendermint/commands"
|
||||
tmos "github.com/tendermint/tendermint/libs/os"
|
||||
@ -18,7 +25,6 @@ import (
|
||||
pvm "github.com/tendermint/tendermint/privval"
|
||||
"github.com/tendermint/tendermint/proxy"
|
||||
"github.com/tendermint/tendermint/rpc/client/local"
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/client"
|
||||
"github.com/cosmos/cosmos-sdk/client/flags"
|
||||
@ -154,7 +160,7 @@ which accepts a path for the resulting pprof file.
|
||||
cmd.Flags().String(flagGRPCAddress, config.DefaultGRPCAddress, "the gRPC server address to listen on")
|
||||
|
||||
cmd.Flags().Bool(flagGRPCWebEnable, true, "Define if the gRPC-Web server should be enabled. (Note: gRPC must also be enabled.)")
|
||||
cmd.Flags().String(flagGRPCWebAddress, config.DefaultGRPCAddress, "The gRPC-Web server address to listen on")
|
||||
cmd.Flags().String(flagGRPCWebAddress, config.DefaultGRPCWebAddress, "The gRPC-Web server address to listen on")
|
||||
|
||||
cmd.Flags().Uint64(FlagStateSyncSnapshotInterval, 0, "State sync snapshot interval")
|
||||
cmd.Flags().Uint32(FlagStateSyncSnapshotKeepRecent, 2, "State sync snapshot to keep")
|
||||
@ -280,7 +286,6 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App
|
||||
}
|
||||
|
||||
var apiSrv *api.Server
|
||||
|
||||
if config.API.Enable {
|
||||
genDoc, err := genDocProvider()
|
||||
if err != nil {
|
||||
@ -326,6 +331,42 @@ func startInProcess(ctx *Context, clientCtx client.Context, appCreator types.App
|
||||
}
|
||||
}
|
||||
|
||||
var rosettaSrv crgserver.Server
|
||||
if config.Rosetta.Enable {
|
||||
offlineMode := config.Rosetta.Offline
|
||||
if !config.GRPC.Enable { // If GRPC is not enabled rosetta cannot work in online mode, so it works in offline mode.
|
||||
offlineMode = true
|
||||
}
|
||||
|
||||
conf := &rosetta.Config{
|
||||
Blockchain: config.Rosetta.Blockchain,
|
||||
Network: config.Rosetta.Network,
|
||||
TendermintRPC: ctx.Config.RPC.ListenAddress,
|
||||
GRPCEndpoint: config.GRPC.Address,
|
||||
Addr: config.Rosetta.Address,
|
||||
Retries: config.Rosetta.Retries,
|
||||
Offline: offlineMode,
|
||||
}
|
||||
conf.WithCodec(clientCtx.InterfaceRegistry, clientCtx.JSONMarshaler.(*codec.ProtoCodec))
|
||||
|
||||
rosettaSrv, err = rosetta.ServerFromConfig(conf)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
errCh := make(chan error)
|
||||
go func() {
|
||||
if err := rosettaSrv.Start(); err != nil {
|
||||
errCh <- err
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-time.After(5 * time.Second): // assume server started successfully
|
||||
}
|
||||
}
|
||||
|
||||
defer func() {
|
||||
if tmNode.IsRunning() {
|
||||
_ = tmNode.Stop()
|
||||
|
||||
@ -28,7 +28,7 @@ func ShowNodeIDCmd() *cobra.Command {
|
||||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
nodeKey, err := p2p.LoadOrGenNodeKey(cfg.NodeKeyFile())
|
||||
nodeKey, err := p2p.LoadNodeKey(cfg.NodeKeyFile())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -48,7 +48,7 @@ func ShowValidatorCmd() *cobra.Command {
|
||||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
valPubKey, err := privValidator.GetPubKey()
|
||||
if err != nil {
|
||||
return err
|
||||
@ -86,7 +86,7 @@ func ShowAddressCmd() *cobra.Command {
|
||||
serverCtx := GetServerContextFromCmd(cmd)
|
||||
cfg := serverCtx.Config
|
||||
|
||||
privValidator := pvm.LoadOrGenFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
privValidator := pvm.LoadFilePV(cfg.PrivValidatorKeyFile(), cfg.PrivValidatorStateFile())
|
||||
valConsAddr := (sdk.ConsAddress)(privValidator.GetAddress())
|
||||
|
||||
output, _ := cmd.Flags().GetString(cli.OutputFlag)
|
||||
|
||||
@ -17,6 +17,7 @@ import (
|
||||
"github.com/rs/zerolog"
|
||||
"github.com/rs/zerolog/log"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/pflag"
|
||||
"github.com/spf13/viper"
|
||||
tmcfg "github.com/tendermint/tendermint/config"
|
||||
tmlog "github.com/tendermint/tendermint/libs/log"
|
||||
@ -63,6 +64,37 @@ func NewContext(v *viper.Viper, config *tmcfg.Config, logger tmlog.Logger) *Cont
|
||||
return &Context{v, config, logger}
|
||||
}
|
||||
|
||||
func bindFlags(basename string, cmd *cobra.Command, v *viper.Viper) (err error) {
|
||||
defer func() {
|
||||
recover()
|
||||
}()
|
||||
|
||||
cmd.Flags().VisitAll(func(f *pflag.Flag) {
|
||||
// Environment variables can't have dashes in them, so bind them to their equivalent
|
||||
// keys with underscores, e.g. --favorite-color to STING_FAVORITE_COLOR
|
||||
err = v.BindEnv(f.Name, fmt.Sprintf("%s_%s", basename, strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = v.BindPFlag(f.Name, f)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// Apply the viper config value to the flag when the flag is not set and viper has a value
|
||||
if !f.Changed && v.IsSet(f.Name) {
|
||||
val := v.Get(f.Name)
|
||||
err = cmd.Flags().Set(f.Name, fmt.Sprintf("%v", val))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// InterceptConfigsPreRunHandler performs a pre-run function for the root daemon
|
||||
// application command. It will create a Viper literal and a default server
|
||||
// Context. The server Tendermint configuration will either be read and parsed
|
||||
@ -98,6 +130,9 @@ func InterceptConfigsPreRunHandler(cmd *cobra.Command) error {
|
||||
|
||||
// return value is a tendermint configuration object
|
||||
serverCtx.Config = config
|
||||
if err = bindFlags(basename, cmd, serverCtx.Viper); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var logWriter io.Writer
|
||||
if strings.ToLower(serverCtx.Viper.GetString(flags.FlagLogFormat)) == tmcfg.LogFormatPlain {
|
||||
|
||||
@ -44,6 +44,7 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/capability"
|
||||
capabilitykeeper "github.com/cosmos/cosmos-sdk/x/capability/keeper"
|
||||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/x/crisis"
|
||||
crisiskeeper "github.com/cosmos/cosmos-sdk/x/crisis/keeper"
|
||||
crisistypes "github.com/cosmos/cosmos-sdk/x/crisis/types"
|
||||
@ -54,6 +55,10 @@ import (
|
||||
"github.com/cosmos/cosmos-sdk/x/evidence"
|
||||
evidencekeeper "github.com/cosmos/cosmos-sdk/x/evidence/keeper"
|
||||
evidencetypes "github.com/cosmos/cosmos-sdk/x/evidence/types"
|
||||
feegrant "github.com/cosmos/cosmos-sdk/x/feegrant"
|
||||
feegrantante "github.com/cosmos/cosmos-sdk/x/feegrant/ante"
|
||||
feegrantkeeper "github.com/cosmos/cosmos-sdk/x/feegrant/keeper"
|
||||
feegranttypes "github.com/cosmos/cosmos-sdk/x/feegrant/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/genutil"
|
||||
genutiltypes "github.com/cosmos/cosmos-sdk/x/genutil/types"
|
||||
"github.com/cosmos/cosmos-sdk/x/gov"
|
||||
@ -87,6 +92,10 @@ import (
|
||||
upgradekeeper "github.com/cosmos/cosmos-sdk/x/upgrade/keeper"
|
||||
upgradetypes "github.com/cosmos/cosmos-sdk/x/upgrade/types"
|
||||
|
||||
authz "github.com/cosmos/cosmos-sdk/x/authz"
|
||||
authzkeeper "github.com/cosmos/cosmos-sdk/x/authz/keeper"
|
||||
authztypes "github.com/cosmos/cosmos-sdk/x/authz/types"
|
||||
|
||||
// unnamed import of statik for swagger UI support
|
||||
_ "github.com/cosmos/cosmos-sdk/client/docs/statik"
|
||||
)
|
||||
@ -115,9 +124,11 @@ var (
|
||||
crisis.AppModuleBasic{},
|
||||
slashing.AppModuleBasic{},
|
||||
ibc.AppModuleBasic{},
|
||||
feegrant.AppModuleBasic{},
|
||||
upgrade.AppModuleBasic{},
|
||||
evidence.AppModuleBasic{},
|
||||
transfer.AppModuleBasic{},
|
||||
authz.AppModuleBasic{},
|
||||
vesting.AppModuleBasic{},
|
||||
)
|
||||
|
||||
@ -171,9 +182,11 @@ type SimApp struct {
|
||||
CrisisKeeper crisiskeeper.Keeper
|
||||
UpgradeKeeper upgradekeeper.Keeper
|
||||
ParamsKeeper paramskeeper.Keeper
|
||||
AuthzKeeper authzkeeper.Keeper
|
||||
IBCKeeper *ibckeeper.Keeper // IBC Keeper must be a pointer in the app, so we can SetRouter on it correctly
|
||||
EvidenceKeeper evidencekeeper.Keeper
|
||||
TransferKeeper ibctransferkeeper.Keeper
|
||||
FeeGrantKeeper feegrantkeeper.Keeper
|
||||
|
||||
// make scoped keepers public for test purposes
|
||||
ScopedIBCKeeper capabilitykeeper.ScopedKeeper
|
||||
@ -215,8 +228,9 @@ func NewSimApp(
|
||||
keys := sdk.NewKVStoreKeys(
|
||||
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey,
|
||||
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
|
||||
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey,
|
||||
govtypes.StoreKey, paramstypes.StoreKey, ibchost.StoreKey, upgradetypes.StoreKey, feegranttypes.StoreKey,
|
||||
evidencetypes.StoreKey, ibctransfertypes.StoreKey, capabilitytypes.StoreKey,
|
||||
authztypes.StoreKey,
|
||||
)
|
||||
tkeys := sdk.NewTransientStoreKeys(paramstypes.TStoreKey)
|
||||
memKeys := sdk.NewMemoryStoreKeys(capabilitytypes.MemStoreKey)
|
||||
@ -269,6 +283,8 @@ func NewSimApp(
|
||||
app.CrisisKeeper = crisiskeeper.NewKeeper(
|
||||
app.GetSubspace(crisistypes.ModuleName), invCheckPeriod, app.BankKeeper, authtypes.FeeCollectorName,
|
||||
)
|
||||
|
||||
app.FeeGrantKeeper = feegrantkeeper.NewKeeper(appCodec, keys[feegranttypes.StoreKey], app.AccountKeeper)
|
||||
app.UpgradeKeeper = upgradekeeper.NewKeeper(skipUpgradeHeights, keys[upgradetypes.StoreKey], appCodec, homePath)
|
||||
|
||||
// register the staking hooks
|
||||
@ -282,6 +298,8 @@ func NewSimApp(
|
||||
appCodec, keys[ibchost.StoreKey], app.GetSubspace(ibchost.ModuleName), app.StakingKeeper, scopedIBCKeeper,
|
||||
)
|
||||
|
||||
app.AuthzKeeper = authzkeeper.NewKeeper(keys[authztypes.StoreKey], appCodec, app.BaseApp.MsgServiceRouter())
|
||||
|
||||
// register the proposal types
|
||||
govRouter := govtypes.NewRouter()
|
||||
govRouter.AddRoute(govtypes.RouterKey, govtypes.ProposalHandler).
|
||||
@ -337,6 +355,7 @@ func NewSimApp(
|
||||
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
||||
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
||||
crisis.NewAppModule(&app.CrisisKeeper, skipGenesisInvariants),
|
||||
feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry),
|
||||
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
|
||||
slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
|
||||
@ -346,6 +365,7 @@ func NewSimApp(
|
||||
evidence.NewAppModule(app.EvidenceKeeper),
|
||||
ibc.NewAppModule(app.IBCKeeper),
|
||||
params.NewAppModule(app.ParamsKeeper),
|
||||
authz.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
|
||||
transferModule,
|
||||
)
|
||||
|
||||
@ -367,7 +387,8 @@ func NewSimApp(
|
||||
app.mm.SetOrderInitGenesis(
|
||||
capabilitytypes.ModuleName, authtypes.ModuleName, banktypes.ModuleName, distrtypes.ModuleName, stakingtypes.ModuleName,
|
||||
slashingtypes.ModuleName, govtypes.ModuleName, minttypes.ModuleName, crisistypes.ModuleName,
|
||||
ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, ibctransfertypes.ModuleName,
|
||||
ibchost.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authztypes.ModuleName, ibctransfertypes.ModuleName,
|
||||
feegranttypes.ModuleName,
|
||||
)
|
||||
|
||||
app.mm.RegisterInvariants(&app.CrisisKeeper)
|
||||
@ -385,6 +406,7 @@ func NewSimApp(
|
||||
auth.NewAppModule(appCodec, app.AccountKeeper, authsims.RandomGenesisAccounts),
|
||||
bank.NewAppModule(appCodec, app.BankKeeper, app.AccountKeeper),
|
||||
capability.NewAppModule(appCodec, *app.CapabilityKeeper),
|
||||
feegrant.NewAppModule(appCodec, app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, app.interfaceRegistry),
|
||||
gov.NewAppModule(appCodec, app.GovKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
mint.NewAppModule(appCodec, app.MintKeeper, app.AccountKeeper),
|
||||
staking.NewAppModule(appCodec, app.StakingKeeper, app.AccountKeeper, app.BankKeeper),
|
||||
@ -392,6 +414,7 @@ func NewSimApp(
|
||||
slashing.NewAppModule(appCodec, app.SlashingKeeper, app.AccountKeeper, app.BankKeeper, app.StakingKeeper),
|
||||
params.NewAppModule(app.ParamsKeeper),
|
||||
evidence.NewAppModule(app.EvidenceKeeper),
|
||||
authz.NewAppModule(appCodec, app.AuthzKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
|
||||
ibc.NewAppModule(app.IBCKeeper),
|
||||
transferModule,
|
||||
)
|
||||
@ -407,8 +430,8 @@ func NewSimApp(
|
||||
app.SetInitChainer(app.InitChainer)
|
||||
app.SetBeginBlocker(app.BeginBlocker)
|
||||
app.SetAnteHandler(
|
||||
ante.NewAnteHandler(
|
||||
app.AccountKeeper, app.BankKeeper, ante.DefaultSigVerificationGasConsumer,
|
||||
feegrantante.NewAnteHandler(
|
||||
app.AccountKeeper, app.BankKeeper, app.FeeGrantKeeper, ante.DefaultSigVerificationGasConsumer,
|
||||
encodingConfig.TxConfig.SignModeHandler(),
|
||||
),
|
||||
)
|
||||
|
||||
@ -157,7 +157,7 @@ func (app *SimApp) prepForZeroHeightGenesis(ctx sdk.Context, jailAllowedAddrs []
|
||||
counter := int16(0)
|
||||
|
||||
for ; iter.Valid(); iter.Next() {
|
||||
addr := sdk.ValAddress(iter.Key()[1:])
|
||||
addr := sdk.ValAddress(stakingtypes.AddressFromValidatorsKey(iter.Key()))
|
||||
validator, found := app.StakingKeeper.GetValidator(ctx, addr)
|
||||
if !found {
|
||||
panic("expected validator, not found")
|
||||
|
||||
@ -21,4 +21,8 @@ const (
|
||||
DefaultWeightCommunitySpendProposal int = 5
|
||||
DefaultWeightTextProposal int = 5
|
||||
DefaultWeightParamChangeProposal int = 5
|
||||
|
||||
// feegrant
|
||||
DefaultWeightGrantFeeAllowance int = 100
|
||||
DefaultWeightRevokeFeeAllowance int = 100
|
||||
)
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
// Profile with:
|
||||
// /usr/local/go/bin/go test -benchmem -run=^$ github.com/cosmos/cosmos-sdk/simapp -bench ^BenchmarkFullAppSimulation$ -Commit=true -cpuprofile cpu.out
|
||||
func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
config, db, dir, logger, _, err := SetupSimulation("goleveldb-app-sim", "Simulation")
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
@ -57,6 +58,7 @@ func BenchmarkFullAppSimulation(b *testing.B) {
|
||||
}
|
||||
|
||||
func BenchmarkInvariants(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
config, db, dir, logger, _, err := SetupSimulation("leveldb-app-invariant-bench", "Simulation")
|
||||
if err != nil {
|
||||
b.Fatalf("simulation setup failed: %s", err.Error())
|
||||
|
||||
@ -19,6 +19,7 @@ import (
|
||||
sdk "github.com/cosmos/cosmos-sdk/types"
|
||||
simtypes "github.com/cosmos/cosmos-sdk/types/simulation"
|
||||
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
|
||||
authztypes "github.com/cosmos/cosmos-sdk/x/authz/types"
|
||||
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
|
||||
capabilitytypes "github.com/cosmos/cosmos-sdk/x/capability/types"
|
||||
distrtypes "github.com/cosmos/cosmos-sdk/x/distribution/types"
|
||||
@ -177,6 +178,7 @@ func TestAppImportExport(t *testing.T) {
|
||||
{app.keys[capabilitytypes.StoreKey], newApp.keys[capabilitytypes.StoreKey], [][]byte{}},
|
||||
{app.keys[ibchost.StoreKey], newApp.keys[ibchost.StoreKey], [][]byte{}},
|
||||
{app.keys[ibctransfertypes.StoreKey], newApp.keys[ibctransfertypes.StoreKey], [][]byte{}},
|
||||
{app.keys[authztypes.StoreKey], newApp.keys[authztypes.StoreKey], [][]byte{}},
|
||||
}
|
||||
|
||||
for _, skp := range storeKeysPrefixes {
|
||||
|
||||
@ -151,6 +151,7 @@ contain valid denominations. Accounts may optionally be supplied with vesting pa
|
||||
bankGenState := banktypes.GetGenesisStateFromAppState(depCdc, appState)
|
||||
bankGenState.Balances = append(bankGenState.Balances, balances)
|
||||
bankGenState.Balances = banktypes.SanitizeGenesisBalances(bankGenState.Balances)
|
||||
bankGenState.Supply = bankGenState.Supply.Add(balances.Coins...)
|
||||
|
||||
bankGenStateBz, err := cdc.MarshalJSON(bankGenState)
|
||||
if err != nil {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user