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

R4R: Refactor account query using Querier #1377

Merged
merged 3 commits into from
Mar 28, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions app/v0/protocol_v0.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,9 @@ func (p *ProtocolV0) configRouters() {
AddRoute("gov", gov.NewHandler(p.govKeeper)).
AddRoute("service", service.NewHandler(p.serviceKeeper)).
AddRoute("guardian", guardian.NewHandler(p.guardianKeeper))

p.queryRouter.
AddRoute("acc", auth.NewQuerier(p.accountMapper)).
AddRoute("gov", gov.NewQuerier(p.govKeeper)).
AddRoute("stake", stake.NewQuerier(p.StakeKeeper, p.cdc))
}
Expand Down
39 changes: 11 additions & 28 deletions client/bank/lcd/query.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
package lcd

import (
"fmt"
"net/http"

"strings"

"github.com/gorilla/mux"
Expand All @@ -26,33 +24,25 @@ func QueryBalancesRequestHandlerFn(
w.Header().Set("Content-Type", "application/json")
vars := mux.Vars(r)
bech32addr := vars["address"]
cliCtx = cliCtx.WithAccountDecoder(decoder)

addr, err := sdk.AccAddressFromBech32(bech32addr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

// the query will return empty if there is no data for this account
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
if err := cliCtx.EnsureAccountExistsFromAddr(addr); err != nil {
utils.WriteErrorResponse(w, http.StatusNoContent, err.Error())
return
}

// decode the value
account, err := decoder(res)
acc, err := cliCtx.GetAccount(addr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

utils.PostProcessResponse(w, cdc, account.GetCoins(), cliCtx.Indent)
utils.PostProcessResponse(w, cdc, acc.GetCoins(), cliCtx.Indent)
}
}

Expand All @@ -63,33 +53,26 @@ func QueryAccountRequestHandlerFn(storeName string, cdc *codec.Codec,
return func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
bech32addr := vars["address"]
cliCtx = cliCtx.WithAccountDecoder(decoder)

addr, err := sdk.AccAddressFromBech32(bech32addr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

res, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), storeName)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't query account. Error: %s", err.Error()))
if err := cliCtx.EnsureAccountExistsFromAddr(addr); err != nil {
utils.WriteErrorResponse(w, http.StatusNoContent, err.Error())
return
}

// the query will return empty if there is no data for this account
if len(res) == 0 {
w.WriteHeader(http.StatusNoContent)
return
}

// decode the value
account, err := decoder(res)
acc, err := cliCtx.GetAccount(addr)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, fmt.Sprintf("couldn't parse query result. Result: %s. Error: %s", res, err.Error()))
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
}

