github.com/evdatsion/aphelion-dpos-bft@v0.32.1/rpc/client/rpc_test.go (about) 1 package client_test 2 3 import ( 4 "fmt" 5 "net/http" 6 "strings" 7 "sync" 8 "testing" 9 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 abci "github.com/evdatsion/aphelion-dpos-bft/abci/types" 14 15 cmn "github.com/evdatsion/aphelion-dpos-bft/libs/common" 16 "github.com/evdatsion/aphelion-dpos-bft/rpc/client" 17 ctypes "github.com/evdatsion/aphelion-dpos-bft/rpc/core/types" 18 rpctest "github.com/evdatsion/aphelion-dpos-bft/rpc/test" 19 "github.com/evdatsion/aphelion-dpos-bft/types" 20 ) 21 22 func getHTTPClient() *client.HTTP { 23 rpcAddr := rpctest.GetConfig().RPC.ListenAddress 24 return client.NewHTTP(rpcAddr, "/websocket") 25 } 26 27 func getLocalClient() *client.Local { 28 return client.NewLocal(node) 29 } 30 31 // GetClients returns a slice of clients for table-driven tests 32 func GetClients() []client.Client { 33 return []client.Client{ 34 getHTTPClient(), 35 getLocalClient(), 36 } 37 } 38 39 func TestCorsEnabled(t *testing.T) { 40 origin := rpctest.GetConfig().RPC.CORSAllowedOrigins[0] 41 remote := strings.Replace(rpctest.GetConfig().RPC.ListenAddress, "tcp", "http", -1) 42 43 req, err := http.NewRequest("GET", remote, nil) 44 require.Nil(t, err, "%+v", err) 45 req.Header.Set("Origin", origin) 46 c := &http.Client{} 47 resp, err := c.Do(req) 48 require.Nil(t, err, "%+v", err) 49 defer resp.Body.Close() 50 51 assert.Equal(t, resp.Header.Get("Access-Control-Allow-Origin"), origin) 52 } 53 54 // Make sure status is correct (we connect properly) 55 func TestStatus(t *testing.T) { 56 for i, c := range GetClients() { 57 moniker := rpctest.GetConfig().Moniker 58 status, err := c.Status() 59 require.Nil(t, err, "%d: %+v", i, err) 60 assert.Equal(t, moniker, status.NodeInfo.Moniker) 61 } 62 } 63 64 // Make sure info is correct (we connect properly) 65 func TestInfo(t *testing.T) { 66 for i, c := range GetClients() { 67 // status, err := c.Status() 68 // require.Nil(t, err, "%+v", err) 69 info, err := c.ABCIInfo() 70 require.Nil(t, err, "%d: %+v", i, err) 71 // TODO: this is not correct - fix merkleeyes! 72 // assert.EqualValues(t, status.SyncInfo.LatestBlockHeight, info.Response.LastBlockHeight) 73 assert.True(t, strings.Contains(info.Response.Data, "size")) 74 } 75 } 76 77 func TestNetInfo(t *testing.T) { 78 for i, c := range GetClients() { 79 nc, ok := c.(client.NetworkClient) 80 require.True(t, ok, "%d", i) 81 netinfo, err := nc.NetInfo() 82 require.Nil(t, err, "%d: %+v", i, err) 83 assert.True(t, netinfo.Listening) 84 assert.Equal(t, 0, len(netinfo.Peers)) 85 } 86 } 87 88 func TestDumpConsensusState(t *testing.T) { 89 for i, c := range GetClients() { 90 // FIXME: fix server so it doesn't panic on invalid input 91 nc, ok := c.(client.NetworkClient) 92 require.True(t, ok, "%d", i) 93 cons, err := nc.DumpConsensusState() 94 require.Nil(t, err, "%d: %+v", i, err) 95 assert.NotEmpty(t, cons.RoundState) 96 assert.Empty(t, cons.Peers) 97 } 98 } 99 100 func TestConsensusState(t *testing.T) { 101 for i, c := range GetClients() { 102 // FIXME: fix server so it doesn't panic on invalid input 103 nc, ok := c.(client.NetworkClient) 104 require.True(t, ok, "%d", i) 105 cons, err := nc.ConsensusState() 106 require.Nil(t, err, "%d: %+v", i, err) 107 assert.NotEmpty(t, cons.RoundState) 108 } 109 } 110 111 func TestHealth(t *testing.T) { 112 for i, c := range GetClients() { 113 nc, ok := c.(client.NetworkClient) 114 require.True(t, ok, "%d", i) 115 _, err := nc.Health() 116 require.Nil(t, err, "%d: %+v", i, err) 117 } 118 } 119 120 func TestGenesisAndValidators(t *testing.T) { 121 for i, c := range GetClients() { 122 123 // make sure this is the right genesis file 124 gen, err := c.Genesis() 125 require.Nil(t, err, "%d: %+v", i, err) 126 // get the genesis validator 127 require.Equal(t, 1, len(gen.Genesis.Validators)) 128 gval := gen.Genesis.Validators[0] 129 130 // get the current validators 131 vals, err := c.Validators(nil) 132 require.Nil(t, err, "%d: %+v", i, err) 133 require.Equal(t, 1, len(vals.Validators)) 134 val := vals.Validators[0] 135 136 // make sure the current set is also the genesis set 137 assert.Equal(t, gval.Power, val.VotingPower) 138 assert.Equal(t, gval.PubKey, val.PubKey) 139 } 140 } 141 142 func TestABCIQuery(t *testing.T) { 143 for i, c := range GetClients() { 144 // write something 145 k, v, tx := MakeTxKV() 146 bres, err := c.BroadcastTxCommit(tx) 147 require.Nil(t, err, "%d: %+v", i, err) 148 apph := bres.Height + 1 // this is where the tx will be applied to the state 149 150 // wait before querying 151 client.WaitForHeight(c, apph, nil) 152 res, err := c.ABCIQuery("/key", k) 153 qres := res.Response 154 if assert.Nil(t, err) && assert.True(t, qres.IsOK()) { 155 assert.EqualValues(t, v, qres.Value) 156 } 157 } 158 } 159 160 // Make some app checks 161 func TestAppCalls(t *testing.T) { 162 assert, require := assert.New(t), require.New(t) 163 for i, c := range GetClients() { 164 165 // get an offset of height to avoid racing and guessing 166 s, err := c.Status() 167 require.Nil(err, "%d: %+v", i, err) 168 // sh is start height or status height 169 sh := s.SyncInfo.LatestBlockHeight 170 171 // look for the future 172 h := sh + 2 173 _, err = c.Block(&h) 174 assert.NotNil(err) // no block yet 175 176 // write something 177 k, v, tx := MakeTxKV() 178 bres, err := c.BroadcastTxCommit(tx) 179 require.Nil(err, "%d: %+v", i, err) 180 require.True(bres.DeliverTx.IsOK()) 181 txh := bres.Height 182 apph := txh + 1 // this is where the tx will be applied to the state 183 184 // wait before querying 185 if err := client.WaitForHeight(c, apph, nil); err != nil { 186 t.Error(err) 187 } 188 _qres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: false}) 189 qres := _qres.Response 190 if assert.Nil(err) && assert.True(qres.IsOK()) { 191 assert.Equal(k, qres.Key) 192 assert.EqualValues(v, qres.Value) 193 } 194 195 // make sure we can lookup the tx with proof 196 ptx, err := c.Tx(bres.Hash, true) 197 require.Nil(err, "%d: %+v", i, err) 198 assert.EqualValues(txh, ptx.Height) 199 assert.EqualValues(tx, ptx.Tx) 200 201 // and we can even check the block is added 202 block, err := c.Block(&apph) 203 require.Nil(err, "%d: %+v", i, err) 204 appHash := block.BlockMeta.Header.AppHash 205 assert.True(len(appHash) > 0) 206 assert.EqualValues(apph, block.BlockMeta.Header.Height) 207 208 // now check the results 209 blockResults, err := c.BlockResults(&txh) 210 require.Nil(err, "%d: %+v", i, err) 211 assert.Equal(txh, blockResults.Height) 212 if assert.Equal(1, len(blockResults.Results.DeliverTx)) { 213 // check success code 214 assert.EqualValues(0, blockResults.Results.DeliverTx[0].Code) 215 } 216 217 // check blockchain info, now that we know there is info 218 info, err := c.BlockchainInfo(apph, apph) 219 require.Nil(err, "%d: %+v", i, err) 220 assert.True(info.LastHeight >= apph) 221 if assert.Equal(1, len(info.BlockMetas)) { 222 lastMeta := info.BlockMetas[0] 223 assert.EqualValues(apph, lastMeta.Header.Height) 224 bMeta := block.BlockMeta 225 assert.Equal(bMeta.Header.AppHash, lastMeta.Header.AppHash) 226 assert.Equal(bMeta.BlockID, lastMeta.BlockID) 227 } 228 229 // and get the corresponding commit with the same apphash 230 commit, err := c.Commit(&apph) 231 require.Nil(err, "%d: %+v", i, err) 232 cappHash := commit.Header.AppHash 233 assert.Equal(appHash, cappHash) 234 assert.NotNil(commit.Commit) 235 236 // compare the commits (note Commit(2) has commit from Block(3)) 237 h = apph - 1 238 commit2, err := c.Commit(&h) 239 require.Nil(err, "%d: %+v", i, err) 240 assert.Equal(block.Block.LastCommit, commit2.Commit) 241 242 // and we got a proof that works! 243 _pres, err := c.ABCIQueryWithOptions("/key", k, client.ABCIQueryOptions{Prove: true}) 244 pres := _pres.Response 245 assert.Nil(err) 246 assert.True(pres.IsOK()) 247 248 // XXX Test proof 249 } 250 } 251 252 func TestBroadcastTxSync(t *testing.T) { 253 require := require.New(t) 254 255 // TODO (melekes): use mempool which is set on RPC rather than getting it from node 256 mempool := node.Mempool() 257 initMempoolSize := mempool.Size() 258 259 for i, c := range GetClients() { 260 _, _, tx := MakeTxKV() 261 bres, err := c.BroadcastTxSync(tx) 262 require.Nil(err, "%d: %+v", i, err) 263 require.Equal(bres.Code, abci.CodeTypeOK) // FIXME 264 265 require.Equal(initMempoolSize+1, mempool.Size()) 266 267 txs := mempool.ReapMaxTxs(len(tx)) 268 require.EqualValues(tx, txs[0]) 269 mempool.Flush() 270 } 271 } 272 273 func TestBroadcastTxCommit(t *testing.T) { 274 require := require.New(t) 275 276 mempool := node.Mempool() 277 for i, c := range GetClients() { 278 _, _, tx := MakeTxKV() 279 bres, err := c.BroadcastTxCommit(tx) 280 require.Nil(err, "%d: %+v", i, err) 281 require.True(bres.CheckTx.IsOK()) 282 require.True(bres.DeliverTx.IsOK()) 283 284 require.Equal(0, mempool.Size()) 285 } 286 } 287 288 func TestUnconfirmedTxs(t *testing.T) { 289 _, _, tx := MakeTxKV() 290 291 mempool := node.Mempool() 292 _ = mempool.CheckTx(tx, nil) 293 294 for i, c := range GetClients() { 295 mc, ok := c.(client.MempoolClient) 296 require.True(t, ok, "%d", i) 297 res, err := mc.UnconfirmedTxs(1) 298 require.Nil(t, err, "%d: %+v", i, err) 299 300 assert.Equal(t, 1, res.Count) 301 assert.Equal(t, 1, res.Total) 302 assert.Equal(t, mempool.TxsBytes(), res.TotalBytes) 303 assert.Exactly(t, types.Txs{tx}, types.Txs(res.Txs)) 304 } 305 306 mempool.Flush() 307 } 308 309 func TestNumUnconfirmedTxs(t *testing.T) { 310 _, _, tx := MakeTxKV() 311 312 mempool := node.Mempool() 313 _ = mempool.CheckTx(tx, nil) 314 mempoolSize := mempool.Size() 315 316 for i, c := range GetClients() { 317 mc, ok := c.(client.MempoolClient) 318 require.True(t, ok, "%d", i) 319 res, err := mc.NumUnconfirmedTxs() 320 require.Nil(t, err, "%d: %+v", i, err) 321 322 assert.Equal(t, mempoolSize, res.Count) 323 assert.Equal(t, mempoolSize, res.Total) 324 assert.Equal(t, mempool.TxsBytes(), res.TotalBytes) 325 } 326 327 mempool.Flush() 328 } 329 330 func TestTx(t *testing.T) { 331 // first we broadcast a tx 332 c := getHTTPClient() 333 _, _, tx := MakeTxKV() 334 bres, err := c.BroadcastTxCommit(tx) 335 require.Nil(t, err, "%+v", err) 336 337 txHeight := bres.Height 338 txHash := bres.Hash 339 340 anotherTxHash := types.Tx("a different tx").Hash() 341 342 cases := []struct { 343 valid bool 344 hash []byte 345 prove bool 346 }{ 347 // only valid if correct hash provided 348 {true, txHash, false}, 349 {true, txHash, true}, 350 {false, anotherTxHash, false}, 351 {false, anotherTxHash, true}, 352 {false, nil, false}, 353 {false, nil, true}, 354 } 355 356 for i, c := range GetClients() { 357 for j, tc := range cases { 358 t.Logf("client %d, case %d", i, j) 359 360 // now we query for the tx. 361 // since there's only one tx, we know index=0. 362 ptx, err := c.Tx(tc.hash, tc.prove) 363 364 if !tc.valid { 365 require.NotNil(t, err) 366 } else { 367 require.Nil(t, err, "%+v", err) 368 assert.EqualValues(t, txHeight, ptx.Height) 369 assert.EqualValues(t, tx, ptx.Tx) 370 assert.Zero(t, ptx.Index) 371 assert.True(t, ptx.TxResult.IsOK()) 372 assert.EqualValues(t, txHash, ptx.Hash) 373 374 // time to verify the proof 375 proof := ptx.Proof 376 if tc.prove && assert.EqualValues(t, tx, proof.Data) { 377 assert.NoError(t, proof.Proof.Verify(proof.RootHash, txHash)) 378 } 379 } 380 } 381 } 382 } 383 384 func TestTxSearch(t *testing.T) { 385 // first we broadcast a tx 386 c := getHTTPClient() 387 _, _, tx := MakeTxKV() 388 bres, err := c.BroadcastTxCommit(tx) 389 require.Nil(t, err, "%+v", err) 390 391 txHeight := bres.Height 392 txHash := bres.Hash 393 394 anotherTxHash := types.Tx("a different tx").Hash() 395 396 for i, c := range GetClients() { 397 t.Logf("client %d", i) 398 399 // now we query for the tx. 400 // since there's only one tx, we know index=0. 401 result, err := c.TxSearch(fmt.Sprintf("tx.hash='%v'", txHash), true, 1, 30) 402 require.Nil(t, err, "%+v", err) 403 require.Len(t, result.Txs, 1) 404 405 ptx := result.Txs[0] 406 assert.EqualValues(t, txHeight, ptx.Height) 407 assert.EqualValues(t, tx, ptx.Tx) 408 assert.Zero(t, ptx.Index) 409 assert.True(t, ptx.TxResult.IsOK()) 410 assert.EqualValues(t, txHash, ptx.Hash) 411 412 // time to verify the proof 413 proof := ptx.Proof 414 if assert.EqualValues(t, tx, proof.Data) { 415 assert.NoError(t, proof.Proof.Verify(proof.RootHash, txHash)) 416 } 417 418 // query by height 419 result, err = c.TxSearch(fmt.Sprintf("tx.height=%d", txHeight), true, 1, 30) 420 require.Nil(t, err, "%+v", err) 421 require.Len(t, result.Txs, 1) 422 423 // query for non existing tx 424 result, err = c.TxSearch(fmt.Sprintf("tx.hash='%X'", anotherTxHash), false, 1, 30) 425 require.Nil(t, err, "%+v", err) 426 require.Len(t, result.Txs, 0) 427 428 // query using a tag (see kvstore application) 429 result, err = c.TxSearch("app.creator='Cosmoshi Netowoko'", false, 1, 30) 430 require.Nil(t, err, "%+v", err) 431 if len(result.Txs) == 0 { 432 t.Fatal("expected a lot of transactions") 433 } 434 435 // query using a tag (see kvstore application) and height 436 result, err = c.TxSearch("app.creator='Cosmoshi Netowoko' AND tx.height<10000", true, 1, 30) 437 require.Nil(t, err, "%+v", err) 438 if len(result.Txs) == 0 { 439 t.Fatal("expected a lot of transactions") 440 } 441 442 // query a non existing tx with page 1 and txsPerPage 1 443 result, err = c.TxSearch("app.creator='Cosmoshi Neetowoko'", true, 1, 1) 444 require.Nil(t, err, "%+v", err) 445 require.Len(t, result.Txs, 0) 446 } 447 } 448 449 func TestBatchedJSONRPCCalls(t *testing.T) { 450 c := getHTTPClient() 451 testBatchedJSONRPCCalls(t, c) 452 } 453 454 func testBatchedJSONRPCCalls(t *testing.T, c *client.HTTP) { 455 k1, v1, tx1 := MakeTxKV() 456 k2, v2, tx2 := MakeTxKV() 457 458 batch := c.NewBatch() 459 r1, err := batch.BroadcastTxCommit(tx1) 460 require.NoError(t, err) 461 r2, err := batch.BroadcastTxCommit(tx2) 462 require.NoError(t, err) 463 require.Equal(t, 2, batch.Count()) 464 bresults, err := batch.Send() 465 require.NoError(t, err) 466 require.Len(t, bresults, 2) 467 require.Equal(t, 0, batch.Count()) 468 469 bresult1, ok := bresults[0].(*ctypes.ResultBroadcastTxCommit) 470 require.True(t, ok) 471 require.Equal(t, *bresult1, *r1) 472 bresult2, ok := bresults[1].(*ctypes.ResultBroadcastTxCommit) 473 require.True(t, ok) 474 require.Equal(t, *bresult2, *r2) 475 apph := cmn.MaxInt64(bresult1.Height, bresult2.Height) + 1 476 477 client.WaitForHeight(c, apph, nil) 478 479 q1, err := batch.ABCIQuery("/key", k1) 480 require.NoError(t, err) 481 q2, err := batch.ABCIQuery("/key", k2) 482 require.NoError(t, err) 483 require.Equal(t, 2, batch.Count()) 484 qresults, err := batch.Send() 485 require.NoError(t, err) 486 require.Len(t, qresults, 2) 487 require.Equal(t, 0, batch.Count()) 488 489 qresult1, ok := qresults[0].(*ctypes.ResultABCIQuery) 490 require.True(t, ok) 491 require.Equal(t, *qresult1, *q1) 492 qresult2, ok := qresults[1].(*ctypes.ResultABCIQuery) 493 require.True(t, ok) 494 require.Equal(t, *qresult2, *q2) 495 496 require.Equal(t, qresult1.Response.Key, k1) 497 require.Equal(t, qresult2.Response.Key, k2) 498 require.Equal(t, qresult1.Response.Value, v1) 499 require.Equal(t, qresult2.Response.Value, v2) 500 } 501 502 func TestBatchedJSONRPCCallsCancellation(t *testing.T) { 503 c := getHTTPClient() 504 _, _, tx1 := MakeTxKV() 505 _, _, tx2 := MakeTxKV() 506 507 batch := c.NewBatch() 508 _, err := batch.BroadcastTxCommit(tx1) 509 require.NoError(t, err) 510 _, err = batch.BroadcastTxCommit(tx2) 511 require.NoError(t, err) 512 // we should have 2 requests waiting 513 require.Equal(t, 2, batch.Count()) 514 // we want to make sure we cleared 2 pending requests 515 require.Equal(t, 2, batch.Clear()) 516 // now there should be no batched requests 517 require.Equal(t, 0, batch.Count()) 518 } 519 520 func TestSendingEmptyJSONRPCRequestBatch(t *testing.T) { 521 c := getHTTPClient() 522 batch := c.NewBatch() 523 _, err := batch.Send() 524 require.Error(t, err, "sending an empty batch of JSON RPC requests should result in an error") 525 } 526 527 func TestClearingEmptyJSONRPCRequestBatch(t *testing.T) { 528 c := getHTTPClient() 529 batch := c.NewBatch() 530 require.Zero(t, batch.Clear(), "clearing an empty batch of JSON RPC requests should result in a 0 result") 531 } 532 533 func TestConcurrentJSONRPCBatching(t *testing.T) { 534 var wg sync.WaitGroup 535 c := getHTTPClient() 536 for i := 0; i < 50; i++ { 537 wg.Add(1) 538 go func() { 539 defer wg.Done() 540 testBatchedJSONRPCCalls(t, c) 541 }() 542 } 543 wg.Wait() 544 }