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

Go kosu/rpc #174

Merged
merged 6 commits into from Jul 23, 2019
Merged
Changes from 1 commit
Commits
File filter...
Filter file types
Jump to…
Jump to file or symbol
Failed to load files and symbols.

Always

Just for now

go-kosu: add rpc subcommand

  • Loading branch information
gchaincl committed Jul 18, 2019
commit ec6e16fe66913340e843905ef96fe6ff9bf77dca
@@ -3,6 +3,7 @@ package abci
import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@@ -21,6 +22,8 @@ const (
KOSUHOME = ".kosu"
)

var errHomeDirNotFound = errors.New("homedir does not exists! Did you run the init command?")

// DefaultHomeDir is the default full path used to store config and data
var DefaultHomeDir = os.ExpandEnv(fmt.Sprintf("$HOME/%s", KOSUHOME))

@@ -31,7 +34,7 @@ func LoadConfig(homedir string) (*config.Config, error) {
}

if !common.FileExists(filepath.Join(homedir, "config", "config.toml")) {
return nil, fmt.Errorf("missing homedir! Did you run the init command?")
return nil, errHomeDirNotFound
}

// Have a config file, load it
@@ -43,7 +46,7 @@ func LoadConfig(homedir string) (*config.Config, error) {
// I don't think this ever returns an err. It seems to create a default config if missing
err := viper.ReadInConfig()
if err != nil {
return nil, fmt.Errorf("missing homedir/config file. Did you run 'kosud --init'?")
return nil, errHomeDirNotFound
}

cfg := config.DefaultConfig()
@@ -12,6 +12,7 @@ import (
"github.com/tendermint/tendermint/libs/log"

"go-kosu/abci"
"go-kosu/rpc"
"go-kosu/witness"
)

@@ -96,8 +97,8 @@ func main() {
}
},
}
rootCmd.Flags().StringVar(&cfg.Home, "home", "~/.kosu", "directory for config and data")
rootCmd.Flags().BoolVar(&cfg.Debug, "debug", false, "enable debuging")
rootCmd.PersistentFlags().StringVar(&cfg.Home, "home", "~/.kosu", "directory for config and data")
rootCmd.PersistentFlags().BoolVar(&cfg.Debug, "debug", false, "enable debuging")
rootCmd.Flags().StringVar(&cfg.Web3, "web3", "wss://ethnet.zaidan.io/ws/kosu", "web3 provider URL")
rootCmd.Flags().BoolVar(&cfg.Init, "init", false, "initializes directory like 'tendermint init' does")

@@ -116,6 +117,8 @@ func main() {
}
})

rootCmd.AddCommand(rpc.NewCommand())

if err := rootCmd.Execute(); err != nil {
stdlog.Fatal(err)
}
@@ -42,6 +42,14 @@ func (c *Client) Subscribe(ctx context.Context, fn func(interface{}), query stri
return nil
}

