github.com/deroproject/derosuite@v2.1.6-1.0.20200307070847-0f2e589c7a2b+incompatible/blockchain/rpcserver/rpcserver.go (about)

     1  // Copyright 2017-2018 DERO Project. All rights reserved.
     2  // Use of this source code in any form is governed by RESEARCH license.
     3  // license can be found in the LICENSE file.
     4  // GPG: 0F39 E425 8C65 3947 702A  8234 08B2 0360 A03A 9DE8
     5  //
     6  //
     7  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
     8  // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
     9  // MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
    10  // THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    11  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
    12  // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    13  // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
    14  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
    15  // THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    16  
    17  package rpcserver
    18  
    19  import "io"
    20  import "fmt"
    21  import "net"
    22  import "time"
    23  import "context"
    24  import "sync"
    25  import "sync/atomic"
    26  
    27  //import "context"
    28  import "net/http"
    29  import "net/http/pprof"
    30  
    31  //import "github.com/intel-go/fastjson"
    32  import "github.com/osamingo/jsonrpc"
    33  import log "github.com/sirupsen/logrus"
    34  import "github.com/prometheus/client_golang/prometheus"
    35  import "github.com/prometheus/client_golang/prometheus/promhttp"
    36  
    37  import "github.com/deroproject/derosuite/config"
    38  import "github.com/deroproject/derosuite/globals"
    39  import "github.com/deroproject/derosuite/blockchain"
    40  import "github.com/deroproject/derosuite/structures"
    41  import "github.com/deroproject/derosuite/metrics"
    42  
    43  var DEBUG_MODE bool
    44  
    45  /* this file implements the rpcserver api, so as wallet and block explorer tools can work without migration */
    46  
    47  // all components requiring access to blockchain must use , this struct to communicate
    48  // this structure must be update while mutex
    49  type RPCServer struct {
    50  	srv        *http.Server
    51  	mux        *http.ServeMux
    52  	Exit_Event chan bool // blockchain is shutting down and we must quit ASAP
    53  	sync.RWMutex
    54  }
    55  
    56  var Exit_In_Progress bool
    57  var chain *blockchain.Blockchain
    58  var logger *log.Entry
    59  
    60  func RPCServer_Start(params map[string]interface{}) (*RPCServer, error) {
    61  
    62  	var err error
    63  	var r RPCServer
    64  
    65  	_ = err
    66  
    67  	r.Exit_Event = make(chan bool)
    68  
    69  	logger = globals.Logger.WithFields(log.Fields{"com": "RPC"}) // all components must use this logger
    70  	chain = params["chain"].(*blockchain.Blockchain)
    71  
    72  	/*
    73  		// test whether chain is okay
    74  		if chain.Get_Height() == 0 {
    75  			return nil, fmt.Errorf("Chain DOES NOT have genesis block")
    76  		}
    77  	*/
    78  
    79  	go r.Run()
    80  	logger.Infof("RPC server started")
    81  	atomic.AddUint32(&globals.Subsystem_Active, 1) // increment subsystem
    82  
    83  	return &r, nil
    84  }
    85  
    86  // shutdown the rpc server component
    87  func (r *RPCServer) RPCServer_Stop() {
    88  	r.Lock()
    89  	defer r.Unlock()
    90  	Exit_In_Progress = true
    91  	close(r.Exit_Event) // send signal to all connections to exit
    92  
    93  	if r.srv != nil {
    94  		r.srv.Shutdown(context.Background()) // shutdown the server
    95  	}
    96  	// TODO we  must wait for connections to kill themselves
    97  	time.Sleep(1 * time.Second)
    98  	logger.Infof("RPC Shutdown")
    99  	atomic.AddUint32(&globals.Subsystem_Active, ^uint32(0)) // this decrement 1 fom subsystem
   100  }
   101  
   102  // setup handlers
   103  func (r *RPCServer) Run() {
   104  
   105  	mr := jsonrpc.NewMethodRepository()
   106  
   107  	if err := mr.RegisterMethod("Main.Echo", EchoHandler{}, EchoParams{}, EchoResult{}); err != nil {
   108  		log.Fatalln(err)
   109  	}
   110  
   111  	// install getblockcount handler
   112  	if err := mr.RegisterMethod("getblockcount", GetBlockCount_Handler{}, structures.GetBlockCount_Params{}, structures.GetBlockCount_Result{}); err != nil {
   113  		log.Fatalln(err)
   114  	}
   115  
   116  	// install on_getblockhash
   117  	if err := mr.RegisterMethod("on_getblockhash", On_GetBlockHash_Handler{}, structures.On_GetBlockHash_Params{}, structures.On_GetBlockHash_Result{}); err != nil {
   118  		log.Fatalln(err)
   119  	}
   120  
   121  	// install getblocktemplate handler
   122  	if err := mr.RegisterMethod("getblocktemplate", GetBlockTemplate_Handler{}, structures.GetBlockTemplate_Params{}, structures.GetBlockTemplate_Result{}); err != nil {
   123  		log.Fatalln(err)
   124  	}
   125  
   126  	// submitblock handler
   127  	if err := mr.RegisterMethod("submitblock", SubmitBlock_Handler{}, structures.SubmitBlock_Params{}, structures.SubmitBlock_Result{}); err != nil {
   128  		log.Fatalln(err)
   129  	}
   130  
   131  	if err := mr.RegisterMethod("getlastblockheader", GetLastBlockHeader_Handler{}, structures.GetLastBlockHeader_Params{}, structures.GetLastBlockHeader_Result{}); err != nil {
   132  		log.Fatalln(err)
   133  	}
   134  
   135  	if err := mr.RegisterMethod("getblockheaderbyhash", GetBlockHeaderByHash_Handler{}, structures.GetBlockHeaderByHash_Params{}, structures.GetBlockHeaderByHash_Result{}); err != nil {
   136  		log.Fatalln(err)
   137  	}
   138  
   139  	if err := mr.RegisterMethod("getblockheaderbyheight", GetBlockHeaderByHeight_Handler{}, structures.GetBlockHeaderByHeight_Params{}, structures.GetBlockHeaderByHeight_Result{}); err != nil {
   140  		log.Fatalln(err)
   141  	}
   142  
   143  	if err := mr.RegisterMethod("getblockheaderbytopoheight", GetBlockHeaderByTopoHeight_Handler{}, structures.GetBlockHeaderByTopoHeight_Params{}, structures.GetBlockHeaderByHeight_Result{}); err != nil {
   144  		log.Fatalln(err)
   145  	}
   146  
   147  	if err := mr.RegisterMethod("getblock", GetBlock_Handler{}, structures.GetBlock_Params{}, structures.GetBlock_Result{}); err != nil {
   148  		log.Fatalln(err)
   149  	}
   150  
   151  	if err := mr.RegisterMethod("get_info", GetInfo_Handler{}, structures.GetInfo_Params{}, structures.GetInfo_Result{}); err != nil {
   152  		log.Fatalln(err)
   153  	}
   154  
   155  	if err := mr.RegisterMethod("gettxpool", GetTxPool_Handler{}, structures.GetTxPool_Params{}, structures.GetTxPool_Result{}); err != nil {
   156  		log.Fatalln(err)
   157  	}
   158  
   159  	// create a new mux
   160  	r.mux = http.NewServeMux()
   161  
   162  	default_address := "127.0.0.1:" + fmt.Sprintf("%d", config.Mainnet.RPC_Default_Port)
   163  	if !globals.IsMainnet() {
   164  		default_address = "127.0.0.1:" + fmt.Sprintf("%d", config.Testnet.RPC_Default_Port)
   165  	}
   166  
   167  	if _, ok := globals.Arguments["--rpc-bind"]; ok && globals.Arguments["--rpc-bind"] != nil {
   168  		addr, err := net.ResolveTCPAddr("tcp", globals.Arguments["--rpc-bind"].(string))
   169  		if err != nil {
   170  			logger.Warnf("--rpc-bind address is invalid, err = %s", err)
   171  		} else {
   172  			if addr.Port == 0 {
   173  				logger.Infof("RPC server is disabled, No ports will be opened for RPC")
   174  				return
   175  			} else {
   176  				default_address = addr.String()
   177  			}
   178  		}
   179  	}
   180  
   181  	logger.Infof("RPC  will listen on %s", default_address)
   182  	r.Lock()
   183  	r.srv = &http.Server{Addr: default_address, Handler: r.mux}
   184  	r.Unlock()
   185  
   186  	r.mux.HandleFunc("/", hello)
   187  	r.mux.Handle("/json_rpc", mr)
   188  
   189  	// handle nasty http requests
   190  	r.mux.HandleFunc("/getheight", getheight)
   191  	r.mux.HandleFunc("/getoutputs.bin", getoutputs) // stream any outputs to server, can make wallet work offline
   192  	r.mux.HandleFunc("/gettransactions", gettransactions)
   193  	r.mux.HandleFunc("/sendrawtransaction", SendRawTransaction_Handler)
   194  	r.mux.HandleFunc("/is_key_image_spent", iskeyimagespent)
   195  
   196  	if DEBUG_MODE {
   197  		// r.mux.HandleFunc("/debug/pprof/", pprof.Index)
   198  
   199  		// Register pprof handlers individually if required
   200  		r.mux.HandleFunc("/debug/pprof/", pprof.Index)
   201  		r.mux.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
   202  		r.mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
   203  		r.mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
   204  		r.mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
   205  
   206  		/*
   207  		       // Register pprof handlers individually if required
   208  		   r.mux.HandleFunc("/cdebug/pprof/", pprof.Index)
   209  		   r.mux.HandleFunc("/cdebug/pprof/cmdline", pprof.Cmdline)
   210  		   r.mux.HandleFunc("/cdebug/pprof/profile", pprof.Profile)
   211  		   r.mux.HandleFunc("/cdebug/pprof/symbol", pprof.Symbol)
   212  		   r.mux.HandleFunc("/cdebug/pprof/trace", pprof.Trace)
   213  		*/
   214  
   215  		// register metrics handler
   216  		r.mux.HandleFunc("/metrics", prometheus.InstrumentHandler("dero", promhttp.HandlerFor(metrics.Registry, promhttp.HandlerOpts{})))
   217  
   218  	}
   219  
   220  	//r.mux.HandleFunc("/json_rpc/debug", mr.ServeDebug)
   221  
   222  	if err := r.srv.ListenAndServe(); err != http.ErrServerClosed {
   223  		logger.Warnf("ERR listening to address err %s", err)
   224  	}
   225  
   226  }
   227  
   228  func hello(w http.ResponseWriter, r *http.Request) {
   229  	io.WriteString(w, "Hello world!")
   230  }