Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add construction endpoints #7854

Merged
merged 18 commits into from
Nov 16, 2020
62 changes: 47 additions & 15 deletions server/rosetta/cosmos/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ type Client struct {

ir codectypes.InterfaceRegistry

client client.Context
clientCtx client.Context
}

// NewSingle instantiates a single network client
Expand All @@ -86,29 +86,53 @@ func NewSingle(grpcEndpoint, tendermintEndpoint string, optsFunc ...OptionFunc)

authClient := auth.NewQueryClient(grpcConn)
bankClient := bank.NewQueryClient(grpcConn)

// NodeURI and Client are set from here otherwise
// WitNodeURI will require to create a new client
// it's done here because WithNodeURI panics if
// connection to tendermint node fails
clientContext := client.Context{
clientCtx := client.Context{
Client: tmRPC,
NodeURI: tendermintEndpoint,
}
clientContext = clientContext.
clientCtx = clientCtx.
WithJSONMarshaler(opts.cdc).
WithInterfaceRegistry(opts.interfaceRegistry).
WithTxConfig(tx.NewTxConfig(opts.cdc, tx.DefaultSignModes)).
WithAccountRetriever(auth.AccountRetriever{}).
WithBroadcastMode(flags.BroadcastBlock)

return &Client{
auth: authClient,
bank: bankClient,
client: clientContext,
ir: opts.interfaceRegistry,
auth: authClient,
bank: bankClient,
clientCtx: clientCtx,
ir: opts.interfaceRegistry,
}, nil
}

func (c *Client) AccountInfo(ctx context.Context, addr string, height *int64) (auth.AccountI, error) {
// if height is set, send height instruction to account
if height != nil {
strHeight := strconv.FormatInt(*height, 10)
ctx = metadata.AppendToOutgoingContext(ctx, grpctypes.GRPCBlockHeightHeader, strHeight)
}
// retrieve account info
accountInfo, err := c.auth.Account(ctx, &auth.QueryAccountRequest{
Address: addr,
})
if err != nil {
return nil, rosetta.FromGRPCToRosettaError(err)
}
// success
var account auth.AccountI
err = c.ir.UnpackAny(accountInfo.Account, &account)
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrCodec, err.Error())
}

return account, nil
}

