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

[rosetta] implement balance tracking and redo tx construction #8729

Merged
merged 39 commits into from
Mar 11, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
702ea89
change(rosetta): data api block and tx parsing
fdymylja Mar 1, 2021
8f711a1
change(rosetta): finalize data API
fdymylja Mar 1, 2021
ab91ce3
add: converter
fdymylja Mar 3, 2021
b3bf68b
fix: casting error message
fdymylja Mar 3, 2021
593f26d
change: rework construction API to support every possible transaction…
fdymylja Mar 4, 2021
fbdeabc
change: make construction stateless
fdymylja Mar 4, 2021
232d83f
chore: cleanup api
fdymylja Mar 4, 2021
b43553e
chore: cleanup api
fdymylja Mar 4, 2021
e2b1a01
chore: reorder methods declaration
fdymylja Mar 5, 2021
5292379
add: signed tx tests
fdymylja Mar 5, 2021
976f2e2
add: ops and signers test
fdymylja Mar 5, 2021
a489652
fix: begin and endblock tx conversions
fdymylja Mar 5, 2021
18bd9e3
add: begin and endblock invalid tests
fdymylja Mar 5, 2021
a32c005
add: balance and signing components tests
fdymylja Mar 5, 2021
9ad9d28
change: remove staking tests
fdymylja Mar 5, 2021
52be187
Merge branch 'master' into frojdi/rosetta-balance-tracking
fdymylja Mar 5, 2021
7375263
chore: lint
fdymylja Mar 5, 2021
2246c6c
chore: lint
fdymylja Mar 5, 2021
4377a09
revert: makefile rosetta test
fdymylja Mar 5, 2021
a1f82bc
chore: lint again
fdymylja Mar 5, 2021
89de69c
chore: move tests to package based ones
fdymylja Mar 5, 2021
687aef2
chore: lint
fdymylja Mar 5, 2021
8a9a140
chore: lint
fdymylja Mar 5, 2021
bf428f4
chore: cleanup ci
fdymylja Mar 5, 2021
c7da349
Merge branch 'master' into frojdi/rosetta-balance-tracking
Mar 5, 2021
8af9112
Merge branch 'master' into frojdi/rosetta-balance-tracking
Mar 5, 2021
b3f5410
chore: address documentation changes
fdymylja Mar 5, 2021
0bb7b16
chore: address documentation changes
fdymylja Mar 5, 2021
daeb5f0
Merge branch 'master' into frojdi/rosetta-balance-tracking
fdymylja Mar 5, 2021
fe10978
Merge branch 'master' into frojdi/rosetta-balance-tracking
Mar 10, 2021
57e684a
Update docs server/rosetta/client_online.go
fdymylja Mar 11, 2021
0c3bad6
Update docs server/rosetta/client_online.go
fdymylja Mar 11, 2021
d6e1622
cleanup spacing server/rosetta/converter_test.go
fdymylja Mar 11, 2021
f28f18d
revert: baseapp.md
fdymylja Mar 11, 2021
2f7a5c8
add: docs for interface implementation
fdymylja Mar 11, 2021
9fda8a2
remove: converter_test.go utils anonymous type
fdymylja Mar 11, 2021
958fba5
change: set interface name constant
fdymylja Mar 11, 2021
6354cfa
chore: add CHANGELOG.md entry
fdymylja Mar 11, 2021
3e47bb4
Merge branch 'master' into frojdi/rosetta-balance-tracking
Mar 11, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
change(rosetta): finalize data API
  • Loading branch information
