package account
import (
+ "context"
+ "encoding/json"
"fmt"
"sync"
"time"
- "context"
- "encoding/json"
- "github.com/bytom/log"
- "github.com/bytom/errors"
- "github.com/bytom/protocol"
"github.com/bytom/blockchain/pin"
- "github.com/golang/groupcache/lru"
- "github.com/bytom/crypto/sha3pool"
- "github.com/bytom/protocol/vm/vmutil"
"github.com/bytom/blockchain/signers"
"github.com/bytom/blockchain/txbuilder"
"github.com/bytom/crypto/ed25519/chainkd"
+ "github.com/bytom/crypto/sha3pool"
+ "github.com/bytom/errors"
+ "github.com/bytom/log"
+ "github.com/bytom/protocol"
+ "github.com/bytom/protocol/vm/vmutil"
+ "github.com/golang/groupcache/lru"
dbm "github.com/tendermint/tmlibs/db"
)
ErrBadIdentifier = errors.New("either ID or alias must be specified, and not both")
)
-func NewManager(db dbm.DB, chain *protocol.Chain , pinStore *pin.Store) *Manager {
+func NewManager(db dbm.DB, chain *protocol.Chain, pinStore *pin.Store) *Manager {
return &Manager{
- db: db,
- chain: chain,
- utxoDB: newReserver(db, chain),
+ db: db,
+ chain: chain,
+ utxoDB: newReserver(db, chain),
pinStore: pinStore,
cache: lru.New(maxAccountCache),
aliasCache: lru.New(maxAccountCache),
// Manager stores accounts and their associated control programs.
type Manager struct {
- db dbm.DB
- chain *protocol.Chain
- utxoDB *reserver
- indexer Saver
+ db dbm.DB
+ chain *protocol.Chain
+ utxoDB *reserver
+ indexer Saver
pinStore *pin.Store
cacheMu sync.Mutex
bytes := m.db.Get(json.RawMessage(id))
if bytes == nil {
- return nil,errors.New("not find this account.")
+ return nil, errors.New("not find this account.")
}
var account Account
err := json.Unmarshal(bytes, &account)
if err != nil {
- return nil,errors.New("failed unmarshal this account.")
+ return nil, errors.New("failed unmarshal this account.")
}
-
m.cacheMu.Lock()
m.cache.Add(id, account.Signer)
m.cacheMu.Unlock()
var b32 [32]byte
for _, p := range progs {
- acp, err := json.Marshal(&struct{
+ acp, err := json.Marshal(&struct {
AccountID string
KeyIndex uint64
ControlProgram []byte
Change bool
- ExpiresAt time.Time}{
- AccountID: p.accountID,
- KeyIndex: p.keyIndex,
- ControlProgram: p.controlProgram,
- Change: p.change,
- ExpiresAt: p.expiresAt})
+ ExpiresAt time.Time
+ }{
+ AccountID: p.accountID,
+ KeyIndex: p.keyIndex,
+ ControlProgram: p.controlProgram,
+ Change: p.change,
+ ExpiresAt: p.expiresAt})
if err != nil {
return errors.Wrap(err, "failed marshal controlProgram")
if m.acpIndexNext >= m.acpIndexCap {
const incrby = 10000 // start 1,increments by 10,000
- if(m.acpIndexCap <= incrby){
+ if m.acpIndexCap <= incrby {
m.acpIndexCap = incrby + 1
- }else{
+ } else {
m.acpIndexCap += incrby
}
m.acpIndexNext = m.acpIndexCap - incrby
}
b.OnRollback(canceler(ctx, a.accounts, res.ID))
- var acct *signers.Signer
- if res.Source.AccountID == ""{
+ var acct *signers.Signer
+ if res.Source.AccountID == "" {
//TODO coinbase
acct = &signers.Signer{}
- }else{
+ } else {
acct, err = a.accounts.findByID(ctx, res.Source.AccountID)
if err != nil {
return err
package account
import (
- "time"
"context"
"encoding/json"
+ "time"
"github.com/bytom/blockchain/query"
"github.com/bytom/blockchain/signers"
--- /dev/null
+package blockchain
+
+import (
+ "github.com/bytom/blockchain/rpc"
+
+ ctypes "github.com/bytom/blockchain/rpc/types"
+)
+
+func (a *BlockchainReactor) getNetInfo() (*ctypes.ResultNetInfo, error) {
+ return rpc.NetInfo(a.sw)
+}
package pseudohsm
import (
- "testing"
- _"github.com/davecgh/go-spew/spew"
"github.com/bytom/errors"
+ _ "github.com/davecgh/go-spew/spew"
+ "testing"
)
const dirPath = "testdata/pseudo"
-
func TestPseudoHSMChainKDKeys(t *testing.T) {
- hsm , _:= New(dirPath)
+ hsm, _ := New(dirPath)
xpub, err := hsm.XCreate("password", "")
if err != nil {
t.Fatal(err)
if !xpub2.XPub.Derive(path).Verify(msg, sig) {
t.Error("expected verify with derived pubkey of sig from derived privkey to succeed")
}
-/* xpubs, _, err := hsm.ListKeys(0, 100)
- if err != nil {
- t.Fatal(err)
- }
- if len(xpubs) != 2 {
- t.Error("expected 2 entries in the db")
- }*/
+ /* xpubs, _, err := hsm.ListKeys(0, 100)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(xpubs) != 2 {
+ t.Error("expected 2 entries in the db")
+ }*/
err = hsm.UpdateAlias(xpub.XPub, "password", "updatealias")
if err != nil {
t.Fatal(err)
}
func TestKeyWithEmptyAlias(t *testing.T) {
- hsm, _:= New(dirPath)
+ hsm, _ := New(dirPath)
for i := 0; i < 2; i++ {
xpub, err := hsm.XCreate("xx", "")
if errors.Root(err) != nil {
}
}
-
func BenchmarkSign(b *testing.B) {
b.StopTimer()
auth := "nowpasswd"
- hsm, _:= New(dirPath)
+ hsm, _ := New(dirPath)
xpub, err := hsm.XCreate(auth, "")
if err != nil {
b.Fatal(err)
hsm *pseudohsm.HSM
mining *cpuminer.CPUMiner
mux *http.ServeMux
+ sw *p2p.Switch
handler http.Handler
fastSync bool
requestsCh chan BlockRequest
m.Handle("/sign-transactions", jsonHandler(bcr.pseudohsmSignTemplates))
m.Handle("/reset-password", jsonHandler(bcr.pseudohsmResetPassword))
m.Handle("/update-alias", jsonHandler(bcr.pseudohsmUpdateAlias))
+ m.Handle("/net-info", jsonHandler(bcr.getNetInfo))
latencyHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if l := latency(m, req); l != nil {
txPool *protocol.TxPool,
accounts *account.Manager,
assets *asset.Registry,
+ sw *p2p.Switch,
hsm *pseudohsm.HSM,
fastSync bool,
pinStore *pin.Store) *BlockchainReactor {
txPool: txPool,
mining: mining,
mux: http.NewServeMux(),
+ sw: sw,
hsm: hsm,
fastSync: fastSync,
requestsCh: requestsCh,
--- /dev/null
+package rpc
+
+import (
+ "github.com/bytom/blockchain/txdb"
+
+ ctypes "github.com/bytom/blockchain/rpc/types"
+)
+
+func BlockHeight(blockStore *txdb.Store) (*ctypes.ResultBlockchainInfo, error) {
+ return &ctypes.ResultBlockchainInfo{LastHeight: blockStore.Height()}, nil
+}
-package core
+package rpc
import (
- "fmt"
-
- ctypes "github.com/bytom/rpc/core/types"
+ ctypes "github.com/bytom/blockchain/rpc/types"
+ "github.com/bytom/p2p"
)
//-----------------------------------------------------------------------------
-func NetInfo() (*ctypes.ResultNetInfo, error) {
+func NetInfo(p2pSwitch *p2p.Switch) (*ctypes.ResultNetInfo, error) {
listening := p2pSwitch.IsListening()
listeners := []string{}
for _, listener := range p2pSwitch.Listeners() {
//-----------------------------------------------------------------------------
// Dial given list of seeds
-func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
+/*func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
if len(seeds) == 0 {
return &ctypes.ResultDialSeeds{}, fmt.Errorf("No seeds provided")
return &ctypes.ResultDialSeeds{}, err
}
return &ctypes.ResultDialSeeds{"Dialing seeds in progress. See /net_info for details"}, nil
-}
+}*/
-// Package rpc implements Chain Core's RPC client.
package rpc
import (
"github.com/bytom/net/http/reqid"
)
-// Chain-specific header fields
+// Bytom-specific header fields
const (
HeaderBlockchainID = "Blockchain-ID"
- HeaderCoreID = "Chain-Core-ID"
+ HeaderCoreID = "Bytom-Core-ID"
HeaderTimeout = "RPC-Timeout"
)
// the RPC client's blockchain ID.
var ErrWrongNetwork = errors.New("connected to a peer on a different network")
-// A Client is a Chain RPC client. It performs RPCs over HTTP using JSON
+// A Client is a Bytom RPC client. It performs RPCs over HTTP using JSON
// request and responses. A Client must be configured with a secret token
// to authenticate with other Cores on the network.
type Client struct {
}
func (c Client) userAgent() string {
- return fmt.Sprintf("Chain; process=%s; buildtag=%s; blockchainID=%s",
+ return fmt.Sprintf("Bytom; process=%s; buildtag=%s; blockchainID=%s",
c.Username, c.BuildTag, c.BlockchainID)
}
"github.com/bytom/p2p"
"github.com/bytom/protocol/bc"
"github.com/bytom/types"
- abci "github.com/tendermint/abci/types"
"github.com/tendermint/go-crypto"
"github.com/tendermint/go-wire/data"
)
IsOutbound bool `json:"is_outbound"`
ConnectionStatus p2p.ConnectionStatus `json:"connection_status"`
}
-
-type ResultDumpConsensusState struct {
- RoundState string `json:"round_state"`
- PeerRoundStates []string `json:"peer_round_states"`
-}
-
-type ResultBroadcastTx struct {
- Code abci.CodeType `json:"code"`
- Data data.Bytes `json:"data"`
- Log string `json:"log"`
-
- Hash data.Bytes `json:"hash"`
-}
-
-type ResultBroadcastTxCommit struct {
- CheckTx abci.Result `json:"check_tx"`
- DeliverTx abci.Result `json:"deliver_tx"`
- Hash data.Bytes `json:"hash"`
- Height int `json:"height"`
-}
-
-type ResultABCIInfo struct {
- Response abci.ResponseInfo `json:"response"`
-}
-
-type ResultABCIQuery struct {
- *abci.ResultQuery `json:"response"`
-}
-
-type ResultUnsafeFlushMempool struct{}
-
-type ResultUnsafeProfile struct{}
-
-type ResultSubscribe struct{}
-
-type ResultUnsubscribe struct{}
}
/*
- nullToken := sql.NullString{
- String: clientToken,
- Valid: clientToken != "",
- }
+ nullToken := sql.NullString{
+ String: clientToken,
+ Valid: clientToken != "",
+ }
- const q = `
- INSERT INTO signers (id, type, xpubs, quorum, client_token)
- VALUES (next_chain_id($1::text), $2, $3, $4, $5)
- ON CONFLICT (client_token) DO NOTHING
- RETURNING id, key_index
- `
- var (
- id string
- keyIndex uint64
- )
- err := db.QueryRowContext(ctx, q, typeIDMap[typ], typ, pq.ByteaArray(xpubBytes), quorum, nullToken).
- Scan(&id, &keyIndex)
- if err == sql.ErrNoRows && clientToken != "" {
- return findByClientToken(ctx, db, clientToken)
- }
- if err != nil && err != sql.ErrNoRows {
- return nil, errors.Wrap(err)
- }
+ const q = `
+ INSERT INTO signers (id, type, xpubs, quorum, client_token)
+ VALUES (next_chain_id($1::text), $2, $3, $4, $5)
+ ON CONFLICT (client_token) DO NOTHING
+ RETURNING id, key_index
+ `
+ var (
+ id string
+ keyIndex uint64
+ )
+ err := db.QueryRowContext(ctx, q, typeIDMap[typ], typ, pq.ByteaArray(xpubBytes), quorum, nullToken).
+ Scan(&id, &keyIndex)
+ if err == sql.ErrNoRows && clientToken != "" {
+ return findByClientToken(ctx, db, clientToken)
+ }
+ if err != nil && err != sql.ErrNoRows {
+ return nil, errors.Wrap(err)
+ }
*/
var (
id string
return nil
}
-
select {
case <-ctx.Done():
return ctx.Err()
case <-a.pinStore.AllWaiter(height):
}
-
return nil
}
"grant": {grant},
"revoke": {revoke},
"wait": {wait},
- "create-account": {createAccount},
- "bind-account": {bindAccount},
+ "create-account": {createAccount},
+ "bind-account": {bindAccount},
"update-account-tags": {updateAccountTags},
"create-asset": {createAsset},
- "bind-asset": {bindAsset},
+ "bind-asset": {bindAsset},
"update-asset-tags": {updateAssetTags},
"build-transaction": {buildTransaction},
"create-control-program": {createControlProgram},
"sub-create-issue-tx": {submitCreateIssueTransaction},
"reset-password": {resetPassword},
"update-alias": {updateAlias},
+ "net-info": {netInfo},
}
func main() {
fmt.Printf("asset id:%v\n", assets[0].ID.String())
}
-
func updateAccountTags(client *rpc.Client, args []string) {
if len(args) != 2 {
fatalln("update-account-tags [<ID>|<alias>] [tags_key:<tags_value>]")
}
// sign-transaction
type param struct {
- Auth string
+ Auth string
Txs []*txbuilder.Template `json:"transactions"`
XPubs []chainkd.XPub `json:"xpubs"`
}
key.XPub = *xpub
client.Call(context.Background(), "/update-alias", &key, nil)
}
+
+func netInfo(client *rpc.Client, args []string) {
+ var response interface{}
+ client.Call(context.Background(), "/net-info", nil, &response)
+ fmt.Printf("net info:%v\n", response)
+}
BaseConfig `mapstructure:",squash"`
// Options for services
- RPC *RPCConfig `mapstructure:"rpc"`
- P2P *P2PConfig `mapstructure:"p2p"`
+ RPC *RPCConfig `mapstructure:"rpc"`
+ P2P *P2PConfig `mapstructure:"p2p"`
Wallet *WalletConfig `mapstructure:"wallet"`
}
BaseConfig: DefaultBaseConfig(),
RPC: DefaultRPCConfig(),
P2P: DefaultP2PConfig(),
- Wallet: DefaultWalletConfig(),
+ Wallet: DefaultWalletConfig(),
}
}
BaseConfig: TestBaseConfig(),
RPC: TestRPCConfig(),
P2P: TestP2PConfig(),
- Wallet: TestWalletConfig(),
+ Wallet: TestWalletConfig(),
}
}
ListenAddress: "tcp://0.0.0.0:46656",
AddrBook: "addrbook.json",
AddrBookStrict: true,
- SkipUPNP: false,
+ SkipUPNP: false,
MaxNumPeers: 50,
}
}
// WalletConfig
type WalletConfig struct {
- Enable bool `mapstructure:"enable"`
+ Enable bool `mapstructure:"enable"`
}
func DefaultWalletConfig() *WalletConfig {
import (
_ "bytes"
- _ "github.com/bytom/common"
_ "crypto/ecdsa"
_ "encoding/hex"
_ "fmt"
+ _ "github.com/bytom/common"
_ "io/ioutil"
_ "math/big"
_ "os"
cfg "github.com/bytom/config"
bytomlog "github.com/bytom/log"
p2p "github.com/bytom/p2p"
- rpccore "github.com/bytom/rpc/core"
- grpccore "github.com/bytom/rpc/grpc"
- rpcserver "github.com/bytom/rpc/lib/server"
crypto "github.com/tendermint/go-crypto"
wire "github.com/tendermint/go-wire"
cmn "github.com/tendermint/tmlibs/common"
// services
evsw types.EventSwitch // pub/sub for services
// blockStore *bc.MemStore
- blockStore *txdb.Store
- bcReactor *bc.BlockchainReactor
- accounts *account.Manager
- assets *asset.Registry
- rpcListeners []net.Listener // rpc servers
+ blockStore *txdb.Store
+ bcReactor *bc.BlockchainReactor
+ accounts *account.Manager
+ assets *asset.Registry
}
var (
mux.Handle("/", &coreHandler)
var handler http.Handler = mux
- //handler = core.AuthHandler(handler, raftDB, accessTokens, tlsConfig)
handler = RedirectHandler(handler)
handler = reqid.Handler(handler)
txPool,
accounts,
assets,
+ sw,
hsm,
fastSync,
pinStore)
return err
}
}
- // Run the RPC server
- if n.config.RPC.ListenAddress != "" {
- listeners, err := n.startRPC()
- if err != nil {
- return err
- }
- n.rpcListeners = listeners
- }
-
return nil
}
// TODO: gracefully disconnect from peers.
n.sw.Stop()
- for _, l := range n.rpcListeners {
- n.Logger.Info("Closing rpc listener", "listener", l)
- if err := l.Close(); err != nil {
- n.Logger.Error("Error closing listener", "listener", l, "error", err)
- }
- }
}
func (n *Node) RunForever() {
n.sw.AddListener(l)
}
-// ConfigureRPC sets all variables in rpccore so they will serve
-// rpc calls from this node
-func (n *Node) ConfigureRPC() {
- rpccore.SetEventSwitch(n.evsw)
- rpccore.SetBlockStore(n.blockStore)
- rpccore.SetSwitch(n.sw)
- rpccore.SetAddrBook(n.addrBook)
- rpccore.SetLogger(n.Logger.With("module", "rpc"))
-}
-
-func (n *Node) startRPC() ([]net.Listener, error) {
- n.ConfigureRPC()
- listenAddrs := strings.Split(n.config.RPC.ListenAddress, ",")
-
- if n.config.RPC.Unsafe {
- rpccore.AddUnsafeRoutes()
- }
-
- // we may expose the rpc over both a unix and tcp socket
- listeners := make([]net.Listener, len(listenAddrs))
- for i, listenAddr := range listenAddrs {
- mux := http.NewServeMux()
- wm := rpcserver.NewWebsocketManager(rpccore.Routes, n.evsw)
- rpcLogger := n.Logger.With("module", "rpc-server")
- wm.SetLogger(rpcLogger)
- mux.HandleFunc("/websocket", wm.WebsocketHandler)
- rpcserver.RegisterRPCFuncs(mux, rpccore.Routes, rpcLogger)
- listener, err := rpcserver.StartHTTPServer(listenAddr, mux, rpcLogger)
- if err != nil {
- return nil, err
- }
- listeners[i] = listener
- }
-
- // we expose a simplified api over grpc for convenience to app devs
- grpcListenAddr := n.config.RPC.GRPCListenAddress
- if grpcListenAddr != "" {
- listener, err := grpccore.StartGRPCServer(grpcListenAddr)
- if err != nil {
- return nil, err
- }
- listeners = append(listeners, listener)
- }
- return listeners, nil
-}
-
func (n *Node) Switch() *p2p.Switch {
return n.sw
}
p2pListener := n.sw.Listeners()[0]
p2pHost := p2pListener.ExternalAddress().IP.String()
p2pPort := p2pListener.ExternalAddress().Port
- rpcListenAddr := n.config.RPC.ListenAddress
+ //rpcListenAddr := n.config.RPC.ListenAddress
// We assume that the rpcListener has the same ExternalAddress.
// This is probably true because both P2P and RPC listeners use UPnP,
// except of course if the rpc is only bound to localhost
nodeInfo.ListenAddr = cmn.Fmt("%v:%v", p2pHost, p2pPort)
- nodeInfo.Other = append(nodeInfo.Other, cmn.Fmt("rpc_addr=%v", rpcListenAddr))
+ //nodeInfo.Other = append(nodeInfo.Other, cmn.Fmt("rpc_addr=%v", rpcListenAddr))
return nodeInfo
}
}
func TestMapCoinbaseTx(t *testing.T) {
-// define the BTM asset id, the soul asset of Bytom
- var BTMAssetID = &bc.AssetID{
- V0: uint64(18446744073709551615),
- V1: uint64(18446744073709551615),
- V2: uint64(18446744073709551615),
- V3: uint64(18446744073709551615),
+ // define the BTM asset id, the soul asset of Bytom
+ var BTMAssetID = &bc.AssetID{
+ V0: uint64(18446744073709551615),
+ V1: uint64(18446744073709551615),
+ V2: uint64(18446744073709551615),
+ V3: uint64(18446744073709551615),
}
oldTx := &TxData{
Version: 1,
lastQueuedSnapshot time.Time
pendingSnapshots chan pendingSnapshot
- txPool *TxPool
+ txPool *TxPool
}
type pendingSnapshot struct {
+++ /dev/null
-package client_test
-
-import (
- "testing"
- "time"
-
- "github.com/bytom/rpc/client"
- "github.com/bytom/types"
- "github.com/stretchr/testify/require"
- merktest "github.com/tendermint/merkleeyes/testutil"
-)
-
-func TestHeaderEvents(t *testing.T) {
- require := require.New(t)
- for i, c := range GetClients() {
- // start for this test it if it wasn't already running
- if !c.IsRunning() {
- // if so, then we start it, listen, and stop it.
- st, err := c.Start()
- require.Nil(err, "%d: %+v", i, err)
- require.True(st, "%d", i)
- defer c.Stop()
- }
-
- evtTyp := types.EventStringNewBlockHeader()
- evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second)
- require.Nil(err, "%d: %+v", i, err)
- _, ok := evt.Unwrap().(types.EventDataNewBlockHeader)
- require.True(ok, "%d: %#v", i, evt)
- // TODO: more checks...
- }
-}
-
-func TestTxEvents(t *testing.T) {
- require := require.New(t)
- for i, c := range GetClients() {
- // start for this test it if it wasn't already running
- if !c.IsRunning() {
- // if so, then we start it, listen, and stop it.
- st, err := c.Start()
- require.Nil(err, "%d: %+v", i, err)
- require.True(st, "%d", i)
- defer c.Stop()
- }
-
- // make the tx
- _, _, tx := merktest.MakeTxKV()
- evtTyp := types.EventStringTx(types.Tx(tx))
-
- // send async
- txres, err := c.BroadcastTxAsync(tx)
- require.Nil(err, "%+v", err)
- require.True(txres.Code.IsOK())
-
- // and wait for confirmation
- evt, err := client.WaitForOneEvent(c, evtTyp, 1*time.Second)
- require.Nil(err, "%d: %+v", i, err)
- // and make sure it has the proper info
- txe, ok := evt.Unwrap().(types.EventDataTx)
- require.True(ok, "%d: %#v", i, evt)
- // make sure this is the proper tx
- require.EqualValues(tx, txe.Tx)
- require.True(txe.Code.IsOK())
- }
-}
+++ /dev/null
-package client
-
-import (
- "time"
-
- "github.com/bytom/types"
- "github.com/pkg/errors"
- cmn "github.com/tendermint/tmlibs/common"
- events "github.com/tendermint/tmlibs/events"
-)
-
-// Waiter is informed of current height, decided whether to quit early
-type Waiter func(delta int) (abort error)
-
-// DefaultWaitStrategy is the standard backoff algorithm,
-// but you can plug in another one
-func DefaultWaitStrategy(delta int) (abort error) {
- if delta > 10 {
- return errors.Errorf("Waiting for %d blocks... aborting", delta)
- } else if delta > 0 {
- // estimate of wait time....
- // wait half a second for the next block (in progress)
- // plus one second for every full block
- delay := time.Duration(delta-1)*time.Second + 500*time.Millisecond
- time.Sleep(delay)
- }
- return nil
-}
-
-// Wait for height will poll status at reasonable intervals until
-// the block at the given height is available.
-//
-// If waiter is nil, we use DefaultWaitStrategy, but you can also
-// provide your own implementation
-func WaitForHeight(c StatusClient, h int, waiter Waiter) error {
- if waiter == nil {
- waiter = DefaultWaitStrategy
- }
- delta := 1
- for delta > 0 {
- s, err := c.Status()
- if err != nil {
- return err
- }
- delta = h - s.LatestBlockHeight
- // wait for the time, or abort early
- if err := waiter(delta); err != nil {
- return err
- }
- }
- return nil
-}
-
-// WaitForOneEvent subscribes to a websocket event for the given
-// event time and returns upon receiving it one time, or
-// when the timeout duration has expired.
-//
-// This handles subscribing and unsubscribing under the hood
-func WaitForOneEvent(evsw types.EventSwitch,
- evtTyp string, timeout time.Duration) (types.TMEventData, error) {
- listener := cmn.RandStr(12)
-
- evts, quit := make(chan events.EventData, 10), make(chan bool, 1)
- // start timeout count-down
- go func() {
- time.Sleep(timeout)
- quit <- true
- }()
-
- // register for the next event of this type
- evsw.AddListenerForEvent(listener, evtTyp, func(data events.EventData) {
- evts <- data
- })
- // make sure to unregister after the test is over
- defer evsw.RemoveListenerForEvent(evtTyp, listener)
- // defer evsw.RemoveListener(listener) // this also works
-
- select {
- case <-quit:
- return types.TMEventData{}, errors.New("timed out waiting for event")
- case evt := <-evts:
- tmevt, ok := evt.(types.TMEventData)
- if ok {
- return tmevt, nil
- }
- return types.TMEventData{}, errors.Errorf("Got unexpected event type: %#v", evt)
- }
-}
+++ /dev/null
-package client_test
-
-import (
- "errors"
- "strings"
- "testing"
-
- "github.com/bytom/rpc/client"
- "github.com/bytom/rpc/client/mock"
- ctypes "github.com/bytom/rpc/core/types"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func TestWaitForHeight(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
-
- // test with error result - immediate failure
- m := &mock.StatusMock{
- Call: mock.Call{
- Error: errors.New("bye"),
- },
- }
- r := mock.NewStatusRecorder(m)
-
- // connection failure always leads to error
- err := client.WaitForHeight(r, 8, nil)
- require.NotNil(err)
- require.Equal("bye", err.Error())
- // we called status once to check
- require.Equal(1, len(r.Calls))
-
- // now set current block height to 10
- m.Call = mock.Call{
- Response: &ctypes.ResultStatus{LatestBlockHeight: 10},
- }
-
- // we will not wait for more than 10 blocks
- err = client.WaitForHeight(r, 40, nil)
- require.NotNil(err)
- require.True(strings.Contains(err.Error(), "aborting"))
- // we called status once more to check
- require.Equal(2, len(r.Calls))
-
- // waiting for the past returns immediately
- err = client.WaitForHeight(r, 5, nil)
- require.Nil(err)
- // we called status once more to check
- require.Equal(3, len(r.Calls))
-
- // since we can't update in a background goroutine (test --race)
- // we use the callback to update the status height
- myWaiter := func(delta int) error {
- // update the height for the next call
- m.Call.Response = &ctypes.ResultStatus{LatestBlockHeight: 15}
- return client.DefaultWaitStrategy(delta)
- }
-
- // we wait for a few blocks
- err = client.WaitForHeight(r, 12, myWaiter)
- require.Nil(err)
- // we called status once to check
- require.Equal(5, len(r.Calls))
-
- pre := r.Calls[3]
- require.Nil(pre.Error)
- prer, ok := pre.Response.(*ctypes.ResultStatus)
- require.True(ok)
- assert.Equal(10, prer.LatestBlockHeight)
-
- post := r.Calls[4]
- require.Nil(post.Error)
- postr, ok := post.Response.(*ctypes.ResultStatus)
- require.True(ok)
- assert.Equal(15, postr.LatestBlockHeight)
-}
+++ /dev/null
-package client
-
-import (
- "encoding/json"
- "fmt"
-
- ctypes "github.com/bytom/rpc/core/types"
- "github.com/bytom/rpc/lib/client"
- "github.com/bytom/types"
- "github.com/pkg/errors"
- data "github.com/tendermint/go-wire/data"
- events "github.com/tendermint/tmlibs/events"
-)
-
-/*
-HTTP is a Client implementation that communicates
-with a tendermint node over json rpc and websockets.
-
-This is the main implementation you probably want to use in
-production code. There are other implementations when calling
-the tendermint node in-process (local), or when you want to mock
-out the server for test code (mock).
-*/
-type HTTP struct {
- remote string
- rpc *rpcclient.JSONRPCClient
- *WSEvents
-}
-
-// New takes a remote endpoint in the form tcp://<host>:<port>
-// and the websocket path (which always seems to be "/websocket")
-func NewHTTP(remote, wsEndpoint string) *HTTP {
- return &HTTP{
- rpc: rpcclient.NewJSONRPCClient(remote),
- remote: remote,
- WSEvents: newWSEvents(remote, wsEndpoint),
- }
-}
-
-func (c *HTTP) _assertIsClient() Client {
- return c
-}
-
-func (c *HTTP) _assertIsNetworkClient() NetworkClient {
- return c
-}
-
-func (c *HTTP) _assertIsEventSwitch() types.EventSwitch {
- return c
-}
-
-func (c *HTTP) Status() (*ctypes.ResultStatus, error) {
- result := new(ctypes.ResultStatus)
- _, err := c.rpc.Call("status", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "Status")
- }
- return result, nil
-}
-
-func (c *HTTP) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- result := new(ctypes.ResultABCIInfo)
- _, err := c.rpc.Call("abci_info", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "ABCIInfo")
- }
- return result, nil
-}
-
-func (c *HTTP) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- result := new(ctypes.ResultABCIQuery)
- _, err := c.rpc.Call("abci_query",
- map[string]interface{}{"path": path, "data": data, "prove": prove},
- result)
- if err != nil {
- return nil, errors.Wrap(err, "ABCIQuery")
- }
- return result, nil
-}
-
-func (c *HTTP) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- result := new(ctypes.ResultBroadcastTxCommit)
- _, err := c.rpc.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
- if err != nil {
- return nil, errors.Wrap(err, "broadcast_tx_commit")
- }
- return result, nil
-}
-
-func (c *HTTP) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return c.broadcastTX("broadcast_tx_async", tx)
-}
-
-func (c *HTTP) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return c.broadcastTX("broadcast_tx_sync", tx)
-}
-
-func (c *HTTP) broadcastTX(route string, tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- result := new(ctypes.ResultBroadcastTx)
- _, err := c.rpc.Call(route, map[string]interface{}{"tx": tx}, result)
- if err != nil {
- return nil, errors.Wrap(err, route)
- }
- return result, nil
-}
-
-func (c *HTTP) NetInfo() (*ctypes.ResultNetInfo, error) {
- result := new(ctypes.ResultNetInfo)
- _, err := c.rpc.Call("net_info", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "NetInfo")
- }
- return result, nil
-}
-
-func (c *HTTP) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
- result := new(ctypes.ResultDumpConsensusState)
- _, err := c.rpc.Call("dump_consensus_state", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "DumpConsensusState")
- }
- return result, nil
-}
-
-func (c *HTTP) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) {
- result := new(ctypes.ResultBlockchainInfo)
- _, err := c.rpc.Call("blockchain",
- map[string]interface{}{"minHeight": minHeight, "maxHeight": maxHeight},
- result)
- if err != nil {
- return nil, errors.Wrap(err, "BlockchainInfo")
- }
- return result, nil
-}
-
-func (c *HTTP) Genesis() (*ctypes.ResultGenesis, error) {
- result := new(ctypes.ResultGenesis)
- _, err := c.rpc.Call("genesis", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "Genesis")
- }
- return result, nil
-}
-
-func (c *HTTP) Block(height int) (*ctypes.ResultBlock, error) {
- result := new(ctypes.ResultBlock)
- _, err := c.rpc.Call("block", map[string]interface{}{"height": height}, result)
- if err != nil {
- return nil, errors.Wrap(err, "Block")
- }
- return result, nil
-}
-
-func (c *HTTP) Commit(height int) (*ctypes.ResultCommit, error) {
- result := new(ctypes.ResultCommit)
- _, err := c.rpc.Call("commit", map[string]interface{}{"height": height}, result)
- if err != nil {
- return nil, errors.Wrap(err, "Commit")
- }
- return result, nil
-}
-
-func (c *HTTP) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
- result := new(ctypes.ResultTx)
- query := map[string]interface{}{
- "hash": hash,
- "prove": prove,
- }
- _, err := c.rpc.Call("tx", query, result)
- if err != nil {
- return nil, errors.Wrap(err, "Tx")
- }
- return result, nil
-}
-
-func (c *HTTP) Validators() (*ctypes.ResultValidators, error) {
- result := new(ctypes.ResultValidators)
- _, err := c.rpc.Call("validators", map[string]interface{}{}, result)
- if err != nil {
- return nil, errors.Wrap(err, "Validators")
- }
- return result, nil
-}
-
-/** websocket event stuff here... **/
-
-type WSEvents struct {
- types.EventSwitch
- remote string
- endpoint string
- ws *rpcclient.WSClient
-
- // used for signaling the goroutine that feeds ws -> EventSwitch
- quit chan bool
- done chan bool
-
- // used to maintain counts of actively listened events
- // so we can properly subscribe/unsubscribe
- // FIXME: thread-safety???
- // FIXME: reuse code from tmlibs/events???
- evtCount map[string]int // count how many time each event is subscribed
- listeners map[string][]string // keep track of which events each listener is listening to
-}
-
-func newWSEvents(remote, endpoint string) *WSEvents {
- return &WSEvents{
- EventSwitch: types.NewEventSwitch(),
- endpoint: endpoint,
- remote: remote,
- quit: make(chan bool, 1),
- done: make(chan bool, 1),
- evtCount: map[string]int{},
- listeners: map[string][]string{},
- }
-}
-
-func (w *WSEvents) _assertIsEventSwitch() types.EventSwitch {
- return w
-}
-
-// Start is the only way I could think the extend OnStart from
-// events.eventSwitch. If only it wasn't private...
-// BaseService.Start -> eventSwitch.OnStart -> WSEvents.Start
-func (w *WSEvents) Start() (bool, error) {
- st, err := w.EventSwitch.Start()
- // if we did start, then OnStart here...
- if st && err == nil {
- ws := rpcclient.NewWSClient(w.remote, w.endpoint)
- _, err = ws.Start()
- if err == nil {
- w.ws = ws
- go w.eventListener()
- }
- }
- return st, errors.Wrap(err, "StartWSEvent")
-}
-
-// Stop wraps the BaseService/eventSwitch actions as Start does
-func (w *WSEvents) Stop() bool {
- stop := w.EventSwitch.Stop()
- if stop {
- // send a message to quit to stop the eventListener
- w.quit <- true
- <-w.done
- w.ws.Stop()
- w.ws = nil
- }
- return stop
-}
-
-/** TODO: more intelligent subscriptions! **/
-func (w *WSEvents) AddListenerForEvent(listenerID, event string, cb events.EventCallback) {
- // no one listening -> subscribe
- if w.evtCount[event] == 0 {
- w.subscribe(event)
- }
- // if this listener was already listening to this event, return early
- for _, s := range w.listeners[listenerID] {
- if event == s {
- return
- }
- }
- // otherwise, add this event to this listener
- w.evtCount[event] += 1
- w.listeners[listenerID] = append(w.listeners[listenerID], event)
- w.EventSwitch.AddListenerForEvent(listenerID, event, cb)
-}
-
-func (w *WSEvents) RemoveListenerForEvent(event string, listenerID string) {
- // if this listener is listening already, splice it out
- found := false
- l := w.listeners[listenerID]
- for i, s := range l {
- if event == s {
- found = true
- w.listeners[listenerID] = append(l[:i], l[i+1:]...)
- break
- }
- }
- // if the listener wasn't already listening to the event, exit early
- if !found {
- return
- }
-
- // now we can update the subscriptions
- w.evtCount[event] -= 1
- if w.evtCount[event] == 0 {
- w.unsubscribe(event)
- }
- w.EventSwitch.RemoveListenerForEvent(event, listenerID)
-}
-
-func (w *WSEvents) RemoveListener(listenerID string) {
- // remove all counts for this listener
- for _, s := range w.listeners[listenerID] {
- w.evtCount[s] -= 1
- if w.evtCount[s] == 0 {
- w.unsubscribe(s)
- }
- }
- w.listeners[listenerID] = nil
-
- // then let the switch do it's magic
- w.EventSwitch.RemoveListener(listenerID)
-}
-
-// eventListener is an infinite loop pulling all websocket events
-// and pushing them to the EventSwitch.
-//
-// the goroutine only stops by closing quit
-func (w *WSEvents) eventListener() {
- for {
- select {
- case res := <-w.ws.ResultsCh:
- // res is json.RawMessage
- err := w.parseEvent(res)
- if err != nil {
- // FIXME: better logging/handling of errors??
- fmt.Printf("ws result: %+v\n", err)
- }
- case err := <-w.ws.ErrorsCh:
- // FIXME: better logging/handling of errors??
- fmt.Printf("ws err: %+v\n", err)
- case <-w.quit:
- // send a message so we can wait for the routine to exit
- // before cleaning up the w.ws stuff
- w.done <- true
- return
- }
- }
-}
-
-// parseEvent unmarshals the json message and converts it into
-// some implementation of types.TMEventData, and sends it off
-// on the merry way to the EventSwitch
-func (w *WSEvents) parseEvent(data []byte) (err error) {
- result := new(ctypes.ResultEvent)
- err = json.Unmarshal(data, result)
- if err != nil {
- // ignore silently (eg. subscribe, unsubscribe and maybe other events)
- // TODO: ?
- return nil
- }
- // looks good! let's fire this baby!
- w.EventSwitch.FireEvent(result.Name, result.Data)
- return nil
-}
-
-// no way of exposing these failures, so we panic.
-// is this right? or silently ignore???
-func (w *WSEvents) subscribe(event string) {
- err := w.ws.Subscribe(event)
- if err != nil {
- panic(err)
- }
-}
-
-func (w *WSEvents) unsubscribe(event string) {
- err := w.ws.Unsubscribe(event)
- if err != nil {
- panic(err)
- }
-}
+++ /dev/null
-/*
-package client provides a general purpose interface (Client) for connecting
-to a tendermint node, as well as higher-level functionality.
-
-The main implementation for production code is client.HTTP, which
-connects via http to the jsonrpc interface of the tendermint node.
-
-For connecting to a node running in the same process (eg. when
-compiling the abci app in the same process), you can use the client.Local
-implementation.
-
-For mocking out server responses during testing to see behavior for
-arbitrary return values, use the mock package.
-
-In addition to the Client interface, which should be used externally
-for maximum flexibility and testability, and two implementations,
-this package also provides helper functions that work on any Client
-implementation.
-*/
-package client
-
-import (
- ctypes "github.com/bytom/rpc/core/types"
- "github.com/bytom/types"
- data "github.com/tendermint/go-wire/data"
-)
-
-// ABCIClient groups together the functionality that principally
-// affects the ABCI app. In many cases this will be all we want,
-// so we can accept an interface which is easier to mock
-type ABCIClient interface {
- // reading from abci app
- ABCIInfo() (*ctypes.ResultABCIInfo, error)
- ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error)
-
- // writing to abci app
- BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error)
- BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
- BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error)
-}
-
-// SignClient groups together the interfaces need to get valid
-// signatures and prove anything about the chain
-type SignClient interface {
- Block(height int) (*ctypes.ResultBlock, error)
- Commit(height int) (*ctypes.ResultCommit, error)
- Validators() (*ctypes.ResultValidators, error)
- Tx(hash []byte, prove bool) (*ctypes.ResultTx, error)
-}
-
-// HistoryClient shows us data from genesis to now in large chunks.
-type HistoryClient interface {
- Genesis() (*ctypes.ResultGenesis, error)
- BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error)
-}
-
-type StatusClient interface {
- // general chain info
- Status() (*ctypes.ResultStatus, error)
-}
-
-// Client wraps most important rpc calls a client would make
-// if you want to listen for events, test if it also
-// implements events.EventSwitch
-type Client interface {
- ABCIClient
- SignClient
- HistoryClient
- StatusClient
-
- // this Client is reactive, you can subscribe to any TMEventData
- // type, given the proper string. see tendermint/types/events.go
- types.EventSwitch
-}
-
-// NetworkClient is general info about the network state. May not
-// be needed usually.
-//
-// Not included in the Client interface, but generally implemented
-// by concrete implementations.
-type NetworkClient interface {
- NetInfo() (*ctypes.ResultNetInfo, error)
- DumpConsensusState() (*ctypes.ResultDumpConsensusState, error)
-}
+++ /dev/null
-package client
-
-import (
- nm "github.com/bytom/node"
- "github.com/bytom/rpc/core"
- ctypes "github.com/bytom/rpc/core/types"
- "github.com/bytom/types"
- data "github.com/tendermint/go-wire/data"
-)
-
-/*
-Local is a Client implementation that directly executes the rpc
-functions on a given node, without going through HTTP or GRPC
-
-This implementation is useful for:
-
-* Running tests against a node in-process without the overhead
-of going through an http server
-* Communication between an ABCI app and tendermin core when they
-are compiled in process.
-
-For real clients, you probably want to use client.HTTP. For more
-powerful control during testing, you probably want the "client/mock" package.
-*/
-type Local struct {
- node *nm.Node
- types.EventSwitch
-}
-
-// NewLocal configures a client that calls the Node directly.
-//
-// Note that given how rpc/core works with package singletons, that
-// you can only have one node per process. So make sure test cases
-// don't run in parallel, or try to simulate an entire network in
-// one process...
-func NewLocal(node *nm.Node) Local {
- node.ConfigureRPC()
- return Local{
- node: node,
- EventSwitch: node.EventSwitch(),
- }
-}
-
-func (c Local) _assertIsClient() Client {
- return c
-}
-
-func (c Local) _assertIsNetworkClient() NetworkClient {
- return c
-}
-
-func (c Local) Status() (*ctypes.ResultStatus, error) {
- return core.Status()
-}
-
-func (c Local) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- return core.ABCIInfo()
-}
-
-func (c Local) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- return core.ABCIQuery(path, data, prove)
-}
-
-func (c Local) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- return core.BroadcastTxCommit(tx)
-}
-
-func (c Local) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return core.BroadcastTxAsync(tx)
-}
-
-func (c Local) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return core.BroadcastTxSync(tx)
-}
-
-func (c Local) NetInfo() (*ctypes.ResultNetInfo, error) {
- return core.NetInfo()
-}
-
-func (c Local) DumpConsensusState() (*ctypes.ResultDumpConsensusState, error) {
- return core.DumpConsensusState()
-}
-
-func (c Local) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
- return core.UnsafeDialSeeds(seeds)
-}
-
-func (c Local) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) {
- return core.BlockchainInfo(minHeight, maxHeight)
-}
-
-func (c Local) Genesis() (*ctypes.ResultGenesis, error) {
- return core.Genesis()
-}
-
-func (c Local) Block(height int) (*ctypes.ResultBlock, error) {
- return core.Block(height)
-}
-
-func (c Local) Commit(height int) (*ctypes.ResultCommit, error) {
- return core.Commit(height)
-}
-
-func (c Local) Validators() (*ctypes.ResultValidators, error) {
- return core.Validators()
-}
-
-func (c Local) Tx(hash []byte, prove bool) (*ctypes.ResultTx, error) {
- return core.Tx(hash, prove)
-}
+++ /dev/null
-package client_test
-
-import (
- "os"
- "testing"
-
- nm "github.com/bytom/node"
- rpctest "github.com/bytom/rpc/test"
- meapp "github.com/tendermint/merkleeyes/app"
-)
-
-var node *nm.Node
-
-func TestMain(m *testing.M) {
- // start a tendermint node (and merkleeyes) in the background to test against
- app := meapp.NewMerkleEyesApp("", 100)
- node = rpctest.StartTendermint(app)
- code := m.Run()
-
- // and shut down proper at the end
- node.Stop()
- node.Wait()
- os.Exit(code)
-}
+++ /dev/null
-package mock
-
-import (
- abci "github.com/tendermint/abci/types"
- data "github.com/tendermint/go-wire/data"
- "github.com/tendermint/tendermint/rpc/client"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- "github.com/tendermint/tendermint/types"
-)
-
-// ABCIApp will send all abci related request to the named app,
-// so you can test app behavior from a client without needing
-// an entire tendermint node
-type ABCIApp struct {
- App abci.Application
-}
-
-func (a ABCIApp) _assertABCIClient() client.ABCIClient {
- return a
-}
-
-func (a ABCIApp) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- return &ctypes.ResultABCIInfo{a.App.Info()}, nil
-}
-
-func (a ABCIApp) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- q := a.App.Query(abci.RequestQuery{data, path, 0, prove})
- return &ctypes.ResultABCIQuery{q.Result()}, nil
-}
-
-func (a ABCIApp) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- res := ctypes.ResultBroadcastTxCommit{}
- res.CheckTx = a.App.CheckTx(tx)
- if !res.CheckTx.IsOK() {
- return &res, nil
- }
- res.DeliverTx = a.App.DeliverTx(tx)
- return &res, nil
-}
-
-func (a ABCIApp) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- c := a.App.CheckTx(tx)
- // and this gets writen in a background thread...
- if c.IsOK() {
- go func() { a.App.DeliverTx(tx) }()
- }
- return &ctypes.ResultBroadcastTx{c.Code, c.Data, c.Log, tx.Hash()}, nil
-}
-
-func (a ABCIApp) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- c := a.App.CheckTx(tx)
- // and this gets writen in a background thread...
- if c.IsOK() {
- go func() { a.App.DeliverTx(tx) }()
- }
- return &ctypes.ResultBroadcastTx{c.Code, c.Data, c.Log, tx.Hash()}, nil
-}
-
-// ABCIMock will send all abci related request to the named app,
-// so you can test app behavior from a client without needing
-// an entire tendermint node
-type ABCIMock struct {
- Info Call
- Query Call
- BroadcastCommit Call
- Broadcast Call
-}
-
-func (m ABCIMock) _assertABCIClient() client.ABCIClient {
- return m
-}
-
-func (m ABCIMock) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- res, err := m.Info.GetResponse(nil)
- if err != nil {
- return nil, err
- }
- return &ctypes.ResultABCIInfo{res.(abci.ResponseInfo)}, nil
-}
-
-func (m ABCIMock) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- res, err := m.Query.GetResponse(QueryArgs{path, data, prove})
- if err != nil {
- return nil, err
- }
- resQuery := res.(abci.ResponseQuery)
- return &ctypes.ResultABCIQuery{resQuery.Result()}, nil
-}
-
-func (m ABCIMock) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- res, err := m.BroadcastCommit.GetResponse(tx)
- if err != nil {
- return nil, err
- }
- return res.(*ctypes.ResultBroadcastTxCommit), nil
-}
-
-func (m ABCIMock) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- res, err := m.Broadcast.GetResponse(tx)
- if err != nil {
- return nil, err
- }
- return res.(*ctypes.ResultBroadcastTx), nil
-}
-
-func (m ABCIMock) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- res, err := m.Broadcast.GetResponse(tx)
- if err != nil {
- return nil, err
- }
- return res.(*ctypes.ResultBroadcastTx), nil
-}
-
-// ABCIRecorder can wrap another type (ABCIApp, ABCIMock, or Client)
-// and record all ABCI related calls.
-type ABCIRecorder struct {
- Client client.ABCIClient
- Calls []Call
-}
-
-func NewABCIRecorder(client client.ABCIClient) *ABCIRecorder {
- return &ABCIRecorder{
- Client: client,
- Calls: []Call{},
- }
-}
-
-func (r *ABCIRecorder) _assertABCIClient() client.ABCIClient {
- return r
-}
-
-type QueryArgs struct {
- Path string
- Data data.Bytes
- Prove bool
-}
-
-func (r *ABCIRecorder) addCall(call Call) {
- r.Calls = append(r.Calls, call)
-}
-
-func (r *ABCIRecorder) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- res, err := r.Client.ABCIInfo()
- r.addCall(Call{
- Name: "abci_info",
- Response: res,
- Error: err,
- })
- return res, err
-}
-
-func (r *ABCIRecorder) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- res, err := r.Client.ABCIQuery(path, data, prove)
- r.addCall(Call{
- Name: "abci_query",
- Args: QueryArgs{path, data, prove},
- Response: res,
- Error: err,
- })
- return res, err
-}
-
-func (r *ABCIRecorder) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- res, err := r.Client.BroadcastTxCommit(tx)
- r.addCall(Call{
- Name: "broadcast_tx_commit",
- Args: tx,
- Response: res,
- Error: err,
- })
- return res, err
-}
-
-func (r *ABCIRecorder) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- res, err := r.Client.BroadcastTxAsync(tx)
- r.addCall(Call{
- Name: "broadcast_tx_async",
- Args: tx,
- Response: res,
- Error: err,
- })
- return res, err
-}
-
-func (r *ABCIRecorder) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- res, err := r.Client.BroadcastTxSync(tx)
- r.addCall(Call{
- Name: "broadcast_tx_sync",
- Args: tx,
- Response: res,
- Error: err,
- })
- return res, err
-}
+++ /dev/null
-package mock_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/pkg/errors"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/tendermint/abci/example/dummy"
- abci "github.com/tendermint/abci/types"
- data "github.com/tendermint/go-wire/data"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- "github.com/tendermint/tendermint/types"
-
- "github.com/tendermint/tendermint/rpc/client/mock"
-)
-
-func TestABCIMock(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
-
- key, value := []byte("foo"), []byte("bar")
- height := uint64(10)
- goodTx := types.Tx{0x01, 0xff}
- badTx := types.Tx{0x12, 0x21}
-
- m := mock.ABCIMock{
- Info: mock.Call{Error: errors.New("foobar")},
- Query: mock.Call{Response: abci.ResponseQuery{
- Key: key,
- Value: value,
- Height: height,
- }},
- // Broadcast commit depends on call
- BroadcastCommit: mock.Call{
- Args: goodTx,
- Response: &ctypes.ResultBroadcastTxCommit{
- CheckTx: abci.Result{Data: data.Bytes("stand")},
- DeliverTx: abci.Result{Data: data.Bytes("deliver")},
- },
- Error: errors.New("bad tx"),
- },
- Broadcast: mock.Call{Error: errors.New("must commit")},
- }
-
- // now, let's try to make some calls
- _, err := m.ABCIInfo()
- require.NotNil(err)
- assert.Equal("foobar", err.Error())
-
- // query always returns the response
- query, err := m.ABCIQuery("/", nil, false)
- require.Nil(err)
- require.NotNil(query)
- assert.EqualValues(key, query.Key)
- assert.EqualValues(value, query.Value)
- assert.Equal(height, query.Height)
-
- // non-commit calls always return errors
- _, err = m.BroadcastTxSync(goodTx)
- require.NotNil(err)
- assert.Equal("must commit", err.Error())
- _, err = m.BroadcastTxAsync(goodTx)
- require.NotNil(err)
- assert.Equal("must commit", err.Error())
-
- // commit depends on the input
- _, err = m.BroadcastTxCommit(badTx)
- require.NotNil(err)
- assert.Equal("bad tx", err.Error())
- bres, err := m.BroadcastTxCommit(goodTx)
- require.Nil(err, "%+v", err)
- assert.EqualValues(0, bres.CheckTx.Code)
- assert.EqualValues("stand", bres.CheckTx.Data)
- assert.EqualValues("deliver", bres.DeliverTx.Data)
-}
-
-func TestABCIRecorder(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
- m := mock.ABCIMock{
- Info: mock.Call{Response: abci.ResponseInfo{
- Data: "data",
- Version: "v0.9.9",
- }},
- Query: mock.Call{Error: errors.New("query")},
- Broadcast: mock.Call{Error: errors.New("broadcast")},
- BroadcastCommit: mock.Call{Error: errors.New("broadcast_commit")},
- }
- r := mock.NewABCIRecorder(m)
-
- require.Equal(0, len(r.Calls))
-
- r.ABCIInfo()
- r.ABCIQuery("path", data.Bytes("data"), true)
- require.Equal(2, len(r.Calls))
-
- info := r.Calls[0]
- assert.Equal("abci_info", info.Name)
- assert.Nil(info.Error)
- assert.Nil(info.Args)
- require.NotNil(info.Response)
- ir, ok := info.Response.(*ctypes.ResultABCIInfo)
- require.True(ok)
- assert.Equal("data", ir.Response.Data)
- assert.Equal("v0.9.9", ir.Response.Version)
-
- query := r.Calls[1]
- assert.Equal("abci_query", query.Name)
- assert.Nil(query.Response)
- require.NotNil(query.Error)
- assert.Equal("query", query.Error.Error())
- require.NotNil(query.Args)
- qa, ok := query.Args.(mock.QueryArgs)
- require.True(ok)
- assert.Equal("path", qa.Path)
- assert.EqualValues("data", qa.Data)
- assert.True(qa.Prove)
-
- // now add some broadcasts
- txs := []types.Tx{{1}, {2}, {3}}
- r.BroadcastTxCommit(txs[0])
- r.BroadcastTxSync(txs[1])
- r.BroadcastTxAsync(txs[2])
-
- require.Equal(5, len(r.Calls))
-
- bc := r.Calls[2]
- assert.Equal("broadcast_tx_commit", bc.Name)
- assert.Nil(bc.Response)
- require.NotNil(bc.Error)
- assert.EqualValues(bc.Args, txs[0])
-
- bs := r.Calls[3]
- assert.Equal("broadcast_tx_sync", bs.Name)
- assert.Nil(bs.Response)
- require.NotNil(bs.Error)
- assert.EqualValues(bs.Args, txs[1])
-
- ba := r.Calls[4]
- assert.Equal("broadcast_tx_async", ba.Name)
- assert.Nil(ba.Response)
- require.NotNil(ba.Error)
- assert.EqualValues(ba.Args, txs[2])
-}
-
-func TestABCIApp(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
- app := dummy.NewDummyApplication()
- m := mock.ABCIApp{app}
-
- // get some info
- info, err := m.ABCIInfo()
- require.Nil(err)
- assert.Equal(`{"size":0}`, info.Response.GetData())
-
- // add a key
- key, value := "foo", "bar"
- tx := fmt.Sprintf("%s=%s", key, value)
- res, err := m.BroadcastTxCommit(types.Tx(tx))
- require.Nil(err)
- assert.True(res.CheckTx.Code.IsOK())
- require.NotNil(res.DeliverTx)
- assert.True(res.DeliverTx.Code.IsOK())
-
- // check the key
- qres, err := m.ABCIQuery("/key", data.Bytes(key), false)
- require.Nil(err)
- assert.EqualValues(value, qres.Value)
-}
+++ /dev/null
-/*
-package mock returns a Client implementation that
-accepts various (mock) implementations of the various methods.
-
-This implementation is useful for using in tests, when you don't
-need a real server, but want a high-level of control about
-the server response you want to mock (eg. error handling),
-or if you just want to record the calls to verify in your tests.
-
-For real clients, you probably want the "http" package. If you
-want to directly call a tendermint node in process, you can use the
-"local" package.
-*/
-package mock
-
-import (
- "reflect"
-
- data "github.com/tendermint/go-wire/data"
- "github.com/tendermint/tendermint/rpc/client"
- "github.com/tendermint/tendermint/rpc/core"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- "github.com/tendermint/tendermint/types"
-)
-
-// Client wraps arbitrary implementations of the various interfaces.
-//
-// We provide a few choices to mock out each one in this package.
-// Nothing hidden here, so no New function, just construct it from
-// some parts, and swap them out them during the tests.
-type Client struct {
- client.ABCIClient
- client.SignClient
- client.HistoryClient
- client.StatusClient
- // create a mock with types.NewEventSwitch()
- types.EventSwitch
-}
-
-func (c Client) _assertIsClient() client.Client {
- return c
-}
-
-// Call is used by recorders to save a call and response.
-// It can also be used to configure mock responses.
-//
-type Call struct {
- Name string
- Args interface{}
- Response interface{}
- Error error
-}
-
-// GetResponse will generate the apporiate response for us, when
-// using the Call struct to configure a Mock handler.
-//
-// When configuring a response, if only one of Response or Error is
-// set then that will always be returned. If both are set, then
-// we return Response if the Args match the set args, Error otherwise.
-func (c Call) GetResponse(args interface{}) (interface{}, error) {
- // handle the case with no response
- if c.Response == nil {
- if c.Error == nil {
- panic("Misconfigured call, you must set either Response or Error")
- }
- return nil, c.Error
- }
- // response without error
- if c.Error == nil {
- return c.Response, nil
- }
- // have both, we must check args....
- if reflect.DeepEqual(args, c.Args) {
- return c.Response, nil
- }
- return nil, c.Error
-}
-
-func (c Client) Status() (*ctypes.ResultStatus, error) {
- return core.Status()
-}
-
-func (c Client) ABCIInfo() (*ctypes.ResultABCIInfo, error) {
- return core.ABCIInfo()
-}
-
-func (c Client) ABCIQuery(path string, data data.Bytes, prove bool) (*ctypes.ResultABCIQuery, error) {
- return core.ABCIQuery(path, data, prove)
-}
-
-func (c Client) BroadcastTxCommit(tx types.Tx) (*ctypes.ResultBroadcastTxCommit, error) {
- return core.BroadcastTxCommit(tx)
-}
-
-func (c Client) BroadcastTxAsync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return core.BroadcastTxAsync(tx)
-}
-
-func (c Client) BroadcastTxSync(tx types.Tx) (*ctypes.ResultBroadcastTx, error) {
- return core.BroadcastTxSync(tx)
-}
-
-func (c Client) NetInfo() (*ctypes.ResultNetInfo, error) {
- return core.NetInfo()
-}
-
-func (c Client) DialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) {
- return core.UnsafeDialSeeds(seeds)
-}
-
-func (c Client) BlockchainInfo(minHeight, maxHeight int) (*ctypes.ResultBlockchainInfo, error) {
- return core.BlockchainInfo(minHeight, maxHeight)
-}
-
-func (c Client) Genesis() (*ctypes.ResultGenesis, error) {
- return core.Genesis()
-}
-
-func (c Client) Block(height int) (*ctypes.ResultBlock, error) {
- return core.Block(height)
-}
-
-func (c Client) Commit(height int) (*ctypes.ResultCommit, error) {
- return core.Commit(height)
-}
-
-func (c Client) Validators() (*ctypes.ResultValidators, error) {
- return core.Validators()
-}
+++ /dev/null
-package mock
-
-import (
- "github.com/tendermint/tendermint/rpc/client"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
-)
-
-// StatusMock returns the result specified by the Call
-type StatusMock struct {
- Call
-}
-
-func (m *StatusMock) _assertStatusClient() client.StatusClient {
- return m
-}
-
-func (m *StatusMock) Status() (*ctypes.ResultStatus, error) {
- res, err := m.GetResponse(nil)
- if err != nil {
- return nil, err
- }
- return res.(*ctypes.ResultStatus), nil
-}
-
-// StatusRecorder can wrap another type (StatusMock, full client)
-// and record the status calls
-type StatusRecorder struct {
- Client client.StatusClient
- Calls []Call
-}
-
-func NewStatusRecorder(client client.StatusClient) *StatusRecorder {
- return &StatusRecorder{
- Client: client,
- Calls: []Call{},
- }
-}
-
-func (r *StatusRecorder) _assertStatusClient() client.StatusClient {
- return r
-}
-
-func (r *StatusRecorder) addCall(call Call) {
- r.Calls = append(r.Calls, call)
-}
-
-func (r *StatusRecorder) Status() (*ctypes.ResultStatus, error) {
- res, err := r.Client.Status()
- r.addCall(Call{
- Name: "status",
- Response: res,
- Error: err,
- })
- return res, err
-}
+++ /dev/null
-package mock_test
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- data "github.com/tendermint/go-wire/data"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
-
- "github.com/tendermint/tendermint/rpc/client/mock"
-)
-
-func TestStatus(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
-
- m := &mock.StatusMock{
- Call: mock.Call{
- Response: &ctypes.ResultStatus{
- LatestBlockHash: data.Bytes("block"),
- LatestAppHash: data.Bytes("app"),
- LatestBlockHeight: 10,
- }},
- }
-
- r := mock.NewStatusRecorder(m)
- require.Equal(0, len(r.Calls))
-
- // make sure response works proper
- status, err := r.Status()
- require.Nil(err, "%+v", err)
- assert.EqualValues("block", status.LatestBlockHash)
- assert.EqualValues(10, status.LatestBlockHeight)
-
- // make sure recorder works properly
- require.Equal(1, len(r.Calls))
- rs := r.Calls[0]
- assert.Equal("status", rs.Name)
- assert.Nil(rs.Args)
- assert.Nil(rs.Error)
- require.NotNil(rs.Response)
- st, ok := rs.Response.(*ctypes.ResultStatus)
- require.True(ok)
- assert.EqualValues("block", st.LatestBlockHash)
- assert.EqualValues(10, st.LatestBlockHeight)
-}
+++ /dev/null
-package client_test
-
-import (
- "strings"
- "testing"
-
- "github.com/bytom/rpc/client"
- rpctest "github.com/bytom/rpc/test"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/tendermint/merkleeyes/iavl"
- merktest "github.com/tendermint/merkleeyes/testutil"
-)
-
-func getHTTPClient() *client.HTTP {
- rpcAddr := rpctest.GetConfig().RPC.ListenAddress
- return client.NewHTTP(rpcAddr, "/websocket")
-}
-
-func getLocalClient() client.Local {
- return client.NewLocal(node)
-}
-
-// GetClients returns a slice of clients for table-driven tests
-func GetClients() []client.Client {
- return []client.Client{
- getHTTPClient(),
- getLocalClient(),
- }
-}
-
-// Make sure status is correct (we connect properly)
-func TestStatus(t *testing.T) {
- for i, c := range GetClients() {
- moniker := rpctest.GetConfig().Moniker
- status, err := c.Status()
- require.Nil(t, err, "%d: %+v", i, err)
- assert.Equal(t, moniker, status.NodeInfo.Moniker)
- }
-}
-
-// Make sure info is correct (we connect properly)
-func TestInfo(t *testing.T) {
- for i, c := range GetClients() {
- // status, err := c.Status()
- // require.Nil(t, err, "%+v", err)
- info, err := c.ABCIInfo()
- require.Nil(t, err, "%d: %+v", i, err)
- // TODO: this is not correct - fix merkleeyes!
- // assert.EqualValues(t, status.LatestBlockHeight, info.Response.LastBlockHeight)
- assert.True(t, strings.HasPrefix(info.Response.Data, "size"))
- }
-}
-
-func TestNetInfo(t *testing.T) {
- for i, c := range GetClients() {
- nc, ok := c.(client.NetworkClient)
- require.True(t, ok, "%d", i)
- netinfo, err := nc.NetInfo()
- require.Nil(t, err, "%d: %+v", i, err)
- assert.True(t, netinfo.Listening)
- assert.Equal(t, 0, len(netinfo.Peers))
- }
-}
-
-func TestDumpConsensusState(t *testing.T) {
- for i, c := range GetClients() {
- // FIXME: fix server so it doesn't panic on invalid input
- nc, ok := c.(client.NetworkClient)
- require.True(t, ok, "%d", i)
- cons, err := nc.DumpConsensusState()
- require.Nil(t, err, "%d: %+v", i, err)
- assert.NotEmpty(t, cons.RoundState)
- assert.Empty(t, cons.PeerRoundStates)
- }
-}
-
-func TestGenesisAndValidators(t *testing.T) {
- for i, c := range GetClients() {
-
- // make sure this is the right genesis file
- gen, err := c.Genesis()
- require.Nil(t, err, "%d: %+v", i, err)
- // get the genesis validator
- require.Equal(t, 1, len(gen.Genesis.Validators))
- gval := gen.Genesis.Validators[0]
-
- // get the current validators
- vals, err := c.Validators()
- require.Nil(t, err, "%d: %+v", i, err)
- require.Equal(t, 1, len(vals.Validators))
- val := vals.Validators[0]
-
- // make sure the current set is also the genesis set
- assert.Equal(t, gval.Amount, val.VotingPower)
- assert.Equal(t, gval.PubKey, val.PubKey)
- }
-}
-
-// Make some app checks
-func TestAppCalls(t *testing.T) {
- assert, require := assert.New(t), require.New(t)
- for i, c := range GetClients() {
-
- // get an offset of height to avoid racing and guessing
- s, err := c.Status()
- require.Nil(err, "%d: %+v", i, err)
- // sh is start height or status height
- sh := s.LatestBlockHeight
-
- // look for the future
- _, err = c.Block(sh + 2)
- assert.NotNil(err) // no block yet
-
- // write something
- k, v, tx := merktest.MakeTxKV()
- bres, err := c.BroadcastTxCommit(tx)
- require.Nil(err, "%d: %+v", i, err)
- require.True(bres.DeliverTx.Code.IsOK())
- txh := bres.Height
- apph := txh + 1 // this is where the tx will be applied to the state
-
- // wait before querying
- client.WaitForHeight(c, apph, nil)
- qres, err := c.ABCIQuery("/key", k, false)
- if assert.Nil(err) && assert.True(qres.Code.IsOK()) {
- // assert.Equal(k, data.GetKey()) // only returned for proofs
- assert.EqualValues(v, qres.Value)
- }
-
- // make sure we can lookup the tx with proof
- // ptx, err := c.Tx(bres.Hash, true)
- ptx, err := c.Tx(bres.Hash, true)
- require.Nil(err, "%d: %+v", i, err)
- assert.Equal(txh, ptx.Height)
- assert.EqualValues(tx, ptx.Tx)
-
- // and we can even check the block is added
- block, err := c.Block(apph)
- require.Nil(err, "%d: %+v", i, err)
- appHash := block.BlockMeta.Header.AppHash
- assert.True(len(appHash) > 0)
- assert.EqualValues(apph, block.BlockMeta.Header.Height)
-
- // check blockchain info, now that we know there is info
- // TODO: is this commented somewhere that they are returned
- // in order of descending height???
- info, err := c.BlockchainInfo(apph, apph)
- require.Nil(err, "%d: %+v", i, err)
- assert.True(info.LastHeight >= apph)
- if assert.Equal(1, len(info.BlockMetas)) {
- lastMeta := info.BlockMetas[0]
- assert.EqualValues(apph, lastMeta.Header.Height)
- bMeta := block.BlockMeta
- assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash)
- assert.Equal(bMeta.BlockID, lastMeta.BlockID)
- }
-
- // and get the corresponding commit with the same apphash
- commit, err := c.Commit(apph)
- require.Nil(err, "%d: %+v", i, err)
- cappHash := commit.Header.AppHash
- assert.Equal(appHash, cappHash)
- assert.NotNil(commit.Commit)
-
- // compare the commits (note Commit(2) has commit from Block(3))
- commit2, err := c.Commit(apph - 1)
- require.Nil(err, "%d: %+v", i, err)
- assert.Equal(block.Block.LastCommit, commit2.Commit)
-
- // and we got a proof that works!
- pres, err := c.ABCIQuery("/key", k, true)
- if assert.Nil(err) && assert.True(pres.Code.IsOK()) {
- proof, err := iavl.ReadProof(pres.Proof)
- if assert.Nil(err) {
- key := pres.Key
- value := pres.Value
- assert.EqualValues(appHash, proof.RootHash)
- valid := proof.Verify(key, value, appHash)
- assert.True(valid)
- }
- }
- }
-}
+++ /dev/null
-package core
-
-import (
- ctypes "github.com/bytom/rpc/core/types"
-)
-
-func BlockHeight() (*ctypes.ResultBlockchainInfo, error) {
- return &ctypes.ResultBlockchainInfo{LastHeight: blockStore.Height()}, nil
-}
+++ /dev/null
-package core
-
-import (
- "github.com/bytom/blockchain/txdb"
- p2p "github.com/bytom/p2p"
- "github.com/bytom/types"
- "github.com/tendermint/tmlibs/log"
-)
-
-type P2P interface {
- Listeners() []p2p.Listener
- Peers() p2p.IPeerSet
- NumPeers() (outbound, inbound, dialig int)
- NodeInfo() *p2p.NodeInfo
- IsListening() bool
- DialSeeds(*p2p.AddrBook, []string) error
-}
-
-var (
- // external, thread safe interfaces
- eventSwitch types.EventSwitch
- blockStore *txdb.Store
- p2pSwitch P2P
-
- addrBook *p2p.AddrBook
-
- logger log.Logger
-)
-
-func SetEventSwitch(evsw types.EventSwitch) {
- eventSwitch = evsw
-}
-
-func SetBlockStore(bs *txdb.Store) {
- blockStore = bs
-}
-
-func SetSwitch(sw P2P) {
- p2pSwitch = sw
-}
-
-func SetAddrBook(book *p2p.AddrBook) {
- addrBook = book
-}
-
-func SetLogger(l log.Logger) {
- logger = l
-}
+++ /dev/null
-package core
-
-import (
- "github.com/bytom/protocol/bc/legacy"
- ctypes "github.com/bytom/rpc/core/types"
-)
-
-func GetWork() (*ctypes.ResultBlockHeaderInfo, error) {
- return &ctypes.ResultBlockHeaderInfo{}, nil
-}
-
-func SubmitWork(height uint64) (bool, error) {
- block := legacy.Block{
- BlockHeader: legacy.BlockHeader{
- Version: 1,
- Height: height,
- },
- }
- blockStore.SaveBlock(&block)
- return true, nil
-}
+++ /dev/null
-package core
-
-import (
- rpc "github.com/bytom/rpc/lib/server"
-)
-
-// TODO: better system than "unsafe" prefix
-var Routes = map[string]*rpc.RPCFunc{
- // subscribe/unsubscribe are reserved for websocket events.
- "net_info": rpc.NewRPCFunc(NetInfo, ""),
- "getwork": rpc.NewRPCFunc(GetWork, ""),
- "submitwork": rpc.NewRPCFunc(SubmitWork, "height"),
- "getBlockHeight": rpc.NewRPCFunc(BlockHeight, ""),
-}
-
-func AddUnsafeRoutes() {
- // control API
- Routes["dial_seeds"] = rpc.NewRPCFunc(UnsafeDialSeeds, "seeds")
-}
+++ /dev/null
-package core
-
-// a single integer is sufficient here
-
-const Version = "3" // rpc routes for profiling, setting config
+++ /dev/null
-package core_grpc
-
-import (
- "fmt"
- "net"
- "strings"
- "time"
-
- "google.golang.org/grpc"
-
- . "github.com/tendermint/tmlibs/common"
-)
-
-// Start the grpcServer in a go routine
-func StartGRPCServer(protoAddr string) (net.Listener, error) {
- parts := strings.SplitN(protoAddr, "://", 2)
- if len(parts) != 2 {
- return nil, fmt.Errorf("Invalid listen address for grpc server (did you forget a tcp:// prefix?) : %s", protoAddr)
- }
- proto, addr := parts[0], parts[1]
- ln, err := net.Listen(proto, addr)
- if err != nil {
- return nil, err
- }
-
- grpcServer := grpc.NewServer()
- //RegisterBroadcastAPIServer(grpcServer, &broadcastAPI{})
- go grpcServer.Serve(ln)
-
- return ln, nil
-}
-
-// Start the client by dialing the server
-func StartGRPCClient(protoAddr string) BroadcastAPIClient {
- conn, err := grpc.Dial(protoAddr, grpc.WithInsecure(), grpc.WithDialer(dialerFunc))
- if err != nil {
- panic(err)
- }
- return NewBroadcastAPIClient(conn)
-}
-
-func dialerFunc(addr string, timeout time.Duration) (net.Conn, error) {
- return Connect(addr)
-}
+++ /dev/null
-#! /bin/bash
-
-protoc --go_out=plugins=grpc:. -I $GOPATH/src/ -I . types.proto
+++ /dev/null
-// Code generated by protoc-gen-go.
-// source: types.proto
-// DO NOT EDIT!
-
-/*
-Package core_grpc is a generated protocol buffer package.
-
-It is generated from these files:
- types.proto
-
-It has these top-level messages:
- RequestBroadcastTx
- ResponseBroadcastTx
-*/
-package core_grpc
-
-import proto "github.com/golang/protobuf/proto"
-import fmt "fmt"
-import math "math"
-import types "github.com/tendermint/abci/types"
-
-import (
- context "golang.org/x/net/context"
- grpc "google.golang.org/grpc"
-)
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ = proto.Marshal
-var _ = fmt.Errorf
-var _ = math.Inf
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the proto package it is being compiled against.
-// A compilation error at this line likely means your copy of the
-// proto package needs to be updated.
-const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
-
-type RequestBroadcastTx struct {
- Tx []byte `protobuf:"bytes,1,opt,name=tx,proto3" json:"tx,omitempty"`
-}
-
-func (m *RequestBroadcastTx) Reset() { *m = RequestBroadcastTx{} }
-func (m *RequestBroadcastTx) String() string { return proto.CompactTextString(m) }
-func (*RequestBroadcastTx) ProtoMessage() {}
-func (*RequestBroadcastTx) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} }
-
-func (m *RequestBroadcastTx) GetTx() []byte {
- if m != nil {
- return m.Tx
- }
- return nil
-}
-
-type ResponseBroadcastTx struct {
- CheckTx *types.ResponseCheckTx `protobuf:"bytes,1,opt,name=check_tx,json=checkTx" json:"check_tx,omitempty"`
- DeliverTx *types.ResponseDeliverTx `protobuf:"bytes,2,opt,name=deliver_tx,json=deliverTx" json:"deliver_tx,omitempty"`
-}
-
-func (m *ResponseBroadcastTx) Reset() { *m = ResponseBroadcastTx{} }
-func (m *ResponseBroadcastTx) String() string { return proto.CompactTextString(m) }
-func (*ResponseBroadcastTx) ProtoMessage() {}
-func (*ResponseBroadcastTx) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} }
-
-func (m *ResponseBroadcastTx) GetCheckTx() *types.ResponseCheckTx {
- if m != nil {
- return m.CheckTx
- }
- return nil
-}
-
-func (m *ResponseBroadcastTx) GetDeliverTx() *types.ResponseDeliverTx {
- if m != nil {
- return m.DeliverTx
- }
- return nil
-}
-
-func init() {
- proto.RegisterType((*RequestBroadcastTx)(nil), "core_grpc.RequestBroadcastTx")
- proto.RegisterType((*ResponseBroadcastTx)(nil), "core_grpc.ResponseBroadcastTx")
-}
-
-// Reference imports to suppress errors if they are not otherwise used.
-var _ context.Context
-var _ grpc.ClientConn
-
-// This is a compile-time assertion to ensure that this generated file
-// is compatible with the grpc package it is being compiled against.
-const _ = grpc.SupportPackageIsVersion4
-
-// Client API for BroadcastAPI service
-
-type BroadcastAPIClient interface {
- BroadcastTx(ctx context.Context, in *RequestBroadcastTx, opts ...grpc.CallOption) (*ResponseBroadcastTx, error)
-}
-
-type broadcastAPIClient struct {
- cc *grpc.ClientConn
-}
-
-func NewBroadcastAPIClient(cc *grpc.ClientConn) BroadcastAPIClient {
- return &broadcastAPIClient{cc}
-}
-
-func (c *broadcastAPIClient) BroadcastTx(ctx context.Context, in *RequestBroadcastTx, opts ...grpc.CallOption) (*ResponseBroadcastTx, error) {
- out := new(ResponseBroadcastTx)
- err := grpc.Invoke(ctx, "/core_grpc.BroadcastAPI/BroadcastTx", in, out, c.cc, opts...)
- if err != nil {
- return nil, err
- }
- return out, nil
-}
-
-// Server API for BroadcastAPI service
-
-type BroadcastAPIServer interface {
- BroadcastTx(context.Context, *RequestBroadcastTx) (*ResponseBroadcastTx, error)
-}
-
-func RegisterBroadcastAPIServer(s *grpc.Server, srv BroadcastAPIServer) {
- s.RegisterService(&_BroadcastAPI_serviceDesc, srv)
-}
-
-func _BroadcastAPI_BroadcastTx_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
- in := new(RequestBroadcastTx)
- if err := dec(in); err != nil {
- return nil, err
- }
- if interceptor == nil {
- return srv.(BroadcastAPIServer).BroadcastTx(ctx, in)
- }
- info := &grpc.UnaryServerInfo{
- Server: srv,
- FullMethod: "/core_grpc.BroadcastAPI/BroadcastTx",
- }
- handler := func(ctx context.Context, req interface{}) (interface{}, error) {
- return srv.(BroadcastAPIServer).BroadcastTx(ctx, req.(*RequestBroadcastTx))
- }
- return interceptor(ctx, in, info, handler)
-}
-
-var _BroadcastAPI_serviceDesc = grpc.ServiceDesc{
- ServiceName: "core_grpc.BroadcastAPI",
- HandlerType: (*BroadcastAPIServer)(nil),
- Methods: []grpc.MethodDesc{
- {
- MethodName: "BroadcastTx",
- Handler: _BroadcastAPI_BroadcastTx_Handler,
- },
- },
- Streams: []grpc.StreamDesc{},
- Metadata: "types.proto",
-}
-
-func init() { proto.RegisterFile("types.proto", fileDescriptor0) }
-
-var fileDescriptor0 = []byte{
- // 226 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xe2, 0xe2, 0x2e, 0xa9, 0x2c, 0x48,
- 0x2d, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x4c, 0xce, 0x2f, 0x4a, 0x8d, 0x4f, 0x2f,
- 0x2a, 0x48, 0x96, 0xd2, 0x49, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, 0xd5, 0x2f,
- 0x49, 0xcd, 0x4b, 0x49, 0x2d, 0xca, 0xcd, 0xcc, 0x2b, 0xd1, 0x2f, 0xc9, 0x2d, 0x2e, 0xd0, 0x07,
- 0x6b, 0xd1, 0x47, 0xd2, 0xa8, 0xa4, 0xc2, 0x25, 0x14, 0x94, 0x5a, 0x58, 0x9a, 0x5a, 0x5c, 0xe2,
- 0x54, 0x94, 0x9f, 0x98, 0x92, 0x9c, 0x58, 0x5c, 0x12, 0x52, 0x21, 0xc4, 0xc7, 0xc5, 0x54, 0x52,
- 0x21, 0xc1, 0xa8, 0xc0, 0xa8, 0xc1, 0x13, 0xc4, 0x54, 0x52, 0xa1, 0x54, 0xc7, 0x25, 0x1c, 0x94,
- 0x5a, 0x5c, 0x90, 0x9f, 0x57, 0x9c, 0x8a, 0xac, 0xcc, 0x90, 0x8b, 0x23, 0x39, 0x23, 0x35, 0x39,
- 0x3b, 0x1e, 0xaa, 0x98, 0xdb, 0x48, 0x4c, 0x0f, 0x62, 0x38, 0x4c, 0xb5, 0x33, 0x48, 0x3a, 0xa4,
- 0x22, 0x88, 0x3d, 0x19, 0xc2, 0x10, 0x32, 0xe1, 0xe2, 0x4c, 0x2c, 0x28, 0x48, 0xcd, 0x4b, 0x01,
- 0xe9, 0x61, 0x02, 0xeb, 0x11, 0x47, 0xd3, 0xe3, 0x08, 0x96, 0x0f, 0xa9, 0x08, 0xe2, 0x48, 0x84,
- 0xb2, 0x8c, 0x62, 0xb8, 0x78, 0xe0, 0xf6, 0x3a, 0x06, 0x78, 0x0a, 0xf9, 0x70, 0x71, 0x23, 0xbb,
- 0x43, 0x56, 0x0f, 0xee, 0x7d, 0x3d, 0x4c, 0xdf, 0x48, 0xc9, 0xa1, 0x48, 0x63, 0x78, 0x23, 0x89,
- 0x0d, 0x1c, 0x14, 0xc6, 0x80, 0x00, 0x00, 0x00, 0xff, 0xff, 0x68, 0x73, 0x87, 0xb0, 0x52, 0x01,
- 0x00, 0x00,
-}
+++ /dev/null
-syntax = "proto3";
-package core_grpc;
-
-import "github.com/tendermint/abci/types/types.proto";
-
-//----------------------------------------
-// Message types
-
-//----------------------------------------
-// Request types
-
-message RequestBroadcastTx {
- bytes tx = 1;
-}
-
-//----------------------------------------
-// Response types
-
-message ResponseBroadcastTx{
- types.ResponseCheckTx check_tx = 1;
- types.ResponseDeliverTx deliver_tx = 2;
-}
-
-//----------------------------------------
-// Service Definition
-
-service BroadcastAPI {
- rpc BroadcastTx(RequestBroadcastTx) returns (ResponseBroadcastTx) ;
-}
+++ /dev/null
-FROM golang:latest
-
-RUN mkdir -p /go/src/github.com/tendermint/tendermint/rpc/lib
-WORKDIR /go/src/github.com/tendermint/tendermint/rpc/lib
-
-COPY Makefile /go/src/github.com/tendermint/tendermint/rpc/lib/
-# COPY glide.yaml /go/src/github.com/tendermint/tendermint/rpc/lib/
-# COPY glide.lock /go/src/github.com/tendermint/tendermint/rpc/lib/
-
-COPY . /go/src/github.com/tendermint/tendermint/rpc/lib
-
-RUN make get_deps
+++ /dev/null
-PACKAGES=$(shell go list ./... | grep -v "test")
-
-all: get_deps test
-
-test:
- @echo "--> Running go test --race"
- @go test --race $(PACKAGES)
- @echo "--> Running integration tests"
- @bash ./test/integration_test.sh
-
-get_deps:
- @echo "--> Running go get"
- @go get -v -d $(PACKAGES)
- @go list -f '{{join .TestImports "\n"}}' ./... | \
- grep -v /vendor/ | sort | uniq | \
- xargs go get -v -d
-
-.PHONY: all test get_deps
+++ /dev/null
-# tendermint/rpc/lib
-
-[![CircleCI](https://circleci.com/gh/tendermint/tendermint/rpc/lib.svg?style=svg)](https://circleci.com/gh/tendermint/tendermint/rpc/lib)
-
-HTTP RPC server supporting calls via uri params, jsonrpc, and jsonrpc over websockets
-
-# Client Requests
-
-Suppose we want to expose the rpc function `HelloWorld(name string, num int)`.
-
-## GET (URI)
-
-As a GET request, it would have URI encoded parameters, and look like:
-
-```
-curl 'http://localhost:8008/hello_world?name="my_world"&num=5'
-```
-
-Note the `'` around the url, which is just so bash doesn't ignore the quotes in `"my_world"`.
-This should also work:
-
-```
-curl http://localhost:8008/hello_world?name=\"my_world\"&num=5
-```
-
-A GET request to `/` returns a list of available endpoints.
-For those which take arguments, the arguments will be listed in order, with `_` where the actual value should be.
-
-## POST (JSONRPC)
-
-As a POST request, we use JSONRPC. For instance, the same request would have this as the body:
-
-```
-{
- "jsonrpc": "2.0",
- "id": "anything",
- "method": "hello_world",
- "params": {
- "name": "my_world",
- "num": 5
- }
-}
-```
-
-With the above saved in file `data.json`, we can make the request with
-
-```
-curl --data @data.json http://localhost:8008
-```
-
-## WebSocket (JSONRPC)
-
-All requests are exposed over websocket in the same form as the POST JSONRPC.
-Websocket connections are available at their own endpoint, typically `/websocket`,
-though this is configurable when starting the server.
-
-# Server Definition
-
-Define some types and routes:
-
-```
-type ResultStatus struct {
- Value string
-}
-
-// Define some routes
-var Routes = map[string]*rpcserver.RPCFunc{
- "status": rpcserver.NewRPCFunc(Status, "arg"),
-}
-
-// an rpc function
-func Status(v string) (*ResultStatus, error) {
- return &ResultStatus{v}, nil
-}
-
-```
-
-Now start the server:
-
-```
-mux := http.NewServeMux()
-rpcserver.RegisterRPCFuncs(mux, Routes)
-wm := rpcserver.NewWebsocketManager(Routes, nil)
-mux.HandleFunc("/websocket", wm.WebsocketHandler)
-logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
-go func() {
- _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger)
- if err != nil {
- panic(err)
- }
-}()
-
-```
-
-Note that unix sockets are supported as well (eg. `/path/to/socket` instead of `0.0.0.0:8008`)
-
-Now see all available endpoints by sending a GET request to `0.0.0.0:8008`.
-Each route is available as a GET request, as a JSONRPCv2 POST request, and via JSONRPCv2 over websockets.
-
-
-# Examples
-
-* [Tendermint](https://github.com/tendermint/tendermint/blob/master/rpc/core/routes.go)
-* [tm-monitor](https://github.com/tendermint/tools/blob/master/tm-monitor/rpc.go)
-
-## CHANGELOG
-
-### 0.7.0
-
-BREAKING CHANGES:
-
-- removed `Client` empty interface
-- `ClientJSONRPC#Call` `params` argument became a map
-- rename `ClientURI` -> `URIClient`, `ClientJSONRPC` -> `JSONRPCClient`
-
-IMPROVEMENTS:
-
-- added `HTTPClient` interface, which can be used for both `ClientURI`
-and `ClientJSONRPC`
-- all params are now optional (Golang's default will be used if some param is missing)
-- added `Call` method to `WSClient` (see method's doc for details)
+++ /dev/null
-machine:
- environment:
- GOPATH: /home/ubuntu/.go_workspace
- REPO: $GOPATH/src/github.com/$CIRCLE_PROJECT_USERNAME/$CIRCLE_PROJECT_REPONAME
- hosts:
- circlehost: 127.0.0.1
- localhost: 127.0.0.1
-
-checkout:
- post:
- - rm -rf $REPO
- - mkdir -p $HOME/.go_workspace/src/github.com/$CIRCLE_PROJECT_USERNAME
- - mv $HOME/$CIRCLE_PROJECT_REPONAME $REPO
-
-dependencies:
- override:
- - "cd $REPO && make get_deps"
-
-test:
- override:
- - "cd $REPO && make test"
+++ /dev/null
-package rpcclient
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-type Tx []byte
-
-type Foo struct {
- Bar int
- Baz string
-}
-
-func TestArgToJSON(t *testing.T) {
- assert := assert.New(t)
- require := require.New(t)
-
- cases := []struct {
- input interface{}
- expected string
- }{
- {[]byte("1234"), "0x31323334"},
- {Tx("654"), "0x363534"},
- {Foo{7, "hello"}, `{"Bar":7,"Baz":"hello"}`},
- }
-
- for i, tc := range cases {
- args := map[string]interface{}{"data": tc.input}
- err := argsToJson(args)
- require.Nil(err, "%d: %+v", i, err)
- require.Equal(1, len(args), "%d", i)
- data, ok := args["data"].(string)
- require.True(ok, "%d: %#v", i, args["data"])
- assert.Equal(tc.expected, data, "%d", i)
- }
-}
+++ /dev/null
-package rpcclient
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net"
- "net/http"
- "net/url"
- "reflect"
- "strings"
-
- types "github.com/bytom/rpc/lib/types"
- "github.com/pkg/errors"
- cmn "github.com/tendermint/tmlibs/common"
-)
-
-// HTTPClient is a common interface for JSONRPCClient and URIClient.
-type HTTPClient interface {
- Call(method string, params map[string]interface{}, result interface{}) (interface{}, error)
-}
-
-// TODO: Deprecate support for IP:PORT or /path/to/socket
-func makeHTTPDialer(remoteAddr string) (string, func(string, string) (net.Conn, error)) {
-
- parts := strings.SplitN(remoteAddr, "://", 2)
- var protocol, address string
- if len(parts) != 2 {
- cmn.PanicSanity(fmt.Sprintf("Expected fully formed listening address, including the tcp:// or unix:// prefix, given %s", remoteAddr))
- } else {
- protocol, address = parts[0], parts[1]
- }
-
- trimmedAddress := strings.Replace(address, "/", ".", -1) // replace / with . for http requests (dummy domain)
- return trimmedAddress, func(proto, addr string) (net.Conn, error) {
- return net.Dial(protocol, address)
- }
-}
-
-// We overwrite the http.Client.Dial so we can do http over tcp or unix.
-// remoteAddr should be fully featured (eg. with tcp:// or unix://)
-func makeHTTPClient(remoteAddr string) (string, *http.Client) {
- address, dialer := makeHTTPDialer(remoteAddr)
- return "http://" + address, &http.Client{
- Transport: &http.Transport{
- Dial: dialer,
- },
- }
-}
-
-//------------------------------------------------------------------------------------
-
-// JSON rpc takes params as a slice
-type JSONRPCClient struct {
- address string
- client *http.Client
-}
-
-func NewJSONRPCClient(remote string) *JSONRPCClient {
- address, client := makeHTTPClient(remote)
- return &JSONRPCClient{
- address: address,
- client: client,
- }
-}
-
-func (c *JSONRPCClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
- request, err := types.MapToRequest("", method, params)
- if err != nil {
- return nil, err
- }
- requestBytes, err := json.Marshal(request)
- if err != nil {
- return nil, err
- }
- // log.Info(string(requestBytes))
- requestBuf := bytes.NewBuffer(requestBytes)
- // log.Info(Fmt("RPC request to %v (%v): %v", c.remote, method, string(requestBytes)))
- httpResponse, err := c.client.Post(c.address, "text/json", requestBuf)
- if err != nil {
- return nil, err
- }
- defer httpResponse.Body.Close()
- responseBytes, err := ioutil.ReadAll(httpResponse.Body)
- if err != nil {
- return nil, err
- }
- // log.Info(Fmt("RPC response: %v", string(responseBytes)))
- return unmarshalResponseBytes(responseBytes, result)
-}
-
-//-------------------------------------------------------------
-
-// URI takes params as a map
-type URIClient struct {
- address string
- client *http.Client
-}
-
-func NewURIClient(remote string) *URIClient {
- address, client := makeHTTPClient(remote)
- return &URIClient{
- address: address,
- client: client,
- }
-}
-
-func (c *URIClient) Call(method string, params map[string]interface{}, result interface{}) (interface{}, error) {
- values, err := argsToURLValues(params)
- if err != nil {
- return nil, err
- }
- // log.Info(Fmt("URI request to %v (%v): %v", c.address, method, values))
- resp, err := c.client.PostForm(c.address+"/"+method, values)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
- responseBytes, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- return nil, err
- }
- return unmarshalResponseBytes(responseBytes, result)
-}
-
-//------------------------------------------------
-
-func unmarshalResponseBytes(responseBytes []byte, result interface{}) (interface{}, error) {
- // read response
- // if rpc/core/types is imported, the result will unmarshal
- // into the correct type
- // log.Notice("response", "response", string(responseBytes))
- var err error
- response := &types.RPCResponse{}
- err = json.Unmarshal(responseBytes, response)
- if err != nil {
- return nil, errors.Errorf("Error unmarshalling rpc response: %v", err)
- }
- errorStr := response.Error
- if errorStr != "" {
- return nil, errors.Errorf("Response error: %v", errorStr)
- }
- // unmarshal the RawMessage into the result
- err = json.Unmarshal(*response.Result, result)
- if err != nil {
- return nil, errors.Errorf("Error unmarshalling rpc response result: %v", err)
- }
- return result, nil
-}
-
-func argsToURLValues(args map[string]interface{}) (url.Values, error) {
- values := make(url.Values)
- if len(args) == 0 {
- return values, nil
- }
- err := argsToJson(args)
- if err != nil {
- return nil, err
- }
- for key, val := range args {
- values.Set(key, val.(string))
- }
- return values, nil
-}
-
-func argsToJson(args map[string]interface{}) error {
- for k, v := range args {
- rt := reflect.TypeOf(v)
- isByteSlice := rt.Kind() == reflect.Slice && rt.Elem().Kind() == reflect.Uint8
- if isByteSlice {
- bytes := reflect.ValueOf(v).Bytes()
- args[k] = fmt.Sprintf("0x%X", bytes)
- continue
- }
-
- // Pass everything else to go-wire
- data, err := json.Marshal(v)
- if err != nil {
- return err
- }
- args[k] = string(data)
- }
- return nil
-}
+++ /dev/null
-package rpcclient
-
-import (
- "encoding/json"
- "net"
- "net/http"
- "time"
-
- types "github.com/bytom/rpc/lib/types"
- "github.com/gorilla/websocket"
- "github.com/pkg/errors"
- cmn "github.com/tendermint/tmlibs/common"
-)
-
-const (
- wsResultsChannelCapacity = 10
- wsErrorsChannelCapacity = 1
- wsWriteTimeoutSeconds = 10
-)
-
-type WSClient struct {
- cmn.BaseService
- Address string // IP:PORT or /path/to/socket
- Endpoint string // /websocket/url/endpoint
- Dialer func(string, string) (net.Conn, error)
- *websocket.Conn
- ResultsCh chan json.RawMessage // closes upon WSClient.Stop()
- ErrorsCh chan error // closes upon WSClient.Stop()
-}
-
-// create a new connection
-func NewWSClient(remoteAddr, endpoint string) *WSClient {
- addr, dialer := makeHTTPDialer(remoteAddr)
- wsClient := &WSClient{
- Address: addr,
- Dialer: dialer,
- Endpoint: endpoint,
- Conn: nil,
- }
- wsClient.BaseService = *cmn.NewBaseService(nil, "WSClient", wsClient)
- return wsClient
-}
-
-func (wsc *WSClient) String() string {
- return wsc.Address + ", " + wsc.Endpoint
-}
-
-// OnStart implements cmn.BaseService interface
-func (wsc *WSClient) OnStart() error {
- wsc.BaseService.OnStart()
- err := wsc.dial()
- if err != nil {
- return err
- }
- wsc.ResultsCh = make(chan json.RawMessage, wsResultsChannelCapacity)
- wsc.ErrorsCh = make(chan error, wsErrorsChannelCapacity)
- go wsc.receiveEventsRoutine()
- return nil
-}
-
-// OnReset implements cmn.BaseService interface
-func (wsc *WSClient) OnReset() error {
- return nil
-}
-
-func (wsc *WSClient) dial() error {
-
- // Dial
- dialer := &websocket.Dialer{
- NetDial: wsc.Dialer,
- Proxy: http.ProxyFromEnvironment,
- }
- rHeader := http.Header{}
- con, _, err := dialer.Dial("ws://"+wsc.Address+wsc.Endpoint, rHeader)
- if err != nil {
- return err
- }
- // Set the ping/pong handlers
- con.SetPingHandler(func(m string) error {
- // NOTE: https://github.com/gorilla/websocket/issues/97
- go con.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds))
- return nil
- })
- con.SetPongHandler(func(m string) error {
- // NOTE: https://github.com/gorilla/websocket/issues/97
- return nil
- })
- wsc.Conn = con
- return nil
-}
-
-// OnStop implements cmn.BaseService interface
-func (wsc *WSClient) OnStop() {
- wsc.BaseService.OnStop()
- wsc.Conn.Close()
- // ResultsCh/ErrorsCh is closed in receiveEventsRoutine.
-}
-
-func (wsc *WSClient) receiveEventsRoutine() {
- for {
- _, data, err := wsc.ReadMessage()
- if err != nil {
- wsc.Logger.Info("WSClient failed to read message", "error", err, "data", string(data))
- wsc.Stop()
- break
- } else {
- var response types.RPCResponse
- err := json.Unmarshal(data, &response)
- if err != nil {
- wsc.Logger.Info("WSClient failed to parse message", "error", err, "data", string(data))
- wsc.ErrorsCh <- err
- continue
- }
- if response.Error != "" {
- wsc.ErrorsCh <- errors.Errorf(response.Error)
- continue
- }
- wsc.ResultsCh <- *response.Result
- }
- }
- // this must be modified in the same go-routine that reads from the
- // connection to avoid race conditions
- wsc.Conn = nil
-
- // Cleanup
- close(wsc.ResultsCh)
- close(wsc.ErrorsCh)
-}
-
-// Subscribe to an event. Note the server must have a "subscribe" route
-// defined.
-func (wsc *WSClient) Subscribe(eventid string) error {
- params := map[string]interface{}{"event": eventid}
- request, err := types.MapToRequest("", "subscribe", params)
- if err == nil {
- err = wsc.WriteJSON(request)
- }
- return err
-}
-
-// Unsubscribe from an event. Note the server must have a "unsubscribe" route
-// defined.
-func (wsc *WSClient) Unsubscribe(eventid string) error {
- params := map[string]interface{}{"event": eventid}
- request, err := types.MapToRequest("", "unsubscribe", params)
- if err == nil {
- err = wsc.WriteJSON(request)
- }
- return err
-}
-
-// Call asynchronously calls a given method by sending an RPCRequest to the
-// server. Results will be available on ResultsCh, errors, if any, on ErrorsCh.
-func (wsc *WSClient) Call(method string, params map[string]interface{}) error {
- request, err := types.MapToRequest("", method, params)
- if err == nil {
- err = wsc.WriteJSON(request)
- }
- return err
-}
+++ /dev/null
-package rpc
-
-import (
- "bytes"
- crand "crypto/rand"
- "encoding/json"
- "fmt"
- "math/rand"
- "net/http"
- "os/exec"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/tendermint/go-wire/data"
- client "github.com/tendermint/tendermint/rpc/lib/client"
- server "github.com/tendermint/tendermint/rpc/lib/server"
- types "github.com/tendermint/tendermint/rpc/lib/types"
- "github.com/tendermint/tmlibs/log"
-)
-
-// Client and Server should work over tcp or unix sockets
-const (
- tcpAddr = "tcp://0.0.0.0:47768"
-
- unixSocket = "/tmp/rpc_test.sock"
- unixAddr = "unix://" + unixSocket
-
- websocketEndpoint = "/websocket/endpoint"
-)
-
-type ResultEcho struct {
- Value string `json:"value"`
-}
-
-type ResultEchoInt struct {
- Value int `json:"value"`
-}
-
-type ResultEchoBytes struct {
- Value []byte `json:"value"`
-}
-
-type ResultEchoDataBytes struct {
- Value data.Bytes `json:"value"`
-}
-
-// Define some routes
-var Routes = map[string]*server.RPCFunc{
- "echo": server.NewRPCFunc(EchoResult, "arg"),
- "echo_ws": server.NewWSRPCFunc(EchoWSResult, "arg"),
- "echo_bytes": server.NewRPCFunc(EchoBytesResult, "arg"),
- "echo_data_bytes": server.NewRPCFunc(EchoDataBytesResult, "arg"),
- "echo_int": server.NewRPCFunc(EchoIntResult, "arg"),
-}
-
-func EchoResult(v string) (*ResultEcho, error) {
- return &ResultEcho{v}, nil
-}
-
-func EchoWSResult(wsCtx types.WSRPCContext, v string) (*ResultEcho, error) {
- return &ResultEcho{v}, nil
-}
-
-func EchoIntResult(v int) (*ResultEchoInt, error) {
- return &ResultEchoInt{v}, nil
-}
-
-func EchoBytesResult(v []byte) (*ResultEchoBytes, error) {
- return &ResultEchoBytes{v}, nil
-}
-
-func EchoDataBytesResult(v data.Bytes) (*ResultEchoDataBytes, error) {
- return &ResultEchoDataBytes{v}, nil
-}
-
-// launch unix and tcp servers
-func init() {
- cmd := exec.Command("rm", "-f", unixSocket)
- err := cmd.Start()
- if err != nil {
- panic(err)
- }
- if err = cmd.Wait(); err != nil {
- panic(err)
- }
-
- mux := http.NewServeMux()
- server.RegisterRPCFuncs(mux, Routes, log.TestingLogger())
- wm := server.NewWebsocketManager(Routes, nil)
- wm.SetLogger(log.TestingLogger())
- mux.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
- go func() {
- _, err := server.StartHTTPServer(tcpAddr, mux, log.TestingLogger())
- if err != nil {
- panic(err)
- }
- }()
-
- mux2 := http.NewServeMux()
- server.RegisterRPCFuncs(mux2, Routes, log.TestingLogger())
- wm = server.NewWebsocketManager(Routes, nil)
- wm.SetLogger(log.TestingLogger())
- mux2.HandleFunc(websocketEndpoint, wm.WebsocketHandler)
- go func() {
- _, err := server.StartHTTPServer(unixAddr, mux2, log.TestingLogger())
- if err != nil {
- panic(err)
- }
- }()
-
- // wait for servers to start
- time.Sleep(time.Second * 2)
-}
-
-func echoViaHTTP(cl client.HTTPClient, val string) (string, error) {
- params := map[string]interface{}{
- "arg": val,
- }
- result := new(ResultEcho)
- if _, err := cl.Call("echo", params, result); err != nil {
- return "", err
- }
- return result.Value, nil
-}
-
-func echoIntViaHTTP(cl client.HTTPClient, val int) (int, error) {
- params := map[string]interface{}{
- "arg": val,
- }
- result := new(ResultEchoInt)
- if _, err := cl.Call("echo_int", params, result); err != nil {
- return 0, err
- }
- return result.Value, nil
-}
-
-func echoBytesViaHTTP(cl client.HTTPClient, bytes []byte) ([]byte, error) {
- params := map[string]interface{}{
- "arg": bytes,
- }
- result := new(ResultEchoBytes)
- if _, err := cl.Call("echo_bytes", params, result); err != nil {
- return []byte{}, err
- }
- return result.Value, nil
-}
-
-func echoDataBytesViaHTTP(cl client.HTTPClient, bytes data.Bytes) (data.Bytes, error) {
- params := map[string]interface{}{
- "arg": bytes,
- }
- result := new(ResultEchoDataBytes)
- if _, err := cl.Call("echo_data_bytes", params, result); err != nil {
- return []byte{}, err
- }
- return result.Value, nil
-}
-
-func testWithHTTPClient(t *testing.T, cl client.HTTPClient) {
- val := "acbd"
- got, err := echoViaHTTP(cl, val)
- require.Nil(t, err)
- assert.Equal(t, got, val)
-
- val2 := randBytes(t)
- got2, err := echoBytesViaHTTP(cl, val2)
- require.Nil(t, err)
- assert.Equal(t, got2, val2)
-
- val3 := data.Bytes(randBytes(t))
- got3, err := echoDataBytesViaHTTP(cl, val3)
- require.Nil(t, err)
- assert.Equal(t, got3, val3)
-
- val4 := rand.Intn(10000)
- got4, err := echoIntViaHTTP(cl, val4)
- require.Nil(t, err)
- assert.Equal(t, got4, val4)
-}
-
-func echoViaWS(cl *client.WSClient, val string) (string, error) {
- params := map[string]interface{}{
- "arg": val,
- }
- err := cl.Call("echo", params)
- if err != nil {
- return "", err
- }
-
- select {
- case msg := <-cl.ResultsCh:
- result := new(ResultEcho)
- err = json.Unmarshal(msg, result)
- if err != nil {
- return "", nil
- }
- return result.Value, nil
- case err := <-cl.ErrorsCh:
- return "", err
- }
-}
-
-func echoBytesViaWS(cl *client.WSClient, bytes []byte) ([]byte, error) {
- params := map[string]interface{}{
- "arg": bytes,
- }
- err := cl.Call("echo_bytes", params)
- if err != nil {
- return []byte{}, err
- }
-
- select {
- case msg := <-cl.ResultsCh:
- result := new(ResultEchoBytes)
- err = json.Unmarshal(msg, result)
- if err != nil {
- return []byte{}, nil
- }
- return result.Value, nil
- case err := <-cl.ErrorsCh:
- return []byte{}, err
- }
-}
-
-func testWithWSClient(t *testing.T, cl *client.WSClient) {
- val := "acbd"
- got, err := echoViaWS(cl, val)
- require.Nil(t, err)
- assert.Equal(t, got, val)
-
- val2 := randBytes(t)
- got2, err := echoBytesViaWS(cl, val2)
- require.Nil(t, err)
- assert.Equal(t, got2, val2)
-}
-
-//-------------
-
-func TestServersAndClientsBasic(t *testing.T) {
- serverAddrs := [...]string{tcpAddr, unixAddr}
- for _, addr := range serverAddrs {
- cl1 := client.NewURIClient(addr)
- fmt.Printf("=== testing server on %s using %v client", addr, cl1)
- testWithHTTPClient(t, cl1)
-
- cl2 := client.NewJSONRPCClient(tcpAddr)
- fmt.Printf("=== testing server on %s using %v client", addr, cl2)
- testWithHTTPClient(t, cl2)
-
- cl3 := client.NewWSClient(tcpAddr, websocketEndpoint)
- _, err := cl3.Start()
- require.Nil(t, err)
- fmt.Printf("=== testing server on %s using %v client", addr, cl3)
- testWithWSClient(t, cl3)
- cl3.Stop()
- }
-}
-
-func TestHexStringArg(t *testing.T) {
- cl := client.NewURIClient(tcpAddr)
- // should NOT be handled as hex
- val := "0xabc"
- got, err := echoViaHTTP(cl, val)
- require.Nil(t, err)
- assert.Equal(t, got, val)
-}
-
-func TestQuotedStringArg(t *testing.T) {
- cl := client.NewURIClient(tcpAddr)
- // should NOT be unquoted
- val := "\"abc\""
- got, err := echoViaHTTP(cl, val)
- require.Nil(t, err)
- assert.Equal(t, got, val)
-}
-
-func TestWSNewWSRPCFunc(t *testing.T) {
- cl := client.NewWSClient(tcpAddr, websocketEndpoint)
- _, err := cl.Start()
- require.Nil(t, err)
- defer cl.Stop()
-
- val := "acbd"
- params := map[string]interface{}{
- "arg": val,
- }
- err = cl.Call("echo_ws", params)
- require.Nil(t, err)
-
- select {
- case msg := <-cl.ResultsCh:
- result := new(ResultEcho)
- err = json.Unmarshal(msg, result)
- require.Nil(t, err)
- got := result.Value
- assert.Equal(t, got, val)
- case err := <-cl.ErrorsCh:
- t.Fatal(err)
- }
-}
-
-func TestWSHandlesArrayParams(t *testing.T) {
- cl := client.NewWSClient(tcpAddr, websocketEndpoint)
- _, err := cl.Start()
- require.Nil(t, err)
- defer cl.Stop()
-
- val := "acbd"
- params := []interface{}{val}
- request, err := types.ArrayToRequest("", "echo_ws", params)
- require.Nil(t, err)
- err = cl.WriteJSON(request)
- require.Nil(t, err)
-
- select {
- case msg := <-cl.ResultsCh:
- result := new(ResultEcho)
- err = json.Unmarshal(msg, result)
- require.Nil(t, err)
- got := result.Value
- assert.Equal(t, got, val)
- case err := <-cl.ErrorsCh:
- t.Fatalf("%+v", err)
- }
-}
-
-func randBytes(t *testing.T) []byte {
- n := rand.Intn(10) + 2
- buf := make([]byte, n)
- _, err := crand.Read(buf)
- require.Nil(t, err)
- return bytes.Replace(buf, []byte("="), []byte{100}, -1)
-}
+++ /dev/null
-package rpcserver
-
-import (
- "bytes"
- "encoding/hex"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/http"
- "reflect"
- "sort"
- "strings"
- "time"
-
- "github.com/gorilla/websocket"
- "github.com/pkg/errors"
-
- types "github.com/bytom/rpc/lib/types"
- cmn "github.com/tendermint/tmlibs/common"
- events "github.com/tendermint/tmlibs/events"
- "github.com/tendermint/tmlibs/log"
-)
-
-// Adds a route for each function in the funcMap, as well as general jsonrpc and websocket handlers for all functions.
-// "result" is the interface on which the result objects are registered, and is popualted with every RPCResponse
-func RegisterRPCFuncs(mux *http.ServeMux, funcMap map[string]*RPCFunc, logger log.Logger) {
- // HTTP endpoints
- for funcName, rpcFunc := range funcMap {
- mux.HandleFunc("/"+funcName, makeHTTPHandler(rpcFunc, logger))
- }
-
- // JSONRPC endpoints
- mux.HandleFunc("/", makeJSONRPCHandler(funcMap, logger))
-}
-
-//-------------------------------------
-// function introspection
-
-// holds all type information for each function
-type RPCFunc struct {
- f reflect.Value // underlying rpc function
- args []reflect.Type // type of each function arg
- returns []reflect.Type // type of each return arg
- argNames []string // name of each argument
- ws bool // websocket only
-}
-
-// wraps a function for quicker introspection
-// f is the function, args are comma separated argument names
-func NewRPCFunc(f interface{}, args string) *RPCFunc {
- return newRPCFunc(f, args, false)
-}
-
-func NewWSRPCFunc(f interface{}, args string) *RPCFunc {
- return newRPCFunc(f, args, true)
-}
-
-func newRPCFunc(f interface{}, args string, ws bool) *RPCFunc {
- var argNames []string
- if args != "" {
- argNames = strings.Split(args, ",")
- }
- return &RPCFunc{
- f: reflect.ValueOf(f),
- args: funcArgTypes(f),
- returns: funcReturnTypes(f),
- argNames: argNames,
- ws: ws,
- }
-}
-
-// return a function's argument types
-func funcArgTypes(f interface{}) []reflect.Type {
- t := reflect.TypeOf(f)
- n := t.NumIn()
- typez := make([]reflect.Type, n)
- for i := 0; i < n; i++ {
- typez[i] = t.In(i)
- }
- return typez
-}
-
-// return a function's return types
-func funcReturnTypes(f interface{}) []reflect.Type {
- t := reflect.TypeOf(f)
- n := t.NumOut()
- typez := make([]reflect.Type, n)
- for i := 0; i < n; i++ {
- typez[i] = t.Out(i)
- }
- return typez
-}
-
-// function introspection
-//-----------------------------------------------------------------------------
-// rpc.json
-
-// jsonrpc calls grab the given method's function info and runs reflect.Call
-func makeJSONRPCHandler(funcMap map[string]*RPCFunc, logger log.Logger) http.HandlerFunc {
- return func(w http.ResponseWriter, r *http.Request) {
- b, _ := ioutil.ReadAll(r.Body)
- // if its an empty request (like from a browser),
- // just display a list of functions
- if len(b) == 0 {
- writeListOfEndpoints(w, r, funcMap)
- return
- }
-
- var request types.RPCRequest
- err := json.Unmarshal(b, &request)
- if err != nil {
- WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error unmarshalling request: %v", err.Error())))
- return
- }
- if len(r.URL.Path) > 1 {
- WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Invalid JSONRPC endpoint %s", r.URL.Path)))
- return
- }
- rpcFunc := funcMap[request.Method]
- if rpcFunc == nil {
- WriteRPCResponseHTTPError(w, http.StatusNotFound, types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
- return
- }
- if rpcFunc.ws {
- WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse(request.ID, nil, "RPC method is only for websockets: "+request.Method))
- return
- }
- args, err := jsonParamsToArgsRPC(rpcFunc, request.Params)
- if err != nil {
- WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse(request.ID, nil, fmt.Sprintf("Error converting json params to arguments: %v", err.Error())))
- return
- }
- returns := rpcFunc.f.Call(args)
- logger.Info("HTTPJSONRPC", "method", request.Method, "args", args, "returns", returns)
- result, err := unreflectResult(returns)
- if err != nil {
- WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse(request.ID, result, err.Error()))
- return
- }
- WriteRPCResponseHTTP(w, types.NewRPCResponse(request.ID, result, ""))
- }
-}
-
-func mapParamsToArgs(rpcFunc *RPCFunc, params map[string]*json.RawMessage, argsOffset int) ([]reflect.Value, error) {
- values := make([]reflect.Value, len(rpcFunc.argNames))
- for i, argName := range rpcFunc.argNames {
- argType := rpcFunc.args[i+argsOffset]
-
- if p, ok := params[argName]; ok && p != nil && len(*p) > 0 {
- val := reflect.New(argType)
- err := json.Unmarshal(*p, val.Interface())
- if err != nil {
- return nil, err
- }
- values[i] = val.Elem()
- } else { // use default for that type
- values[i] = reflect.Zero(argType)
- }
- }
-
- return values, nil
-}
-
-func arrayParamsToArgs(rpcFunc *RPCFunc, params []*json.RawMessage, argsOffset int) ([]reflect.Value, error) {
- if len(rpcFunc.argNames) != len(params) {
- return nil, errors.Errorf("Expected %v parameters (%v), got %v (%v)",
- len(rpcFunc.argNames), rpcFunc.argNames, len(params), params)
- }
-
- values := make([]reflect.Value, len(params))
- for i, p := range params {
- argType := rpcFunc.args[i+argsOffset]
- val := reflect.New(argType)
- err := json.Unmarshal(*p, val.Interface())
- if err != nil {
- return nil, err
- }
- values[i] = val.Elem()
- }
- return values, nil
-}
-
-// raw is unparsed json (from json.RawMessage) encoding either a map or an array.
-//
-// argsOffset should be 0 for RPC calls, and 1 for WS requests, where len(rpcFunc.args) != len(rpcFunc.argNames).
-// Example:
-// rpcFunc.args = [rpctypes.WSRPCContext string]
-// rpcFunc.argNames = ["arg"]
-func jsonParamsToArgs(rpcFunc *RPCFunc, raw []byte, argsOffset int) ([]reflect.Value, error) {
- // first, try to get the map..
- var m map[string]*json.RawMessage
- err := json.Unmarshal(raw, &m)
- if err == nil {
- return mapParamsToArgs(rpcFunc, m, argsOffset)
- }
-
- // otherwise, try an array
- var a []*json.RawMessage
- err = json.Unmarshal(raw, &a)
- if err == nil {
- return arrayParamsToArgs(rpcFunc, a, argsOffset)
- }
-
- // otherwise, bad format, we cannot parse
- return nil, errors.Errorf("Unknown type for JSON params: %v. Expected map or array", err)
-}
-
-// Convert a []interface{} OR a map[string]interface{} to properly typed values
-func jsonParamsToArgsRPC(rpcFunc *RPCFunc, params *json.RawMessage) ([]reflect.Value, error) {
- return jsonParamsToArgs(rpcFunc, *params, 0)
-}
-
-// Same as above, but with the first param the websocket connection
-func jsonParamsToArgsWS(rpcFunc *RPCFunc, params *json.RawMessage, wsCtx types.WSRPCContext) ([]reflect.Value, error) {
- values, err := jsonParamsToArgs(rpcFunc, *params, 1)
- if err != nil {
- return nil, err
- }
- return append([]reflect.Value{reflect.ValueOf(wsCtx)}, values...), nil
-}
-
-// rpc.json
-//-----------------------------------------------------------------------------
-// rpc.http
-
-// convert from a function name to the http handler
-func makeHTTPHandler(rpcFunc *RPCFunc, logger log.Logger) func(http.ResponseWriter, *http.Request) {
- // Exception for websocket endpoints
- if rpcFunc.ws {
- return func(w http.ResponseWriter, r *http.Request) {
- WriteRPCResponseHTTPError(w, http.StatusMethodNotAllowed, types.NewRPCResponse("", nil, "This RPC method is only for websockets"))
- }
- }
- // All other endpoints
- return func(w http.ResponseWriter, r *http.Request) {
- logger.Debug("HTTP HANDLER", "req", r)
- args, err := httpParamsToArgs(rpcFunc, r)
- if err != nil {
- WriteRPCResponseHTTPError(w, http.StatusBadRequest, types.NewRPCResponse("", nil, fmt.Sprintf("Error converting http params to args: %v", err.Error())))
- return
- }
- returns := rpcFunc.f.Call(args)
- logger.Info("HTTPRestRPC", "method", r.URL.Path, "args", args, "returns", returns)
- result, err := unreflectResult(returns)
- if err != nil {
- WriteRPCResponseHTTPError(w, http.StatusInternalServerError, types.NewRPCResponse("", nil, err.Error()))
- return
- }
- WriteRPCResponseHTTP(w, types.NewRPCResponse("", result, ""))
- }
-}
-
-// Covert an http query to a list of properly typed values.
-// To be properly decoded the arg must be a concrete type from tendermint (if its an interface).
-func httpParamsToArgs(rpcFunc *RPCFunc, r *http.Request) ([]reflect.Value, error) {
- values := make([]reflect.Value, len(rpcFunc.args))
-
- for i, name := range rpcFunc.argNames {
- argType := rpcFunc.args[i]
-
- values[i] = reflect.Zero(argType) // set default for that type
-
- arg := GetParam(r, name)
- // log.Notice("param to arg", "argType", argType, "name", name, "arg", arg)
-
- if "" == arg {
- continue
- }
-
- v, err, ok := nonJsonToArg(argType, arg)
- if err != nil {
- return nil, err
- }
- if ok {
- values[i] = v
- continue
- }
-
- values[i], err = _jsonStringToArg(argType, arg)
- if err != nil {
- return nil, err
- }
- }
-
- return values, nil
-}
-
-func _jsonStringToArg(ty reflect.Type, arg string) (reflect.Value, error) {
- v := reflect.New(ty)
- err := json.Unmarshal([]byte(arg), v.Interface())
- if err != nil {
- return v, err
- }
- v = v.Elem()
- return v, nil
-}
-
-func nonJsonToArg(ty reflect.Type, arg string) (reflect.Value, error, bool) {
- isQuotedString := strings.HasPrefix(arg, `"`) && strings.HasSuffix(arg, `"`)
- isHexString := strings.HasPrefix(strings.ToLower(arg), "0x")
- expectingString := ty.Kind() == reflect.String
- expectingByteSlice := ty.Kind() == reflect.Slice && ty.Elem().Kind() == reflect.Uint8
-
- if isHexString {
- if !expectingString && !expectingByteSlice {
- err := errors.Errorf("Got a hex string arg, but expected '%s'",
- ty.Kind().String())
- return reflect.ValueOf(nil), err, false
- }
-
- var value []byte
- value, err := hex.DecodeString(arg[2:])
- if err != nil {
- return reflect.ValueOf(nil), err, false
- }
- if ty.Kind() == reflect.String {
- return reflect.ValueOf(string(value)), nil, true
- }
- return reflect.ValueOf([]byte(value)), nil, true
- }
-
- if isQuotedString && expectingByteSlice {
- v := reflect.New(reflect.TypeOf(""))
- err := json.Unmarshal([]byte(arg), v.Interface())
- if err != nil {
- return reflect.ValueOf(nil), err, false
- }
- v = v.Elem()
- return reflect.ValueOf([]byte(v.String())), nil, true
- }
-
- return reflect.ValueOf(nil), nil, false
-}
-
-// rpc.http
-//-----------------------------------------------------------------------------
-// rpc.websocket
-
-const (
- writeChanCapacity = 1000
- wsWriteTimeoutSeconds = 30 // each write times out after this
- wsReadTimeoutSeconds = 30 // connection times out if we haven't received *anything* in this long, not even pings.
- wsPingTickerSeconds = 10 // send a ping every PingTickerSeconds.
-)
-
-// a single websocket connection
-// contains listener id, underlying ws connection,
-// and the event switch for subscribing to events
-type wsConnection struct {
- cmn.BaseService
-
- remoteAddr string
- baseConn *websocket.Conn
- writeChan chan types.RPCResponse
- readTimeout *time.Timer
- pingTicker *time.Ticker
-
- funcMap map[string]*RPCFunc
- evsw events.EventSwitch
-}
-
-// new websocket connection wrapper
-func NewWSConnection(baseConn *websocket.Conn, funcMap map[string]*RPCFunc, evsw events.EventSwitch) *wsConnection {
- wsc := &wsConnection{
- remoteAddr: baseConn.RemoteAddr().String(),
- baseConn: baseConn,
- writeChan: make(chan types.RPCResponse, writeChanCapacity), // error when full.
- funcMap: funcMap,
- evsw: evsw,
- }
- wsc.BaseService = *cmn.NewBaseService(nil, "wsConnection", wsc)
- return wsc
-}
-
-// wsc.Start() blocks until the connection closes.
-func (wsc *wsConnection) OnStart() error {
- wsc.BaseService.OnStart()
-
- // these must be set before the readRoutine is created, as it may
- // call wsc.Stop(), which accesses these timers
- wsc.readTimeout = time.NewTimer(time.Second * wsReadTimeoutSeconds)
- wsc.pingTicker = time.NewTicker(time.Second * wsPingTickerSeconds)
-
- // Read subscriptions/unsubscriptions to events
- go wsc.readRoutine()
-
- // Custom Ping handler to touch readTimeout
- wsc.baseConn.SetPingHandler(func(m string) error {
- // NOTE: https://github.com/gorilla/websocket/issues/97
- go wsc.baseConn.WriteControl(websocket.PongMessage, []byte(m), time.Now().Add(time.Second*wsWriteTimeoutSeconds))
- wsc.readTimeout.Reset(time.Second * wsReadTimeoutSeconds)
- return nil
- })
- wsc.baseConn.SetPongHandler(func(m string) error {
- // NOTE: https://github.com/gorilla/websocket/issues/97
- wsc.readTimeout.Reset(time.Second * wsReadTimeoutSeconds)
- return nil
- })
- go wsc.readTimeoutRoutine()
-
- // Write responses, BLOCKING.
- wsc.writeRoutine()
- return nil
-}
-
-func (wsc *wsConnection) OnStop() {
- wsc.BaseService.OnStop()
- if wsc.evsw != nil {
- wsc.evsw.RemoveListener(wsc.remoteAddr)
- }
- wsc.readTimeout.Stop()
- wsc.pingTicker.Stop()
- // The write loop closes the websocket connection
- // when it exits its loop, and the read loop
- // closes the writeChan
-}
-
-func (wsc *wsConnection) readTimeoutRoutine() {
- select {
- case <-wsc.readTimeout.C:
- wsc.Logger.Info("Stopping connection due to read timeout")
- wsc.Stop()
- case <-wsc.Quit:
- return
- }
-}
-
-// Implements WSRPCConnection
-func (wsc *wsConnection) GetRemoteAddr() string {
- return wsc.remoteAddr
-}
-
-// Implements WSRPCConnection
-func (wsc *wsConnection) GetEventSwitch() events.EventSwitch {
- return wsc.evsw
-}
-
-// Implements WSRPCConnection
-// Blocking write to writeChan until service stops.
-// Goroutine-safe
-func (wsc *wsConnection) WriteRPCResponse(resp types.RPCResponse) {
- select {
- case <-wsc.Quit:
- return
- case wsc.writeChan <- resp:
- }
-}
-
-// Implements WSRPCConnection
-// Nonblocking write.
-// Goroutine-safe
-func (wsc *wsConnection) TryWriteRPCResponse(resp types.RPCResponse) bool {
- select {
- case <-wsc.Quit:
- return false
- case wsc.writeChan <- resp:
- return true
- default:
- return false
- }
-}
-
-// Read from the socket and subscribe to or unsubscribe from events
-func (wsc *wsConnection) readRoutine() {
- // Do not close writeChan, to allow WriteRPCResponse() to fail.
- // defer close(wsc.writeChan)
-
- for {
- select {
- case <-wsc.Quit:
- return
- default:
- var in []byte
- // Do not set a deadline here like below:
- // wsc.baseConn.SetReadDeadline(time.Now().Add(time.Second * wsReadTimeoutSeconds))
- // The client may not send anything for a while.
- // We use `readTimeout` to handle read timeouts.
- _, in, err := wsc.baseConn.ReadMessage()
- if err != nil {
- wsc.Logger.Info("Failed to read from connection", "remote", wsc.remoteAddr, "err", err.Error())
- // an error reading the connection,
- // kill the connection
- wsc.Stop()
- return
- }
- var request types.RPCRequest
- err = json.Unmarshal(in, &request)
- if err != nil {
- errStr := fmt.Sprintf("Error unmarshaling data: %s", err.Error())
- wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, errStr))
- continue
- }
-
- // Now, fetch the RPCFunc and execute it.
-
- rpcFunc := wsc.funcMap[request.Method]
- if rpcFunc == nil {
- wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, "RPC method unknown: "+request.Method))
- continue
- }
- var args []reflect.Value
- if rpcFunc.ws {
- wsCtx := types.WSRPCContext{Request: request, WSRPCConnection: wsc}
- args, err = jsonParamsToArgsWS(rpcFunc, request.Params, wsCtx)
- } else {
- args, err = jsonParamsToArgsRPC(rpcFunc, request.Params)
- }
- if err != nil {
- wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
- continue
- }
- returns := rpcFunc.f.Call(args)
- wsc.Logger.Info("WSJSONRPC", "method", request.Method, "args", args, "returns", returns)
- result, err := unreflectResult(returns)
- if err != nil {
- wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, nil, err.Error()))
- continue
- } else {
- wsc.WriteRPCResponse(types.NewRPCResponse(request.ID, result, ""))
- continue
- }
-
- }
- }
-}
-
-// receives on a write channel and writes out on the socket
-func (wsc *wsConnection) writeRoutine() {
- defer wsc.baseConn.Close()
- for {
- select {
- case <-wsc.Quit:
- return
- case <-wsc.pingTicker.C:
- err := wsc.baseConn.WriteMessage(websocket.PingMessage, []byte{})
- if err != nil {
- wsc.Logger.Error("Failed to write ping message on websocket", "error", err)
- wsc.Stop()
- return
- }
- case msg := <-wsc.writeChan:
- jsonBytes, err := json.MarshalIndent(msg, "", " ")
- if err != nil {
- wsc.Logger.Error("Failed to marshal RPCResponse to JSON", "error", err)
- } else {
- wsc.baseConn.SetWriteDeadline(time.Now().Add(time.Second * wsWriteTimeoutSeconds))
- if err = wsc.baseConn.WriteMessage(websocket.TextMessage, jsonBytes); err != nil {
- wsc.Logger.Error("Failed to write response on websocket", "error", err)
- wsc.Stop()
- return
- }
- }
- }
- }
-}
-
-//----------------------------------------
-
-// Main manager for all websocket connections
-// Holds the event switch
-// NOTE: The websocket path is defined externally, e.g. in node/node.go
-type WebsocketManager struct {
- websocket.Upgrader
- funcMap map[string]*RPCFunc
- evsw events.EventSwitch
- logger log.Logger
-}
-
-func NewWebsocketManager(funcMap map[string]*RPCFunc, evsw events.EventSwitch) *WebsocketManager {
- return &WebsocketManager{
- funcMap: funcMap,
- evsw: evsw,
- Upgrader: websocket.Upgrader{
- ReadBufferSize: 1024,
- WriteBufferSize: 1024,
- CheckOrigin: func(r *http.Request) bool {
- // TODO
- return true
- },
- },
- logger: log.NewNopLogger(),
- }
-}
-
-func (wm *WebsocketManager) SetLogger(l log.Logger) {
- wm.logger = l
-}
-
-// Upgrade the request/response (via http.Hijack) and starts the wsConnection.
-func (wm *WebsocketManager) WebsocketHandler(w http.ResponseWriter, r *http.Request) {
- wsConn, err := wm.Upgrade(w, r, nil)
- if err != nil {
- // TODO - return http error
- wm.logger.Error("Failed to upgrade to websocket connection", "error", err)
- return
- }
-
- // register connection
- con := NewWSConnection(wsConn, wm.funcMap, wm.evsw)
- wm.logger.Info("New websocket connection", "remote", con.remoteAddr)
- con.Start() // Blocking
-}
-
-// rpc.websocket
-//-----------------------------------------------------------------------------
-
-// NOTE: assume returns is result struct and error. If error is not nil, return it
-func unreflectResult(returns []reflect.Value) (interface{}, error) {
- errV := returns[1]
- if errV.Interface() != nil {
- return nil, errors.Errorf("%v", errV.Interface())
- }
- rv := returns[0]
- // the result is a registered interface,
- // we need a pointer to it so we can marshal with type byte
- rvp := reflect.New(rv.Type())
- rvp.Elem().Set(rv)
- return rvp.Interface(), nil
-}
-
-// writes a list of available rpc endpoints as an html page
-func writeListOfEndpoints(w http.ResponseWriter, r *http.Request, funcMap map[string]*RPCFunc) {
- noArgNames := []string{}
- argNames := []string{}
- for name, funcData := range funcMap {
- if len(funcData.args) == 0 {
- noArgNames = append(noArgNames, name)
- } else {
- argNames = append(argNames, name)
- }
- }
- sort.Strings(noArgNames)
- sort.Strings(argNames)
- buf := new(bytes.Buffer)
- buf.WriteString("<html><body>")
- buf.WriteString("<br>Available endpoints:<br>")
-
- for _, name := range noArgNames {
- link := fmt.Sprintf("http://%s/%s", r.Host, name)
- buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
- }
-
- buf.WriteString("<br>Endpoints that require arguments:<br>")
- for _, name := range argNames {
- link := fmt.Sprintf("http://%s/%s?", r.Host, name)
- funcData := funcMap[name]
- for i, argName := range funcData.argNames {
- link += argName + "=_"
- if i < len(funcData.argNames)-1 {
- link += "&"
- }
- }
- buf.WriteString(fmt.Sprintf("<a href=\"%s\">%s</a></br>", link, link))
- }
- buf.WriteString("</body></html>")
- w.Header().Set("Content-Type", "text/html")
- w.WriteHeader(200)
- w.Write(buf.Bytes())
-}
+++ /dev/null
-package rpcserver
-
-import (
- "encoding/hex"
- "net/http"
- "regexp"
- "strconv"
-
- "github.com/pkg/errors"
-)
-
-var (
- // Parts of regular expressions
- atom = "[A-Z0-9!#$%&'*+\\-/=?^_`{|}~]+"
- dotAtom = atom + `(?:\.` + atom + `)*`
- domain = `[A-Z0-9.-]+\.[A-Z]{2,4}`
-
- RE_HEX = regexp.MustCompile(`^(?i)[a-f0-9]+$`)
- RE_EMAIL = regexp.MustCompile(`^(?i)(` + dotAtom + `)@(` + dotAtom + `)$`)
- RE_ADDRESS = regexp.MustCompile(`^(?i)[a-z0-9]{25,34}$`)
- RE_HOST = regexp.MustCompile(`^(?i)(` + domain + `)$`)
-
- //RE_ID12 = regexp.MustCompile(`^[a-zA-Z0-9]{12}$`)
-)
-
-func GetParam(r *http.Request, param string) string {
- s := r.URL.Query().Get(param)
- if s == "" {
- s = r.FormValue(param)
- }
- return s
-}
-
-func GetParamByteSlice(r *http.Request, param string) ([]byte, error) {
- s := GetParam(r, param)
- return hex.DecodeString(s)
-}
-
-func GetParamInt64(r *http.Request, param string) (int64, error) {
- s := GetParam(r, param)
- i, err := strconv.ParseInt(s, 10, 64)
- if err != nil {
- return 0, errors.Errorf(param, err.Error())
- }
- return i, nil
-}
-
-func GetParamInt32(r *http.Request, param string) (int32, error) {
- s := GetParam(r, param)
- i, err := strconv.ParseInt(s, 10, 32)
- if err != nil {
- return 0, errors.Errorf(param, err.Error())
- }
- return int32(i), nil
-}
-
-func GetParamUint64(r *http.Request, param string) (uint64, error) {
- s := GetParam(r, param)
- i, err := strconv.ParseUint(s, 10, 64)
- if err != nil {
- return 0, errors.Errorf(param, err.Error())
- }
- return i, nil
-}
-
-func GetParamUint(r *http.Request, param string) (uint, error) {
- s := GetParam(r, param)
- i, err := strconv.ParseUint(s, 10, 64)
- if err != nil {
- return 0, errors.Errorf(param, err.Error())
- }
- return uint(i), nil
-}
-
-func GetParamRegexp(r *http.Request, param string, re *regexp.Regexp) (string, error) {
- s := GetParam(r, param)
- if !re.MatchString(s) {
- return "", errors.Errorf(param, "Did not match regular expression %v", re.String())
- }
- return s, nil
-}
-
-func GetParamFloat64(r *http.Request, param string) (float64, error) {
- s := GetParam(r, param)
- f, err := strconv.ParseFloat(s, 64)
- if err != nil {
- return 0, errors.Errorf(param, err.Error())
- }
- return f, nil
-}
+++ /dev/null
-// Commons for HTTP handling
-package rpcserver
-
-import (
- "bufio"
- "encoding/json"
- "fmt"
- "net"
- "net/http"
- "runtime/debug"
- "strings"
- "time"
-
- types "github.com/bytom/rpc/lib/types"
- "github.com/pkg/errors"
- "github.com/tendermint/tmlibs/log"
-)
-
-func StartHTTPServer(listenAddr string, handler http.Handler, logger log.Logger) (listener net.Listener, err error) {
- // listenAddr should be fully formed including tcp:// or unix:// prefix
- var proto, addr string
- parts := strings.SplitN(listenAddr, "://", 2)
- if len(parts) != 2 {
- logger.Error("WARNING (tendermint/rpc/lib): Please use fully formed listening addresses, including the tcp:// or unix:// prefix")
- // we used to allow addrs without tcp/unix prefix by checking for a colon
- // TODO: Deprecate
- proto = types.SocketType(listenAddr)
- addr = listenAddr
- // return nil, errors.Errorf("Invalid listener address %s", lisenAddr)
- } else {
- proto, addr = parts[0], parts[1]
- }
-
- logger.Info(fmt.Sprintf("Starting RPC HTTP server on %s socket %v", proto, addr))
- listener, err = net.Listen(proto, addr)
- if err != nil {
- return nil, errors.Errorf("Failed to listen to %v: %v", listenAddr, err)
- }
-
- go func() {
- res := http.Serve(
- listener,
- RecoverAndLogHandler(handler, logger),
- )
- logger.Error("RPC HTTP server stopped", "result", res)
- }()
- return listener, nil
-}
-
-func WriteRPCResponseHTTPError(w http.ResponseWriter, httpCode int, res types.RPCResponse) {
- jsonBytes, err := json.MarshalIndent(res, "", " ")
- if err != nil {
- panic(err)
- }
-
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(httpCode)
- w.Write(jsonBytes)
-}
-
-func WriteRPCResponseHTTP(w http.ResponseWriter, res types.RPCResponse) {
- jsonBytes, err := json.MarshalIndent(res, "", " ")
- if err != nil {
- panic(err)
- }
- w.Header().Set("Content-Type", "application/json")
- w.WriteHeader(200)
- w.Write(jsonBytes)
-}
-
-//-----------------------------------------------------------------------------
-
-// Wraps an HTTP handler, adding error logging.
-// If the inner function panics, the outer function recovers, logs, sends an
-// HTTP 500 error response.
-func RecoverAndLogHandler(handler http.Handler, logger log.Logger) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // Wrap the ResponseWriter to remember the status
- rww := &ResponseWriterWrapper{-1, w}
- begin := time.Now()
-
- // Common headers
- origin := r.Header.Get("Origin")
- rww.Header().Set("Access-Control-Allow-Origin", origin)
- rww.Header().Set("Access-Control-Allow-Credentials", "true")
- rww.Header().Set("Access-Control-Expose-Headers", "X-Server-Time")
- rww.Header().Set("X-Server-Time", fmt.Sprintf("%v", begin.Unix()))
-
- defer func() {
- // Send a 500 error if a panic happens during a handler.
- // Without this, Chrome & Firefox were retrying aborted ajax requests,
- // at least to my localhost.
- if e := recover(); e != nil {
-
- // If RPCResponse
- if res, ok := e.(types.RPCResponse); ok {
- WriteRPCResponseHTTP(rww, res)
- } else {
- // For the rest,
- logger.Error("Panic in RPC HTTP handler", "error", e, "stack", string(debug.Stack()))
- rww.WriteHeader(http.StatusInternalServerError)
- WriteRPCResponseHTTP(rww, types.NewRPCResponse("", nil, fmt.Sprintf("Internal Server Error: %v", e)))
- }
- }
-
- // Finally, log.
- durationMS := time.Since(begin).Nanoseconds() / 1000000
- if rww.Status == -1 {
- rww.Status = 200
- }
- logger.Info("Served RPC HTTP response",
- "method", r.Method, "url", r.URL,
- "status", rww.Status, "duration", durationMS,
- "remoteAddr", r.RemoteAddr,
- )
- }()
-
- handler.ServeHTTP(rww, r)
- })
-}
-
-// Remember the status for logging
-type ResponseWriterWrapper struct {
- Status int
- http.ResponseWriter
-}
-
-func (w *ResponseWriterWrapper) WriteHeader(status int) {
- w.Status = status
- w.ResponseWriter.WriteHeader(status)
-}
-
-// implements http.Hijacker
-func (w *ResponseWriterWrapper) Hijack() (net.Conn, *bufio.ReadWriter, error) {
- return w.ResponseWriter.(http.Hijacker).Hijack()
-}
+++ /dev/null
-package rpcserver
-
-import (
- "encoding/json"
- "strconv"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/tendermint/go-wire/data"
-)
-
-func TestParseJSONMap(t *testing.T) {
- assert := assert.New(t)
-
- input := []byte(`{"value":"1234","height":22}`)
-
- // naive is float,string
- var p1 map[string]interface{}
- err := json.Unmarshal(input, &p1)
- if assert.Nil(err) {
- h, ok := p1["height"].(float64)
- if assert.True(ok, "%#v", p1["height"]) {
- assert.EqualValues(22, h)
- }
- v, ok := p1["value"].(string)
- if assert.True(ok, "%#v", p1["value"]) {
- assert.EqualValues("1234", v)
- }
- }
-
- // preloading map with values doesn't help
- tmp := 0
- p2 := map[string]interface{}{
- "value": &data.Bytes{},
- "height": &tmp,
- }
- err = json.Unmarshal(input, &p2)
- if assert.Nil(err) {
- h, ok := p2["height"].(float64)
- if assert.True(ok, "%#v", p2["height"]) {
- assert.EqualValues(22, h)
- }
- v, ok := p2["value"].(string)
- if assert.True(ok, "%#v", p2["value"]) {
- assert.EqualValues("1234", v)
- }
- }
-
- // preload here with *pointers* to the desired types
- // struct has unknown types, but hard-coded keys
- tmp = 0
- p3 := struct {
- Value interface{} `json:"value"`
- Height interface{} `json:"height"`
- }{
- Height: &tmp,
- Value: &data.Bytes{},
- }
- err = json.Unmarshal(input, &p3)
- if assert.Nil(err) {
- h, ok := p3.Height.(*int)
- if assert.True(ok, "%#v", p3.Height) {
- assert.Equal(22, *h)
- }
- v, ok := p3.Value.(*data.Bytes)
- if assert.True(ok, "%#v", p3.Value) {
- assert.EqualValues([]byte{0x12, 0x34}, *v)
- }
- }
-
- // simplest solution, but hard-coded
- p4 := struct {
- Value data.Bytes `json:"value"`
- Height int `json:"height"`
- }{}
- err = json.Unmarshal(input, &p4)
- if assert.Nil(err) {
- assert.EqualValues(22, p4.Height)
- assert.EqualValues([]byte{0x12, 0x34}, p4.Value)
- }
-
- // so, let's use this trick...
- // dynamic keys on map, and we can deserialize to the desired types
- var p5 map[string]*json.RawMessage
- err = json.Unmarshal(input, &p5)
- if assert.Nil(err) {
- var h int
- err = json.Unmarshal(*p5["height"], &h)
- if assert.Nil(err) {
- assert.Equal(22, h)
- }
-
- var v data.Bytes
- err = json.Unmarshal(*p5["value"], &v)
- if assert.Nil(err) {
- assert.Equal(data.Bytes{0x12, 0x34}, v)
- }
- }
-}
-
-func TestParseJSONArray(t *testing.T) {
- assert := assert.New(t)
-
- input := []byte(`["1234",22]`)
-
- // naive is float,string
- var p1 []interface{}
- err := json.Unmarshal(input, &p1)
- if assert.Nil(err) {
- v, ok := p1[0].(string)
- if assert.True(ok, "%#v", p1[0]) {
- assert.EqualValues("1234", v)
- }
- h, ok := p1[1].(float64)
- if assert.True(ok, "%#v", p1[1]) {
- assert.EqualValues(22, h)
- }
- }
-
- // preloading map with values helps here (unlike map - p2 above)
- tmp := 0
- p2 := []interface{}{&data.Bytes{}, &tmp}
- err = json.Unmarshal(input, &p2)
- if assert.Nil(err) {
- v, ok := p2[0].(*data.Bytes)
- if assert.True(ok, "%#v", p2[0]) {
- assert.EqualValues([]byte{0x12, 0x34}, *v)
- }
- h, ok := p2[1].(*int)
- if assert.True(ok, "%#v", p2[1]) {
- assert.EqualValues(22, *h)
- }
- }
-}
-
-func TestParseRPC(t *testing.T) {
- assert := assert.New(t)
-
- demo := func(height int, name string) {}
- call := NewRPCFunc(demo, "height,name")
-
- cases := []struct {
- raw string
- height int64
- name string
- fail bool
- }{
- // should parse
- {`[7, "flew"]`, 7, "flew", false},
- {`{"name": "john", "height": 22}`, 22, "john", false},
- // defaults
- {`{"name": "solo", "unused": "stuff"}`, 0, "solo", false},
- // should fail - wrong types/lenght
- {`["flew", 7]`, 0, "", true},
- {`[7,"flew",100]`, 0, "", true},
- {`{"name": -12, "height": "fred"}`, 0, "", true},
- }
- for idx, tc := range cases {
- i := strconv.Itoa(idx)
- data := []byte(tc.raw)
- vals, err := jsonParamsToArgs(call, data, 0)
- if tc.fail {
- assert.NotNil(err, i)
- } else {
- assert.Nil(err, "%s: %+v", i, err)
- if assert.Equal(2, len(vals), i) {
- assert.Equal(tc.height, vals[0].Int(), i)
- assert.Equal(tc.name, vals[1].String(), i)
- }
- }
-
- }
-
-}
+++ /dev/null
-{
- "jsonrpc": "2.0",
- "id": "",
- "method": "hello_world",
- "params": {
- "name": "my_world",
- "num": 5
- }
-}
+++ /dev/null
-#!/usr/bin/env bash
-set -e
-
-# Get the directory of where this script is.
-SOURCE="${BASH_SOURCE[0]}"
-while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done
-DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )"
-
-# Change into that dir because we expect that.
-pushd "$DIR"
-
-echo "==> Building the server"
-go build -o rpcserver main.go
-
-echo "==> (Re)starting the server"
-PID=$(pgrep rpcserver || echo "")
-if [[ $PID != "" ]]; then
- kill -9 "$PID"
-fi
-./rpcserver &
-PID=$!
-sleep 2
-
-echo "==> simple request"
-R1=$(curl -s 'http://localhost:8008/hello_world?name="my_world"&num=5')
-R2=$(curl -s --data @data.json http://localhost:8008)
-if [[ "$R1" != "$R2" ]]; then
- echo "responses are not identical:"
- echo "R1: $R1"
- echo "R2: $R2"
- echo "FAIL"
- exit 1
-else
- echo "OK"
-fi
-
-echo "==> request with 0x-prefixed hex string arg"
-R1=$(curl -s 'http://localhost:8008/hello_world?name=0x41424344&num=123')
-R2='{"jsonrpc":"2.0","id":"","result":{"Result":"hi ABCD 123"},"error":""}'
-if [[ "$R1" != "$R2" ]]; then
- echo "responses are not identical:"
- echo "R1: $R1"
- echo "R2: $R2"
- echo "FAIL"
- exit 1
-else
- echo "OK"
-fi
-
-echo "==> request with missing params"
-R1=$(curl -s 'http://localhost:8008/hello_world')
-R2='{"jsonrpc":"2.0","id":"","result":{"Result":"hi 0"},"error":""}'
-if [[ "$R1" != "$R2" ]]; then
- echo "responses are not identical:"
- echo "R1: $R1"
- echo "R2: $R2"
- echo "FAIL"
- exit 1
-else
- echo "OK"
-fi
-
-echo "==> request with unquoted string arg"
-R1=$(curl -s 'http://localhost:8008/hello_world?name=abcd&num=123')
-R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: invalid character 'a' looking for beginning of value\"}"
-if [[ "$R1" != "$R2" ]]; then
- echo "responses are not identical:"
- echo "R1: $R1"
- echo "R2: $R2"
- echo "FAIL"
- exit 1
-else
- echo "OK"
-fi
-
-echo "==> request with string type when expecting number arg"
-R1=$(curl -s 'http://localhost:8008/hello_world?name="abcd"&num=0xabcd')
-R2="{\"jsonrpc\":\"2.0\",\"id\":\"\",\"result\":null,\"error\":\"Error converting http params to args: Got a hex string arg, but expected 'int'\"}"
-if [[ "$R1" != "$R2" ]]; then
- echo "responses are not identical:"
- echo "R1: $R1"
- echo "R2: $R2"
- echo "FAIL"
- exit 1
-else
- echo "OK"
-fi
-
-echo "==> Stopping the server"
-kill -9 $PID
-
-rm -f rpcserver
-
-popd
-exit 0
+++ /dev/null
-package main
-
-import (
- "fmt"
- "net/http"
- "os"
-
- rpcserver "github.com/bytom/rpc/lib/server"
- cmn "github.com/tendermint/tmlibs/common"
- "github.com/tendermint/tmlibs/log"
-)
-
-var routes = map[string]*rpcserver.RPCFunc{
- "hello_world": rpcserver.NewRPCFunc(HelloWorld, "name,num"),
-}
-
-func HelloWorld(name string, num int) (Result, error) {
- return Result{fmt.Sprintf("hi %s %d", name, num)}, nil
-}
-
-type Result struct {
- Result string
-}
-
-func main() {
- mux := http.NewServeMux()
- logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
- rpcserver.RegisterRPCFuncs(mux, routes, logger)
- _, err := rpcserver.StartHTTPServer("0.0.0.0:8008", mux, logger)
- if err != nil {
- cmn.Exit(err.Error())
- }
-
- // Wait forever
- cmn.TrapSignal(func() {
- })
-
-}
+++ /dev/null
-package rpctypes
-
-import (
- "encoding/json"
- "strings"
-
- events "github.com/tendermint/tmlibs/events"
-)
-
-type RPCRequest struct {
- JSONRPC string `json:"jsonrpc"`
- ID string `json:"id"`
- Method string `json:"method"`
- Params *json.RawMessage `json:"params"` // must be map[string]interface{} or []interface{}
-}
-
-func NewRPCRequest(id string, method string, params json.RawMessage) RPCRequest {
- return RPCRequest{
- JSONRPC: "2.0",
- ID: id,
- Method: method,
- Params: ¶ms,
- }
-}
-
-func MapToRequest(id string, method string, params map[string]interface{}) (RPCRequest, error) {
- payload, err := json.Marshal(params)
- if err != nil {
- return RPCRequest{}, err
- }
- request := NewRPCRequest(id, method, payload)
- return request, nil
-}
-
-func ArrayToRequest(id string, method string, params []interface{}) (RPCRequest, error) {
- payload, err := json.Marshal(params)
- if err != nil {
- return RPCRequest{}, err
- }
- request := NewRPCRequest(id, method, payload)
- return request, nil
-}
-
-//----------------------------------------
-
-type RPCResponse struct {
- JSONRPC string `json:"jsonrpc"`
- ID string `json:"id"`
- Result *json.RawMessage `json:"result"`
- Error string `json:"error"`
-}
-
-func NewRPCResponse(id string, res interface{}, err string) RPCResponse {
- var raw *json.RawMessage
- if res != nil {
- var js []byte
- js, err2 := json.Marshal(res)
- if err2 == nil {
- rawMsg := json.RawMessage(js)
- raw = &rawMsg
- } else {
- err = err2.Error()
- }
- }
- return RPCResponse{
- JSONRPC: "2.0",
- ID: id,
- Result: raw,
- Error: err,
- }
-}
-
-//----------------------------------------
-
-// *wsConnection implements this interface.
-type WSRPCConnection interface {
- GetRemoteAddr() string
- GetEventSwitch() events.EventSwitch
- WriteRPCResponse(resp RPCResponse)
- TryWriteRPCResponse(resp RPCResponse) bool
-}
-
-// websocket-only RPCFuncs take this as the first parameter.
-type WSRPCContext struct {
- Request RPCRequest
- WSRPCConnection
-}
-
-//----------------------------------------
-// sockets
-//
-// Determine if its a unix or tcp socket.
-// If tcp, must specify the port; `0.0.0.0` will return incorrectly as "unix" since there's no port
-func SocketType(listenAddr string) string {
- socketType := "unix"
- if len(strings.Split(listenAddr, ":")) >= 2 {
- socketType = "tcp"
- }
- return socketType
-}
+++ /dev/null
-package rpc
-
-const Maj = "0"
-const Min = "7"
-const Fix = "0"
-
-const Version = Maj + "." + Min + "." + Fix
+++ /dev/null
-package rpctest
-
-import (
- "bytes"
- crand "crypto/rand"
- "fmt"
- "math/rand"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-
- abci "github.com/tendermint/abci/types"
- "github.com/tendermint/go-wire/data"
- . "github.com/tendermint/tmlibs/common"
-
- "github.com/tendermint/tendermint/rpc/core"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- rpc "github.com/tendermint/tendermint/rpc/lib/client"
- "github.com/tendermint/tendermint/state/txindex/null"
- "github.com/tendermint/tendermint/types"
-)
-
-//--------------------------------------------------------------------------------
-// Test the HTTP client
-// These tests assume the dummy app
-//--------------------------------------------------------------------------------
-
-//--------------------------------------------------------------------------------
-// status
-
-func TestURIStatus(t *testing.T) {
- testStatus(t, GetURIClient())
-}
-
-func TestJSONStatus(t *testing.T) {
- testStatus(t, GetJSONClient())
-}
-
-func testStatus(t *testing.T, client rpc.HTTPClient) {
- moniker := GetConfig().Moniker
- result := new(ctypes.ResultStatus)
- _, err := client.Call("status", map[string]interface{}{}, result)
- require.Nil(t, err)
- assert.Equal(t, moniker, result.NodeInfo.Moniker)
-}
-
-//--------------------------------------------------------------------------------
-// broadcast tx sync
-
-// random bytes (excluding byte('='))
-func randBytes(t *testing.T) []byte {
- n := rand.Intn(10) + 2
- buf := make([]byte, n)
- _, err := crand.Read(buf)
- require.Nil(t, err)
- return bytes.Replace(buf, []byte("="), []byte{100}, -1)
-}
-
-func TestURIBroadcastTxSync(t *testing.T) {
- testBroadcastTxSync(t, GetURIClient())
-}
-
-func TestJSONBroadcastTxSync(t *testing.T) {
- testBroadcastTxSync(t, GetJSONClient())
-}
-
-func testBroadcastTxSync(t *testing.T, client rpc.HTTPClient) {
- mem := node.MempoolReactor().Mempool
- initMemSize := mem.Size()
- result := new(ctypes.ResultBroadcastTx)
- tx := randBytes(t)
- _, err := client.Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result)
- require.Nil(t, err)
-
- require.Equal(t, abci.CodeType_OK, result.Code)
- require.Equal(t, initMemSize+1, mem.Size())
- txs := mem.Reap(1)
- require.EqualValues(t, tx, txs[0])
- mem.Flush()
-}
-
-//--------------------------------------------------------------------------------
-// query
-
-func testTxKV(t *testing.T) ([]byte, []byte, types.Tx) {
- k := randBytes(t)
- v := randBytes(t)
- return k, v, types.Tx(Fmt("%s=%s", k, v))
-}
-
-func sendTx(t *testing.T, client rpc.HTTPClient) ([]byte, []byte) {
- result := new(ctypes.ResultBroadcastTxCommit)
- k, v, tx := testTxKV(t)
- _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
- require.Nil(t, err)
- require.NotNil(t, 0, result.DeliverTx, "%#v", result)
- require.EqualValues(t, 0, result.CheckTx.Code, "%#v", result)
- require.EqualValues(t, 0, result.DeliverTx.Code, "%#v", result)
- return k, v
-}
-
-func TestURIABCIQuery(t *testing.T) {
- testABCIQuery(t, GetURIClient())
-}
-
-func TestJSONABCIQuery(t *testing.T) {
- testABCIQuery(t, GetJSONClient())
-}
-
-func testABCIQuery(t *testing.T, client rpc.HTTPClient) {
- k, _ := sendTx(t, client)
- time.Sleep(time.Millisecond * 500)
- result := new(ctypes.ResultABCIQuery)
- _, err := client.Call("abci_query",
- map[string]interface{}{"path": "", "data": data.Bytes(k), "prove": false}, result)
- require.Nil(t, err)
-
- require.EqualValues(t, 0, result.Code)
-
- // XXX: specific to value returned by the dummy
- require.NotEqual(t, 0, len(result.Value))
-}
-
-//--------------------------------------------------------------------------------
-// broadcast tx commit
-
-func TestURIBroadcastTxCommit(t *testing.T) {
- testBroadcastTxCommit(t, GetURIClient())
-}
-
-func TestJSONBroadcastTxCommit(t *testing.T) {
- testBroadcastTxCommit(t, GetJSONClient())
-}
-
-func testBroadcastTxCommit(t *testing.T, client rpc.HTTPClient) {
- require := require.New(t)
-
- result := new(ctypes.ResultBroadcastTxCommit)
- tx := randBytes(t)
- _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": tx}, result)
- require.Nil(err)
-
- checkTx := result.CheckTx
- require.Equal(abci.CodeType_OK, checkTx.Code)
- deliverTx := result.DeliverTx
- require.Equal(abci.CodeType_OK, deliverTx.Code)
- mem := node.MempoolReactor().Mempool
- require.Equal(0, mem.Size())
- // TODO: find tx in block
-}
-
-//--------------------------------------------------------------------------------
-// query tx
-
-func TestURITx(t *testing.T) {
- testTx(t, GetURIClient(), true)
-
- core.SetTxIndexer(&null.TxIndex{})
- defer core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
-
- testTx(t, GetURIClient(), false)
-}
-
-func TestJSONTx(t *testing.T) {
- testTx(t, GetJSONClient(), true)
-
- core.SetTxIndexer(&null.TxIndex{})
- testTx(t, GetJSONClient(), false)
- core.SetTxIndexer(node.ConsensusState().GetState().TxIndexer)
-}
-
-func testTx(t *testing.T, client rpc.HTTPClient, withIndexer bool) {
- assert, require := assert.New(t), require.New(t)
-
- // first we broadcast a tx
- result := new(ctypes.ResultBroadcastTxCommit)
- txBytes := randBytes(t)
- tx := types.Tx(txBytes)
- _, err := client.Call("broadcast_tx_commit", map[string]interface{}{"tx": txBytes}, result)
- require.Nil(err)
-
- checkTx := result.CheckTx
- require.Equal(abci.CodeType_OK, checkTx.Code)
- deliverTx := result.DeliverTx
- require.Equal(abci.CodeType_OK, deliverTx.Code)
- mem := node.MempoolReactor().Mempool
- require.Equal(0, mem.Size())
-
- txHash := tx.Hash()
- txHash2 := types.Tx("a different tx").Hash()
-
- cases := []struct {
- valid bool
- hash []byte
- prove bool
- }{
- // only valid if correct hash provided
- {true, txHash, false},
- {true, txHash, true},
- {false, txHash2, false},
- {false, txHash2, true},
- {false, nil, false},
- {false, nil, true},
- }
-
- for i, tc := range cases {
- idx := fmt.Sprintf("%d", i)
-
- // now we query for the tx.
- // since there's only one tx, we know index=0.
- result2 := new(ctypes.ResultTx)
- query := map[string]interface{}{
- "hash": tc.hash,
- "prove": tc.prove,
- }
- _, err = client.Call("tx", query, result2)
- valid := (withIndexer && tc.valid)
- if !valid {
- require.NotNil(err, idx)
- } else {
- require.Nil(err, idx)
- assert.Equal(tx, result2.Tx, idx)
- assert.Equal(result.Height, result2.Height, idx)
- assert.Equal(0, result2.Index, idx)
- assert.Equal(abci.CodeType_OK, result2.TxResult.Code, idx)
- // time to verify the proof
- proof := result2.Proof
- if tc.prove && assert.Equal(tx, proof.Data, idx) {
- assert.True(proof.Proof.Verify(proof.Index, proof.Total, tx.Hash(), proof.RootHash), idx)
- }
- }
- }
-
-}
-
-//--------------------------------------------------------------------------------
-// Test the websocket service
-
-var wsTyp = "JSONRPC"
-
-// make a simple connection to the server
-func TestWSConnect(t *testing.T) {
- wsc := GetWSClient()
- wsc.Stop()
-}
-
-// receive a new block message
-func TestWSNewBlock(t *testing.T) {
- wsc := GetWSClient()
- eid := types.EventStringNewBlock()
- require.Nil(t, wsc.Subscribe(eid))
-
- defer func() {
- require.Nil(t, wsc.Unsubscribe(eid))
- wsc.Stop()
- }()
- waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error {
- // fmt.Println("Check:", b)
- return nil
- })
-}
-
-// receive a few new block messages in a row, with increasing height
-func TestWSBlockchainGrowth(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping test in short mode.")
- }
- wsc := GetWSClient()
- eid := types.EventStringNewBlock()
- require.Nil(t, wsc.Subscribe(eid))
-
- defer func() {
- require.Nil(t, wsc.Unsubscribe(eid))
- wsc.Stop()
- }()
-
- // listen for NewBlock, ensure height increases by 1
-
- var initBlockN int
- for i := 0; i < 3; i++ {
- waitForEvent(t, wsc, eid, true, func() {}, func(eid string, eventData interface{}) error {
- block := eventData.(types.TMEventData).Unwrap().(types.EventDataNewBlock).Block
- if i == 0 {
- initBlockN = block.Header.Height
- } else {
- if block.Header.Height != initBlockN+i {
- return fmt.Errorf("Expected block %d, got block %d", initBlockN+i, block.Header.Height)
- }
- }
-
- return nil
- })
- }
-}
-
-func TestWSTxEvent(t *testing.T) {
- require := require.New(t)
- wsc := GetWSClient()
- tx := randBytes(t)
-
- // listen for the tx I am about to submit
- eid := types.EventStringTx(types.Tx(tx))
- require.Nil(wsc.Subscribe(eid))
-
- defer func() {
- require.Nil(wsc.Unsubscribe(eid))
- wsc.Stop()
- }()
-
- // send an tx
- result := new(ctypes.ResultBroadcastTx)
- _, err := GetJSONClient().Call("broadcast_tx_sync", map[string]interface{}{"tx": tx}, result)
- require.Nil(err)
-
- waitForEvent(t, wsc, eid, true, func() {}, func(eid string, b interface{}) error {
- evt, ok := b.(types.TMEventData).Unwrap().(types.EventDataTx)
- require.True(ok, "Got wrong event type: %#v", b)
- require.Equal(tx, []byte(evt.Tx), "Returned different tx")
- require.Equal(abci.CodeType_OK, evt.Code)
- return nil
- })
-}
-
-/* TODO: this with dummy app..
-func TestWSDoubleFire(t *testing.T) {
- if testing.Short() {
- t.Skip("skipping test in short mode.")
- }
- con := newWSCon(t)
- eid := types.EventStringAccInput(user[0].Address)
- subscribe(t, con, eid)
- defer func() {
- unsubscribe(t, con, eid)
- con.Close()
- }()
- amt := int64(100)
- toAddr := user[1].Address
- // broadcast the transaction, wait to hear about it
- waitForEvent(t, con, eid, true, func() {
- tx := makeDefaultSendTxSigned(t, wsTyp, toAddr, amt)
- broadcastTx(t, wsTyp, tx)
- }, func(eid string, b []byte) error {
- return nil
- })
- // but make sure we don't hear about it twice
- waitForEvent(t, con, eid, false, func() {
- }, func(eid string, b []byte) error {
- return nil
- })
-}*/
+++ /dev/null
-package rpctest
-
-import (
- "testing"
-
- "golang.org/x/net/context"
-
- "github.com/stretchr/testify/require"
- "github.com/tendermint/tendermint/rpc/grpc"
-)
-
-//-------------------------------------------
-
-func TestBroadcastTx(t *testing.T) {
- require := require.New(t)
- res, err := GetGRPCClient().BroadcastTx(context.Background(), &core_grpc.RequestBroadcastTx{[]byte("this is a tx")})
- require.Nil(err)
- require.EqualValues(0, res.CheckTx.Code)
- require.EqualValues(0, res.DeliverTx.Code)
-}
+++ /dev/null
-package rpctest
-
-import (
- "encoding/json"
- "fmt"
- "math/rand"
- "os"
- "path/filepath"
- "strings"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
- "github.com/tendermint/tmlibs/log"
-
- abci "github.com/tendermint/abci/types"
- cfg "github.com/tendermint/tendermint/config"
- nm "github.com/tendermint/tendermint/node"
- "github.com/tendermint/tendermint/proxy"
- ctypes "github.com/tendermint/tendermint/rpc/core/types"
- core_grpc "github.com/tendermint/tendermint/rpc/grpc"
- client "github.com/tendermint/tendermint/rpc/lib/client"
- "github.com/tendermint/tendermint/types"
-)
-
-var config *cfg.Config
-
-// f**ing long, but unique for each test
-func makePathname() string {
- // get path
- p, err := os.Getwd()
- if err != nil {
- panic(err)
- }
- fmt.Println(p)
- sep := string(filepath.Separator)
- return strings.Replace(p, sep, "_", -1)
-}
-
-func randPort() int {
- // returns between base and base + spread
- base, spread := 20000, 20000
- return base + rand.Intn(spread)
-}
-
-func makeAddrs() (string, string, string) {
- start := randPort()
- return fmt.Sprintf("tcp://0.0.0.0:%d", start),
- fmt.Sprintf("tcp://0.0.0.0:%d", start+1),
- fmt.Sprintf("tcp://0.0.0.0:%d", start+2)
-}
-
-// GetConfig returns a config for the test cases as a singleton
-func GetConfig() *cfg.Config {
- if config == nil {
- pathname := makePathname()
- config = cfg.ResetTestRoot(pathname)
-
- // and we use random ports to run in parallel
- tm, rpc, grpc := makeAddrs()
- config.P2P.ListenAddress = tm
- config.RPC.ListenAddress = rpc
- config.RPC.GRPCListenAddress = grpc
- }
- return config
-}
-
-// GetURIClient gets a uri client pointing to the test tendermint rpc
-func GetURIClient() *client.URIClient {
- rpcAddr := GetConfig().RPC.ListenAddress
- return client.NewURIClient(rpcAddr)
-}
-
-// GetJSONClient gets a http/json client pointing to the test tendermint rpc
-func GetJSONClient() *client.JSONRPCClient {
- rpcAddr := GetConfig().RPC.ListenAddress
- return client.NewJSONRPCClient(rpcAddr)
-}
-
-func GetGRPCClient() core_grpc.BroadcastAPIClient {
- grpcAddr := config.RPC.GRPCListenAddress
- return core_grpc.StartGRPCClient(grpcAddr)
-}
-
-func GetWSClient() *client.WSClient {
- rpcAddr := GetConfig().RPC.ListenAddress
- wsc := client.NewWSClient(rpcAddr, "/websocket")
- if _, err := wsc.Start(); err != nil {
- panic(err)
- }
- return wsc
-}
-
-// StartTendermint starts a test tendermint server in a go routine and returns when it is initialized
-func StartTendermint(app abci.Application) *nm.Node {
- node := NewTendermint(app)
- node.Start()
- fmt.Println("Tendermint running!")
- return node
-}
-
-// NewTendermint creates a new tendermint server and sleeps forever
-func NewTendermint(app abci.Application) *nm.Node {
- // Create & start node
- config := GetConfig()
- logger := log.NewTMLogger(log.NewSyncWriter(os.Stdout))
- logger = log.NewFilter(logger, log.AllowError())
- privValidatorFile := config.PrivValidatorFile()
- privValidator := types.LoadOrGenPrivValidator(privValidatorFile, logger)
- papp := proxy.NewLocalClientCreator(app)
- node := nm.NewNode(config, privValidator, papp, logger)
- return node
-}
-
-//--------------------------------------------------------------------------------
-// Utilities for testing the websocket service
-
-// wait for an event; do things that might trigger events, and check them when they are received
-// the check function takes an event id and the byte slice read off the ws
-func waitForEvent(t *testing.T, wsc *client.WSClient, eventid string, dieOnTimeout bool, f func(), check func(string, interface{}) error) {
- // go routine to wait for webscoket msg
- goodCh := make(chan interface{})
- errCh := make(chan error)
-
- // Read message
- go func() {
- var err error
- LOOP:
- for {
- select {
- case r := <-wsc.ResultsCh:
- result := new(ctypes.ResultEvent)
- err = json.Unmarshal(r, result)
- if err != nil {
- // cant distinguish between error and wrong type ...
- continue
- }
- if result.Name == eventid {
- goodCh <- result.Data
- break LOOP
- }
- case err := <-wsc.ErrorsCh:
- errCh <- err
- break LOOP
- case <-wsc.Quit:
- break LOOP
- }
- }
- }()
-
- // do stuff (transactions)
- f()
-
- // wait for an event or timeout
- timeout := time.NewTimer(10 * time.Second)
- select {
- case <-timeout.C:
- if dieOnTimeout {
- wsc.Stop()
- require.True(t, false, "%s event was not received in time", eventid)
- }
- // else that's great, we didn't hear the event
- // and we shouldn't have
- case eventData := <-goodCh:
- if dieOnTimeout {
- // message was received and expected
- // run the check
- require.Nil(t, check(eventid, eventData))
- } else {
- wsc.Stop()
- require.True(t, false, "%s event was not expected", eventid)
- }
- case err := <-errCh:
- panic(err) // Show the stack trace.
- }
-}
-
-//--------------------------------------------------------------------------------
+++ /dev/null
-/*
-package tests contain integration tests and helper functions for testing
-the RPC interface
-
-In particular, it allows us to spin up a tendermint node in process, with
-a live RPC server, which we can use to verify our rpc calls. It provides
-all data structures, enabling us to do more complex tests (like node_test.go)
-that introspect the blocks themselves to validate signatures and the like.
-
-It currently only spins up one node, it would be interesting to expand it
-to multiple nodes to see the real effects of validating partially signed
-blocks.
-*/
-package rpctest
-
-import (
- "os"
- "testing"
-
- "github.com/tendermint/abci/example/dummy"
- nm "github.com/tendermint/tendermint/node"
-)
-
-var node *nm.Node
-
-func TestMain(m *testing.M) {
- // start a tendermint node (and merkleeyes) in the background to test against
- app := dummy.NewDummyApplication()
- node = StartTendermint(app)
- code := m.Run()
-
- // and shut down proper at the end
- node.Stop()
- node.Wait()
- os.Exit(code)
-}
+++ /dev/null
-curl -X POST --data '{"jsonrpc":"2.0", "method": "net_info", "params":[], "id":"67"}' http://127.0.0.1:46657