func (c *Client) Call(result interface{}, method string, args ...interface{}) error {
return c.rpc.Call(result, method, args...)
func (c *Client) Call(result interface{}, ns, method string, args ...interface{}) error {
return c.rpc.Call(result, ns+"_"+method, args...)
}

func (c *Client) LatestHeight() (int64, error) {
var latestHeight int64
if err := c.Call(&latestHeight, "kosu", "latestHeight"); err != nil {
return 0, err
}
return latestHeight, nil
}
@@ -0,0 +1,104 @@
package rpc

import (
"errors"
"fmt"
"go-kosu/abci"
"log"
"net/http"
"sync"

"github.com/ethereum/go-ethereum/rpc"
"github.com/spf13/cobra"
)

// NewCommand returns a new cobra.Command to be attached as a sub-command
func NewCommand() *cobra.Command {
var (
url string

http bool
httpPort int

ws bool
wsPort int

key []byte
)
cmd := &cobra.Command{
Use: "rpc",
Short: "starts the rpc bridge",
Long: "The RPC bridge exposes a set of kosud functionalities over JSON-RPC 2.0",
PreRunE: func(cmd *cobra.Command, args []string) error {
if http == false && ws == false {
return errors.New("both `--ws` and `--http` where false, you need to enable at least one")
}

var homeDir string
if home := cmd.Flag("home"); home == nil {
homeDir = "~/kosu"
} else {
homeDir = home.Value.String()
}

var err error
key, err = abci.LoadPrivateKey(homeDir)
if err != nil {
return err
}

return nil
},
Run: func(cmd *cobra.Command, args []string) {
client := abci.NewHTTPClient(url, key)
srv := NewServer(client)

wg := sync.WaitGroup{}
if http == true {
wg.Add(1)
go func() {
defer wg.Done()
if err := startHTTP(srv, httpPort); err != nil {
log.Printf("http: %s", err)
}
}()
}

if ws == true {
wg.Add(1)
go func() {
defer wg.Done()
if err := startWS(srv, wsPort); err != nil {
log.Printf("ws: %s", err)
}
}()
}
wg.Wait()
},
}

cmd.Flags().StringVar(&url, "url", "http://localhost:26657", "URL exposed by kosud")
cmd.Flags().BoolVar(&http, "http", true, "Starts the HTTP server")
cmd.Flags().IntVar(&httpPort, "http-port", 14341, "HTTP server listening port")

cmd.Flags().BoolVar(&ws, "ws", true, "Starts the WebSocket server")
cmd.Flags().IntVar(&wsPort, "ws-port", 14342, "WebSocket server listening port")

return cmd
}

func startHTTP(srv *rpc.Server, port int) error {
bind := fmt.Sprintf(":%d", port)
log.Printf("Starting HTTP server on %s", bind)
return http.ListenAndServe(bind, srv)
}

func startWS(srv *rpc.Server, port int) error {
bind := fmt.Sprintf(":%d", port)
log.Printf("Starting WS server on %s", bind)

return http.ListenAndServe(
bind,
srv.WebsocketHandler([]string{"*"}),
)
}
@@ -2,45 +2,20 @@ package rpc

import (
"context"
"fmt"
"io/ioutil"
"os"
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"go-kosu/abci"

"github.com/stretchr/testify/require"
"github.com/tendermint/tendermint/libs/db"
"github.com/tendermint/tendermint/libs/log"
)

func TestRPCCall(t *testing.T) {
server := NewServer(nil)
client := DialInProc(server)
require.NoError(t, client.Call(nil, "kosu_foo"))
}

func TestRPCSubscription(t *testing.T) {
_, closer := startServer(t, db.NewMemDB())
defer closer()
abciClient := abci.NewHTTPClient("http://localhost:26657", nil)

server := NewServer(abciClient)
client := DialInProc(server)

fn := func(i interface{}) {
fmt.Printf("i = %+v\n", i)
}
ctx, cancel := context.WithCancel(context.Background())

err := client.Subscribe(ctx, fn, "tm.event = 'NewBlock'")
require.NoError(t, err)

time.Sleep(3 * time.Second)
cancel()
}

// TODO use tests/support.go version of startServer
func startServer(t *testing.T, db db.DB) (*abci.App, func()) {
// Create a temp dir and initialize tendermint there
@@ -63,3 +38,33 @@ func startServer(t *testing.T, db db.DB) (*abci.App, func()) {
os.RemoveAll(dir)
}
}

func TestRPCLatestHeight(t *testing.T) {
_, closer := startServer(t, db.NewMemDB())
defer closer()
client := DialInProc(
NewServer(
abci.NewHTTPClient("http://localhost:26657", nil),
),
)

// Get the initial (prior the first block is mined)
latest, err := client.LatestHeight()
require.NoError(t, err)
assert.EqualValues(t, 0, latest)

ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
fn := func(i interface{}) {
// this is invoked when a block is mined
latest, err := client.LatestHeight()
require.NoError(t, err)
assert.EqualValues(t, 1, latest)

cancel()
}

err = client.Subscribe(ctx, fn, "tm.event = 'NewBlock'")
require.NoError(t, err)

<-ctx.Done()
}
@@ -52,6 +52,16 @@ func (s *Service) Subscribe(ctx context.Context, query string) (*rpc.Subscriptio
return rpcSub, nil
}

func (s *Service) Foo() error {
return nil
// LatestHeight returns the height of the best known block
// The `latestHeight` method will return the integer height of the latest block committed to the blockchain.",
func (s *Service) LatestHeight() (int64, error) {
res, err := s.abci.Block(nil)
if err != nil {
return 0, err
}
if res.Block == nil {
return 0, nil
}

return res.Block.Height, nil
}
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.