fdymylja committed Mar 1, 2021
commit 8f711a1d45b4bdae56516d0d3ddad560f9baae39
39 changes: 34 additions & 5 deletions server/rosetta/client_offline.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import (
"encoding/hex"
"strings"

banktypes "github.com/cosmos/cosmos-sdk/x/bank/types"

"github.com/btcsuite/btcd/btcec"
"github.com/coinbase/rosetta-sdk-go/types"
crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
Expand All @@ -20,11 +22,15 @@ import (
func (c *Client) OperationStatuses() []*types.OperationStatus {
return []*types.OperationStatus{
{
Status: StatusSuccess,
Status: StatusTxSuccess,
Successful: true,
},
{
Status: StatusReverted,
Status: StatusTxReverted,
Successful: false,
},
{
Status: StatusTxUnconfirmed,
Successful: false,
},
}
Expand All @@ -37,17 +43,20 @@ func (c *Client) Version() string {
func (c *Client) SupportedOperations() []string {
var supportedOperations []string
for _, ii := range c.ir.ListImplementations("cosmos.base.v1beta1.Msg") {
resolve, err := c.ir.Resolve(ii)
resolvedMsg, err := c.ir.Resolve(ii)
if err != nil {
continue
}

if _, ok := resolve.(Msg); ok {
if _, ok := resolvedMsg.(Msg); ok {
supportedOperations = append(supportedOperations, strings.TrimLeft(ii, "/"))
}
}

supportedOperations = append(supportedOperations, OperationFee)
supportedOperations = append(
supportedOperations,
OperationFee, banktypes.EventTypeCoinSpent, banktypes.EventTypeCoinReceived,
)

return supportedOperations
}
Expand Down Expand Up @@ -219,3 +228,23 @@ func (c *Client) PreprocessOperationsToOptions(_ context.Context, req *types.Con
OptionGas: gas,
}, nil
}

func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) {
if pubKey.CurveType != "secp256k1" {
return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported")
}

cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256())
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
}

compressedPublicKey := make([]byte, secp256k1.PubKeySize)
copy(compressedPublicKey, cmp.SerializeCompressed())

pk := secp256k1.PubKey{Key: compressedPublicKey}

return &types.AccountIdentifier{
Address: sdk.AccAddress(pk.Address()).String(),
}, nil
}
161 changes: 55 additions & 106 deletions server/rosetta/client_online.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package rosetta
import (
"bytes"
"context"
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
Expand All @@ -12,15 +13,10 @@ import (

abcitypes "github.com/tendermint/tendermint/abci/types"

"github.com/tendermint/btcd/btcec"

"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"

"github.com/coinbase/rosetta-sdk-go/types"
"google.golang.org/grpc/metadata"

"github.com/tendermint/tendermint/rpc/client/http"
tmtypes "github.com/tendermint/tendermint/rpc/core/types"
"google.golang.org/grpc"

crgerrs "github.com/tendermint/cosmos-rosetta-gateway/errors"
Expand All @@ -31,7 +27,6 @@ import (
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
sdk "github.com/cosmos/cosmos-sdk/types"
grpctypes "github.com/cosmos/cosmos-sdk/types/grpc"
authclient "github.com/cosmos/cosmos-sdk/x/auth/client"
authtx "github.com/cosmos/cosmos-sdk/x/auth/tx"
auth "github.com/cosmos/cosmos-sdk/x/auth/types"
bank "github.com/cosmos/cosmos-sdk/x/bank/types"
Expand All @@ -54,27 +49,8 @@ type Client struct {

clientCtx client.Context

version string
}

func (c *Client) AccountIdentifierFromPublicKey(pubKey *types.PublicKey) (*types.AccountIdentifier, error) {
if pubKey.CurveType != "secp256k1" {
return nil, crgerrs.WrapError(crgerrs.ErrUnsupportedCurve, "only secp256k1 supported")
}

cmp, err := btcec.ParsePubKey(pubKey.Bytes, btcec.S256())
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, err.Error())
}

compressedPublicKey := make([]byte, secp256k1.PubKeySize)
copy(compressedPublicKey, cmp.SerializeCompressed())

pk := secp256k1.PubKey{Key: compressedPublicKey}

return &types.AccountIdentifier{
Address: sdk.AccAddress(pk.Address()).String(),
}, nil
txDecoder sdk.TxDecoder
version string
}

// NewClient instantiates a new online servicer
Expand All @@ -87,9 +63,10 @@ func NewClient(cfg *Config) (*Client, error) {
}

return &Client{
config: cfg,
ir: cfg.InterfaceRegistry,
version: fmt.Sprintf("%s/%s", info.AppName, v),
config: cfg,
ir: cfg.InterfaceRegistry,
version: fmt.Sprintf("%s/%s", info.AppName, v),
txDecoder: authtx.NewTxConfig(cfg.Codec, authtx.DefaultSignModes).TxDecoder(),
}, nil
}

Expand Down Expand Up @@ -147,7 +124,7 @@ func (c *Client) BlockByHash(ctx context.Context, hash string) (crgtypes.BlockRe
return crgtypes.BlockResponse{}, err
}

return buildBlockResponse(block), nil
return tmResultBlockToRosettaBlockResponse(block), nil
}