func (c *Client) Balances(ctx context.Context, addr string, height *int64) ([]sdk.Coin, error) {
if height != nil {
strHeight := strconv.FormatInt(*height, 10)
Expand All @@ -130,7 +154,7 @@ func (c *Client) BlockByHash(ctx context.Context, hash string) (*tmtypes.ResultB
return nil, nil, rosetta.WrapError(rosetta.ErrBadArgument, fmt.Sprintf("invalid block hash: %s", err))
}

block, err := c.client.Client.BlockByHash(ctx, bHash)
block, err := c.clientCtx.Client.BlockByHash(ctx, bHash)
if err != nil {
return nil, nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error()) // can be either a connection error or bad argument?
}
Expand All @@ -144,7 +168,7 @@ func (c *Client) BlockByHash(ctx context.Context, hash string) (*tmtypes.ResultB

// BlockByHeight returns the block and the transactions contained inside it given its height
func (c *Client) BlockByHeight(ctx context.Context, height *int64) (*tmtypes.ResultBlock, []*rosetta.SdkTxWithHash, error) {
block, err := c.client.Client.Block(ctx, height)
block, err := c.clientCtx.Client.Block(ctx, height)
if err != nil {
return nil, nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}
Expand All @@ -167,12 +191,12 @@ func (c *Client) Coins(ctx context.Context) (sdk.Coins, error) {
// ListTransactionsInBlock returns the list of the transactions in a block given its height
func (c *Client) ListTransactionsInBlock(ctx context.Context, height int64) ([]*rosetta.SdkTxWithHash, error) {
txQuery := fmt.Sprintf(`tx.height=%d`, height)
txList, err := c.client.Client.TxSearch(ctx, txQuery, true, nil, nil, "")
txList, err := c.clientCtx.Client.TxSearch(ctx, txQuery, true, nil, nil, "")
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}

sdkTxs, err := conversion.TmResultTxsToSdkTxs(c.client.TxConfig.TxDecoder(), txList.Txs)
sdkTxs, err := conversion.TmResultTxsToSdkTxs(c.clientCtx.TxConfig.TxDecoder(), txList.Txs)
if err != nil {
return nil, err
}
Expand All @@ -181,7 +205,7 @@ func (c *Client) ListTransactionsInBlock(ctx context.Context, height int64) ([]*

// GetTx returns a transaction given its hash
func (c *Client) GetTx(_ context.Context, hash string) (sdk.Tx, error) {
txResp, err := authclient.QueryTx(c.client, hash)
txResp, err := authclient.QueryTx(c.clientCtx, hash)
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}
Expand All @@ -201,7 +225,7 @@ func (c *Client) GetUnconfirmedTx(_ context.Context, hash string) (sdk.Tx, error

// Mempool returns the unconfirmed transactions in the mempool
func (c *Client) Mempool(ctx context.Context) (*tmtypes.ResultUnconfirmedTxs, error) {
txs, err := c.client.Client.UnconfirmedTxs(ctx, nil)
txs, err := c.clientCtx.Client.UnconfirmedTxs(ctx, nil)
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}
Expand All @@ -210,17 +234,25 @@ func (c *Client) Mempool(ctx context.Context) (*tmtypes.ResultUnconfirmedTxs, er

// Peers gets the number of peers
func (c *Client) Peers(ctx context.Context) ([]tmtypes.Peer, error) {
netInfo, err := c.client.Client.NetInfo(ctx)
netInfo, err := c.clientCtx.Client.NetInfo(ctx)
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}
return netInfo.Peers, nil
}

func (c *Client) Status(ctx context.Context) (*tmtypes.ResultStatus, error) {
status, err := c.client.Client.Status(ctx)
status, err := c.clientCtx.Client.Status(ctx)
if err != nil {
return nil, rosetta.WrapError(rosetta.ErrUnknown, err.Error())
}
return status, err
}

func (c *Client) GetTxConfig() client.TxConfig {
return c.clientCtx.TxConfig
}

func (c *Client) PostTx(txBytes []byte) (res *sdk.TxResponse, err error) {
return c.clientCtx.BroadcastTx(txBytes)
}
41 changes: 39 additions & 2 deletions server/rosetta/cosmos/conversion/data.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ package conversion

import (
"fmt"
"strconv"
"strings"
"time"

"github.com/coinbase/rosetta-sdk-go/types"
Expand Down Expand Up @@ -65,7 +67,7 @@ func ResultTxSearchToTransaction(txs []*rosetta.SdkTxWithHash) []*types.Transact

// SdkTxResponseToOperations converts a tx response to operations
func SdkTxToOperations(tx sdk.Tx, hasError, withoutStatus bool) []*types.Operation {
return toOperations(tx.GetMsgs(), hasError, withoutStatus)
return ToOperations(tx.GetMsgs(), hasError, withoutStatus)
}

// TendermintTxsToTxIdentifiers converts a tendermint raw transaction into a rosetta tx identifier
Expand All @@ -85,7 +87,7 @@ func TendermintBlockToBlockIdentifier(block *tmcoretypes.ResultBlock) *types.Blo
}
}

func toOperations(msgs []sdk.Msg, hasError bool, withoutStatus bool) []*types.Operation {
func ToOperations(msgs []sdk.Msg, hasError bool, withoutStatus bool) []*types.Operation {
var operations []*types.Operation
for i, msg := range msgs {
switch msg.Type() { // nolint
Expand Down Expand Up @@ -132,6 +134,41 @@ func toOperations(msgs []sdk.Msg, hasError bool, withoutStatus bool) []*types.Op
return operations
}

// GetTransferTxDataFromOperations extracts the from and to addresses from a list of operations.
// We assume that it comes formated in the correct way. And that the balance of the sender is the same
// as the receiver operations.
func GetTransferTxDataFromOperations(ops []*types.Operation) (*banktypes.MsgSend, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BuildMsgSendFromOperations could be more accurate.

var (
from, to sdk.AccAddress
sendAmt sdk.Coin
err error
)

for _, op := range ops {
if strings.HasPrefix(op.Amount.Value, "-") {
from, err = sdk.AccAddressFromBech32(op.Account.Address)
if err != nil {
return nil, err
}
} else {
to, err = sdk.AccAddressFromBech32(op.Account.Address)
if err != nil {
return nil, err
}

amount, err := strconv.ParseInt(op.Amount.Value, 10, 64)
if err != nil {
return nil, fmt.Errorf("invalid amount")
}

sendAmt = sdk.NewCoin(op.Amount.Currency.Symbol, sdk.NewInt(amount))
}
}

msg := banktypes.NewMsgSend(from, to, sdk.NewCoins(sendAmt))
return msg, nil
}

// TmPeersToRosettaPeers converts tendermint peers to rosetta ones
func TmPeersToRosettaPeers(peers []tmcoretypes.Peer) []*types.Peer {
converted := make([]*types.Peer, len(peers))
Expand Down
8 changes: 7 additions & 1 deletion server/rosetta/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,13 @@ var (
// ErrUnsupportedCurve is returned when the curve specified is not supported
ErrUnsupportedCurve = NewError(15, "unsupported curve, expected secp256k1", false)
// ErrInvalidPubkey is returned when the public key is invalid
ErrInvalidPubkey = NewError(8, "invalid pubkey", false)
ErrInvalidPubkey = NewError(8, "invalid pubkey", false)
ErrInterpreting = NewError(1, "error interpreting data from node", false)
ErrInvalidAddress = NewError(7, "invalid address", false)
ErrInvalidMemo = NewError(11, "invalid memo", false)
ErrInvalidOperation = NewError(4, "invalid operation", false)
ErrInvalidRequest = NewError(6, "invalid request", false)
ErrInvalidTransaction = NewError(5, "invalid transaction", false)
)

// AllowedErrors lists all the rosetta allowed errors
Expand Down
Loading