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:
@@ -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
|
||||
@@ -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
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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")
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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))
|
||||
}
|
||||
@@ -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
|
||||
@@ -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=
|
||||
@@ -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")
|
||||
)
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user