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

Bianjie/lcd implementation #2118

Closed
Closed
Show file tree
Hide file tree
Changes from 21 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
6aa8309
Merge pull request #8 from cosmos/master
Jul 11, 2018
21e36b5
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk
Jul 12, 2018
22161af
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk
Jul 17, 2018
4e61859
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk
Jul 18, 2018
170daf3
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk
Jul 27, 2018
34548db
lcd implemeation
Aug 16, 2018
eccb889
Add golang dependency for swagger
Aug 16, 2018
2047673
Add key rest api
Aug 16, 2018
85f47d2
Add stake query api to lcd swagger
Aug 16, 2018
8e0ca8d
Merge branch 'master' of https://github.com/cosmos/cosmos-sdk
wukongcheng Aug 18, 2018
60acd5c
Merge branch 'bianjie/lcd' of https://github.com/cosmos/cosmos-sdk in…
wukongcheng Aug 18, 2018
187dda4
Merge branch 'bianjie/lcd' of https://github.com/cosmos/cosmos-sdk in…
Aug 18, 2018
22495e9
Merge pull request #58 from HaoyangLiu/haoyang/lcd-implementation
wukongcheng Aug 20, 2018
26af936
Refactor code according to lint result
Aug 21, 2018
4a0d1c5
Merge pull request #63 from HaoyangLiu/haoyang/lcd-implementation
wukongcheng Aug 21, 2018
4607ca9
Refactor comment and fix test failure
Aug 21, 2018
7528534
Add lcd test for swagger lcd
Aug 21, 2018
473fe21
Fix for test lint warnning
Aug 21, 2018
95e367a
Refactor comment
Aug 21, 2018
9d944b0
Add new test for lcd with swagger
Aug 22, 2018
fc40419
Merge pull request #66 from HaoyangLiu/haoyang/lcd-implementation
wukongcheng Aug 22, 2018
f266472
Refactor lcd according to code review
Aug 23, 2018
6cdf21d
Refactor code according to code review
Aug 24, 2018
79ec713
Merge pull request #73 from HaoyangLiu/haoyang/lcd-implementation-new
wukongcheng Aug 24, 2018
9409e73
Fix errors in test_cover and test_lint
Aug 24, 2018
9ff57af
Merge pull request #77 from HaoyangLiu/haoyang/lcd-implementation-new
wukongcheng Aug 24, 2018
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
18 changes: 18 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Gopkg.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@
name = "github.com/stretchr/testify"
version = "=1.2.1"

[[constraint]]
name = "github.com/swaggo/gin-swagger"
version = "=v1.0.0"

[[constraint]]
name = "github.com/swaggo/swag"
version = "=v1.3.2"

[[override]]
name = "github.com/tendermint/go-amino"
version = "=0.10.1"
Expand Down
15 changes: 15 additions & 0 deletions client/context/context.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/spf13/viper"

rpcclient "github.com/tendermint/tendermint/rpc/client"
tendermintLite"github.com/tendermint/tendermint/lite"
)

const ctxAccStoreName = "acc"
Expand All @@ -30,6 +31,8 @@ type CLIContext struct {
Async bool
JSON bool
PrintResponse bool
Cert tendermintLite.Certifier
Copy link
Collaborator

Choose a reason for hiding this comment

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

Certifier

ClientMgr *ClientManager
Copy link
Collaborator

Choose a reason for hiding this comment

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

ClientManager

}

// NewCLIContext returns a new initialized CLIContext with parameters from the
Expand Down Expand Up @@ -113,3 +116,15 @@ func (ctx CLIContext) WithUseLedger(useLedger bool) CLIContext {
ctx.UseLedger = useLedger
return ctx
}

// WithCert - return a copy of the context with an updated Cert
Copy link
Collaborator

Choose a reason for hiding this comment

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

Use Tendermint Lint to avoid verbose comments. You won't need to add the function name at the beginning of the comment with it

func (ctx CLIContext) WithCert(cert tendermintLite.Certifier) CLIContext {
ctx.Cert = cert
Copy link
Collaborator

Choose a reason for hiding this comment

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

change ctx.Cert to ctx.Certifier

return ctx
}

// WithClientMgr - return a copy of the context with an updated ClientMgr
func (ctx CLIContext) WithClientMgr(clientMgr *ClientManager) CLIContext {
ctx.ClientMgr = clientMgr
Copy link
Collaborator

@fedekunze fedekunze Aug 22, 2018

Choose a reason for hiding this comment

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

change ctx.ClientMgr to ctx.ClientManager

return ctx
}
46 changes: 46 additions & 0 deletions client/context/loadbalancing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package context

import (
rpcclient "github.com/tendermint/tendermint/rpc/client"
"strings"
"sync"
"github.com/pkg/errors"
)

// ClientManager is a manager of a set of rpc clients to full nodes.
// This manager can do load balancing upon these rpc clients.
type ClientManager struct {
clients []rpcclient.Client
currentIndex int
mutex sync.RWMutex
}

