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 #2101

Closed
Closed
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
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
ClientMgr *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
func (c CLIContext) WithCert(cert tendermintLite.Certifier) CLIContext {
c.Cert = cert
return c
}

// WithCert - return a copy of the context with an updated ClientMgr
func (c CLIContext) WithClientMgr(clientMgr *ClientManager) CLIContext {
c.ClientMgr = clientMgr
return c
}
44 changes: 44 additions & 0 deletions client/context/loadbalancing.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package context

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

type ClientManager struct {
clients []rpcclient.Client
currentIndex int
mutex sync.RWMutex
}

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
} else {
return nil, errors.New("missing node URIs")
}
}

func (mgr *ClientManager) getClient() rpcclient.Client {
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())
}
59 changes: 58 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 @@ -296,10 +303,42 @@ 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
}

if ctx.Cert == nil {
return resp.Value,errors.Errorf("missing valid certifier to verify data from untrusted node")
}

// 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 +348,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
36 changes: 36 additions & 0 deletions client/httputils/httputils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package httputils

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

func NewError(ctx *gin.Context, errCode int, err error) {
errorResponse := HTTPError{
Api: "2.0",
Code: errCode,
ErrMsg: err.Error(),
}
ctx.JSON(errCode, errorResponse)
}

func Response(ctx *gin.Context, data interface{}) {
response := HTTPResponse{
Api: "2.0",
Code: 0,
Result: data,
}
ctx.JSON(http.StatusOK, response)
}

type HTTPResponse struct {
Api string `json:"rest api" example:"2.0"`
Code int `json:"code" example:"0"`
Result interface{} `json:"result"`
}

type HTTPError struct {
Api string `json:"rest api" example:"2.0"`
Code int `json:"code" example:"500"`
ErrMsg string `json:"error message"`
}
77 changes: 77 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,71 @@ func AddNewKeyRequestHandler(w http.ResponseWriter, r *http.Request) {
w.Write(bz)
}

func AddNewKeyRequest(gtx *gin.Context) {
var kb keys.Keybase
var m NewKeyBody

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

if err := gtx.BindJSON(&m); err != nil {
httputils.NewError(gtx, http.StatusBadRequest, err)
return
}
if err != nil {
httputils.NewError(gtx, http.StatusBadRequest, err)
return
}
if len(m.Name) < 1 || len(m.Name) > 16 {
httputils.NewError(gtx, http.StatusBadRequest, errors.New("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, errors.New("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, errors.New("Account password length should be between 8 and 16"))
return
}

// check if already exists
infos, err := kb.List()
for _, i := range infos {
if i.GetName() == m.Name {
httputils.NewError(gtx, http.StatusConflict, errors.New(fmt.Sprintf("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

httputils.Response(gtx, keyOutput)

}

// 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 +326,12 @@ func SeedRequestHandler(w http.ResponseWriter, r *http.Request) {
seed := getSeed(algo)
w.Write([]byte(seed))
}

func SeedRequest(gtx *gin.Context) {

algo := keys.SigningAlgo("secp256k1")

seed := getSeed(algo)

httputils.Response(gtx, seed)
}
Loading