accountRes, err := bank.ConvertAccountCoin(cliCtx, account)
accountRes, err := bank.ConvertAccountCoin(cliCtx, acc)
if err != nil {
utils.WriteErrorResponse(w, http.StatusInternalServerError, err.Error())
return
Expand Down
29 changes: 18 additions & 11 deletions client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,15 +67,13 @@ func (cliCtx CLIContext) GetAccount(address []byte) (auth.Account, error) {
return nil, errors.New("account decoder required but not provided")
}

res, err := cliCtx.QueryStore(auth.AddressStoreKey(address), cliCtx.AccountStore)
res, err := cliCtx.queryAccount(address)
if err != nil {
return nil, err
} else if len(res) == 0 {
return nil, ErrInvalidAccount(address)
}

account, err := cliCtx.AccDecoder(res)
if err != nil {
var account auth.Account
if err := cliCtx.Codec.UnmarshalJSON(res, &account); err != nil {
return nil, err
}

Expand Down Expand Up @@ -138,16 +136,26 @@ func (cliCtx CLIContext) EnsureAccountExists() error {
// address. Instead of using the context's from name, a direct address is
// given. An error is returned if it does not.
func (cliCtx CLIContext) EnsureAccountExistsFromAddr(addr sdk.AccAddress) error {
accountBytes, err := cliCtx.QueryStore(auth.AddressStoreKey(addr), cliCtx.AccountStore)
_, err := cliCtx.queryAccount(addr)
return err
}

// queryAccount queries an account using custom query endpoint of auth module
// returns an error if result is `null` otherwise account data
func (cliCtx CLIContext) queryAccount(addr sdk.AccAddress) ([]byte, error) {
bz, err := cliCtx.Codec.MarshalJSON(auth.NewQueryAccountParams(addr))
if err != nil {
return err
return nil, err
}

if len(accountBytes) == 0 {
return ErrInvalidAccount(addr)
route := fmt.Sprintf("custom/%s/%s", cliCtx.AccountStore, auth.QueryAccount)

res, err := cliCtx.QueryWithData(route, bz)
if err != nil {
return nil, err
}

return nil
return res, nil
}

// query performs a query from a Tendermint node with the provided store name
Expand Down Expand Up @@ -398,7 +406,6 @@ func (cliCtx CLIContext) GetLatestHeight() (int64, error) {
return status.SyncInfo.LatestBlockHeight, nil
}


func (cliCtx CLIContext) NumUnconfirmedTxs() (*ctypes.ResultUnconfirmedTxs, error) {
client := &http.Client{}
url := strings.Replace(cliCtx.NodeURI, "tcp", "http", 1)
Expand Down
12 changes: 1 addition & 11 deletions modules/auth/account_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,10 @@ import (

"github.com/stretchr/testify/require"

"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"

codec "github.com/irisnet/irishub/codec"
"github.com/irisnet/irishub/codec"
sdk "github.com/irisnet/irishub/types"
)

func keyPubAddr() (crypto.PrivKey, crypto.PubKey, sdk.AccAddress) {
key := ed25519.GenPrivKey()
pub := key.PubKey()
addr := sdk.AccAddress(pub.Address())
return key, pub, addr
}

func TestBaseAddressPubKey(t *testing.T) {
_, pub1, addr1 := keyPubAddr()
_, pub2, addr2 := keyPubAddr()
Expand Down
101 changes: 4 additions & 97 deletions modules/auth/ante_test.go
Original file line number Diff line number Diff line change
@@ -1,113 +1,20 @@
package auth

import (
"fmt"
"testing"

"github.com/irisnet/irishub/codec"
"github.com/irisnet/irishub/modules/params"
sdk "github.com/irisnet/irishub/types"
"github.com/stretchr/testify/require"
abci "github.com/tendermint/tendermint/abci/types"
"github.com/tendermint/tendermint/crypto"
"github.com/tendermint/tendermint/crypto/ed25519"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/tendermint/tendermint/crypto/secp256k1"
"github.com/tendermint/tendermint/libs/log"
"github.com/tendermint/tendermint/crypto/multisig"
"github.com/irisnet/irishub/codec"
"github.com/irisnet/irishub/modules/params"
)

func newTestMsg(addrs ...sdk.AccAddress) *sdk.TestMsg {
return sdk.NewTestMsg(addrs...)
}

func newStdFee() StdFee {
return NewStdFee(5000,
sdk.NewInt64Coin("atom", 150),
)
}

// coins to more than cover the fee
func newCoins() sdk.Coins {
return sdk.Coins{
sdk.NewInt64Coin("atom", 10000000),
}
}

// generate a priv key and return it with its address
func privAndAddr() (crypto.PrivKey, sdk.AccAddress) {
priv := ed25519.GenPrivKey()
addr := sdk.AccAddress(priv.PubKey().Address())
return priv, addr
}

// run the tx through the anteHandler and ensure its valid
func checkValidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool) {
_, result, abort := anteHandler(ctx, tx, simulate)
require.False(t, abort)
require.Equal(t, sdk.CodeOK, result.Code)
require.True(t, result.IsOK())
}

// run the tx through the anteHandler and ensure it fails with the given code
func checkInvalidTx(t *testing.T, anteHandler sdk.AnteHandler, ctx sdk.Context, tx sdk.Tx, simulate bool, code sdk.CodeType) {
newCtx, result, abort := anteHandler(ctx, tx, simulate)
require.True(t, abort)
require.Equal(t, code, result.Code, fmt.Sprintf("Expected %v, got %v", code, result))
require.Equal(t, sdk.CodespaceRoot, result.Codespace)

if code == sdk.CodeOutOfGas {
stdTx, ok := tx.(StdTx)
require.True(t, ok, "tx must be in form auth.StdTx")
// GasWanted set correctly
require.Equal(t, stdTx.Fee.Gas, result.GasWanted, "Gas wanted not set correctly")
require.True(t, result.GasUsed > result.GasWanted, "GasUsed not greated than GasWanted")
// Check that context is set correctly
require.Equal(t, result.GasUsed, newCtx.GasMeter().GasConsumed(), "Context not updated correctly")
}
}

func newTestTx(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee) sdk.Tx {
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, "")
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, "")
return tx
}

func newTestTxWithMemo(ctx sdk.Context, msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, memo string) sdk.Tx {
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
signBytes := StdSignBytes(ctx.ChainID(), accNums[i], seqs[i], fee, msgs, memo)
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, memo)
return tx
}

// All signers sign over the same StdSignDoc. Should always create invalid signatures
func newTestTxWithSignBytes(msgs []sdk.Msg, privs []crypto.PrivKey, accNums []uint64, seqs []uint64, fee StdFee, signBytes []byte, memo string) sdk.Tx {
sigs := make([]StdSignature, len(privs))
for i, priv := range privs {
sig, err := priv.Sign(signBytes)
if err != nil {
panic(err)
}
sigs[i] = StdSignature{PubKey: priv.PubKey(), Signature: sig, AccountNumber: accNums[i], Sequence: seqs[i]}
}
tx := NewStdTx(msgs, fee, sigs, memo)
return tx
}

// Test various error cases in the AnteHandler control flow.
func TestAnteHandlerSigErrors(t *testing.T) {
// setup
Expand Down Expand Up @@ -788,4 +695,4 @@ func TestAnteHandlerSigLimitExceeded(t *testing.T) {
[]uint64{0, 0, 0, 0, 0, 0, 0, 0}, []uint64{0, 0, 0, 0, 0, 0, 0, 0}
tx = newTestTx(ctx, msgs, privs, accnums, seqs, fee)
checkInvalidTx(t, anteHandler, ctx, tx, false, sdk.CodeTooManySignatures)
}
}
3 changes: 1 addition & 2 deletions modules/auth/keeper.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package auth
import (
"fmt"

codec "github.com/irisnet/irishub/codec"
"github.com/irisnet/irishub/codec"
sdk "github.com/irisnet/irishub/types"
"github.com/tendermint/tendermint/crypto"
)
Expand All @@ -22,7 +22,6 @@ var (
// This AccountKeeper encodes/decodes accounts using the
// go-amino (binary) encoding/decoding library.
type AccountKeeper struct {

// The (unexposed) key used to access the store from the Context.
key sdk.StoreKey

Expand Down
57 changes: 57 additions & 0 deletions modules/auth/querier.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package auth

import (
"fmt"

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

"github.com/irisnet/irishub/codec"
sdk "github.com/irisnet/irishub/types"
)

// query endpoints supported by the auth Querier
const (
QueryAccount = "account"
)

// creates a querier for auth REST endpoints
func NewQuerier(keeper AccountKeeper) sdk.Querier {
return func(ctx sdk.Context, path []string, req abci.RequestQuery) ([]byte, sdk.Error) {
switch path[0] {
case QueryAccount:
return queryAccount(ctx, req, keeper)
default:
return nil, sdk.ErrUnknownRequest("unknown auth query endpoint")
}
}
}

// defines the params for query: "custom/acc/account"
type QueryAccountParams struct {
Address sdk.AccAddress
}

func NewQueryAccountParams(addr sdk.AccAddress) QueryAccountParams {
return QueryAccountParams{
Address: addr,
}
}

func queryAccount(ctx sdk.Context, req abci.RequestQuery, keeper AccountKeeper) ([]byte, sdk.Error) {
var params QueryAccountParams
if err := keeper.cdc.UnmarshalJSON(req.Data, &params); err != nil {
return nil, sdk.ErrInternal(fmt.Sprintf("failed to parse params: %s", err))
}

account := keeper.GetAccount(ctx, params.Address)
if account == nil {
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", params.Address))
}

bz, err := codec.MarshalJSONIndent(keeper.cdc, account)
if err != nil {
return nil, sdk.ErrInternal(sdk.AppendMsgToErr("could not marshal result to JSON", err.Error()))
}

return bz, nil
}
Loading