// NewClientManager create a new ClientManager
func NewClientManager(nodeURIs string) (*ClientManager,error) {
if nodeURIs != "" {
nodeURLArray := strings.Split(nodeURIs, ",")
var clients []rpcclient.Client
for _, url := range nodeURLArray {
client := rpcclient.NewHTTP(url, "/websocket")
clients = append(clients, client)
}
mgr := &ClientManager{
currentIndex: 0,
clients: clients,
}
return mgr, nil
}
return nil, errors.New("missing node URIs")
}

func (mgr *ClientManager) getClient() rpcclient.Client {
Copy link
Collaborator

Choose a reason for hiding this comment

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

(mgr *ClientManager) ––> (manager *ClientManager)

mgr.mutex.Lock()
defer mgr.mutex.Unlock()

client := mgr.clients[mgr.currentIndex]
mgr.currentIndex++
if mgr.currentIndex >= len(mgr.clients){
mgr.currentIndex = 0
}
return client
}
16 changes: 16 additions & 0 deletions client/context/loadbalancing_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package context

import (
"testing"
"github.com/stretchr/testify/assert"
)

func TestLoadBalancing(t *testing.T) {
nodeURIs := "10.10.10.10:26657,20.20.20.20:26657,30.30.30.30:26657"
clientMgr,err := NewClientManager(nodeURIs)
assert.Empty(t,err)
endpoint := clientMgr.getClient()
assert.NotEqual(t,endpoint,clientMgr.getClient())
clientMgr.getClient()
assert.Equal(t,endpoint,clientMgr.getClient())
}
64 changes: 63 additions & 1 deletion client/context/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,18 @@ import (
cmn "github.com/tendermint/tendermint/libs/common"
rpcclient "github.com/tendermint/tendermint/rpc/client"
ctypes "github.com/tendermint/tendermint/rpc/core/types"
"github.com/cosmos/cosmos-sdk/store"
"github.com/cosmos/cosmos-sdk/wire"
"strings"
tendermintLiteProxy "github.com/tendermint/tendermint/lite/proxy"
)