func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.BlockResponse, error) {
Expand All @@ -156,50 +133,26 @@ func (c *Client) BlockByHeight(ctx context.Context, height *int64) (crgtypes.Blo
return crgtypes.BlockResponse{}, err
}

return buildBlockResponse(block), nil
}

func buildBlockResponse(block *tmtypes.ResultBlock) crgtypes.BlockResponse {
return crgtypes.BlockResponse{
Block: TMBlockToRosettaBlockIdentifier(block),
ParentBlock: TMBlockToRosettaParentBlockIdentifier(block),
MillisecondTimestamp: timeToMilliseconds(block.Block.Time),
TxCount: int64(len(block.Block.Txs)),
}
return tmResultBlockToRosettaBlockResponse(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
}

txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}

return crgtypes.BlockTransactionsResponse{
BlockResponse: blockResp,
Transactions: sdkTxsWithHashToRosettaTxs(txs),
}, nil
return c.blockTxs(ctx, &blockResp.Block.Index)
}

func (c *Client) BlockTransactionsByHeight(ctx context.Context, height *int64) (crgtypes.BlockTransactionsResponse, error) {
blockResp, err := c.BlockByHeight(ctx, height)
blockTxResp, err := c.blockTxs(ctx, height)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}

txs, err := c.listTransactionsInBlock(ctx, blockResp.Block.Index)
if err != nil {
return crgtypes.BlockTransactionsResponse{}, err
}

return crgtypes.BlockTransactionsResponse{
BlockResponse: blockResp,
Transactions: sdkTxsWithHashToRosettaTxs(txs),
}, nil
return blockTxResp, nil
}

// Coins fetches the existing coins in the application
Expand All @@ -211,24 +164,9 @@ func (c *Client) coins(ctx context.Context) (sdk.Coins, error) {
return supply.Supply, nil
}

// listTransactionsInBlock returns the list of the transactions in a block given its height
func (c *Client) listTransactionsInBlock(ctx context.Context, height int64) ([]*sdkTxWithHash, error) {
txQuery := fmt.Sprintf(`tx.height=%d`, height)
txList, err := c.clientCtx.Client.TxSearch(ctx, txQuery, true, nil, nil, "")
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}

sdkTxs, err := tmResultTxsToSdkTxsWithHash(c.clientCtx.TxConfig.TxDecoder(), txList.Txs)
if err != nil {
return nil, err
}
return sdkTxs, nil
}

