2018-04-13 16:42:25 +03:00
// Copyright 2018 The Grin Developers
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
extern crate grin_api as api ;
extern crate grin_chain as chain ;
extern crate grin_core as core ;
extern crate grin_p2p as p2p ;
2018-04-24 11:18:24 +03:00
extern crate grin_servers as servers ;
2018-04-13 16:42:25 +03:00
extern crate grin_util as util ;
extern crate grin_wallet as wallet ;
extern crate bufstream ;
extern crate serde_json ;
2018-10-02 14:23:51 +03:00
#[ macro_use ]
extern crate slog ;
2018-04-13 16:42:25 +03:00
mod framework ;
use bufstream ::BufStream ;
use serde_json ::Value ;
2018-06-14 15:16:14 +03:00
use std ::io ::prelude ::{ BufRead , Write } ;
use std ::net ::TcpStream ;
2018-04-13 16:42:25 +03:00
2018-10-02 14:23:51 +03:00
use std ::process ;
2018-10-14 12:01:34 +03:00
use std ::sync ::atomic ::AtomicBool ;
use std ::sync ::Arc ;
2018-06-14 15:16:14 +03:00
use std ::{ thread , time } ;
2018-04-13 16:42:25 +03:00
2018-06-14 15:16:14 +03:00
use core ::global ::{ self , ChainTypes } ;
2018-10-02 14:23:51 +03:00
use util ::LOGGER ;
2018-04-13 16:42:25 +03:00
2018-04-24 11:18:24 +03:00
use framework ::{ config , stratum_config } ;
2018-04-13 16:42:25 +03:00
// Create a grin server, and a stratum server.
// Simulate a few JSONRpc requests and verify the results.
// Validate disconnected workers
// Validate broadcasting new jobs
#[ test ]
fn basic_stratum_server ( ) {
util ::init_test_logger ( ) ;
global ::set_mining_mode ( ChainTypes ::AutomatedTesting ) ;
let test_name_dir = " stratum_server " ;
framework ::clean_all_output ( test_name_dir ) ;
// Create a server
let s = servers ::Server ::new ( config ( 4000 , test_name_dir , 0 ) ) . unwrap ( ) ;
// Get mining config with stratumserver enabled
2018-04-24 11:18:24 +03:00
let mut stratum_cfg = stratum_config ( ) ;
stratum_cfg . burn_reward = true ;
stratum_cfg . attempt_time_per_block = 999 ;
stratum_cfg . enable_stratum_server = Some ( true ) ;
stratum_cfg . stratum_server_addr = Some ( String ::from ( " 127.0.0.1:11101 " ) ) ;
2018-04-13 16:42:25 +03:00
// Start stratum server
2018-04-24 11:18:24 +03:00
s . start_stratum_server ( stratum_cfg ) ;
2018-04-13 16:42:25 +03:00
// Wait for stratum server to start and
// Verify stratum server accepts connections
loop {
if let Ok ( _stream ) = TcpStream ::connect ( " 127.0.0.1:11101 " ) {
break ;
} else {
thread ::sleep ( time ::Duration ::from_millis ( 500 ) ) ;
}
// As this stream falls out of scope it will be disconnected
}
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " stratum server connected " ) ;
2018-04-13 16:42:25 +03:00
// Create a few new worker connections
let mut workers = vec! [ ] ;
for _n in 0 .. 5 {
let w = TcpStream ::connect ( " 127.0.0.1:11101 " ) . unwrap ( ) ;
w . set_nonblocking ( true )
. expect ( " Failed to set TcpStream to non-blocking " ) ;
let stream = BufStream ::new ( w ) ;
workers . push ( stream ) ;
}
assert! ( workers . len ( ) = = 5 ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " workers length verification ok " ) ;
2018-04-13 16:42:25 +03:00
// Simulate a worker lost connection
workers . remove ( 4 ) ;
// Swallow the genesis block
2018-07-08 19:37:09 +03:00
thread ::sleep ( time ::Duration ::from_secs ( 5 ) ) ; // Wait for the server to broadcast
2018-04-13 16:42:25 +03:00
let mut response = String ::new ( ) ;
for n in 0 .. workers . len ( ) {
let _result = workers [ n ] . read_line ( & mut response ) ;
}
// Verify a few stratum JSONRpc commands
// getjobtemplate - expected block template result
let mut response = String ::new ( ) ;
let job_req = " { \" id \" : \" Stratum \" , \" jsonrpc \" : \" 2.0 \" , \" method \" : \" getjobtemplate \" } \n " ;
workers [ 2 ] . write ( job_req . as_bytes ( ) ) . unwrap ( ) ;
workers [ 2 ] . flush ( ) . unwrap ( ) ;
thread ::sleep ( time ::Duration ::from_secs ( 1 ) ) ; // Wait for the server to reply
match workers [ 2 ] . read_line ( & mut response ) {
Ok ( _ ) = > {
let r : Value = serde_json ::from_str ( & response ) . unwrap ( ) ;
assert_eq! ( r [ " error " ] , serde_json ::Value ::Null ) ;
assert_ne! ( r [ " result " ] , serde_json ::Value ::Null ) ;
}
Err ( _e ) = > {
assert! ( false ) ;
}
}
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " a few stratum JSONRpc commands verification ok " ) ;
2018-04-13 16:42:25 +03:00
// keepalive - expected "ok" result
let mut response = String ::new ( ) ;
let job_req = " { \" id \" : \" 3 \" , \" jsonrpc \" : \" 2.0 \" , \" method \" : \" keepalive \" } \n " ;
2018-04-23 11:41:35 +03:00
let ok_resp = " { \" id \" : \" 3 \" , \" jsonrpc \" : \" 2.0 \" , \" method \" : \" keepalive \" , \" result \" : \" ok \" , \" error \" :null} \n " ;
2018-04-13 16:42:25 +03:00
workers [ 2 ] . write ( job_req . as_bytes ( ) ) . unwrap ( ) ;
workers [ 2 ] . flush ( ) . unwrap ( ) ;
thread ::sleep ( time ::Duration ::from_secs ( 1 ) ) ; // Wait for the server to reply
let _st = workers [ 2 ] . read_line ( & mut response ) ;
assert_eq! ( response . as_str ( ) , ok_resp ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " keepalive test ok " ) ;
2018-04-13 16:42:25 +03:00
// "doesnotexist" - error expected
let mut response = String ::new ( ) ;
let job_req = " { \" id \" : \" 4 \" , \" jsonrpc \" : \" 2.0 \" , \" method \" : \" doesnotexist \" } \n " ;
2018-04-23 11:41:35 +03:00
let ok_resp = " { \" id \" : \" 4 \" , \" jsonrpc \" : \" 2.0 \" , \" method \" : \" doesnotexist \" , \" result \" :null, \" error \" :{ \" code \" :-32601, \" message \" : \" Method not found \" }} \n " ;
2018-04-13 16:42:25 +03:00
workers [ 3 ] . write ( job_req . as_bytes ( ) ) . unwrap ( ) ;
workers [ 3 ] . flush ( ) . unwrap ( ) ;
thread ::sleep ( time ::Duration ::from_secs ( 1 ) ) ; // Wait for the server to reply
let _st = workers [ 3 ] . read_line ( & mut response ) ;
assert_eq! ( response . as_str ( ) , ok_resp ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " worker doesnotexist test ok " ) ;
2018-04-13 16:42:25 +03:00
2018-04-16 11:58:19 +03:00
// Verify stratum server and worker stats
let stats = s . get_server_stats ( ) . unwrap ( ) ;
assert_eq! ( stats . stratum_stats . block_height , 1 ) ; // just 1 genesis block
assert_eq! ( stats . stratum_stats . num_workers , 4 ) ; // 5 - 1 = 4
assert_eq! ( stats . stratum_stats . worker_stats [ 5 ] . is_connected , false ) ; // worker was removed
assert_eq! ( stats . stratum_stats . worker_stats [ 1 ] . is_connected , true ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " stratum server and worker stats verification ok " ) ;
2018-04-13 16:42:25 +03:00
// Start mining blocks
2018-10-14 12:01:34 +03:00
let stop = Arc ::new ( AtomicBool ::new ( false ) ) ;
s . start_test_miner ( None , stop . clone ( ) ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " test miner started " ) ;
// This test is supposed to complete in 3 seconds,
// so let's set a timeout on 10s to avoid infinite waiting happened in Travis-CI.
2018-10-14 12:01:34 +03:00
let _handler = thread ::spawn ( | | {
2018-10-02 14:23:51 +03:00
thread ::sleep ( time ::Duration ::from_secs ( 10 ) ) ;
error! ( LOGGER , " basic_stratum_server test fail on timeout! " ) ;
thread ::sleep ( time ::Duration ::from_millis ( 100 ) ) ;
process ::exit ( 1 ) ;
} ) ;
2018-04-13 16:42:25 +03:00
2018-04-16 11:58:19 +03:00
// Simulate a worker lost connection
workers . remove ( 1 ) ;
2018-10-14 12:01:34 +03:00
// Wait for a few mined blocks
thread ::sleep ( time ::Duration ::from_secs ( 3 ) ) ;
s . stop_test_miner ( stop ) ;
2018-04-13 16:42:25 +03:00
// Verify blocks are being broadcast to workers
let expected = String ::from ( " job " ) ;
let mut jobtemplate = String ::new ( ) ;
let _st = workers [ 2 ] . read_line ( & mut jobtemplate ) ;
let job_template : Value = serde_json ::from_str ( & jobtemplate ) . unwrap ( ) ;
assert_eq! ( job_template [ " method " ] , expected ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " blocks broadcasting to workers test ok " ) ;
2018-04-16 11:58:19 +03:00
// Verify stratum server and worker stats
let stats = s . get_server_stats ( ) . unwrap ( ) ;
assert_eq! ( stats . stratum_stats . num_workers , 3 ) ; // 5 - 2 = 3
assert_eq! ( stats . stratum_stats . worker_stats [ 2 ] . is_connected , false ) ; // worker was removed
assert_ne! ( stats . stratum_stats . block_height , 1 ) ;
2018-10-02 14:23:51 +03:00
info! ( LOGGER , " basic_stratum_server test done and ok. " ) ;
2018-04-13 16:42:25 +03:00
}