// GetNode returns an RPC client. If the context's client is not defined, an
// error is returned.
func (ctx CLIContext) GetNode() (rpcclient.Client, error) {
if ctx.ClientMgr != nil {
return ctx.ClientMgr.getClient(), nil
}
if ctx.Client == nil {
return nil, errors.New("no RPC client defined")
}
Expand Down Expand Up @@ -277,6 +284,7 @@ func (ctx CLIContext) ensureBroadcastTx(txBytes []byte) error {
return nil
}

// nolint: gocyclo
Copy link
Collaborator

Choose a reason for hiding this comment

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

refactor the function and split it into smaller ones instead

// query performs a query from a Tendermint node with the provided store name
// and path.
func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err error) {
Expand All @@ -296,10 +304,46 @@ func (ctx CLIContext) query(path string, key common.HexBytes) (res []byte, err e
}

resp := result.Response
if !resp.IsOK() {
if resp.Code != uint32(0) {
return res, errors.Errorf("query failed: (%d) %s", resp.Code, resp.Log)
}

// Data from trusted node or subspace doesn't need verification
if ctx.TrustNode || !isQueryStoreWithProof(path) {
return resp.Value,nil
}

// TODO: Later we consider to return error for missing valid certifier to verify data from untrusted node
if ctx.Cert == nil {
if ctx.Logger != nil {
io.WriteString(ctx.Logger, fmt.Sprintf("Missing valid certifier to verify data from untrusted node\n"))
}
return resp.Value, nil
}

// AppHash for height H is in header H+1
commit, err := tendermintLiteProxy.GetCertifiedCommit(resp.Height+1, node, ctx.Cert)
if err != nil {
return nil, err
}

var multiStoreProof store.MultiStoreProof
cdc := wire.NewCodec()
err = cdc.UnmarshalBinary(resp.Proof, &multiStoreProof)
if err != nil {
return res, errors.Wrap(err, "failed to unmarshalBinary rangeProof")
}

// Validate the substore commit hash against trusted appHash
substoreCommitHash, err := store.VerifyMultiStoreCommitInfo(multiStoreProof.StoreName, multiStoreProof.CommitIDList, commit.Header.AppHash)
if err != nil {
return nil, errors.Wrap(err, "failed in verifying the proof against appHash")
}
err = store.VerifyRangeProof(resp.Key, resp.Value, substoreCommitHash, &multiStoreProof.RangeProof)
if err != nil {
return nil, errors.Wrap(err, "failed in the range proof verification")
}

return resp.Value, nil
}

Expand All @@ -309,3 +353,21 @@ func (ctx CLIContext) queryStore(key cmn.HexBytes, storeName, endPath string) ([
path := fmt.Sprintf("/store/%s/%s", storeName, endPath)
return ctx.query(path, key)
}

// isQueryStoreWithProof expects a format like /<queryType>/<storeName>/<subpath>
// queryType can be app or store
// if subpath equals to store or key, then return true
func isQueryStoreWithProof(path string) (bool) {
if !strings.HasPrefix(path, "/") {
return false
}
paths := strings.SplitN(path[1:], "/", 3)
if len(paths) != 3 {
return false
}
// WARNING This should be consistent with query method in iavlstore.go
if paths[2] == "store" || paths[2] == "key" {
return true
}
return false
}
4 changes: 4 additions & 0 deletions client/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ const (
FlagAsync = "async"
FlagJson = "json"
FlagPrintResponse = "print-response"
FlagListenAddr = "laddr"
FlagSwaggerHostIP = "swagger-host-ip"
FlagModules = "modules"
FlagNodeList = "node-list"
)

// LineBreak can be included in a command list to provide a blank line
Expand Down
32 changes: 32 additions & 0 deletions client/httputils/httputils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package httputils

import (
"github.com/gin-gonic/gin"
"net/http"
)

// NewError create error http response
func NewError(ctx *gin.Context, errCode int, err error) {
errorResponse := HTTPError{
API: "2.0",
Code: errCode,
}
if err != nil {
errorResponse.ErrMsg = err.Error()
}

ctx.JSON(errCode, errorResponse)
}

// NormalResponse create normal http response
func NormalResponse(ctx *gin.Context, data []byte) {
ctx.Status(http.StatusOK)
ctx.Writer.Write(data)
}

// HTTPError is http response with error
type HTTPError struct {
API string `json:"rest api" example:"2.0"`
Code int `json:"code" example:"500"`
ErrMsg string `json:"error message"`
}
92 changes: 92 additions & 0 deletions client/keys/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ import (
"github.com/cosmos/cosmos-sdk/crypto/keys"

"github.com/tendermint/tendermint/libs/cli"
"github.com/gin-gonic/gin"
"github.com/cosmos/cosmos-sdk/client/httputils"
"regexp/syntax"
)

const (
Expand Down Expand Up @@ -236,6 +239,85 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
w.Write(bz)
}

// AddNewKeyRequest is the handler of adding new key in swagger rest server
// nolint: gocyclo
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here. Refactor and split

func AddNewKeyRequest(gtx *gin.Context) {
var m NewKeyBody
body, err := ioutil.ReadAll(gtx.Request.Body)
if err != nil {
httputils.NewError(gtx, http.StatusBadRequest, err)
return
}
err = json.Unmarshal(body, &m)
Copy link
Collaborator

@fedekunze fedekunze Aug 22, 2018

Choose a reason for hiding this comment

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

Use cdc.UnmarshalJSON for consistency

if err != nil {
httputils.NewError(gtx, http.StatusBadRequest, err)
return
}

if len(m.Name) < 1 || len(m.Name) > 16 {
httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account name length should not be longer than 16"))
return
}
for _, char := range []rune(m.Name) {
if !syntax.IsWordChar(char) {
httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account name should not contains any char beyond [_0-9A-Za-z]"))
return
}
}
if len(m.Password) < 8 || len(m.Password) > 16 {
httputils.NewError(gtx, http.StatusBadRequest, fmt.Errorf("account password length should be no less than 8 and no greater than 16"))
return
}

kb, err := GetKeyBase()
if err != nil {
httputils.NewError(gtx, http.StatusInternalServerError, err)
return
}

// check if already exists
infos, err := kb.List()
if err != nil {
httputils.NewError(gtx, http.StatusInternalServerError, err)
return
}

for _, i := range infos {
if i.GetName() == m.Name {
httputils.NewError(gtx, http.StatusConflict, fmt.Errorf("account with name %s already exists", m.Name))
return
}
}

// create account
seed := m.Seed
if seed == "" {
seed = getSeed(keys.Secp256k1)
}
info, err := kb.CreateKey(m.Name, seed, m.Password)
if err != nil {
httputils.NewError(gtx, http.StatusInternalServerError, err)
return
}

keyOutput, err := Bech32KeyOutput(info)
if err != nil {
httputils.NewError(gtx, http.StatusInternalServerError, err)
return
}

keyOutput.Seed = seed

bz, err := json.Marshal(keyOutput)
if err != nil {
httputils.NewError(gtx, http.StatusInternalServerError, err)
return
}

httputils.NormalResponse(gtx, bz)

}

// function to just a new seed to display in the UI before actually persisting it in the keybase
func getSeed(algo keys.SigningAlgo) string {
kb := client.MockKeyBase()
Expand All @@ -258,3 +340,13 @@ func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
seed := getSeed(algo)
w.Write([]byte(seed))
}

// SeedRequest is the handler of creating seed in swagger rest server
func SeedRequest(gtx *gin.Context) {

algo := keys.SigningAlgo("secp256k1")

seed := getSeed(algo)

httputils.NormalResponse(gtx, []byte(seed))
}
Loading