func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes []byte) (ops []*types.Operation, signers []*types.AccountIdentifier, err error) {
txConfig := c.getTxConfig()
rawTx, err := txConfig.TxDecoder()(txBytes)
rawTx, err := c.txDecoder(txBytes)
if err != nil {
return nil, nil, err
}
Expand All @@ -253,22 +191,30 @@ func (c *Client) TxOperationsAndSignersAccountIdentifiers(signed bool, txBytes [
}

// GetTx returns a transaction given its hash
func (c *Client) GetTx(_ context.Context, hash string) (*types.Transaction, error) {
txResp, err := authclient.QueryTx(c.clientCtx, hash)
func (c *Client) GetTx(ctx context.Context, hash string) (*types.Transaction, error) {
hashBytes, err := hex.DecodeString(hash)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
var sdkTx sdk.Tx
err = c.ir.UnpackAny(txResp.Tx, &sdkTx)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrCodec, err.Error())
return nil, crgerrs.WrapError(crgerrs.ErrCodec, fmt.Sprintf("bad tx hash: %s", err))
}

// here we check for the hash length to understand if it is a begin or endblock tx or a standard tendermint tx
switch len(hashBytes) {
case beginEndBlockTxSize:
// verify if it's end or begin block operations we're trying to query
switch hashBytes[0] {
case beginBlockHashStart:
return c.beginBlockTx(ctx, hashBytes[1:])
case endBlockHashStart:
return c.endBlockTx(ctx, hashBytes[1:])
default:
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, fmt.Sprintf("bad begin endblock starting byte: %x", hashBytes[0]))
}
// standard tx...
case sha256.Size:
return c.getTx(ctx, hashBytes)
default:
return nil, crgerrs.WrapError(crgerrs.ErrBadArgument, fmt.Sprintf("invalid tx size: %d", len(hashBytes)))
}
return sdkTxWithHashToOperations(&sdkTxWithHash{
HexHash: txResp.TxHash,
Code: txResp.Code,
Log: txResp.RawLog,
Tx: sdkTx,
}), nil
}

// GetUnconfirmedTx gets an unconfirmed transaction given its hash
Expand All @@ -283,22 +229,25 @@ func (c *Client) GetUnconfirmedTx(ctx context.Context, hash string) (*types.Tran
return nil, crgerrs.WrapError(crgerrs.ErrInterpreting, "invalid hash")
}

for _, tx := range res.Txs {
if bytes.Equal(tx.Hash(), hashAsBytes) {
sdkTx, err := tmTxToSdkTx(c.clientCtx.TxConfig.TxDecoder(), tx)
if err != nil {
return nil, err
}
// 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, fmt.Sprintf("endblock and begin block txs cannot be unconfirmed"))
case deliverTxSize:
break
}

return &types.Transaction{
TransactionIdentifier: TmTxToRosettaTxsIdentifier(tx),
Operations: sdkTxToOperations(sdkTx, false, false),
Metadata: nil,
}, nil
// iterate over unconfirmed txs to find the one with matching hash
for _, unconfirmedTx := range res.Txs {
if !bytes.Equal(unconfirmedTx.Hash(), hashAsBytes) {
continue
}
}

return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool")
return sdkTxToRosettaTx(c.txDecoder, unconfirmedTx, nil)
}
return nil, crgerrs.WrapError(crgerrs.ErrNotFound, "transaction not found in mempool: "+hash)
}

// Mempool returns the unconfirmed transactions in the mempool
Expand All @@ -308,7 +257,7 @@ func (c *Client) Mempool(ctx context.Context) ([]*types.TransactionIdentifier, e
return nil, err
}

return TMTxsToRosettaTxsIdentifiers(txs.Txs), nil
return tmTxsToRosettaTxsIdentifiers(txs.Txs), nil
}

// Peers gets the number of peers
Expand All @@ -317,15 +266,15 @@ func (c *Client) Peers(ctx context.Context) ([]*types.Peer, error) {
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
return TmPeersToRosettaPeers(netInfo.Peers), nil
return tmPeersToRosettaPeers(netInfo.Peers), nil
}

func (c *Client) Status(ctx context.Context) (*types.SyncStatus, error) {
status, err := c.clientCtx.Client.Status(ctx)
if err != nil {
return nil, crgerrs.WrapError(crgerrs.ErrUnknown, err.Error())
}
return TMStatusToRosettaSyncStatus(status), err
return tmStatusToRosettaSyncStatus(status), err
}

func (c *Client) getTxConfig() client.TxConfig {
Expand Down
Loading