fix: misc fixes for cosmos-rosetta (#13583)

### Description

Closes:
https://github.com/cosmos/cosmos-sdk/issues/13083
https://github.com/cosmos/cosmos-sdk/issues/11402
https://github.com/cosmos/cosmos-sdk/issues/10678
https://github.com/cosmos/cosmos-sdk/issues/12358
https://github.com/cosmos/cosmos-sdk/issues/10776
https://github.com/cosmos/cosmos-sdk/issues/12934

### Author Checklist

*All items are required. Please add a note to the item if the item is not applicable and
please add links to any relevant follow up issues.*

I have...

- [x] included the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [x] added `!` to the type prefix if API or client breaking change
- [x] targeted the correct branch (see [PR Targeting](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#pr-targeting))
- [x] provided a link to the relevant issue or specification
- [x] followed the guidelines for [building modules](https://github.com/cosmos/cosmos-sdk/blob/main/docs/building-modules)
- [x] included the necessary unit and integration [tests](https://github.com/cosmos/cosmos-sdk/blob/main/CONTRIBUTING.md#testing)
- [x] added a changelog entry to `CHANGELOG.md`
- [x] included comments for [documenting Go code](https://blog.golang.org/godoc)
- [x] updated the relevant documentation or specification
- [x] reviewed "Files changed" and left comments if necessary
- [ ] confirmed all CI checks have passed

### Reviewers Checklist

*All items are required. Please add a note if the item is not applicable and please add
your handle next to the items reviewed if you only reviewed selected items.*

I have...

- [ ] confirmed the correct [type prefix](https://github.com/commitizen/conventional-commit-types/blob/v3.0.0/index.json) in the PR title
- [ ] confirmed `!` in the type prefix if API or client breaking change
- [ ] confirmed all author checklist items have been addressed 
- [ ] reviewed state machine logic
- [ ] reviewed API design and naming
- [ ] reviewed documentation is accurate
- [ ] reviewed tests and test coverage
- [ ] manually tested (if applicable)
This commit is contained in:
sleepy ramen
2022-11-04 11:48:45 +00:00
committed by GitHub
parent 2bb114acec
commit 39c5c079cd
36 changed files with 1157 additions and 65 deletions
+11
View File
@@ -0,0 +1,11 @@
#!/usr/bin/make -f
all: build
build:
go build ./cmd/rosetta.go
test:
go test -mod=readonly -race ./...
.PHONY: all build test
+135
View File
@@ -0,0 +1,135 @@
package rosetta
import (
"context"
"encoding/hex"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// ---------- cosmos-rosetta-gateway.types.NetworkInformationProvider implementation ------------ //
func (c *Client) OperationStatuses() []*types.OperationStatus {
return []*types.OperationStatus{
{
Status: StatusTxSuccess,
Successful: true,
},
{
Status: StatusTxReverted,
Successful: false,
},
}
}
func (c *Client) Version() string {
return c.version
}
func (c *Client) SupportedOperations() []string {
return c.supportedOperations
}
// ---------- cosmos-rosetta-gateway.types.OfflineClient implementation ------------ //
func (c *Client) SignedTx(_ context.Context, txBytes []byte, signatures []*types.Signature) (signedTxBytes []byte, err error) {
return c.converter.ToSDK().SignedTx(txBytes, signatures)
}
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")
}
tx, err := c.converter.ToSDK().UnsignedTx(request.Operations)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrInvalidOperation, err.Error())
}
metadata := new(ConstructionMetadata)
if err = metadata.FromMetadata(request.Metadata); err != nil {
return nil, err
}
txBytes, payloads, err := c.converter.ToRosetta().SigningComponents(tx, metadata, request.PublicKeys)
if err != nil {
return nil, err
}
return &types.ConstructionPayloadsResponse{
UnsignedTransaction: hex.EncodeToString(txBytes),
Payloads: payloads,
}, nil
}
func (c *Client) PreprocessOperationsToOptions(_ context.Context, req *types.ConstructionPreprocessRequest) (response *types.ConstructionPreprocessResponse, err error) {
if len(req.Operations) == 0 {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "no operations")
}
// now we need to parse the operations to cosmos sdk messages
tx, err := c.converter.ToSDK().UnsignedTx(req.Operations)
if err != nil {
return nil, err
}
// get the signers
signers := tx.GetSigners()
signersStr := make([]string, len(signers))
accountIdentifiers := make([]*types.AccountIdentifier, len(signers))
for i, sig := range signers {
addr := sig.String()
signersStr[i] = addr
accountIdentifiers[i] = &types.AccountIdentifier{
Address: addr,
}
}
// get the metadata request information
meta := new(ConstructionPreprocessMetadata)
err = meta.FromMetadata(req.Metadata)
if err != nil {
return nil, err
}
if meta.GasPrice == "" {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "no gas prices")
}
if meta.GasLimit == 0 {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "no gas limit")
}
// prepare the options to return
options := &PreprocessOperationsOptionsResponse{
ExpectedSigners: signersStr,
Memo: meta.Memo,
GasLimit: meta.GasLimit,
GasPrice: meta.GasPrice,
}
metaOptions, err := options.ToMetadata()
if err != nil {
return nil, err
}
return &types.ConstructionPreprocessResponse{
Options: metaOptions,
RequiredPublicKeys: accountIdentifiers,
}, nil
}
func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) {
pk, err := c.converter.ToSDK().PubKey(pubKey)
if err != nil {
return nil, err
}
return &types.AccountIdentifier{
Address: sdk.AccAddress(pk.Address()).String(),
}, nil
}
+572
View File
@@ -0,0 +1,572 @@
package rosetta
import (
"bytes"
"context"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"regexp"
"strconv"
"time"
"github.com/cosmos/cosmos-sdk/version"
abcitypes "github.com/tendermint/tendermint/abci/types"
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/grpc/metadata"
"github.com/tendermint/tendermint/rpc/client/http"
"google.golang.org/grpc"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
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"
"github.com/cosmos/cosmos-sdk/types/query"
tmrpc "github.com/tendermint/tendermint/rpc/client"
)
// interface assertion
var _ crgtypes.Client = (*Client)(nil)
const (
defaultNodeTimeout = time.Minute
tmWebsocketPath = "/websocket"
)
// Client implements a single network client to interact with cosmos based chains
type Client struct {
supportedOperations []string
config *Config
auth auth.QueryClient
bank bank.QueryClient
tmRPC tmrpc.Client
version string
converter Converter
}
// NewClient instantiates a new online servicer
func NewClient(cfg *Config) (*Client, error) {
info := version.NewInfo()
v := info.Version
if v == "" {
v = "unknown"
}
txConfig := authtx.NewTxConfig(cfg.Codec, authtx.DefaultSignModes)
var supportedOperations []string
for _, ii := range cfg.InterfaceRegistry.ListImplementations(sdk.MsgInterfaceProtoName) {
resolvedMsg, err := cfg.InterfaceRegistry.Resolve(ii)
if err != nil {
continue
}
if _, ok := resolvedMsg.(sdk.Msg); ok {
supportedOperations = append(supportedOperations, ii)
}
}
supportedOperations = append(
supportedOperations,
bank.EventTypeCoinSpent,
bank.EventTypeCoinReceived,
bank.EventTypeCoinBurn,
)
return &Client{
supportedOperations: supportedOperations,
config: cfg,
auth: nil,
bank: nil,
tmRPC: nil,
version: fmt.Sprintf("%s/%s", info.AppName, v),
converter: NewConverter(cfg.Codec, cfg.InterfaceRegistry, txConfig),
}, nil
}
// ---------- cosmos-rosetta-gateway.types.Client implementation ------------ //
// Bootstrap is gonna connect the client to the endpoints
func (c *Client) Bootstrap() error {
grpcConn, err := grpc.Dial(c.config.GRPCEndpoint, grpc.WithTransportCredentials(insecure.NewCredentials()))
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)
c.auth = authClient
c.bank = bankClient
c.tmRPC = tmRPC
return nil
}
// Ready performs a health check and returns an error if the client is not ready.
func (c *Client) Ready() error {
ctx, cancel := context.WithTimeout(context.Background(), defaultNodeTimeout)
defer cancel()
_, err := c.tmRPC.Health(ctx)
if err != nil {
return err
}
_, err = c.tmRPC.Status(ctx)
if err != nil {
return err
}
_, err = c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
if err != nil {
return err
}
return nil
}
func (c *Client) accountInfo(ctx context.Context, addr string, height *int64) (*SignerData, 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)
}
signerData, err := c.converter.ToRosetta().SignerData(accountInfo.Account)
if err != nil {
return nil, err
}
return signerData, nil
}
func (c *Client) Balances(ctx context.Context, addr string, height *int64) ([]*rosettatypes.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 c.converter.ToRosetta().Amounts(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.tmRPC.BlockByHash(ctx, bHash)
if err != nil {
return crgtypes.BlockResponse{}, crgerrs.WrapError(crgerrs.ErrBadGateway, err.Error())
}
return c.converter.ToRosetta().BlockResponse(block), nil
}
func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.BlockResponse, error) {
height, err := c.getHeight(ctx, height)
if err != nil {
return crgtypes.BlockResponse{}, crgerrs.WrapError(crgerrs.ErrBadGateway, err.Error())
}
block, err := c.tmRPC.Block(ctx, height)
if err != nil {
return crgtypes.BlockResponse{}, crgerrs.WrapError(crgerrs.ErrInternal, err.Error())
}
return c.converter.ToRosetta().BlockResponse(block), nil
}
func (c *Client) BlockTransactionsByHash(ctx context.Context, hash string) (crgtypes.BlockTransactionsResponse, error) {
// TODO(fdymylja): use a faster path, by searching the block by hash, instead of doing a double query operation
blockResp, err := c.BlockByHash(ctx, hash)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}
return c.blockTxs(ctx, &blockResp.Block.Index)
}
func (c *Client) BlockTransactionsByHeight(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) {
height, err := c.getHeight(ctx, height)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, crgerrs.WrapError(crgerrs.ErrBadGateway, err.Error())
}
blockTxResp, err := c.blockTxs(ctx, height)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}
return blockTxResp, nil
}
// Coins fetches the existing coins in the application
func (c *Client) coins(ctx context.Context) (sdk.Coins, error) {
var result sdk.Coins
supply, err := c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{})
if err != nil {
return nil, crgerrs.FromGRPCToRosettaError(err)
}
pages := supply.GetPagination().GetTotal()
for i := uint64(0); i < pages; i++ {
// get next key
page := supply.GetPagination()
if page == nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, fmt.Sprintf("error pagination"))
}
nextKey := page.GetNextKey()
supply, err = c.bank.TotalSupply(ctx, &bank.QueryTotalSupplyRequest{Pagination: &query.PageRequest{Key: nextKey}})
if err != nil {
return nil, crgerrs.FromGRPCToRosettaError(err)
}
result = append(result[:0], supply.Supply[:]...)
}
return result, nil
}
func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes []byte) (ops []*rosettatypes.Operation, signers []*rosettatypes.AccountIdentifier, err error) {
switch signed {
case false:
rosTx, err := c.converter.ToRosetta().Tx(txBytes, nil)
if err != nil {
return nil, nil, err
}
return rosTx.Operations, nil, err
default:
ops, signers, err = c.converter.ToRosetta().OpsAndSigners(txBytes)
return
}
}
// GetTx returns a transaction given its hash. For Rosetta we make a synthetic transaction for BeginBlock
//
// and EndBlock to adhere to balance tracking rules.
func (c *Client) GetTx(ctx context.Context, hash string) (*rosettatypes.Transaction, error) {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, fmt.Sprintf("bad tx hash: %s", err))
}
// get tx type and hash
txType, hashBytes := c.converter.ToSDK().HashToTxType(hashBytes)
// construct rosetta tx
switch txType {
// handle begin block hash
case BeginBlockTx:
// get block height by hash
block, err := c.tmRPC.BlockByHash(ctx, hashBytes)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
// get block txs
fullBlock, err := c.blockTxs(ctx, &block.Block.Height)
if err != nil {
return nil, err
}
return fullBlock.Transactions[0], nil
// handle deliver tx hash
case DeliverTxTx:
rawTx, err := c.tmRPC.Tx(ctx, hashBytes, true)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
return c.converter.ToRosetta().Tx(rawTx.Tx, &rawTx.TxResult)
// handle end block hash
case EndBlockTx:
// get block height by hash
block, err := c.tmRPC.BlockByHash(ctx, hashBytes)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
// get block txs
fullBlock, err := c.blockTxs(ctx, &block.Block.Height)
if err != nil {
return nil, err
}
// get last tx
return fullBlock.Transactions[len(fullBlock.Transactions)-1], nil
// unrecognized tx
default:
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, fmt.Sprintf("invalid tx hash provided: %s", hash))
}
}
// GetUnconfirmedTx gets an unconfirmed transaction given its hash
func (c *Client) GetUnconfirmedTx(ctx context.Context, hash string) (*rosettatypes.Transaction, error) {
res, err := c.tmRPC.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")
}
// assert that correct tx length is provided
switch len(hashAsBytes) {
default:
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, fmt.Sprintf("unrecognized tx size: %d", len(hashAsBytes)))
case BeginEndBlockTxSize:
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "endblock and begin block txs cannot be unconfirmed")
case DeliverTxSize:
break
}
// iterate over unconfirmed txs to find the one with matching hash
for _, unconfirmedTx := range res.Txs {
if !bytes.Equal(unconfirmedTx.Hash(), hashAsBytes) {
continue
}
return c.converter.ToRosetta().Tx(unconfirmedTx, nil)
}
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool: "+hash)
}
// Mempool returns the unconfirmed transactions in the mempool
func (c *Client) Mempool(ctx context.Context) ([]*rosettatypes.TransactionIdentifier, error) {
txs, err := c.tmRPC.UnconfirmedTxs(ctx, nil)
if err != nil {
return nil, err
}
return c.converter.ToRosetta().TxIdentifiers(txs.Txs), nil
}
// Peers gets the number of peers
func (c *Client) Peers(ctx context.Context) ([]*rosettatypes.Peer, error) {
netInfo, err := c.tmRPC.NetInfo(ctx)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
return c.converter.ToRosetta().Peers(netInfo.Peers), nil
}
func (c *Client) Status(ctx context.Context) (*rosettatypes.SyncStatus, error) {
status, err := c.tmRPC.Status(ctx)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
return c.converter.ToRosetta().SyncStatus(status), err
}
func (c *Client) PostTx(txBytes []byte) (*rosettatypes.TransactionIdentifier, map[string]interface{}, error) {
// sync ensures it will go through checkTx
res, err := c.tmRPC.BroadcastTxSync(context.Background(), 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.Log),
)
}
return &rosettatypes.TransactionIdentifier{
Hash: fmt.Sprintf("%X", res.Hash),
},
map[string]interface{}{
Log: res.Log,
}, nil
}
// construction endpoints
// ConstructionMetadataFromOptions builds the metadata given the options
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
}
constructionOptions := new(PreprocessOperationsOptionsResponse)
err = constructionOptions.FromMetadata(options)
if err != nil {
return nil, err
}
// if default fees suggestion is enabled and gas limit or price is unset, use default
if c.config.EnableFeeSuggestion {
if constructionOptions.GasLimit <= 0 {
constructionOptions.GasLimit = uint64(c.config.GasToSuggest)
}
if constructionOptions.GasPrice == "" {
denom := c.config.DenomToSuggest
constructionOptions.GasPrice = c.config.GasPrices.AmountOf(denom).String() + denom
}
}
if constructionOptions.GasLimit > 0 && constructionOptions.GasPrice != "" {
gasPrice, err := sdk.ParseDecCoin(constructionOptions.GasPrice)
if err != nil {
return nil, err
}
if !gasPrice.IsPositive() {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "gas price must be positive")
}
}
signersData := make([]*SignerData, len(constructionOptions.ExpectedSigners))
for i, signer := range constructionOptions.ExpectedSigners {
accountInfo, err := c.accountInfo(ctx, signer, nil)
if err != nil {
return nil, err
}
signersData[i] = accountInfo
}
status, err := c.tmRPC.Status(ctx)
if err != nil {
return nil, err
}
metadataResp := ConstructionMetadata{
ChainID: status.NodeInfo.Network,
SignersData: signersData,
GasLimit: constructionOptions.GasLimit,
GasPrice: constructionOptions.GasPrice,
Memo: constructionOptions.Memo,
}
return metadataResp.ToMetadata()
}
func (c *Client) blockTxs(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) {
// get block info
blockInfo, err := c.tmRPC.Block(ctx, height)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}
// get block events
blockResults, err := c.tmRPC.BlockResults(ctx, height)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}
if len(blockResults.TxsResults) != len(blockInfo.Block.Txs) {
// wtf?
panic("block results transactions do now match block transactions")
}
// process begin and end block txs
beginBlockTx := &rosettatypes.Transaction{
TransactionIdentifier: &rosettatypes.TransactionIdentifier{Hash: c.converter.ToRosetta().BeginBlockTxHash(blockInfo.BlockID.Hash)},
Operations: AddOperationIndexes(
nil,
c.converter.ToRosetta().BalanceOps(StatusTxSuccess, blockResults.BeginBlockEvents),
),
}
endBlockTx := &rosettatypes.Transaction{
TransactionIdentifier: &rosettatypes.TransactionIdentifier{Hash: c.converter.ToRosetta().EndBlockTxHash(blockInfo.BlockID.Hash)},
Operations: AddOperationIndexes(
nil,
c.converter.ToRosetta().BalanceOps(StatusTxSuccess, blockResults.EndBlockEvents),
),
}
deliverTx := make([]*rosettatypes.Transaction, len(blockInfo.Block.Txs))
// process normal txs
for i, tx := range blockInfo.Block.Txs {
rosTx, err := c.converter.ToRosetta().Tx(tx, blockResults.TxsResults[i])
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}
deliverTx[i] = rosTx
}
finalTxs := make([]*rosettatypes.Transaction, 0, 2+len(deliverTx))
finalTxs = append(finalTxs, beginBlockTx)
finalTxs = append(finalTxs, deliverTx...)
finalTxs = append(finalTxs, endBlockTx)
return crgtypes.BlockTransactionsResponse{
BlockResponse: c.converter.ToRosetta().BlockResponse(blockInfo),
Transactions: finalTxs,
}, nil
}
func (c *Client) getHeight(ctx context.Context, height *int64) (realHeight *int64, err error) {
if height != nil && *height == -1 {
genesisChunk, err := c.tmRPC.GenesisChunked(ctx, 0)
if err != nil {
return nil, err
}
heightNum, err := extractInitialHeightFromGenesisChunk(genesisChunk.Data)
if err != nil {
return nil, err
}
realHeight = &heightNum
} else {
realHeight = height
}
return
}
var initialHeightRE = regexp.MustCompile(`"initial_height":"(\d+)"`)
func extractInitialHeightFromGenesisChunk(genesisChunk string) (int64, error) {
firstChunk, err := base64.StdEncoding.DecodeString(genesisChunk)
if err != nil {
return 0, err
}
matches := initialHeightRE.FindStringSubmatch(string(firstChunk))
if len(matches) != 2 {
return 0, errors.New("failed to fetch initial_height")
}
heightStr := matches[1]
return strconv.ParseInt(heightStr, 10, 64)
}
+15
View File
@@ -0,0 +1,15 @@
package rosetta
import (
"encoding/base64"
"testing"
"github.com/stretchr/testify/require"
)
func TestRegex(t *testing.T) {
genesisChuck := base64.StdEncoding.EncodeToString([]byte(`"genesis_time":"2021-09-28T09:00:00Z","chain_id":"bombay-12","initial_height":"5900001","consensus_params":{"block":{"max_bytes":"5000000","max_gas":"1000000000","time_iota_ms":"1000"},"evidence":{"max_age_num_blocks":"100000","max_age_duration":"172800000000000","max_bytes":"50000"},"validator":{"pub_key_types":["ed25519"]},"version":{}},"validators":[{"address":"EEA4891F5F8D523A6B4B3EAC84B5C08655A00409","pub_key":{"type":"tendermint/PubKeyEd25519","value":"UX71gTBNumQq42qRd6j/K8XN/y3/HAcuAJxj97utawI="},"power":"60612","name":"BTC.Secure"},{"address":"973F589DE1CC8A54ABE2ABE0E0A4ABF13A9EBAE4","pub_key":{"type":"tendermint/PubKeyEd25519","value":"AmGQvQSAAXzSIscx/6o4rVdRMT9QvairQHaCXsWhY+c="},"power":"835","name":"MoonletWallet"},{"address":"831F402BDA0C9A3F260D4F221780BC22A4C3FB23","pub_key":{"type":"tendermint/PubKeyEd25519","value":"Tw8yKbPNEo113ZNbJJ8joeXokoMdBoazRTwb1NQ77WA="},"power":"102842","name":"BlockNgine"},{"address":"F2683F267D2B4C8714B44D68612DB37A8DD2EED7","pub_key":{"type":"tendermint/PubKeyEd25519","value":"PVE4IcWDE6QEqJSEkx55IDkg5zxBo8tVRzKFMJXYFSQ="},"power":"23200","name":"Luna Station 88"},{"address":"9D2428CBAC68C654BE11BE405344C560E6A0F626","pub_key":{"type":"tendermint/PubKeyEd25519","value":"93hzGmZjPRqOnQkb8BULjqanW3M2p1qIcLVTGkf1Zhk="},"power":"35420","name":"Terra-India"},{"address":"DC9897F22E74BF1B66E2640FA461F785F9BA7627","pub_key":{"type":"tendermint/PubKeyEd25519","value":"mlYb/Dzqwh0YJjfH59OZ4vtp+Zhdq5Oj5MNaGHq1X0E="},"power":"25163","name":"SolidStake"},{"address":"AA1A027E270A2BD7AF154999E6DE9D39C5711DE7","pub_key":{"type":"tendermint/PubKeyEd25519","value":"28z8FlpbC7sR0f1Q8OWFASDNi0FAmdldzetwQ07JJzg="},"power":"34529","name":"syncnode"},{"address":"E548735750DC5015ADDE3B0E7A1294C3B868680B","pub_key":{"type":"tendermint/PubKeyEd25519","value":"BTDtLSKp4wpQrWBwmGvp9isWC5jXaAtX1nrJtsCEWew="},"power":"36082","name":"OneStar"}`))
height, err := extractInitialHeightFromGenesisChunk(genesisChuck)
require.NoError(t, err)
require.Equal(t, height, int64(5900001))
}
+41
View File
@@ -0,0 +1,41 @@
package cmd
import (
"fmt"
"github.com/spf13/cobra"
"cosmossdk.io/tools/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.Codec) *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 {
fmt.Printf("[Rosetta]- Error while creating server: %s", err.Error())
return err
}
return rosettaSrv.Start()
},
}
rosetta.SetFlags(cmd.Flags())
return cmd
}
+22
View 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
}
+269
View File
@@ -0,0 +1,269 @@
package rosetta
import (
"fmt"
"strings"
"time"
"github.com/coinbase/rosetta-sdk-go/types"
"github.com/spf13/pflag"
crg "cosmossdk.io/tools/rosetta/lib/server"
clientflags "github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/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
// DefaultEnableFeeSuggestion indicates to use fee suggestion if `construction/metadata` is called without gas limit and price
DefaultEnableFeeSuggestion = false
// DenomToSuggest defines the default denom for fee suggestion
DenomToSuggest = "uatom"
// DefaultPrices defines the default list of prices to suggest
DefaultPrices = "1uatom,1stake"
)
// configuration flags
const (
FlagBlockchain = "blockchain"
FlagNetwork = "network"
FlagTendermintEndpoint = "tendermint"
FlagGRPCEndpoint = "grpc"
FlagAddr = "addr"
FlagRetries = "retries"
FlagOffline = "offline"
FlagEnableFeeSuggestion = "enable-fee-suggestion"
FlagGasToSuggest = "gas-to-suggest"
FlagDenomToSuggest = "denom-to-suggest"
FlagPricesToSuggest = "prices-to-suggest"
)
// 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
// EnableFeeSuggestion indicates to use fee suggestion when `construction/metadata` is called without gas limit and price
EnableFeeSuggestion bool
// GasToSuggest defines the gas limit for fee suggestion
GasToSuggest int
// DenomToSuggest defines the default denom for fee suggestion
DenomToSuggest string
// GasPrices defines the gas prices for fee suggestion
GasPrices sdk.DecCoins
// 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.GasToSuggest <= 0 {
return fmt.Errorf("gas to suggest must be positive")
}
if c.EnableFeeSuggestion {
found := false
for i := 0; i < c.GasPrices.Len(); i++ {
if c.GasPrices.GetDenomByIndex(i) == c.DenomToSuggest {
found = true
break
}
}
if !found {
return fmt.Errorf("default suggest denom is not found in prices to suggest")
}
}
// 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
}
enableDefaultFeeSuggestion, err := flags.GetBool(FlagEnableFeeSuggestion)
if err != nil {
return nil, err
}
gasToSuggest, err := flags.GetInt(FlagGasToSuggest)
if err != nil {
return nil, err
}
denomToSuggest, err := flags.GetString(FlagDenomToSuggest)
if err != nil {
return nil, err
}
var prices sdk.DecCoins
if enableDefaultFeeSuggestion {
pricesToSuggest, err := flags.GetString(FlagPricesToSuggest)
if err != nil {
return nil, err
}
prices, err = sdk.ParseDecCoins(pricesToSuggest)
if err != nil {
return nil, err
}
}
conf := &Config{
Blockchain: blockchain,
Network: network,
TendermintRPC: tendermintRPC,
GRPCEndpoint: gRPCEndpoint,
Addr: addr,
Retries: retries,
Offline: offline,
EnableFeeSuggestion: enableDefaultFeeSuggestion,
GasToSuggest: gasToSuggest,
DenomToSuggest: denomToSuggest,
GasPrices: prices,
}
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")
flags.Bool(FlagEnableFeeSuggestion, DefaultEnableFeeSuggestion, "enable default fee suggestion")
flags.Int(FlagGasToSuggest, clientflags.DefaultGasLimit, "default gas for fee suggestion")
flags.String(FlagDenomToSuggest, DenomToSuggest, "default denom for fee suggestion")
flags.String(FlagPricesToSuggest, DefaultPrices, "default prices for fee suggestion")
}
+768
View File
@@ -0,0 +1,768 @@
package rosetta
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"cosmossdk.io/math"
"github.com/btcsuite/btcd/btcec/v2"
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
tmcoretypes "github.com/tendermint/tendermint/rpc/core/types"
tmtypes "github.com/tendermint/tendermint/types"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"
)
// Converter is a utility that can be used to convert
// back and forth from rosetta to sdk and tendermint types
// IMPORTANT NOTES:
// - IT SHOULD BE USED ONLY TO DEAL WITH THINGS
// IN A STATELESS WAY! IT SHOULD NEVER INTERACT DIRECTLY
// WITH TENDERMINT RPC AND COSMOS GRPC
//
// - IT SHOULD RETURN cosmos rosetta gateway error types!
type Converter interface {
// ToSDK exposes the methods that convert
// rosetta types to cosmos sdk and tendermint types
ToSDK() ToSDKConverter
// ToRosetta exposes the methods that convert
// sdk and tendermint types to rosetta types
ToRosetta() ToRosettaConverter
}
// ToRosettaConverter is an interface that exposes
// all the functions used to convert sdk and
// tendermint types to rosetta known types
type ToRosettaConverter interface {
// BlockResponse returns a block response given a result block
BlockResponse(block *tmcoretypes.ResultBlock) crgtypes.BlockResponse
// BeginBlockToTx converts the given begin block hash to rosetta transaction hash
BeginBlockTxHash(blockHash []byte) string
// EndBlockTxHash converts the given endblock hash to rosetta transaction hash
EndBlockTxHash(blockHash []byte) string
// Amounts converts sdk.Coins to rosetta.Amounts
Amounts(ownedCoins []sdk.Coin, availableCoins sdk.Coins) []*rosettatypes.Amount
// Ops converts an sdk.Msg to rosetta operations
Ops(status string, msg sdk.Msg) ([]*rosettatypes.Operation, error)
// OpsAndSigners takes raw transaction bytes and returns rosetta operations and the expected signers
OpsAndSigners(txBytes []byte) (ops []*rosettatypes.Operation, signers []*rosettatypes.AccountIdentifier, err error)
// Meta converts an sdk.Msg to rosetta metadata
Meta(msg sdk.Msg) (meta map[string]interface{}, err error)
// SignerData returns account signing data from a queried any account
SignerData(anyAccount *codectypes.Any) (*SignerData, error)
// SigningComponents returns rosetta's components required to build a signable transaction
SigningComponents(tx authsigning.Tx, metadata *ConstructionMetadata, rosPubKeys []*rosettatypes.PublicKey) (txBytes []byte, payloadsToSign []*rosettatypes.SigningPayload, err error)
// Tx converts a tendermint transaction and tx result if provided to a rosetta tx
Tx(rawTx tmtypes.Tx, txResult *abci.ResponseDeliverTx) (*rosettatypes.Transaction, error)
// TxIdentifiers converts a tendermint tx to transaction identifiers
TxIdentifiers(txs []tmtypes.Tx) []*rosettatypes.TransactionIdentifier
// BalanceOps converts events to balance operations
BalanceOps(status string, events []abci.Event) []*rosettatypes.Operation
// SyncStatus converts a tendermint status to sync status
SyncStatus(status *tmcoretypes.ResultStatus) *rosettatypes.SyncStatus
// Peers converts tendermint peers to rosetta
Peers(peers []tmcoretypes.Peer) []*rosettatypes.Peer
}
// ToSDKConverter is an interface that exposes
// all the functions used to convert rosetta types
// to tendermint and sdk types
type ToSDKConverter interface {
// UnsignedTx converts rosetta operations to an unsigned cosmos sdk transactions
UnsignedTx(ops []*rosettatypes.Operation) (tx authsigning.Tx, err error)
// SignedTx adds the provided signatures after decoding the unsigned transaction raw bytes
// and returns the signed tx bytes
SignedTx(txBytes []byte, signatures []*rosettatypes.Signature) (signedTxBytes []byte, err error)
// Msg converts metadata to an sdk message
Msg(meta map[string]interface{}, msg sdk.Msg) (err error)
// HashToTxType returns the transaction type (end block, begin block or deliver tx)
// and the real hash to query in order to get information
HashToTxType(hashBytes []byte) (txType TransactionType, realHash []byte)
// PubKey attempts to convert a rosetta public key to cosmos sdk one
PubKey(pk *rosettatypes.PublicKey) (cryptotypes.PubKey, error)
}
type converter struct {
newTxBuilder func() sdkclient.TxBuilder
txBuilderFromTx func(tx sdk.Tx) (sdkclient.TxBuilder, error)
txDecode sdk.TxDecoder
txEncode sdk.TxEncoder
bytesToSign func(tx authsigning.Tx, signerData authsigning.SignerData) (b []byte, err error)
ir codectypes.InterfaceRegistry
cdc *codec.ProtoCodec
}
func NewConverter(cdc *codec.ProtoCodec, ir codectypes.InterfaceRegistry, cfg sdkclient.TxConfig) Converter {
return converter{
newTxBuilder: cfg.NewTxBuilder,
txBuilderFromTx: cfg.WrapTxBuilder,
txDecode: cfg.TxDecoder(),
txEncode: cfg.TxEncoder(),
bytesToSign: func(tx authsigning.Tx, signerData authsigning.SignerData) (b []byte, err error) {
bytesToSign, err := cfg.SignModeHandler().GetSignBytes(signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON, signerData, tx)
if err != nil {
return nil, err
}
return crypto.Sha256(bytesToSign), nil
},
ir: ir,
cdc: cdc,
}
}
func (c converter) ToSDK() ToSDKConverter {
return c
}
func (c converter) ToRosetta() ToRosettaConverter {
return c
}
// OpsToUnsignedTx returns all the sdk.Msgs given the operations
func (c converter) UnsignedTx(ops []*rosettatypes.Operation) (tx authsigning.Tx, err error) {
builder := c.newTxBuilder()
var msgs []sdk.Msg
for i := 0; i < len(ops); i++ {
op := ops[i]
protoMessage, err := c.ir.Resolve(op.Type)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "operation not found: "+op.Type)
}
msg, ok := protoMessage.(sdk.Msg)
if !ok {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "operation is not a valid supported sdk.Msg: "+op.Type)
}
err = c.Msg(op.Metadata, msg)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
// verify message correctness
if err = msg.ValidateBasic(); err != nil {
return nil, crgerrs.WrapError(
crgerrs.ErrBadArgument,
fmt.Sprintf("validation of operation at index %d failed: %s", op.OperationIdentifier.Index, err),
)
}
signers := msg.GetSigners()
// check if there are enough signers
if len(signers) == 0 {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, fmt.Sprintf("operation at index %d got no signers", op.OperationIdentifier.Index))
}
// append the msg
msgs = append(msgs, msg)
// if there's only one signer then simply continue
if len(signers) == 1 {
continue
}
// after we have got the msg, we need to verify if the message has multiple signers
// if it has got multiple signers, then we need to fetch all the related operations
// which involve the other signers of the msg, we expect to find them in order
// so if the msg is named "v1.test.Send" and it expects 3 signers, the next 3 operations
// must be with the same name "v1.test.Send" and contain the other signers
// then we can just skip their processing
for j := 0; j < len(signers)-1; j++ {
skipOp := ops[i+j] // get the next index
// verify that the operation is equal to the new one
if skipOp.Type != op.Type {
return nil, crgerrs.WrapError(
crgerrs.ErrBadArgument,
fmt.Sprintf("operation at index %d should have had type %s got: %s", i+j, op.Type, skipOp.Type),
)
}
if !reflect.DeepEqual(op.Metadata, skipOp.Metadata) {
return nil, crgerrs.WrapError(
crgerrs.ErrBadArgument,
fmt.Sprintf("operation at index %d should have had metadata equal to %#v, got: %#v", i+j, op.Metadata, skipOp.Metadata))
}
i++ // increase so we skip it
}
}
if err := builder.SetMsgs(msgs...); err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
}
return builder.GetTx(), nil
}
// Msg unmarshals the rosetta metadata to the given sdk.Msg
func (c converter) Msg(meta map[string]interface{}, msg sdk.Msg) error {
metaBytes, err := json.Marshal(meta)
if err != nil {
return err
}
return c.cdc.UnmarshalJSON(metaBytes, msg)
}
func (c converter) Meta(msg sdk.Msg) (meta map[string]interface{}, err error) {
b, err := c.cdc.MarshalJSON(msg)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
err = json.Unmarshal(b, &meta)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
return
}
// Ops will create an operation for each msg signer
// with the message proto name as type, and the raw fields
// as metadata
func (c converter) Ops(status string, msg sdk.Msg) ([]*rosettatypes.Operation, error) {
opName := sdk.MsgTypeURL(msg)
meta, err := c.Meta(msg)
if err != nil {
return nil, err
}
ops := make([]*rosettatypes.Operation, len(msg.GetSigners()))
for i, signer := range msg.GetSigners() {
op := &rosettatypes.Operation{
Type: opName,
Status: &status,
Account: &rosettatypes.AccountIdentifier{Address: signer.String()},
Metadata: meta,
}
ops[i] = op
}
return ops, nil
}
// Tx converts a tendermint raw transaction and its result (if provided) to a rosetta transaction
func (c converter) Tx(rawTx tmtypes.Tx, txResult *abci.ResponseDeliverTx) (*rosettatypes.Transaction, error) {
// decode tx
tx, err := c.txDecode(rawTx)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
// get initial status, as per sdk design, if one msg fails
// the whole TX will be considered failing, so we can't have
// 1 msg being success and 1 msg being reverted
status := StatusTxSuccess
switch txResult {
// if nil, we're probably checking an unconfirmed tx
// or trying to build a new transaction, so status
// is not put inside
case nil:
status = ""
// set the status
default:
if txResult.Code != abci.CodeTypeOK {
status = StatusTxReverted
}
}
// get operations from msgs
msgs := tx.GetMsgs()
var rawTxOps []*rosettatypes.Operation
for _, msg := range msgs {
ops, err := c.Ops(status, msg)
if err != nil {
return nil, err
}
rawTxOps = append(rawTxOps, ops...)
}
// now get balance events from response deliver tx
var balanceOps []*rosettatypes.Operation
// tx result might be nil, in case we're querying an unconfirmed tx from the mempool
if txResult != nil {
balanceOps = c.BalanceOps(StatusTxSuccess, txResult.Events) // force set to success because no events for failed tx
}
// now normalize indexes
totalOps := AddOperationIndexes(rawTxOps, balanceOps)
return &rosettatypes.Transaction{
TransactionIdentifier: &rosettatypes.TransactionIdentifier{Hash: fmt.Sprintf("%X", rawTx.Hash())},
Operations: totalOps,
}, nil
}
func (c converter) BalanceOps(status string, events []abci.Event) []*rosettatypes.Operation {
var ops []*rosettatypes.Operation
for _, e := range events {
balanceOps, ok := sdkEventToBalanceOperations(status, e)
if !ok {
continue
}
ops = append(ops, balanceOps...)
}
return ops
}
// sdkEventToBalanceOperations converts an event to a rosetta balance operation
// it will panic if the event is malformed because it might mean the sdk spec
// has changed and rosetta needs to reflect those changes too.
// The balance operations are multiple, one for each denom.
func sdkEventToBalanceOperations(status string, event abci.Event) (operations []*rosettatypes.Operation, isBalanceEvent bool) {
var (
accountIdentifier string
coinChange sdk.Coins
isSub bool
)
switch event.Type {
default:
return nil, false
case banktypes.EventTypeCoinSpent:
spender := sdk.MustAccAddressFromBech32(string(event.Attributes[0].Value))
coins, err := sdk.ParseCoinsNormalized(string(event.Attributes[1].Value))
if err != nil {
panic(err)
}
isSub = true
coinChange = coins
accountIdentifier = spender.String()
case banktypes.EventTypeCoinReceived:
receiver := sdk.MustAccAddressFromBech32(string(event.Attributes[0].Value))
coins, err := sdk.ParseCoinsNormalized(string(event.Attributes[1].Value))
if err != nil {
panic(err)
}
isSub = false
coinChange = coins
accountIdentifier = receiver.String()
// rosetta does not have the concept of burning coins, so we need to mock
// the burn as a send to an address that cannot be resolved to anything
case banktypes.EventTypeCoinBurn:
coins, err := sdk.ParseCoinsNormalized(string(event.Attributes[1].Value))
if err != nil {
panic(err)
}
coinChange = coins
accountIdentifier = BurnerAddressIdentifier
}
operations = make([]*rosettatypes.Operation, len(coinChange))
for i, coin := range coinChange {
value := coin.Amount.String()
// in case the event is a subtract balance one the rewrite value with
// the negative coin identifier
if isSub {
value = "-" + value
}
op := &rosettatypes.Operation{
Type: event.Type,
Status: &status,
Account: &rosettatypes.AccountIdentifier{Address: accountIdentifier},
Amount: &rosettatypes.Amount{
Value: value,
Currency: &rosettatypes.Currency{
Symbol: coin.Denom,
Decimals: 0,
},
},
}
operations[i] = op
}
return operations, true
}
// Amounts converts []sdk.Coin to rosetta amounts
func (c converter) Amounts(ownedCoins []sdk.Coin, availableCoins sdk.Coins) []*rosettatypes.Amount {
amounts := make([]*rosettatypes.Amount, len(availableCoins))
ownedCoinsMap := make(map[string]math.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] = &rosettatypes.Amount{
Value: sdk.NewInt(0).String(),
Currency: &rosettatypes.Currency{
Symbol: coin.Denom,
},
}
continue
}
amounts[i] = &rosettatypes.Amount{
Value: value.String(),
Currency: &rosettatypes.Currency{
Symbol: coin.Denom,
},
}
}
return amounts
}
// AddOperationIndexes adds the indexes to operations adhering to specific rules:
// operations related to messages will be always before than the balance ones
func AddOperationIndexes(msgOps []*rosettatypes.Operation, balanceOps []*rosettatypes.Operation) (finalOps []*rosettatypes.Operation) {
lenMsgOps := len(msgOps)
lenBalanceOps := len(balanceOps)
finalOps = make([]*rosettatypes.Operation, 0, lenMsgOps+lenBalanceOps)
var currentIndex int64
// add indexes to msg ops
for _, op := range msgOps {
op.OperationIdentifier = &rosettatypes.OperationIdentifier{
Index: currentIndex,
}
finalOps = append(finalOps, op)
currentIndex++
}
// add indexes to balance ops
for _, op := range balanceOps {
op.OperationIdentifier = &rosettatypes.OperationIdentifier{
Index: currentIndex,
}
finalOps = append(finalOps, op)
currentIndex++
}
return finalOps
}
// EndBlockTxHash produces a mock endblock hash that rosetta can query
// for endblock operations, it also serves the purpose of representing
// part of the state changes happening at endblock level (balance ones)
func (c converter) EndBlockTxHash(hash []byte) string {
final := append([]byte{EndBlockHashStart}, hash...)
return fmt.Sprintf("%X", final)
}
// BeginBlockTxHash produces a mock beginblock hash that rosetta can query
// for beginblock operations, it also serves the purpose of representing
// part of the state changes happening at beginblock level (balance ones)
func (c converter) BeginBlockTxHash(hash []byte) string {
final := append([]byte{BeginBlockHashStart}, hash...)
return fmt.Sprintf("%X", final)
}
// HashToTxType takes the provided hash bytes from rosetta and discerns if they are
// a deliver tx type or endblock/begin block hash, returning the real hash afterwards
func (c converter) HashToTxType(hashBytes []byte) (txType TransactionType, realHash []byte) {
switch len(hashBytes) {
case DeliverTxSize:
return DeliverTxTx, hashBytes
case BeginEndBlockTxSize:
switch hashBytes[0] {
case BeginBlockHashStart:
return BeginBlockTx, hashBytes[1:]
case EndBlockHashStart:
return EndBlockTx, hashBytes[1:]
default:
return UnrecognizedTx, nil
}
default:
return UnrecognizedTx, nil
}
}
// StatusToSyncStatus converts a tendermint status to rosetta sync status
func (c converter) SyncStatus(status *tmcoretypes.ResultStatus) *rosettatypes.SyncStatus {
// determine sync status
stage := StatusPeerSynced
if status.SyncInfo.CatchingUp {
stage = StatusPeerSyncing
}
return &rosettatypes.SyncStatus{
CurrentIndex: &status.SyncInfo.LatestBlockHeight,
TargetIndex: nil, // sync info does not allow us to get target height
Stage: &stage,
}
}
// TxIdentifiers converts a tendermint raw transactions into an array of rosetta tx identifiers
func (c converter) TxIdentifiers(txs []tmtypes.Tx) []*rosettatypes.TransactionIdentifier {
converted := make([]*rosettatypes.TransactionIdentifier, len(txs))
for i, tx := range txs {
converted[i] = &rosettatypes.TransactionIdentifier{Hash: fmt.Sprintf("%X", tx.Hash())}
}
return converted
}
// tmResultBlockToRosettaBlockResponse converts a tendermint result block to block response
func (c converter) BlockResponse(block *tmcoretypes.ResultBlock) crgtypes.BlockResponse {
var parentBlock *rosettatypes.BlockIdentifier
switch block.Block.Height {
case 1:
parentBlock = &rosettatypes.BlockIdentifier{
Index: 1,
Hash: fmt.Sprintf("%X", block.BlockID.Hash.Bytes()),
}
default:
parentBlock = &rosettatypes.BlockIdentifier{
Index: block.Block.Height - 1,
Hash: fmt.Sprintf("%X", block.Block.LastBlockID.Hash.Bytes()),
}
}
return crgtypes.BlockResponse{
Block: &rosettatypes.BlockIdentifier{
Index: block.Block.Height,
Hash: block.Block.Hash().String(),
},
ParentBlock: parentBlock,
MillisecondTimestamp: timeToMilliseconds(block.Block.Time),
TxCount: int64(len(block.Block.Txs)),
}
}
// Peers converts tm peers to rosetta peers
func (c converter) Peers(peers []tmcoretypes.Peer) []*rosettatypes.Peer {
converted := make([]*rosettatypes.Peer, len(peers))
for i, peer := range peers {
converted[i] = &rosettatypes.Peer{
PeerID: peer.NodeInfo.Moniker,
Metadata: map[string]interface{}{
"addr": peer.NodeInfo.ListenAddr,
},
}
}
return converted
}
// OpsAndSigners takes transactions bytes and returns the operation, is signed is true it will return
// the account identifiers which have signed the transaction
func (c converter) OpsAndSigners(txBytes []byte) (ops []*rosettatypes.Operation, signers []*rosettatypes.AccountIdentifier, err error) {
rosTx, err := c.ToRosetta().Tx(txBytes, nil)
if err != nil {
return nil, nil, err
}
ops = rosTx.Operations
// get the signers
sdkTx, err := c.txDecode(txBytes)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
txBuilder, err := c.txBuilderFromTx(sdkTx)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
for _, signer := range txBuilder.GetTx().GetSigners() {
signers = append(signers, &rosettatypes.AccountIdentifier{
Address: signer.String(),
})
}
return
}
func (c converter) SignedTx(txBytes []byte, signatures []*rosettatypes.Signature) (signedTxBytes []byte, err error) {
rawTx, err := c.txDecode(txBytes)
if err != nil {
return nil, err
}
txBuilder, err := c.txBuilderFromTx(rawTx)
if err != nil {
return nil, err
}
notSignedSigs, err := txBuilder.GetTx().GetSignaturesV2() //
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
if len(notSignedSigs) != len(signatures) {
return nil, crgerrs.WrapError(
crgerrs.ErrInvalidTransaction,
fmt.Sprintf("expected transaction to have signers data matching the provided signatures: %d <-> %d", len(notSignedSigs), len(signatures)))
}
signedSigs := make([]signing.SignatureV2, len(notSignedSigs))
for i, signature := range signatures {
// TODO(fdymylja): here we should check that the public key matches...
signedSigs[i] = signing.SignatureV2{
PubKey: notSignedSigs[i].PubKey,
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_LEGACY_AMINO_JSON,
Signature: signature.Bytes,
},
Sequence: notSignedSigs[i].Sequence,
}
}
if err = txBuilder.SetSignatures(signedSigs...); err != nil {
return nil, err
}
txBytes, err = c.txEncode(txBuilder.GetTx())
if err != nil {
return nil, err
}
return txBytes, nil
}
func (c converter) PubKey(pubKey *rosettatypes.PublicKey) (cryptotypes.PubKey, error) {
if pubKey.CurveType != "secp256k1" {
return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported")
}
cmp, err := btcec.ParsePubKey(pubKey.Bytes)
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 pk, nil
}
// SigningComponents takes a sdk tx and construction metadata and returns signable components
func (c converter) SigningComponents(tx authsigning.Tx, metadata *ConstructionMetadata, rosPubKeys []*rosettatypes.PublicKey) (txBytes []byte, payloadsToSign []*rosettatypes.SigningPayload, err error) {
// verify metadata correctness
feeAmount, err := sdk.ParseCoinsNormalized(metadata.GasPrice)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
}
signers := tx.GetSigners()
// assert the signers data provided in options are the same as the expected signing accounts
// and that the number of rosetta provided public keys equals the one of the signers
if len(metadata.SignersData) != len(signers) || len(signers) != len(rosPubKeys) {
return nil, nil, crgerrs.WrapError(crgerrs.ErrBadArgument, "signers data and account identifiers mismatch")
}
// add transaction metadata
builder, err := c.txBuilderFromTx(tx)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
builder.SetFeeAmount(feeAmount)
builder.SetGasLimit(metadata.GasLimit)
builder.SetMemo(metadata.Memo)
// build signatures
partialSignatures := make([]signing.SignatureV2, len(signers))
payloadsToSign = make([]*rosettatypes.SigningPayload, len(signers))
// pub key ordering matters, in a future release this check might be relaxed
for i, signer := range signers {
// assert that the provided public keys are correctly ordered
// by checking if the signer at index i matches the pubkey at index
pubKey, err := c.ToSDK().PubKey(rosPubKeys[0])
if err != nil {
return nil, nil, err
}
if !bytes.Equal(pubKey.Address().Bytes(), signer.Bytes()) {
return nil, nil, crgerrs.WrapError(
crgerrs.ErrBadArgument,
fmt.Sprintf("public key at index %d does not match the expected transaction signer: %X <-> %X", i, rosPubKeys[i].Bytes, signer.Bytes()),
)
}
// set the signer data
signerData := authsigning.SignerData{
Address: signer.String(),
ChainID: metadata.ChainID,
AccountNumber: metadata.SignersData[i].AccountNumber,
Sequence: metadata.SignersData[i].Sequence,
PubKey: pubKey,
}
// get signature bytes
signBytes, err := c.bytesToSign(tx, signerData)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrUnknown, fmt.Sprintf("unable to sign tx: %s", err.Error()))
}
// set payload
payloadsToSign[i] = &rosettatypes.SigningPayload{
AccountIdentifier: &rosettatypes.AccountIdentifier{Address: signer.String()},
Bytes: signBytes,
SignatureType: rosettatypes.Ecdsa,
}
// set partial signature
partialSignatures[i] = signing.SignatureV2{
PubKey: pubKey,
Data: &signing.SingleSignatureData{}, // needs to be set to empty otherwise the codec will cry
Sequence: metadata.SignersData[i].Sequence,
}
}
// now we set the partial signatures in the tx
// because we will need to decode the sequence
// information of each account in a stateless way
err = builder.SetSignatures(partialSignatures...)
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
// finally encode the tx
txBytes, err = c.txEncode(builder.GetTx())
if err != nil {
return nil, nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
return txBytes, payloadsToSign, nil
}
// SignerData converts the given any account to signer data
func (c converter) SignerData(anyAccount *codectypes.Any) (*SignerData, error) {
var acc auth.AccountI
err := c.ir.UnpackAny(anyAccount, &acc)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
return &SignerData{
AccountNumber: acc.GetAccountNumber(),
Sequence: acc.GetSequence(),
}, nil
}
+343
View File
@@ -0,0 +1,343 @@
package rosetta_test
import (
"cosmossdk.io/tools/rosetta"
"encoding/hex"
"encoding/json"
"testing"
abci "github.com/tendermint/tendermint/abci/types"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
rosettatypes "github.com/coinbase/rosetta-sdk-go/types"
"github.com/stretchr/testify/suite"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
)
type ConverterTestSuite struct {
suite.Suite
c rosetta.Converter
unsignedTxBytes []byte
unsignedTx authsigning.Tx
ir codectypes.InterfaceRegistry
cdc *codec.ProtoCodec
txConf client.TxConfig
}
func (s *ConverterTestSuite) SetupTest() {
// create an unsigned tx
const unsignedTxHex = "0a8e010a8b010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126b0a2d636f736d6f733134376b6c68377468356a6b6a793361616a736a3272717668747668396d666465333777713567122d636f736d6f73316d6e7670386c786b616679346c787777617175356561653764787630647a36687767797436331a0b0a057374616b651202313612600a4c0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad12020a0012100a0a0a057374616b651201311090a10f1a00"
unsignedTxBytes, err := hex.DecodeString(unsignedTxHex)
s.Require().NoError(err)
s.unsignedTxBytes = unsignedTxBytes
// instantiate converter
cdc, ir := rosetta.MakeCodec()
txConfig := authtx.NewTxConfig(cdc, authtx.DefaultSignModes)
s.c = rosetta.NewConverter(cdc, ir, txConfig)
// add utils
s.ir = ir
s.cdc = cdc
s.txConf = txConfig
// add authsigning tx
sdkTx, err := txConfig.TxDecoder()(unsignedTxBytes)
s.Require().NoError(err)
builder, err := txConfig.WrapTxBuilder(sdkTx)
s.Require().NoError(err)
s.unsignedTx = builder.GetTx()
}
func (s *ConverterTestSuite) TestFromRosettaOpsToTxSuccess() {
addr1 := sdk.AccAddress("address1").String()
addr2 := sdk.AccAddress("address2").String()
msg1 := &bank.MsgSend{
FromAddress: addr1,
ToAddress: addr2,
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
}
msg2 := &bank.MsgSend{
FromAddress: addr2,
ToAddress: addr1,
Amount: sdk.NewCoins(sdk.NewInt64Coin("utxo", 10)),
}
ops, err := s.c.ToRosetta().Ops("", msg1)
s.Require().NoError(err)
ops2, err := s.c.ToRosetta().Ops("", msg2)
s.Require().NoError(err)
ops = append(ops, ops2...)
tx, err := s.c.ToSDK().UnsignedTx(ops)
s.Require().NoError(err)
getMsgs := tx.GetMsgs()
s.Require().Equal(2, len(getMsgs))
s.Require().Equal(getMsgs[0], msg1)
s.Require().Equal(getMsgs[1], msg2)
}
func (s *ConverterTestSuite) TestFromRosettaOpsToTxErrors() {
s.Run("unrecognized op", func() {
op := &rosettatypes.Operation{
Type: "non-existent",
}
_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
s.Run("codec type but not sdk.Msg", func() {
op := &rosettatypes.Operation{
Type: "cosmos.crypto.ed25519.PubKey",
}
_, err := s.c.ToSDK().UnsignedTx([]*rosettatypes.Operation{op})
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
}
func (s *ConverterTestSuite) TestMsgToMetaMetaToMsg() {
msg := &bank.MsgSend{
FromAddress: "addr1",
ToAddress: "addr2",
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
}
msg.Route()
meta, err := s.c.ToRosetta().Meta(msg)
s.Require().NoError(err)
copyMsg := new(bank.MsgSend)
err = s.c.ToSDK().Msg(meta, copyMsg)
s.Require().NoError(err)
s.Require().Equal(msg, copyMsg)
}
func (s *ConverterTestSuite) TestSignedTx() {
s.Run("success", func() {
const payloadsJSON = `[{"hex_bytes":"82ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5","signing_payload":{"address":"cosmos147klh7th5jkjy3aajsj2rqvhtvh9mfde37wq5g","hex_bytes":"ed574d84b095250280de38bf8c254e4a1f8755e5bd300b1f6ca2671688136ecc","account_identifier":{"address":"cosmos147klh7th5jkjy3aajsj2rqvhtvh9mfde37wq5g"},"signature_type":"ecdsa"},"public_key":{"hex_bytes":"034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad","curve_type":"secp256k1"},"signature_type":"ecdsa"}]`
const expectedSignedTxHex = "0a8e010a8b010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e64126b0a2d636f736d6f733134376b6c68377468356a6b6a793361616a736a3272717668747668396d666465333777713567122d636f736d6f73316d6e7670386c786b616679346c787777617175356561653764787630647a36687767797436331a0b0a057374616b651202313612620a4e0a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a21034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad12040a02087f12100a0a0a057374616b651201311090a10f1a4082ccce81a3e4a7272249f0e25c3037a316ee2acce76eb0c25db00ef6634a4d57303b2420edfdb4c9a635ad8851fe5c7a9379b7bc2baadc7d74f7e76ac97459b5"
var payloads []*rosettatypes.Signature
s.Require().NoError(json.Unmarshal([]byte(payloadsJSON), &payloads))
signedTx, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, payloads)
s.Require().NoError(err)
signedTxHex := hex.EncodeToString(signedTx)
s.Require().Equal(signedTxHex, expectedSignedTxHex)
})
s.Run("signers data and signing payloads mismatch", func() {
_, err := s.c.ToSDK().SignedTx(s.unsignedTxBytes, nil)
s.Require().ErrorIs(err, crgerrs.ErrInvalidTransaction)
})
}
func (s *ConverterTestSuite) TestOpsAndSigners() {
s.Run("success", func() {
addr1 := sdk.AccAddress("address1").String()
addr2 := sdk.AccAddress("address2").String()
msg := &bank.MsgSend{
FromAddress: addr1,
ToAddress: addr2,
Amount: sdk.NewCoins(sdk.NewInt64Coin("test", 10)),
}
builder := s.txConf.NewTxBuilder()
s.Require().NoError(builder.SetMsgs(msg))
sdkTx := builder.GetTx()
txBytes, err := s.txConf.TxEncoder()(sdkTx)
s.Require().NoError(err)
ops, signers, err := s.c.ToRosetta().OpsAndSigners(txBytes)
s.Require().NoError(err)
s.Require().Equal(len(ops), len(sdkTx.GetMsgs())*len(sdkTx.GetSigners()), "operation number mismatch")
s.Require().Equal(len(signers), len(sdkTx.GetSigners()), "signers number mismatch")
})
}
func (s *ConverterTestSuite) TestBeginEndBlockAndHashToTxType() {
const deliverTxHex = "5229A67AA008B5C5F1A0AEA77D4DEBE146297A30AAEF01777AF10FAD62DD36AB"
deliverTxBytes, err := hex.DecodeString(deliverTxHex)
s.Require().NoError(err)
endBlockTxHex := s.c.ToRosetta().EndBlockTxHash(deliverTxBytes)
beginBlockTxHex := s.c.ToRosetta().BeginBlockTxHash(deliverTxBytes)
txType, hash := s.c.ToSDK().HashToTxType(deliverTxBytes)
s.Require().Equal(rosetta.DeliverTxTx, txType)
s.Require().Equal(deliverTxBytes, hash, "deliver tx hash should not change")
endBlockTxBytes, err := hex.DecodeString(endBlockTxHex)
s.Require().NoError(err)
txType, hash = s.c.ToSDK().HashToTxType(endBlockTxBytes)
s.Require().Equal(rosetta.EndBlockTx, txType)
s.Require().Equal(deliverTxBytes, hash, "end block tx hash should be equal to a block hash")
beginBlockTxBytes, err := hex.DecodeString(beginBlockTxHex)
s.Require().NoError(err)
txType, hash = s.c.ToSDK().HashToTxType(beginBlockTxBytes)
s.Require().Equal(rosetta.BeginBlockTx, txType)
s.Require().Equal(deliverTxBytes, hash, "begin block tx hash should be equal to a block hash")
txType, hash = s.c.ToSDK().HashToTxType([]byte("invalid"))
s.Require().Equal(rosetta.UnrecognizedTx, txType)
s.Require().Nil(hash)
txType, hash = s.c.ToSDK().HashToTxType(append([]byte{0x3}, deliverTxBytes...))
s.Require().Equal(rosetta.UnrecognizedTx, txType)
s.Require().Nil(hash)
}
func (s *ConverterTestSuite) TestSigningComponents() {
s.Run("invalid metadata coins", func() {
_, _, err := s.c.ToRosetta().SigningComponents(nil, &rosetta.ConstructionMetadata{GasPrice: "invalid"}, nil)
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
s.Run("length signers data does not match signers", func() {
_, _, err := s.c.ToRosetta().SigningComponents(s.unsignedTx, &rosetta.ConstructionMetadata{GasPrice: "10stake"}, nil)
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
s.Run("length pub keys does not match signers", func() {
_, _, err := s.c.ToRosetta().SigningComponents(
s.unsignedTx,
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
{
AccountNumber: 0,
Sequence: 0,
},
}},
nil)
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
s.Run("ros pub key is valid but not the one we expect", func() {
validButUnexpected, err := hex.DecodeString("030da9096a40eb1d6c25f1e26e9cbf8941fc84b8f4dc509c8df5e62a29ab8f2415")
s.Require().NoError(err)
_, _, err = s.c.ToRosetta().SigningComponents(
s.unsignedTx,
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
{
AccountNumber: 0,
Sequence: 0,
},
}},
[]*rosettatypes.PublicKey{
{
Bytes: validButUnexpected,
CurveType: rosettatypes.Secp256k1,
},
})
s.Require().ErrorIs(err, crgerrs.ErrBadArgument)
})
s.Run("success", func() {
expectedPubKey, err := hex.DecodeString("034c92046950c876f4a5cb6c7797d6eeb9ef80d67ced4d45fb62b1e859240ba9ad")
s.Require().NoError(err)
_, _, err = s.c.ToRosetta().SigningComponents(
s.unsignedTx,
&rosetta.ConstructionMetadata{GasPrice: "10stake", SignersData: []*rosetta.SignerData{
{
AccountNumber: 0,
Sequence: 0,
},
}},
[]*rosettatypes.PublicKey{
{
Bytes: expectedPubKey,
CurveType: rosettatypes.Secp256k1,
},
})
s.Require().NoError(err)
})
}
func (s *ConverterTestSuite) TestBalanceOps() {
s.Run("not a balance op", func() {
notBalanceOp := abci.Event{
Type: "not-a-balance-op",
}
ops := s.c.ToRosetta().BalanceOps("", []abci.Event{notBalanceOp})
s.Len(ops, 0, "expected no balance ops")
})
s.Run("multiple balance ops from 2 multicoins event", func() {
subBalanceOp := bank.NewCoinSpentEvent(
sdk.AccAddress("test"),
sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
)
addBalanceOp := bank.NewCoinReceivedEvent(
sdk.AccAddress("test"),
sdk.NewCoins(sdk.NewInt64Coin("test", 10), sdk.NewInt64Coin("utxo", 10)),
)
ops := s.c.ToRosetta().BalanceOps("", []abci.Event{(abci.Event)(subBalanceOp), (abci.Event)(addBalanceOp)})
s.Len(ops, 4)
})
s.Run("spec broken", func() {
s.Require().Panics(func() {
specBrokenSub := abci.Event{
Type: bank.EventTypeCoinSpent,
}
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
})
s.Require().Panics(func() {
specBrokenSub := abci.Event{
Type: bank.EventTypeCoinBurn,
}
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
})
s.Require().Panics(func() {
specBrokenSub := abci.Event{
Type: bank.EventTypeCoinReceived,
}
_ = s.c.ToRosetta().BalanceOps("", []abci.Event{specBrokenSub})
})
})
}
func TestConverterTestSuite(t *testing.T) {
suite.Run(t, new(ConverterTestSuite))
}
+115
View File
@@ -0,0 +1,115 @@
module cosmossdk.io/tools/rosetta
go 1.19
require (
cosmossdk.io/math v1.0.0-beta.3
github.com/btcsuite/btcd/btcec/v2 v2.2.1
github.com/coinbase/rosetta-sdk-go v0.8.1
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20220909113810-4882f933b1a1
github.com/spf13/cobra v1.5.0
github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.0
github.com/tendermint/tendermint v0.37.0-alpha.2
google.golang.org/grpc v1.49.0
)
require (
cosmossdk.io/errors v1.0.0-beta.7 // indirect
filippo.io/edwards25519 v1.0.0-rc.1 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.0 // indirect
github.com/btcsuite/btcd v0.22.1 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/confio/ics23/go v0.7.0 // indirect
github.com/cosmos/btcutil v1.0.4 // indirect
github.com/cosmos/cosmos-proto v1.0.0-alpha7 // indirect
github.com/cosmos/go-bip39 v1.0.0 // indirect
github.com/cosmos/gogoproto v1.4.1 // indirect
github.com/cosmos/gorocksdb v1.2.0 // indirect
github.com/cosmos/iavl v0.19.1 // indirect
github.com/cosmos/ledger-cosmos-go v0.11.1 // indirect
github.com/cosmos/ledger-go v0.9.2 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dustin/go-humanize v1.0.0 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/fsnotify/fsnotify v1.5.4 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.0.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.0.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 // indirect
github.com/inconshreveable/mousetrap v1.0.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/klauspost/compress v1.15.9 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/magiconair/properties v1.8.6 // indirect
github.com/mattn/go-isatty v0.0.16 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.13.0 // indirect
github.com/prometheus/client_model v0.2.0 // indirect
github.com/prometheus/common v0.37.0 // indirect
github.com/prometheus/procfs v0.8.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/spf13/afero v1.8.2 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/viper v1.13.0 // indirect
github.com/subosito/gotenv v1.4.1 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect
github.com/tendermint/btcd v0.1.1 // indirect
github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tendermint/tm-db v0.6.7 // indirect
github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect
golang.org/x/net v0.0.0-20220726230323-06994584191e // indirect
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
// Update to rosetta-sdk-go temporarly to have `check:spec` passing. See https://github.com/coinbase/rosetta-sdk-go/issues/449
replace github.com/coinbase/rosetta-sdk-go => github.com/coinbase/rosetta-sdk-go v0.8.2-0.20221007214527-e03849ba430a
+890
View File
@@ -0,0 +1,890 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
cosmossdk.io/api v0.2.0 h1:7qFwlsTzr71NycTa16ywI9kV7WylzAgd0o7qtqTS4yc=
cosmossdk.io/core v0.2.0 h1:gwDzEzCX4tyegDAgmNOsVaiSKG4ILg7wo2D5KW0bhMQ=
cosmossdk.io/depinject v1.0.0-alpha.2 h1:pVcPnqc8bY2GCHVMj77rk6Ew7uz0K3QhrUHdqoKvO5g=
cosmossdk.io/errors v1.0.0-beta.7 h1:gypHW76pTQGVnHKo6QBkb4yFOJjC+sUGRc5Al3Odj1w=
cosmossdk.io/errors v1.0.0-beta.7/go.mod h1:mz6FQMJRku4bY7aqS/Gwfcmr/ue91roMEKAmDUDpBfE=
cosmossdk.io/math v1.0.0-beta.3 h1:TbZxSopz2LqjJ7aXYfn7nJSb8vNaBklW6BLpcei1qwM=
cosmossdk.io/math v1.0.0-beta.3/go.mod h1:3LYasri3Zna4XpbrTNdKsWmD5fHHkaNAod/mNT9XdE4=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.0.0-rc.1 h1:m0VOOB23frXZvAOK44usCgLWvtsxIoMCTBGJZlpmGfU=
filippo.io/edwards25519 v1.0.0-rc.1/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs=
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4=
github.com/99designs/keyring v1.2.1 h1:tYLp1ULvO7i3fI5vE21ReQuj99QFSs7lGm0xWyJo87o=
github.com/99designs/keyring v1.2.1/go.mod h1:fc+wB5KTk9wQ9sDx0kFXB3A0MaeGHM9AwRStKOQ5vOA=
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=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d h1:nalkkPQcITbvhmL4+C4cKA87NW0tfm3Kl9VXRoPywFg=
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d/go.mod h1:URdX5+vg25ts3aCh8H5IFZybJYKWhJHYMTnf+ULtoC4=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/OneOfOne/xxhash v1.2.2 h1:KMrpdQIwFcEqXDklaen+P1axHaj9BSKzvpUUfnHldSE=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
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/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA=
github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY=
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
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.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y=
github.com/btcsuite/btcd/btcec/v2 v2.2.1 h1:xP60mv8fvp+0khmrN0zTdPC3cNm24rfeE6lh2R/Yv3E=
github.com/btcsuite/btcd/btcec/v2 v2.2.1/go.mod h1:9/CSmJxmuvqzX9Wh2fXMWToLOHhPd11lSPuIupwTkI8=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA=
github.com/btcsuite/btcutil v0.0.0-20180706230648-ab6388e0c60a/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg=
github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ=
github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg=
github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY=
github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc=
github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY=
github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
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/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
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/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/coinbase/rosetta-sdk-go v0.8.2-0.20221007214527-e03849ba430a h1:tAQukG4KWS+9jBQs/lkFfKfONI01QJ1YoxnjzHAEh88=
github.com/coinbase/rosetta-sdk-go v0.8.2-0.20221007214527-e03849ba430a/go.mod h1:tXPR6AIW9ogsH4tYIaFOKOgfJNanCvcyl7JKLd4DToc=
github.com/confio/ics23/go v0.7.0 h1:00d2kukk7sPoHWL4zZBZwzxnpA2pec1NPdwbSokJ5w8=
github.com/confio/ics23/go v0.7.0/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
github.com/cosmos/btcutil v1.0.4 h1:n7C2ngKXo7UC9gNyMNLbzqz7Asuf+7Qv4gnX/rOdQ44=
github.com/cosmos/btcutil v1.0.4/go.mod h1:Ffqc8Hn6TJUdDgHBwIZLtrLQC1KdJ9jGJl/TvgUaxbU=
github.com/cosmos/cosmos-proto v1.0.0-alpha7 h1:yqYUOHF2jopwZh4dVQp3xgqwftE5/2hkrwIV6vkUbO0=
github.com/cosmos/cosmos-proto v1.0.0-alpha7/go.mod h1:dosO4pSAbJF8zWCzCoTWP7nNsjcvSUBQmniFxDg5daw=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20220909113810-4882f933b1a1 h1:3pV8sNiSQUxaTnDF9vt7evhyOqzA9NPsDjtW/tPQ77w=
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20220909113810-4882f933b1a1/go.mod h1:hrFzf2pwhaWdVdeDWgFraviUPf8E0fpn4PN/XGhQ26A=
github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y=
github.com/cosmos/go-bip39 v1.0.0 h1:pcomnQdrdH22njcAatO0yWojsUnCO3y2tNoV1cb6hHY=
github.com/cosmos/go-bip39 v1.0.0/go.mod h1:RNJv0H/pOIVgxw6KS7QeX2a0Uo0aKUlfhZ4xuwvCdJw=
github.com/cosmos/gogoproto v1.4.1 h1:WoyH+0/jbCTzpKNvyav5FL1ZTWsp1im1MxEpJEzKUB8=
github.com/cosmos/gogoproto v1.4.1/go.mod h1:Ac9lzL4vFpBMcptJROQ6dQ4M3pOEK5Z/l0Q9p+LoCr4=
github.com/cosmos/gorocksdb v1.2.0 h1:d0l3jJG8M4hBouIZq0mDUHZ+zjOx044J3nGRskwTb4Y=
github.com/cosmos/gorocksdb v1.2.0/go.mod h1:aaKvKItm514hKfNJpUJXnnOWeBnk2GL4+Qw9NHizILw=
github.com/cosmos/iavl v0.19.1 h1:3gaq9b6SjiB0KBTygRnAvEGml2pQlu1TH8uma5g63Ys=
github.com/cosmos/iavl v0.19.1/go.mod h1:X9PKD3J0iFxdmgNLa7b2LYWdsGd90ToV5cAONApkEPw=
github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4=
github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY=
github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI=
github.com/cosmos/ledger-go v0.9.2/go.mod h1:oZJ2hHAZROdlHiwTg4t7kP+GKIIkBT+o6c9QWFanOyI=
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creachadair/taskgroup v0.3.2 h1:zlfutDS+5XG40AOxcHDSThxKzns8Tnr9jnr6VqkYlkM=
github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0=
github.com/danieljoos/wincred v1.1.2/go.mod h1:GijpziifJoIBfYh+S7BbkdUTU4LfM+QnGqR5Vl2tAx0=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/dgraph-io/badger/v2 v2.2007.4 h1:TRWBQg8UrlUhaFdco01nO2uXwzKS7zd+HVdwV/GHc4o=
github.com/dgraph-io/badger/v2 v2.2007.4/go.mod h1:vSw/ax2qojzbN6eXHIx6KPKtCSHJN/Uz0X0VPruTIhk=
github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E=
github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI=
github.com/dgraph-io/ristretto v0.1.0/go.mod h1:fux0lOrBhrVCJd3lcTHsIJhq1T2rokOu6v9Vcb3Q9ug=
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
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/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 v1.5.0 h1:3j8ya4Z4kMCwT5nXIKFSV84YS+HdqSSO0VsTQxaLAeM=
github.com/dvsekhvalnov/jose2go v1.5.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
github.com/facebookgo/stack v0.0.0-20160209184415-751773369052 h1:JWuenKqqX8nojtoVVWjGfOF9635RETekkoH6Cc9SX0A=
github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4 h1:7HZCaLC5+BZpmbhCOZJ293Lz68O7PYrF2EzeiFMwCLk=
github.com/felixge/httpsnoop v1.0.2 h1:+nS9g82KMXccJ/wp0zyRW9ZBHFETmMGtkk+2CTTrW4o=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
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-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.12.0 h1:e4o3o3IsBfAKQh5Qbbiqyfu97Ku7jrO/JbohvztANh4=
github.com/go-kit/kit v0.12.0/go.mod h1:lHd+EkCZPIwYItmGDDRdhinkzX2A1sj+M9biaEaizzs=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU=
github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0=
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
github.com/gogo/gateway v1.1.0 h1:u0SuhL9+Il+UbjM9VIE3ntfRujKbvVpFvNB4HbjeVQ0=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.0/go.mod h1:Qd/q+1AKNOZr9uGQzbzCmRO6sUih6GTPZv6a1/R87v0=
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4=
github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v0.0.0-20170612174753-24818f796faf/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/orderedcode v0.0.1 h1:UzfcAexk9Vhv8+9pNOgRu41f16lHq725vPwnSeiG/Us=
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=
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
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 v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw=
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU=
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0=
github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/gtank/ristretto255 v0.1.2 h1:JEqUCPA1NvLq5DwYtuzigd7ss8fwbYay9fi4/5uMzcc=
github.com/gtank/ristretto255 v0.1.2/go.mod h1:Ph5OpO6c7xKUGROZfWVLiJf9icMDwUeIvY4OmlYW69o=
github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc=
github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
github.com/hashicorp/go-uuid v1.0.0 h1:RS8zrF7PhGwyNPOtxSClXXj9HA8feRnJzgnI1RJCSnM=
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3 h1:aSVUgRRRtOrZOC1fYmY9gV0e9z/Iu+xNVSASWjsuyGU=
github.com/hdevalence/ed25519consensus v0.0.0-20220222234857-c00d1f31bab3/go.mod h1:5PC6ZNPde8bBqU/ewGZig35+UIZtw9Ytxez8/q5ZyFE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/improbable-eng/grpc-web v0.15.0 h1:BN+7z6uNXZ1tQGcNAuaU1YjsLTApzkjt2tzCixLaUPQ=
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jmhodges/levigo v1.0.0 h1:q5EC36kV79HWeTBWsod3mG11EgStG3qArTKcvlksN1U=
github.com/jmhodges/levigo v1.0.0/go.mod h1:Q6Qx+uH3RAqyK4rFQroq9RL7mdkABMcfhEI+nNuzMJQ=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
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/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.15.9 h1:wKRjX6JRtDdrE9qwa4b/Cip7ACOshUI4smpCQanqjSY=
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
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.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/lib/pq v1.10.6 h1:jbk+ZieJ0D7EVGJYpL9QTz7/YW6UHbmdnZWYyK5cdBs=
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI=
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 h1:QRUSJEgZn2Snx0EmT/QLXibWjSUDjKWvXIT19NBVp94=
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/minio/highwayhash v1.0.2 h1:Aak5U0nElisjDCfPSG79Tgzkn2gl66NxOMspRrKnA/g=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/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=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs=
github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns=
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/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
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=
github.com/onsi/ginkgo v1.14.0 h1:2mOpI4JVVPBN+WQRa0WKH2eXR+Ey+uK4n7Zj0aYpIQA=
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.20.0 h1:8W0cWlwFkflGPLltQvLRB7ZVD5HuP6ng320w2IS245Q=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.0.5 h1:ipoSadvV8oGUjnUbMub59IDPPwfxF694nG/jwbMiyQg=
github.com/pelletier/go-toml/v2 v2.0.5/go.mod h1:OMHamSCAODeSsVrwwvcJOaoN0LIUIaFVNZzmWyNfXas=
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/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/rakyll/statik v0.1.7 h1:OF3QCZUuyPxuGEP7B4ypUa7sB/iHtqOTDYZXGM8KOdQ=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 h1:N/ElC8H3+5XpJzTSTfLsJV/mx9Q9g7kxmchpfZyxgzM=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
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/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k=
github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U=
github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/sasha-s/go-deadlock v0.3.1 h1:sqv7fDNShgjcaxkO0JNcOAlr8B9+cV5Ey/OB71efZx0=
github.com/sasha-s/go-deadlock v0.3.1/go.mod h1:F73l+cr82YSh10GxyRI6qZiCgK64VaZjwesgfQ1/iLM=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.8.2 h1:xehSyVa0YnHWsJ49JFljMpg1HX19V6NDZ1fkm1Xznbo=
github.com/spf13/afero v1.8.2/go.mod h1:CtAatgMJh6bJEIs48Ay/FOnkljP3WeGUG0MC1RfAqwo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU=
github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
github.com/spf13/viper v1.13.0 h1:BWSJ/M+f+3nmdz9bxB+bWX28kkALN2ok11D0rSo8EJU=
github.com/spf13/viper v1.13.0/go.mod h1:Icm2xNL3/8uyh/wFuB1jI7TiTNKp8632Nwegu+zgdYw=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
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/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=
github.com/tendermint/go-amino v0.16.0/go.mod h1:TQU0M1i/ImAo+tYpZi73AU3V/dKeCoMC9Sphe2ZwGME=
github.com/tendermint/tendermint v0.37.0-alpha.2 h1:G82quAWZUDYMTc9G7b/tntFATfm7nm6PrsvjsuMZDPI=
github.com/tendermint/tendermint v0.37.0-alpha.2/go.mod h1:DSnBfCufF48DhIpmU2inmVo8YiEGus6uInwf46Iu01k=
github.com/tendermint/tm-db v0.6.7 h1:fE00Cbl0jayAoqlExN6oyQJ7fR/ZtoVOmvPJ//+shu8=
github.com/tendermint/tm-db v0.6.7/go.mod h1:byQDzFkZV1syXr/ReXS808NxA2xvyuuVgXOJ/088L6I=
github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/zondax/hid v0.9.0/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266 h1:O9XLFXGkVswDFmH9LaYpqu+r/AAFWqr0DL6V00KEVFg=
github.com/zondax/hid v0.9.1-0.20220302062450-5552068d2266/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWpEM=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191206172530-e9b2fee46413/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
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-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-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA=
golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
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/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.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220726230323-06994584191e h1:wOQNKh1uuDGRnmgF0jDxh7ctgGy/3P4rYWQRVJD4/Yg=
golang.org/x/net v0.0.0-20220726230323-06994584191e/go.mod h1:AaygXjzTFtRAg2ttMY5RMuhpJ3cNnI0XpyFJD1iQRSM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-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-20200317015054-43a5402ce75a/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/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/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-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200523222454-059865788121/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-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210819135213-f52c844e1c1c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/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-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/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-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200423170343-7949de9c1215/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b h1:SfSkJugek6xm7lWywqth4r2iTrYLpD8lOj1nMIIhMNM=
google.golang.org/genproto v0.0.0-20220725144611-272f38e5d71b/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.49.0 h1:WTLtQzmQori5FUH25Pq4WT22oCsv8USpQ+F6rqtsmxw=
google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b h1:QRR6H1YWRnHb4Y/HeNFCTJLFVxaq6wH4YuVdsUOr75U=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
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/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
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=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
nhooyr.io/websocket v1.8.6 h1:s+C3xAMLwGmlI31Nyn/eAehUlZPwfYZu2JXM621Q5/k=
pgregory.net/rapid v0.4.7/go.mod h1:UYpPVyjFHzYBGHIxLFoupi8vwk6rXNzRY9OMvVxFIOU=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
+146
View File
@@ -0,0 +1,146 @@
package errors
// errors.go contains all the errors returned by the adapter implementation
// plus some extra utilities to parse those errors
import (
"fmt"
grpccodes "google.golang.org/grpc/codes"
grpcstatus "google.golang.org/grpc/status"
"github.com/coinbase/rosetta-sdk-go/types"
)
// ListErrors lists all the registered errors
func ListErrors() []*types.Error {
return registry.list()
}
// SealAndListErrors seals the registry and lists its errors
func SealAndListErrors() []*types.Error {
registry.seal()
return registry.list()
}
// Error defines an error that can be converted to a Rosetta API error.
type Error struct {
rosErr *types.Error
}
func (e *Error) Error() string {
if e.rosErr == nil {
return ErrUnknown.Error()
}
return fmt.Sprintf("rosetta: (%d) %s", e.rosErr.Code, e.rosErr.Message)
}
// Is implements errors.Is for *Error, two errors are considered equal
// if their error codes are identical
func (e *Error) Is(err error) bool {
// assert it can be casted
rosErr, ok := err.(*Error)
if rosErr == nil || !ok {
return false
}
// check that both *Error's are correctly initialized to avoid dereference panics
if rosErr.rosErr == nil || e.rosErr == nil {
return false
}
// messages are equal if their error codes match
return rosErr.rosErr.Code == e.rosErr.Code
}
// WrapError wraps the rosetta error with additional context
func WrapError(err *Error, msg string) *Error {
return &Error{rosErr: &types.Error{
Code: err.rosErr.Code,
Message: err.rosErr.Message,
Description: err.rosErr.Description,
Retriable: err.rosErr.Retriable,
Details: map[string]interface{}{
"info": msg,
},
}}
}
// ToRosetta attempts to converting an error into a rosetta
// error, if the error cannot be converted it will be parsed as unknown
func ToRosetta(err error) *types.Error {
// if it's null or not known
rosErr, ok := err.(*Error)
if rosErr == nil || !ok {
return ToRosetta(WrapError(ErrUnknown, ErrUnknown.Error()))
}
return rosErr.rosErr
}
// FromGRPCToRosettaError converts a gRPC error to rosetta error
func FromGRPCToRosettaError(err error) *Error {
status, ok := grpcstatus.FromError(err)
if !ok {
return WrapError(ErrUnknown, err.Error())
}
switch status.Code() {
case grpccodes.NotFound:
return WrapError(ErrNotFound, status.Message())
case grpccodes.FailedPrecondition:
return WrapError(ErrBadArgument, status.Message())
case grpccodes.InvalidArgument:
return WrapError(ErrBadArgument, status.Message())
case grpccodes.Internal:
return WrapError(ErrInternal, status.Message())
default:
return WrapError(ErrUnknown, status.Message())
}
}
func RegisterError(code int32, message string, retryable bool, description string) *Error {
e := &Error{rosErr: &types.Error{
Code: code,
Message: message,
Description: &description,
Retriable: retryable,
Details: nil,
}}
registry.add(e)
return e
}
// Default error list
var (
// ErrUnknown defines an unknown error, if this is returned it means
// the library is ignoring an error
ErrUnknown = RegisterError(0, "unknown", false, "unknown error")
// ErrOffline is returned when there is an attempt to query an endpoint in offline mode
ErrOffline = RegisterError(1, "cannot query endpoint in offline mode", false, "returned when querying an online endpoint in offline mode")
// ErrNetworkNotSupported is returned when there is an attempt to query a network which is not supported
ErrNetworkNotSupported = RegisterError(2, "network is not supported", false, "returned when querying a non supported network")
// ErrCodec is returned when there's an error while marshalling or unmarshalling data
ErrCodec = RegisterError(3, "encode/decode error", true, "returned when there are errors encoding or decoding information to and from the node")
// ErrInvalidOperation is returned when the operation supplied to rosetta is not a valid one
ErrInvalidOperation = RegisterError(4, "invalid operation", false, "returned when the operation is not valid")
// ErrInvalidTransaction is returned when the provided hex bytes of a TX are not valid
ErrInvalidTransaction = RegisterError(5, "invalid transaction", false, "returned when the transaction is invalid")
// ErrInvalidAddress is returned when the byte of the address are bad
ErrInvalidAddress = RegisterError(7, "invalid address", false, "returned when the address is malformed")
// ErrInvalidPubkey is returned when the public key is invalid
ErrInvalidPubkey = RegisterError(8, "invalid pubkey", false, "returned when the public key is invalid")
// ErrInterpreting is returned when there are errors interpreting the data from the node, most likely related to breaking changes, version incompatibilities
ErrInterpreting = RegisterError(9, "error interpreting data from node", false, "returned when there are issues interpreting requests or response from node")
ErrInvalidMemo = RegisterError(11, "invalid memo", false, "returned when the memo is invalid")
// ErrBadArgument is returned when the request is malformed
ErrBadArgument = RegisterError(400, "bad argument", false, "request is malformed")
// ErrNotFound is returned when the required object was not found
// retry is set to true because something that is not found now
// might be found later, example: a TX
ErrNotFound = RegisterError(404, "not found", true, "returned when the node does not find what the client is asking for")
// ErrInternal is returned when the node is experiencing internal errors
ErrInternal = RegisterError(500, "internal error", false, "returned when the node experiences internal errors")
// ErrBadGateway is returned when there are problems interacting with the nodes
ErrBadGateway = RegisterError(502, "bad gateway", true, "return when the node is unreachable")
// ErrNotImplemented is returned when a method is not implemented yet
ErrNotImplemented = RegisterError(14, "not implemented", false, "returned when querying an endpoint which is not implemented")
// ErrUnsupportedCurve is returned when the curve specified is not supported
ErrUnsupportedCurve = RegisterError(15, "unsupported curve, expected secp256k1", false, "returned when using an unsupported crypto curve")
)
+68
View File
@@ -0,0 +1,68 @@
package errors
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestRegisterError(t *testing.T) {
var error *Error
// this is the number of errors registered by default in errors.go
registeredErrorsCount := 16
assert.Equal(t, len(registry.list()), registeredErrorsCount)
assert.ElementsMatch(t, registry.list(), ListErrors())
// add a new Error
error = RegisterError(69, "nice!", false, "nice!")
assert.NotNil(t, error)
// now we have a new error
registeredErrorsCount++
assert.Equal(t, len(ListErrors()), registeredErrorsCount)
// re-register an error should not change anything
error = RegisterError(69, "nice!", false, "nice!")
assert.Equal(t, len(ListErrors()), registeredErrorsCount)
// test sealing
assert.Equal(t, registry.sealed, false)
errors := SealAndListErrors()
assert.Equal(t, registry.sealed, true)
assert.Equal(t, len(errors), registeredErrorsCount)
// add a new error on a sealed registry
error = RegisterError(1024, "bytes", false, "bytes")
assert.NotNil(t, error)
}
func TestError_Error(t *testing.T) {
var error *Error
// nil cases
assert.False(t, ErrOffline.Is(error))
error = &Error{}
assert.False(t, ErrOffline.Is(error))
// wrong type
assert.False(t, ErrOffline.Is(&MyError{}))
// test with wrapping an error
error = WrapError(ErrOffline, "offline")
assert.True(t, ErrOffline.Is(error))
// test equality
assert.False(t, ErrOffline.Is(ErrBadGateway))
assert.True(t, ErrBadGateway.Is(ErrBadGateway))
}
func TestToRosetta(t *testing.T) {
var error *Error
// nil case
assert.NotNil(t, ToRosetta(error))
// wrong type
assert.NotNil(t, ToRosetta(&MyError{}))
}
type MyError struct{}
func (e *MyError) Error() string {
return ""
}
func (e *MyError) Is(err error) bool {
return true
}
+48
View File
@@ -0,0 +1,48 @@
package errors
import (
"fmt"
"os"
"sync"
"github.com/coinbase/rosetta-sdk-go/types"
)
type errorRegistry struct {
mu *sync.RWMutex
sealed bool
errors map[int32]*types.Error
}
func (r *errorRegistry) add(err *Error) {
r.mu.Lock()
defer r.mu.Unlock()
if r.sealed {
_, _ = fmt.Fprintln(os.Stderr, "[ROSETTA] WARNING: attempts to register errors after seal will be ignored")
}
if _, ok := r.errors[err.rosErr.Code]; ok {
_, _ = fmt.Fprintln(os.Stderr, "[ROSETTA] WARNING: attempts to register an already registered error will be ignored, code: ", err.rosErr.Code)
}
r.errors[err.rosErr.Code] = err.rosErr
}
func (r errorRegistry) list() []*types.Error {
r.mu.RLock()
defer r.mu.RUnlock()
rosErrs := make([]*types.Error, 0, len(registry.errors))
for _, v := range r.errors {
rosErrs = append(rosErrs, v)
}
return rosErrs
}
func (r *errorRegistry) seal() {
r.mu.Lock()
defer r.mu.Unlock()
r.sealed = true
}
var registry = errorRegistry{
mu: new(sync.RWMutex),
errors: make(map[int32]*types.Error),
}
@@ -0,0 +1,174 @@
package service
import (
"context"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/coinbase/rosetta-sdk-go/types"
"cosmossdk.io/tools/rosetta/lib/errors"
)
// ConstructionCombine Combine creates a network-specific transaction from an unsigned transaction
// and an array of provided signatures. The signed transaction returned from this method will be
// sent to the /construction/submit endpoint by the caller.
func (on OnlineNetwork) ConstructionCombine(ctx context.Context, request *types.ConstructionCombineRequest) (*types.ConstructionCombineResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.UnsignedTransaction)
if err != nil {
return nil, errors.ToRosetta(err)
}
signedTx, err := on.client.SignedTx(ctx, txBytes, request.Signatures)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionCombineResponse{
SignedTransaction: hex.EncodeToString(signedTx),
}, nil
}
// ConstructionDerive Derive returns the AccountIdentifier associated with a public key.
func (on OnlineNetwork) ConstructionDerive(_ context.Context, request *types.ConstructionDeriveRequest) (*types.ConstructionDeriveResponse, *types.Error) {
account, err := on.client.AccountIdentifierFromPublicKey(request.PublicKey)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionDeriveResponse{
AccountIdentifier: account,
Metadata: nil,
}, nil
}
// ConstructionHash TransactionHash returns the network-specific transaction hash for a signed
// transaction.
func (on OnlineNetwork) ConstructionHash(ctx context.Context, request *types.ConstructionHashRequest) (*types.TransactionIdentifierResponse, *types.Error) {
bz, err := hex.DecodeString(request.SignedTransaction)
if err != nil {
return nil, errors.ToRosetta(errors.WrapError(errors.ErrInvalidTransaction, "error decoding tx"))
}
hash := sha256.Sum256(bz)
bzHash := hash[:]
hashString := hex.EncodeToString(bzHash)
return &types.TransactionIdentifierResponse{
TransactionIdentifier: &types.TransactionIdentifier{
Hash: strings.ToUpper(hashString),
},
}, nil
}
// ConstructionMetadata Get any information required to construct a transaction for a specific
// network (i.e. ChainID, Gas, Memo, ...).
func (on OnlineNetwork) ConstructionMetadata(ctx context.Context, request *types.ConstructionMetadataRequest) (*types.ConstructionMetadataResponse, *types.Error) {
metadata, err := on.client.ConstructionMetadataFromOptions(ctx, request.Options)
if err != nil {
return nil, errors.ToRosetta(err)
}
response := &types.ConstructionMetadataResponse{
Metadata: metadata,
}
if metadata["gas_price"] != nil && metadata["gas_limit"] != nil {
gasPrice, ok := metadata["gas_price"].(string)
if !ok {
return nil, errors.ToRosetta(errors.WrapError(errors.ErrBadArgument, "invalid gas_price"))
}
if gasPrice == "" { // gas_price is unset. skip fee suggestion
return response, nil
}
price, err := sdk.ParseDecCoin(gasPrice)
if err != nil {
return nil, errors.ToRosetta(err)
}
gasLimit, ok := metadata["gas_limit"].(float64)
if !ok {
return nil, errors.ToRosetta(errors.WrapError(errors.ErrBadArgument, "invalid gas_limit"))
}
if gasLimit == 0 { // gas_limit is unset. skip fee suggestion
return response, nil
}
gas := sdk.NewIntFromUint64(uint64(gasLimit))
suggestedFee := types.Amount{
Value: strconv.FormatInt(price.Amount.MulInt64(gas.Int64()).Ceil().TruncateInt64(), 10),
Currency: &(types.Currency{
Symbol: price.Denom,
Decimals: 0,
}),
}
response.SuggestedFee = []*types.Amount{&suggestedFee}
}
return response, nil
}
// ConstructionParse Parse is called on both unsigned and signed transactions to understand the
// intent of the formulated transaction. This is run as a sanity check before signing (after
// /construction/payloads) and before broadcast (after /construction/combine).
func (on OnlineNetwork) ConstructionParse(ctx context.Context, request *types.ConstructionParseRequest) (*types.ConstructionParseResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.Transaction)
if err != nil {
err := errors.WrapError(errors.ErrInvalidTransaction, err.Error())
return nil, errors.ToRosetta(err)
}
ops, signers, err := on.client.TxOperationsAndSignersAccountIdentifiers(request.Signed, txBytes)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.ConstructionParseResponse{
Operations: ops,
AccountIdentifierSigners: signers,
Metadata: nil,
}, nil
}
// ConstructionPayloads Payloads is called with an array of operations and the response from
// /construction/metadata. It returns an unsigned transaction blob and a collection of payloads that
// must be signed by particular AccountIdentifiers using a certain SignatureType.
func (on OnlineNetwork) ConstructionPayloads(ctx context.Context, request *types.ConstructionPayloadsRequest) (*types.ConstructionPayloadsResponse, *types.Error) {
payload, err := on.client.ConstructionPayload(ctx, request)
if err != nil {
return nil, errors.ToRosetta(err)
}
return payload, nil
}
// ConstructionPreprocess Preprocess is called prior to /construction/payloads to construct a
// request for any metadata that is needed for transaction construction given (i.e. account nonce).
func (on OnlineNetwork) ConstructionPreprocess(ctx context.Context, request *types.ConstructionPreprocessRequest) (*types.ConstructionPreprocessResponse, *types.Error) {
options, err := on.client.PreprocessOperationsToOptions(ctx, request)
if err != nil {
return nil, errors.ToRosetta(err)
}
return options, nil
}
// ConstructionSubmit Submit a pre-signed transaction to the node. This call does not block on the
// transaction being included in a block. Rather, it returns immediately with an indication of
// whether or not the transaction was included in the mempool.
func (on OnlineNetwork) ConstructionSubmit(ctx context.Context, request *types.ConstructionSubmitRequest) (*types.TransactionIdentifierResponse, *types.Error) {
txBytes, err := hex.DecodeString(request.SignedTransaction)
if err != nil {
return nil, errors.ToRosetta(err)
}
res, meta, err := on.client.PostTx(txBytes)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.TransactionIdentifierResponse{
TransactionIdentifier: res,
Metadata: meta,
}, nil
}
+182
View File
@@ -0,0 +1,182 @@
package service
import (
"context"
"cosmossdk.io/tools/rosetta/lib/errors"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
"github.com/coinbase/rosetta-sdk-go/types"
)
// AccountBalance retrieves the account balance of an address
// rosetta requires us to fetch the block information too
func (on OnlineNetwork) AccountBalance(ctx context.Context, request *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
var (
height int64
block crgtypes.BlockResponse
err error
)
switch {
case request.BlockIdentifier == nil:
syncStatus, err := on.client.Status(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
block, err = on.client.BlockByHeight(ctx, syncStatus.CurrentIndex)
if err != nil {
return nil, errors.ToRosetta(err)
}
case request.BlockIdentifier.Hash != nil:
block, err = on.client.BlockByHash(ctx, *request.BlockIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
height = block.Block.Index
case request.BlockIdentifier.Index != nil:
height = *request.BlockIdentifier.Index
block, err = on.client.BlockByHeight(ctx, &height)
if err != nil {
return nil, errors.ToRosetta(err)
}
}
accountCoins, err := on.client.Balances(ctx, request.AccountIdentifier.Address, &height)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.AccountBalanceResponse{
BlockIdentifier: block.Block,
Balances: accountCoins,
Metadata: nil,
}, nil
}
// Block gets the transactions in the given block
func (on OnlineNetwork) Block(ctx context.Context, request *types.BlockRequest) (*types.BlockResponse, *types.Error) {
var (
blockResponse crgtypes.BlockTransactionsResponse
err error
)
// When fetching data by BlockIdentifier, it may be possible to only specify the index or hash.
// If neither property is specified, it is assumed that the client is making a request at the current block.
switch {
case request.BlockIdentifier == nil: // unlike AccountBalance(), BlockIdentifer is mandatory by spec 1.4.10.
err := errors.WrapError(errors.ErrBadArgument, "block identifier needs to be specified")
return nil, errors.ToRosetta(err)
case request.BlockIdentifier.Hash != nil:
blockResponse, err = on.client.BlockTransactionsByHash(ctx, *request.BlockIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
case request.BlockIdentifier.Index != nil:
blockResponse, err = on.client.BlockTransactionsByHeight(ctx, request.BlockIdentifier.Index)
if err != nil {
return nil, errors.ToRosetta(err)
}
default:
// both empty
blockResponse, err = on.client.BlockTransactionsByHeight(ctx, nil)
if err != nil {
return nil, errors.ToRosetta(err)
}
}
// Both of index and hash can be specified in reuqest, so make sure they are not mismatching.
if request.BlockIdentifier.Index != nil && *request.BlockIdentifier.Index != blockResponse.Block.Index {
err := errors.WrapError(errors.ErrBadArgument, "mismatching index")
return nil, errors.ToRosetta(err)
}
if request.BlockIdentifier.Hash != nil && *request.BlockIdentifier.Hash != blockResponse.Block.Hash {
err := errors.WrapError(errors.ErrBadArgument, "mismatching hash")
return nil, errors.ToRosetta(err)
}
return &types.BlockResponse{
Block: &types.Block{
BlockIdentifier: blockResponse.Block,
ParentBlockIdentifier: blockResponse.ParentBlock,
Timestamp: blockResponse.MillisecondTimestamp,
Transactions: blockResponse.Transactions,
Metadata: nil,
},
OtherTransactions: nil,
}, nil
}
// BlockTransaction gets the given transaction in the specified block, we do not need to check the block itself too
// due to the fact that tendermint achieves instant finality
func (on OnlineNetwork) BlockTransaction(ctx context.Context, request *types.BlockTransactionRequest) (*types.BlockTransactionResponse, *types.Error) {
tx, err := on.client.GetTx(ctx, request.TransactionIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.BlockTransactionResponse{
Transaction: tx,
}, nil
}
// Mempool fetches the transactions contained in the mempool
func (on OnlineNetwork) Mempool(ctx context.Context, _ *types.NetworkRequest) (*types.MempoolResponse, *types.Error) {
txs, err := on.client.Mempool(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.MempoolResponse{
TransactionIdentifiers: txs,
}, nil
}
// MempoolTransaction fetches a single transaction in the mempool
// NOTE: it is not implemented yet
func (on OnlineNetwork) MempoolTransaction(ctx context.Context, request *types.MempoolTransactionRequest) (*types.MempoolTransactionResponse, *types.Error) {
tx, err := on.client.GetUnconfirmedTx(ctx, request.TransactionIdentifier.Hash)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.MempoolTransactionResponse{
Transaction: tx,
}, nil
}
func (on OnlineNetwork) NetworkList(_ context.Context, _ *types.MetadataRequest) (*types.NetworkListResponse, *types.Error) {
return &types.NetworkListResponse{NetworkIdentifiers: []*types.NetworkIdentifier{on.network}}, nil
}
func (on OnlineNetwork) NetworkOptions(_ context.Context, _ *types.NetworkRequest) (*types.NetworkOptionsResponse, *types.Error) {
return on.networkOptions, nil
}
func (on OnlineNetwork) NetworkStatus(ctx context.Context, _ *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
syncStatus, err := on.client.Status(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
block, err := on.client.BlockByHeight(ctx, syncStatus.CurrentIndex)
if err != nil {
return nil, errors.ToRosetta(err)
}
peers, err := on.client.Peers(ctx)
if err != nil {
return nil, errors.ToRosetta(err)
}
return &types.NetworkStatusResponse{
CurrentBlockIdentifier: block.Block,
CurrentBlockTimestamp: block.MillisecondTimestamp,
GenesisBlockIdentifier: on.genesisBlockIdentifier,
OldestBlockIdentifier: nil,
SyncStatus: syncStatus,
Peers: peers,
}, nil
}
@@ -0,0 +1,63 @@
package service
import (
"context"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
)
// NewOffline instantiates the instance of an offline network
// whilst the offline network does not support the DataAPI,
// it supports a subset of the construction API.
func NewOffline(network *types.NetworkIdentifier, client crgtypes.Client) (crgtypes.API, error) {
return OfflineNetwork{
OnlineNetwork{
client: client,
network: network,
networkOptions: networkOptionsFromClient(client, nil),
},
}, nil
}
// OfflineNetwork implements an offline data API
// which is basically a data API that constantly
// returns errors, because it cannot be used if offline
type OfflineNetwork struct {
OnlineNetwork
}
// Implement DataAPI in offline mode, which means no method is available
func (o OfflineNetwork) AccountBalance(_ context.Context, _ *types.AccountBalanceRequest) (*types.AccountBalanceResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) Block(_ context.Context, _ *types.BlockRequest) (*types.BlockResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) BlockTransaction(_ context.Context, _ *types.BlockTransactionRequest) (*types.BlockTransactionResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) Mempool(_ context.Context, _ *types.NetworkRequest) (*types.MempoolResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) MempoolTransaction(_ context.Context, _ *types.MempoolTransactionRequest) (*types.MempoolTransactionResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) NetworkStatus(_ context.Context, _ *types.NetworkRequest) (*types.NetworkStatusResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) ConstructionSubmit(_ context.Context, _ *types.ConstructionSubmitRequest) (*types.TransactionIdentifierResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
func (o OfflineNetwork) ConstructionMetadata(_ context.Context, _ *types.ConstructionMetadataRequest) (*types.ConstructionMetadataResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
@@ -0,0 +1,86 @@
package service
import (
"context"
"fmt"
"os"
"time"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
"github.com/tendermint/tendermint/libs/log"
)
// genesisBlockFetchTimeout defines a timeout to fetch the genesis block
const (
genesisBlockFetchTimeout = 15 * time.Second
genesisHashEnv = "GENESIS_HASH"
)
// NewOnlineNetwork builds a single network adapter.
// It will get the Genesis block on the beginning to avoid calling it everytime.
func NewOnlineNetwork(network *types.NetworkIdentifier, client crgtypes.Client, logger log.Logger) (crgtypes.API, error) {
ctx, cancel := context.WithTimeout(context.Background(), genesisBlockFetchTimeout)
defer cancel()
var genesisHeight int64 = -1 // to use initial_height in genesis.json
block, err := client.BlockByHeight(ctx, &genesisHeight)
if err != nil {
return OnlineNetwork{}, err
}
// Get genesis hash from ENV. It should be set by an external script since is not possible to get
// using tendermint API
genesisHash := os.Getenv(genesisHashEnv)
if genesisHash == "" {
logger.Error(fmt.Sprintf("Genesis hash env '%s' is not properly set!", genesisHashEnv))
}
block.Block.Hash = genesisHash
return OnlineNetwork{
client: client,
network: network,
networkOptions: networkOptionsFromClient(client, block.Block),
genesisBlockIdentifier: block.Block,
}, nil
}
// OnlineNetwork groups together all the components required for the full rosetta implementation
type OnlineNetwork struct {
client crgtypes.Client // used to query cosmos app + tendermint
network *types.NetworkIdentifier // identifies the network, it's static
networkOptions *types.NetworkOptionsResponse // identifies the network options, it's static
genesisBlockIdentifier *types.BlockIdentifier // identifies genesis block, it's static
}
// AccountsCoins - relevant only for UTXO based chain
// see https://www.rosetta-api.org/docs/AccountApi.html#accountcoins
func (o OnlineNetwork) AccountCoins(_ context.Context, _ *types.AccountCoinsRequest) (*types.AccountCoinsResponse, *types.Error) {
return nil, crgerrs.ToRosetta(crgerrs.ErrOffline)
}
// networkOptionsFromClient builds network options given the client
func networkOptionsFromClient(client crgtypes.Client, genesisBlock *types.BlockIdentifier) *types.NetworkOptionsResponse {
var tsi *int64
if genesisBlock != nil {
tsi = &(genesisBlock.Index)
}
return &types.NetworkOptionsResponse{
Version: &types.Version{
RosettaVersion: crgtypes.SpecVersion,
NodeVersion: client.Version(),
},
Allow: &types.Allow{
OperationStatuses: client.OperationStatuses(),
OperationTypes: client.SupportedOperations(),
Errors: crgerrs.SealAndListErrors(),
HistoricalBalanceLookup: true,
TimestampStartIndex: tsi,
},
}
}
+125
View File
@@ -0,0 +1,125 @@
package server
import (
"fmt"
"net/http"
"os"
"time"
assert "github.com/coinbase/rosetta-sdk-go/asserter"
"github.com/coinbase/rosetta-sdk-go/server"
"github.com/coinbase/rosetta-sdk-go/types"
"cosmossdk.io/tools/rosetta/lib/internal/service"
crgtypes "cosmossdk.io/tools/rosetta/lib/types"
"github.com/tendermint/tendermint/libs/log"
)
const (
DefaultRetries = 5
DefaultRetryWait = 5 * time.Second
)
// 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
}
type Server struct {
h http.Handler
addr string
logger log.Logger
}
func (h Server) Start() error {
h.logger.Info(fmt.Sprintf("Rosetta server listening on add %s", h.addr))
return http.ListenAndServe(h.addr, h.h) //nolint:gosec
}
func NewServer(settings Settings) (Server, error) {
asserter, err := assert.NewServer(
settings.Client.SupportedOperations(),
true,
[]*types.NetworkIdentifier{settings.Network},
nil,
false,
"",
)
if err != nil {
return Server{}, fmt.Errorf("cannot build asserter: %w", err)
}
logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
var adapter crgtypes.API
switch settings.Offline {
case true:
adapter, err = newOfflineAdapter(settings)
case false:
adapter, err = newOnlineAdapter(settings, logger)
}
if err != nil {
return Server{}, err
}
h := server.NewRouter(
server.NewAccountAPIController(adapter, asserter),
server.NewBlockAPIController(adapter, asserter),
server.NewNetworkAPIController(adapter, asserter),
server.NewMempoolAPIController(adapter, asserter),
server.NewConstructionAPIController(adapter, asserter),
)
return Server{
h: h,
addr: settings.Listen,
logger: logger,
}, nil
}
func newOfflineAdapter(settings Settings) (crgtypes.API, error) {
if settings.Client == nil {
return nil, fmt.Errorf("client is nil")
}
return service.NewOffline(settings.Network, settings.Client)
}
func newOnlineAdapter(settings Settings, logger log.Logger) (crgtypes.API, error) {
if settings.Client == nil {
return nil, fmt.Errorf("client is nil")
}
if settings.Retries <= 0 {
settings.Retries = DefaultRetries
}
if settings.RetryWait == 0 {
settings.RetryWait = DefaultRetryWait
}
var err error
err = settings.Client.Bootstrap()
if err != nil {
return nil, err
}
for i := 0; i < settings.Retries; i++ {
err = settings.Client.Ready()
if err != nil {
logger.Error(fmt.Sprintf("[Rosetta]- Client is not ready: %v. Retrying ...", err))
time.Sleep(settings.RetryWait)
continue
}
return service.NewOnlineNetwork(settings.Network, settings.Client, logger)
}
return nil, fmt.Errorf("maximum number of retries exceeded, last error: %w", err)
}
+164
View File
@@ -0,0 +1,164 @@
package types
import (
"context"
"github.com/coinbase/rosetta-sdk-go/server"
"github.com/coinbase/rosetta-sdk-go/types"
)
// SpecVersion defines the specification of rosetta
const SpecVersion = ""
// NetworkInformationProvider defines the interface used to provide information regarding
// the network and the version of the cosmos sdk used
type NetworkInformationProvider interface {
// SupportedOperations lists the operations supported by the implementation
SupportedOperations() []string
// OperationStatuses returns the list of statuses supported by the implementation
OperationStatuses() []*types.OperationStatus
// Version returns the version of the node
Version() string
}
// Client defines the API the client implementation should provide.
type Client interface {
// Bootstrap 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)
// BlockByHash gets a block and its transaction at the provided height
BlockByHash(ctx context.Context, hash string) (BlockResponse, error)
// BlockByHeight 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)
// BlockTransactionsByHeight 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 builds metadata map from an option map
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) (resp *types.ConstructionPreprocessResponse, err error)
// AccountIdentifierFromPublicKey returns the account identifier given the public key
AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error)
}
type BlockTransactionsResponse struct {
BlockResponse
Transactions []*types.Transaction
}
type BlockResponse struct {
Block *types.BlockIdentifier
ParentBlock *types.BlockIdentifier
MillisecondTimestamp int64
TxCount int64
}
// API defines the exposed APIs
// if the service is online
type API interface {
DataAPI
ConstructionAPI
}
// DataAPI defines the full data API implementation
type DataAPI interface {
server.NetworkAPIServicer
server.AccountAPIServicer
server.BlockAPIServicer
server.MempoolAPIServicer
}
var _ server.ConstructionAPIServicer = ConstructionAPI(nil)
// ConstructionAPI defines the full construction API with
// the online and offline endpoints
type ConstructionAPI interface {
ConstructionOnlineAPI
ConstructionOfflineAPI
}
// ConstructionOnlineAPI defines the construction methods
// allowed in an online implementation
type ConstructionOnlineAPI interface {
ConstructionMetadata(
context.Context,
*types.ConstructionMetadataRequest,
) (*types.ConstructionMetadataResponse, *types.Error)
ConstructionSubmit(
context.Context,
*types.ConstructionSubmitRequest,
) (*types.TransactionIdentifierResponse, *types.Error)
}
// ConstructionOfflineAPI defines the construction methods
// allowed
type ConstructionOfflineAPI interface {
ConstructionCombine(
context.Context,
*types.ConstructionCombineRequest,
) (*types.ConstructionCombineResponse, *types.Error)
ConstructionDerive(
context.Context,
*types.ConstructionDeriveRequest,
) (*types.ConstructionDeriveResponse, *types.Error)
ConstructionHash(
context.Context,
*types.ConstructionHashRequest,
) (*types.TransactionIdentifierResponse, *types.Error)
ConstructionParse(
context.Context,
*types.ConstructionParseRequest,
) (*types.ConstructionParseResponse, *types.Error)
ConstructionPayloads(
context.Context,
*types.ConstructionPayloadsRequest,
) (*types.ConstructionPayloadsResponse, *types.Error)
ConstructionPreprocess(
context.Context,
*types.ConstructionPreprocessRequest,
) (*types.ConstructionPreprocessResponse, *types.Error)
}
+104
View File
@@ -0,0 +1,104 @@
package rosetta
import (
"crypto/sha256"
)
// statuses
const (
StatusTxSuccess = "Success"
StatusTxReverted = "Reverted"
StatusPeerSynced = "synced"
StatusPeerSyncing = "syncing"
)
// In rosetta all state transitions must be represented as transactions
// since in tendermint begin block and end block are state transitions
// which are not represented as transactions we mock only the balance changes
// happening at those levels as transactions. (check BeginBlockTxHash for more info)
const (
DeliverTxSize = sha256.Size
BeginEndBlockTxSize = DeliverTxSize + 1
EndBlockHashStart = 0x0
BeginBlockHashStart = 0x1
)
const (
// BurnerAddressIdentifier mocks the account identifier of a burner address
// all coins burned in the sdk will be sent to this identifier, which per sdk.AccAddress
// design we will never be able to query (as of now).
// Rosetta does not understand supply contraction.
BurnerAddressIdentifier = "burner"
)
// TransactionType is used to distinguish if a rosetta provided hash
// represents endblock, beginblock or deliver tx
type TransactionType int
const (
UnrecognizedTx TransactionType = iota
BeginBlockTx
EndBlockTx
DeliverTxTx
)
// metadata options
// misc
const (
Log = "log"
)
// ConstructionPreprocessMetadata is used to represent
// the metadata rosetta can provide during preprocess options
type ConstructionPreprocessMetadata struct {
Memo string `json:"memo"`
GasLimit uint64 `json:"gas_limit"`
GasPrice string `json:"gas_price"`
}
func (c *ConstructionPreprocessMetadata) FromMetadata(meta map[string]interface{}) error {
return unmarshalMetadata(meta, c)
}
// PreprocessOperationsOptionsResponse is the structured metadata options returned by the preprocess operations endpoint
type PreprocessOperationsOptionsResponse struct {
ExpectedSigners []string `json:"expected_signers"`
Memo string `json:"memo"`
GasLimit uint64 `json:"gas_limit"`
GasPrice string `json:"gas_price"`
}
func (c PreprocessOperationsOptionsResponse) ToMetadata() (map[string]interface{}, error) {
return marshalMetadata(c)
}
func (c *PreprocessOperationsOptionsResponse) FromMetadata(meta map[string]interface{}) error {
return unmarshalMetadata(meta, c)
}
// SignerData contains information on the signers when the request
// is being created, used to populate the account information
type SignerData struct {
AccountNumber uint64 `json:"account_number"`
Sequence uint64 `json:"sequence"`
}
// ConstructionMetadata are the metadata options used to
// construct a transaction. It is returned by ConstructionMetadataFromOptions
// and fed to ConstructionPayload to process the bytes to sign.
type ConstructionMetadata struct {
ChainID string `json:"chain_id"`
SignersData []*SignerData `json:"signer_data"`
GasLimit uint64 `json:"gas_limit"`
GasPrice string `json:"gas_price"`
Memo string `json:"memo"`
}
func (c ConstructionMetadata) ToMetadata() (map[string]interface{}, error) {
return marshalMetadata(c)
}
func (c *ConstructionMetadata) FromMetadata(meta map[string]interface{}) error {
return unmarshalMetadata(meta, c)
}
+43
View File
@@ -0,0 +1,43 @@
package rosetta
import (
"encoding/json"
"time"
crgerrs "cosmossdk.io/tools/rosetta/lib/errors"
)
// timeToMilliseconds converts time to milliseconds timestamp
func timeToMilliseconds(t time.Time) int64 {
return t.UnixNano() / (int64(time.Millisecond) / int64(time.Nanosecond))
}
// unmarshalMetadata unmarshals the given meta to the target
func unmarshalMetadata(meta map[string]interface{}, target interface{}) error {
b, err := json.Marshal(meta)
if err != nil {
return crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
err = json.Unmarshal(b, target)
if err != nil {
return crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
return nil
}
// marshalMetadata marshals the given interface to map[string]interface{}
func marshalMetadata(o interface{}) (meta map[string]interface{}, err error) {
b, err := json.Marshal(o)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
}
meta = make(map[string]interface{})
err = json.Unmarshal(b, &meta)
if err != nil {
return nil, err
}
return
}