From: Guanghua Guo <1536310027@qq.com> Date: Wed, 25 Oct 2017 05:57:50 +0000 (+0800) Subject: Replace grpc to jsonrpc (#69) X-Git-Tag: v1.0.5~463^2~11 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=06729c21ae783627a20859618824ada9b0c4a573;p=bytom%2Fbytom.git Replace grpc to jsonrpc (#69) * Add rpc about network status * Bind p2p.Switch and jsonrpc * Delete grpc && Add net info call in rpcclient * Format code --- diff --git a/blockchain/account/accounts.go b/blockchain/account/accounts.go index e466b908..11196e45 100644 --- a/blockchain/account/accounts.go +++ b/blockchain/account/accounts.go @@ -2,22 +2,22 @@ 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" ) @@ -29,11 +29,11 @@ var ( 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), @@ -43,10 +43,10 @@ func NewManager(db dbm.DB, chain *protocol.Chain , pinStore *pin.Store) *Manager // 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 @@ -215,16 +215,15 @@ func (m *Manager) findByID(ctx context.Context, id string) (*signers.Signer, err 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() @@ -293,17 +292,18 @@ func (m *Manager) insertAccountControlProgram(ctx context.Context, progs ...*con 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") @@ -324,9 +324,9 @@ func (m *Manager) nextIndex(ctx context.Context) (uint64, error) { 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 diff --git a/blockchain/account/builder.go b/blockchain/account/builder.go index ec015cea..43a4a70b 100644 --- a/blockchain/account/builder.go +++ b/blockchain/account/builder.go @@ -126,11 +126,11 @@ func (a *spendUTXOAction) Build(ctx context.Context, b *txbuilder.TemplateBuilde } 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 diff --git a/blockchain/account/indexer.go b/blockchain/account/indexer.go index 48970cda..94808351 100644 --- a/blockchain/account/indexer.go +++ b/blockchain/account/indexer.go @@ -1,9 +1,9 @@ package account import ( - "time" "context" "encoding/json" + "time" "github.com/bytom/blockchain/query" "github.com/bytom/blockchain/signers" diff --git a/blockchain/net.go b/blockchain/net.go new file mode 100644 index 00000000..ce280bee --- /dev/null +++ b/blockchain/net.go @@ -0,0 +1,11 @@ +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) +} diff --git a/blockchain/pseudohsm/pseudohsm_test.go b/blockchain/pseudohsm/pseudohsm_test.go index aac0ae00..9f213e5c 100644 --- a/blockchain/pseudohsm/pseudohsm_test.go +++ b/blockchain/pseudohsm/pseudohsm_test.go @@ -1,16 +1,15 @@ 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) @@ -41,13 +40,13 @@ func TestPseudoHSMChainKDKeys(t *testing.T) { 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) @@ -67,7 +66,7 @@ func TestPseudoHSMChainKDKeys(t *testing.T) { } 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 { @@ -80,12 +79,11 @@ func TestKeyWithEmptyAlias(t *testing.T) { } } - 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) diff --git a/blockchain/reactor.go b/blockchain/reactor.go index 5e71dc40..9fadfdf9 100644 --- a/blockchain/reactor.go +++ b/blockchain/reactor.go @@ -64,6 +64,7 @@ type BlockchainReactor struct { hsm *pseudohsm.HSM mining *cpuminer.CPUMiner mux *http.ServeMux + sw *p2p.Switch handler http.Handler fastSync bool requestsCh chan BlockRequest @@ -182,6 +183,7 @@ func (bcr *BlockchainReactor) BuildHander() { 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 { @@ -239,6 +241,7 @@ func NewBlockchainReactor(store *txdb.Store, txPool *protocol.TxPool, accounts *account.Manager, assets *asset.Registry, + sw *p2p.Switch, hsm *pseudohsm.HSM, fastSync bool, pinStore *pin.Store) *BlockchainReactor { @@ -260,6 +263,7 @@ func NewBlockchainReactor(store *txdb.Store, txPool: txPool, mining: mining, mux: http.NewServeMux(), + sw: sw, hsm: hsm, fastSync: fastSync, requestsCh: requestsCh, diff --git a/blockchain/rpc/info.go b/blockchain/rpc/info.go new file mode 100644 index 00000000..fe24810e --- /dev/null +++ b/blockchain/rpc/info.go @@ -0,0 +1,11 @@ +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 +} diff --git a/rpc/core/net.go b/blockchain/rpc/net.go similarity index 83% rename from rpc/core/net.go rename to blockchain/rpc/net.go index 30caa7e3..48918058 100644 --- a/rpc/core/net.go +++ b/blockchain/rpc/net.go @@ -1,14 +1,13 @@ -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() { @@ -32,7 +31,7 @@ func NetInfo() (*ctypes.ResultNetInfo, error) { //----------------------------------------------------------------------------- // 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") @@ -44,4 +43,4 @@ func UnsafeDialSeeds(seeds []string) (*ctypes.ResultDialSeeds, error) { return &ctypes.ResultDialSeeds{}, err } return &ctypes.ResultDialSeeds{"Dialing seeds in progress. See /net_info for details"}, nil -} +}*/ diff --git a/blockchain/rpc/rpc.go b/blockchain/rpc/rpc.go index 76a10169..1465a204 100644 --- a/blockchain/rpc/rpc.go +++ b/blockchain/rpc/rpc.go @@ -1,4 +1,3 @@ -// Package rpc implements Chain Core's RPC client. package rpc import ( @@ -17,10 +16,10 @@ 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" ) @@ -28,7 +27,7 @@ const ( // 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 { @@ -45,7 +44,7 @@ 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) } diff --git a/rpc/core/types/responses.go b/blockchain/rpc/types/responses.go similarity index 68% rename from rpc/core/types/responses.go rename to blockchain/rpc/types/responses.go index 63a5e550..b991693a 100644 --- a/rpc/core/types/responses.go +++ b/blockchain/rpc/types/responses.go @@ -7,7 +7,6 @@ import ( "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" ) @@ -72,39 +71,3 @@ type Peer struct { 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{} diff --git a/rpc/core/types/responses_test.go b/blockchain/rpc/types/responses_test.go similarity index 100% rename from rpc/core/types/responses_test.go rename to blockchain/rpc/types/responses_test.go diff --git a/blockchain/signers/signers.go b/blockchain/signers/signers.go index 1b58e30d..3428720a 100644 --- a/blockchain/signers/signers.go +++ b/blockchain/signers/signers.go @@ -100,29 +100,29 @@ func Create(ctx context.Context, db dbm.DB, typ string, xpubs []chainkd.XPub, qu } /* - 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 diff --git a/blockchain/transact.go b/blockchain/transact.go index d12fe164..eb5b6fe3 100644 --- a/blockchain/transact.go +++ b/blockchain/transact.go @@ -232,14 +232,12 @@ func (a *BlockchainReactor) finalizeTxWait(ctx context.Context, txTemplate *txbu return nil } - select { case <-ctx.Done(): return ctx.Err() case <-a.pinStore.AllWaiter(height): } - return nil } diff --git a/cmd/bytomcli/main.go b/cmd/bytomcli/main.go index 33dd348d..64c518cf 100644 --- a/cmd/bytomcli/main.go +++ b/cmd/bytomcli/main.go @@ -60,11 +60,11 @@ var commands = map[string]*command{ "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}, @@ -92,6 +92,7 @@ var commands = map[string]*command{ "sub-create-issue-tx": {submitCreateIssueTransaction}, "reset-password": {resetPassword}, "update-alias": {updateAlias}, + "net-info": {netInfo}, } func main() { @@ -439,7 +440,6 @@ func bindAsset(client *rpc.Client, args []string) { 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 [|] [tags_key:]") @@ -966,7 +966,7 @@ func signTransactions(client *rpc.Client, args []string) { } // sign-transaction type param struct { - Auth string + Auth string Txs []*txbuilder.Template `json:"transactions"` XPubs []chainkd.XPub `json:"xpubs"` } @@ -1037,3 +1037,9 @@ func updateAlias(client *rpc.Client, args []string) { 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) +} diff --git a/config/config.go b/config/config.go index cfea436d..401d0e24 100644 --- a/config/config.go +++ b/config/config.go @@ -11,8 +11,8 @@ type Config struct { 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"` } @@ -21,7 +21,7 @@ func DefaultConfig() *Config { BaseConfig: DefaultBaseConfig(), RPC: DefaultRPCConfig(), P2P: DefaultP2PConfig(), - Wallet: DefaultWalletConfig(), + Wallet: DefaultWalletConfig(), } } @@ -30,7 +30,7 @@ func TestConfig() *Config { BaseConfig: TestBaseConfig(), RPC: TestRPCConfig(), P2P: TestP2PConfig(), - Wallet: TestWalletConfig(), + Wallet: TestWalletConfig(), } } @@ -191,7 +191,7 @@ func DefaultP2PConfig() *P2PConfig { ListenAddress: "tcp://0.0.0.0:46656", AddrBook: "addrbook.json", AddrBookStrict: true, - SkipUPNP: false, + SkipUPNP: false, MaxNumPeers: 50, } } @@ -211,7 +211,7 @@ func (p *P2PConfig) AddrBookFile() string { // WalletConfig type WalletConfig struct { - Enable bool `mapstructure:"enable"` + Enable bool `mapstructure:"enable"` } func DefaultWalletConfig() *WalletConfig { diff --git a/crypto/crypto_test.go b/crypto/crypto_test.go index 7b866054..4950317a 100644 --- a/crypto/crypto_test.go +++ b/crypto/crypto_test.go @@ -18,10 +18,10 @@ package crypto import ( _ "bytes" - _ "github.com/bytom/common" _ "crypto/ecdsa" _ "encoding/hex" _ "fmt" + _ "github.com/bytom/common" _ "io/ioutil" _ "math/big" _ "os" diff --git a/node/node.go b/node/node.go index 5b22a114..73a5bf67 100644 --- a/node/node.go +++ b/node/node.go @@ -30,9 +30,6 @@ import ( 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" @@ -59,11 +56,10 @@ type Node struct { // 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 ( @@ -125,7 +121,6 @@ func rpcInit(h *bc.BlockchainReactor, config *cfg.Config) { mux.Handle("/", &coreHandler) var handler http.Handler = mux - //handler = core.AuthHandler(handler, raftDB, accessTokens, tlsConfig) handler = RedirectHandler(handler) handler = reqid.Handler(handler) @@ -252,6 +247,7 @@ func NewNode(config *cfg.Config, logger log.Logger) *Node { txPool, accounts, assets, + sw, hsm, fastSync, pinStore) @@ -322,15 +318,6 @@ func (n *Node) OnStart() error { 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 } @@ -341,12 +328,6 @@ func (n *Node) OnStop() { // 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() { @@ -370,52 +351,6 @@ func (n *Node) AddListener(l p2p.Listener) { 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 } @@ -443,13 +378,13 @@ func (n *Node) makeNodeInfo() *p2p.NodeInfo { 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 } diff --git a/protocol/bc/legacy/map_test.go b/protocol/bc/legacy/map_test.go index 3ab5df9d..da342cc2 100644 --- a/protocol/bc/legacy/map_test.go +++ b/protocol/bc/legacy/map_test.go @@ -63,12 +63,12 @@ func TestMapTx(t *testing.T) { } 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, diff --git a/protocol/protocol.go b/protocol/protocol.go index d60c8b5a..dae7cc11 100644 --- a/protocol/protocol.go +++ b/protocol/protocol.go @@ -57,7 +57,7 @@ type Chain struct { lastQueuedSnapshot time.Time pendingSnapshots chan pendingSnapshot - txPool *TxPool + txPool *TxPool } type pendingSnapshot struct { diff --git a/rpc/client/event_test.go b/rpc/client/event_test.go deleted file mode 100644 index 87c32d27..00000000 --- a/rpc/client/event_test.go +++ /dev/null @@ -1,65 +0,0 @@ -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()) - } -} diff --git a/rpc/client/helpers.go b/rpc/client/helpers.go deleted file mode 100644 index ad83e020..00000000 --- a/rpc/client/helpers.go +++ /dev/null @@ -1,88 +0,0 @@ -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) - } -} diff --git a/rpc/client/helpers_test.go b/rpc/client/helpers_test.go deleted file mode 100644 index 2424fa68..00000000 --- a/rpc/client/helpers_test.go +++ /dev/null @@ -1,76 +0,0 @@ -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) -} diff --git a/rpc/client/httpclient.go b/rpc/client/httpclient.go deleted file mode 100644 index 2ac9faef..00000000 --- a/rpc/client/httpclient.go +++ /dev/null @@ -1,363 +0,0 @@ -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://: -// 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) - } -} diff --git a/rpc/client/interface.go b/rpc/client/interface.go deleted file mode 100644 index 14338457..00000000 --- a/rpc/client/interface.go +++ /dev/null @@ -1,84 +0,0 @@ -/* -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) -} diff --git a/rpc/client/localclient.go b/rpc/client/localclient.go deleted file mode 100644 index 41dae37e..00000000 --- a/rpc/client/localclient.go +++ /dev/null @@ -1,110 +0,0 @@ -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) -} diff --git a/rpc/client/main_test.go b/rpc/client/main_test.go deleted file mode 100644 index 275a3bb7..00000000 --- a/rpc/client/main_test.go +++ /dev/null @@ -1,24 +0,0 @@ -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) -} diff --git a/rpc/client/mock/abci.go b/rpc/client/mock/abci.go deleted file mode 100644 index 0d101255..00000000 --- a/rpc/client/mock/abci.go +++ /dev/null @@ -1,194 +0,0 @@ -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 -} diff --git a/rpc/client/mock/abci_test.go b/rpc/client/mock/abci_test.go deleted file mode 100644 index 935f9ff9..00000000 --- a/rpc/client/mock/abci_test.go +++ /dev/null @@ -1,170 +0,0 @@ -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) -} diff --git a/rpc/client/mock/client.go b/rpc/client/mock/client.go deleted file mode 100644 index bf8d78dc..00000000 --- a/rpc/client/mock/client.go +++ /dev/null @@ -1,129 +0,0 @@ -/* -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() -} diff --git a/rpc/client/mock/status.go b/rpc/client/mock/status.go deleted file mode 100644 index af0f5335..00000000 --- a/rpc/client/mock/status.go +++ /dev/null @@ -1,55 +0,0 @@ -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 -} diff --git a/rpc/client/mock/status_test.go b/rpc/client/mock/status_test.go deleted file mode 100644 index e4adf52b..00000000 --- a/rpc/client/mock/status_test.go +++ /dev/null @@ -1,46 +0,0 @@ -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) -} diff --git a/rpc/client/rpc_test.go b/rpc/client/rpc_test.go deleted file mode 100644 index 994ee15b..00000000 --- a/rpc/client/rpc_test.go +++ /dev/null @@ -1,184 +0,0 @@ -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) - } - } - } -} diff --git a/rpc/core/info.go b/rpc/core/info.go deleted file mode 100644 index 7a7da332..00000000 --- a/rpc/core/info.go +++ /dev/null @@ -1,9 +0,0 @@ -package core - -import ( - ctypes "github.com/bytom/rpc/core/types" -) - -func BlockHeight() (*ctypes.ResultBlockchainInfo, error) { - return &ctypes.ResultBlockchainInfo{LastHeight: blockStore.Height()}, nil -} diff --git a/rpc/core/pipe.go b/rpc/core/pipe.go deleted file mode 100644 index 9cb9d3fe..00000000 --- a/rpc/core/pipe.go +++ /dev/null @@ -1,48 +0,0 @@ -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 -} diff --git a/rpc/core/pow.go b/rpc/core/pow.go deleted file mode 100644 index 3f39709a..00000000 --- a/rpc/core/pow.go +++ /dev/null @@ -1,21 +0,0 @@ -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 -} diff --git a/rpc/core/routes.go b/rpc/core/routes.go deleted file mode 100644 index d668d3c2..00000000 --- a/rpc/core/routes.go +++ /dev/null @@ -1,19 +0,0 @@ -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") -} diff --git a/rpc/core/version.go b/rpc/core/version.go deleted file mode 100644 index e283de47..00000000 --- a/rpc/core/version.go +++ /dev/null @@ -1,5 +0,0 @@ -package core - -// a single integer is sufficient here - -const Version = "3" // rpc routes for profiling, setting config diff --git a/rpc/grpc/client_server.go b/rpc/grpc/client_server.go deleted file mode 100644 index c6fdc50a..00000000 --- a/rpc/grpc/client_server.go +++ /dev/null @@ -1,44 +0,0 @@ -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) -} diff --git a/rpc/grpc/compile.sh b/rpc/grpc/compile.sh deleted file mode 100644 index 2c4629c8..00000000 --- a/rpc/grpc/compile.sh +++ /dev/null @@ -1,3 +0,0 @@ -#! /bin/bash - -protoc --go_out=plugins=grpc:. -I $GOPATH/src/ -I . types.proto diff --git a/rpc/grpc/types.pb.go b/rpc/grpc/types.pb.go deleted file mode 100644 index 43b43c87..00000000 --- a/rpc/grpc/types.pb.go +++ /dev/null @@ -1,174 +0,0 @@ -// 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, -} diff --git a/rpc/grpc/types.proto b/rpc/grpc/types.proto deleted file mode 100644 index 3090e3d0..00000000 --- a/rpc/grpc/types.proto +++ /dev/null @@ -1,29 +0,0 @@ -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) ; -} diff --git a/rpc/lib/Dockerfile b/rpc/lib/Dockerfile deleted file mode 100644 index a194711b..00000000 --- a/rpc/lib/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -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 diff --git a/rpc/lib/Makefile b/rpc/lib/Makefile deleted file mode 100644 index 0937558a..00000000 --- a/rpc/lib/Makefile +++ /dev/null @@ -1,18 +0,0 @@ -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 diff --git a/rpc/lib/README.md b/rpc/lib/README.md deleted file mode 100644 index de481c2f..00000000 --- a/rpc/lib/README.md +++ /dev/null @@ -1,121 +0,0 @@ -# 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) diff --git a/rpc/lib/circle.yml b/rpc/lib/circle.yml deleted file mode 100644 index 0308a4e7..00000000 --- a/rpc/lib/circle.yml +++ /dev/null @@ -1,21 +0,0 @@ -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" diff --git a/rpc/lib/client/args_test.go b/rpc/lib/client/args_test.go deleted file mode 100644 index ccabd0d2..00000000 --- a/rpc/lib/client/args_test.go +++ /dev/null @@ -1,39 +0,0 @@ -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) - } -} diff --git a/rpc/lib/client/http_client.go b/rpc/lib/client/http_client.go deleted file mode 100644 index 2d0c9e86..00000000 --- a/rpc/lib/client/http_client.go +++ /dev/null @@ -1,185 +0,0 @@ -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 -} diff --git a/rpc/lib/client/ws_client.go b/rpc/lib/client/ws_client.go deleted file mode 100644 index 33239112..00000000 --- a/rpc/lib/client/ws_client.go +++ /dev/null @@ -1,160 +0,0 @@ -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 -} diff --git a/rpc/lib/rpc_test.go b/rpc/lib/rpc_test.go deleted file mode 100644 index c7bed052..00000000 --- a/rpc/lib/rpc_test.go +++ /dev/null @@ -1,335 +0,0 @@ -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) -} diff --git a/rpc/lib/server/handlers.go b/rpc/lib/server/handlers.go deleted file mode 100644 index 58c2f027..00000000 --- a/rpc/lib/server/handlers.go +++ /dev/null @@ -1,659 +0,0 @@ -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("") - buf.WriteString("
Available endpoints:
") - - for _, name := range noArgNames { - link := fmt.Sprintf("http://%s/%s", r.Host, name) - buf.WriteString(fmt.Sprintf("%s
", link, link)) - } - - buf.WriteString("
Endpoints that require arguments:
") - 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("%s
", link, link)) - } - buf.WriteString("") - w.Header().Set("Content-Type", "text/html") - w.WriteHeader(200) - w.Write(buf.Bytes()) -} diff --git a/rpc/lib/server/http_params.go b/rpc/lib/server/http_params.go deleted file mode 100644 index 56506067..00000000 --- a/rpc/lib/server/http_params.go +++ /dev/null @@ -1,90 +0,0 @@ -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 -} diff --git a/rpc/lib/server/http_server.go b/rpc/lib/server/http_server.go deleted file mode 100644 index 442b7c42..00000000 --- a/rpc/lib/server/http_server.go +++ /dev/null @@ -1,136 +0,0 @@ -// 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() -} diff --git a/rpc/lib/server/parse_test.go b/rpc/lib/server/parse_test.go deleted file mode 100644 index 3c6d6edd..00000000 --- a/rpc/lib/server/parse_test.go +++ /dev/null @@ -1,174 +0,0 @@ -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) - } - } - - } - -} diff --git a/rpc/lib/test/data.json b/rpc/lib/test/data.json deleted file mode 100644 index 83283ec3..00000000 --- a/rpc/lib/test/data.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "jsonrpc": "2.0", - "id": "", - "method": "hello_world", - "params": { - "name": "my_world", - "num": 5 - } -} diff --git a/rpc/lib/test/integration_test.sh b/rpc/lib/test/integration_test.sh deleted file mode 100755 index 7c23be7d..00000000 --- a/rpc/lib/test/integration_test.sh +++ /dev/null @@ -1,95 +0,0 @@ -#!/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 diff --git a/rpc/lib/test/main.go b/rpc/lib/test/main.go deleted file mode 100644 index 047a1d86..00000000 --- a/rpc/lib/test/main.go +++ /dev/null @@ -1,38 +0,0 @@ -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() { - }) - -} diff --git a/rpc/lib/types/types.go b/rpc/lib/types/types.go deleted file mode 100644 index 8076e4b0..00000000 --- a/rpc/lib/types/types.go +++ /dev/null @@ -1,100 +0,0 @@ -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 -} diff --git a/rpc/lib/version.go b/rpc/lib/version.go deleted file mode 100644 index 8828f260..00000000 --- a/rpc/lib/version.go +++ /dev/null @@ -1,7 +0,0 @@ -package rpc - -const Maj = "0" -const Min = "7" -const Fix = "0" - -const Version = Maj + "." + Min + "." + Fix diff --git a/rpc/test/client_test.go b/rpc/test/client_test.go deleted file mode 100644 index b7df6784..00000000 --- a/rpc/test/client_test.go +++ /dev/null @@ -1,352 +0,0 @@ -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 - }) -}*/ diff --git a/rpc/test/grpc_test.go b/rpc/test/grpc_test.go deleted file mode 100644 index 4935a09d..00000000 --- a/rpc/test/grpc_test.go +++ /dev/null @@ -1,20 +0,0 @@ -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) -} diff --git a/rpc/test/helpers.go b/rpc/test/helpers.go deleted file mode 100644 index 51bb0b8f..00000000 --- a/rpc/test/helpers.go +++ /dev/null @@ -1,178 +0,0 @@ -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. - } -} - -//-------------------------------------------------------------------------------- diff --git a/rpc/test/main_test.go b/rpc/test/main_test.go deleted file mode 100644 index f95176d9..00000000 --- a/rpc/test/main_test.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -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) -} diff --git a/rpc/test/net_info.sh b/rpc/test/net_info.sh deleted file mode 100755 index 1c7879a2..00000000 --- a/rpc/test/net_info.sh +++ /dev/null @@ -1 +0,0 @@ -curl -X POST --data '{"jsonrpc":"2.0", "method": "net_info", "params":[], "id":"67"}' http://127.0.0.1:46657