Fix reorg issues
[electrum-server.git] / server.py
1 #!/usr/bin/env python
2 # Copyright(C) 2012 thomasv@gitorious
3
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU Affero General Public License as
6 # published by the Free Software Foundation, either version 3 of the
7 # License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful, but
10 # WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Affero General Public License for more details.
13 #
14 # You should have received a copy of the GNU Affero General Public
15 # License along with this program.  If not, see
16 # <http://www.gnu.org/licenses/agpl.html>.
17
18 import ConfigParser
19 import logging
20 import socket
21 import sys
22 import time
23 import threading
24 import traceback
25
26 import json
27 import os
28
29 logging.basicConfig()
30
31 if sys.maxsize <= 2**32:
32     print "Warning: it looks like you are using a 32bit system. You may experience crashes caused by mmap"
33
34
35 def attempt_read_config(config, filename):
36     try:
37         with open(filename, 'r') as f:
38             config.readfp(f)
39     except IOError:
40         pass
41
42
43 def create_config():
44     config = ConfigParser.ConfigParser()
45     # set some defaults, which will be overwritten by the config file
46     config.add_section('server')
47     config.set('server', 'banner', 'Welcome to Electrum!')
48     config.set('server', 'host', 'localhost')
49     config.set('server', 'report_host', '')
50     config.set('server', 'stratum_tcp_port', '40001')
51     config.set('server', 'stratum_http_port', '7081')
52     config.set('server', 'stratum_tcp_ssl_port', '40002')
53     config.set('server', 'stratum_http_ssl_port', '7082')
54     config.set('server', 'report_stratum_tcp_port', '')
55     config.set('server', 'report_stratum_http_port', '')
56     config.set('server', 'report_stratum_tcp_ssl_port', '')
57     config.set('server', 'report_stratum_http_ssl_port', '')
58     config.set('server', 'ssl_certfile', '')
59     config.set('server', 'ssl_keyfile', '')
60     config.set('server', 'password', '')
61     config.set('server', 'irc', 'no')
62     config.set('server', 'irc_nick', '')
63     config.set('server', 'novacoin', '')
64     config.set('server', 'datadir', '')
65
66     # use leveldb as default
67     config.set('server', 'backend', 'leveldb')
68     config.add_section('leveldb')
69     config.set('leveldb', 'path_fulltree', '/dev/shm/electrum_db')
70     config.set('leveldb', 'pruning_limit', '100')
71
72     for path in ('/etc/', ''):
73         filename = path + 'electrum.conf'
74         attempt_read_config(config, filename)
75
76     try:
77         with open('/etc/electrum.banner', 'r') as f:
78             config.set('server', 'banner', f.read())
79     except IOError:
80         pass
81
82     return config
83
84
85 def run_rpc_command(params):
86     cmd = params[0]
87     import xmlrpclib
88     server = xmlrpclib.ServerProxy('http://localhost:7000')
89     func = getattr(server, cmd)
90     r = func(*params[1:])
91
92     if cmd == 'info':
93         now = time.time()
94         print 'type           address         sub  version  time'
95         for item in r:
96             print '%4s   %21s   %3s  %7s  %.2f' % (item.get('name'),
97                                                    item.get('address'),
98                                                    item.get('subscriptions'),
99                                                    item.get('version'),
100                                                    (now - item.get('time')),
101                                                    )
102     else:
103         print r
104
105
106 def cmd_info():
107     return map(lambda s: {"time": s.time,
108                           "name": s.name,
109                           "address": s.address,
110                           "version": s.version,
111                           "subscriptions": len(s.subscriptions)},
112                dispatcher.request_dispatcher.get_sessions())
113
114 def cmd_debug(s):
115     if s:
116         from guppy import hpy
117         h = hpy()
118         bp = dispatcher.request_dispatcher.processors['blockchain']
119         try:
120             result = str(eval(s))
121         except:
122             result = "error"
123         return result
124
125
126 def get_port(config, name):
127     try:
128         return config.getint('server', name)
129     except:
130         return None
131
132 if __name__ == '__main__':
133     config = create_config()
134     password = config.get('server', 'password')
135     host = config.get('server', 'host')
136     stratum_tcp_port = get_port(config, 'stratum_tcp_port')
137     stratum_http_port = get_port(config, 'stratum_http_port')
138     stratum_tcp_ssl_port = get_port(config, 'stratum_tcp_ssl_port')
139     stratum_http_ssl_port = get_port(config, 'stratum_http_ssl_port')
140     ssl_certfile = config.get('server', 'ssl_certfile')
141     ssl_keyfile = config.get('server', 'ssl_keyfile')
142
143     if stratum_tcp_ssl_port or stratum_http_ssl_port:
144         assert ssl_certfile and ssl_keyfile
145
146     if len(sys.argv) > 1:
147         try:
148             run_rpc_command(sys.argv[1:])
149         except socket.error:
150             print "server not running"
151             sys.exit(1)
152         sys.exit(0)
153
154     try:
155         run_rpc_command(['getpid'])
156         is_running = True
157     except socket.error:
158         is_running = False
159
160     if is_running:
161         print "server already running"
162         sys.exit(1)
163
164
165     from processor import Dispatcher, print_log
166     from backends.irc import ServerProcessor
167     from transports.stratum_tcp import TcpServer
168     from transports.stratum_http import HttpServer
169
170     backend_name = config.get('server', 'backend')
171     if backend_name == 'libbitcoin':
172         from backends.libbitcoin import BlockchainProcessor
173     elif backend_name == 'leveldb':
174         from backends.bitcoind import BlockchainProcessor
175     else:
176         print "Unknown backend '%s' specified\n" % backend_name
177         sys.exit(1)
178
179     print "\n\n\n\n\n"
180     print_log("Starting Electrum server on", host)
181
182     # Create hub
183     dispatcher = Dispatcher(config)
184     shared = dispatcher.shared
185
186     # handle termination signals
187     import signal
188     def handler(signum = None, frame = None):
189         print_log('Signal handler called with signal', signum)
190         shared.stop()
191     for sig in [signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT]:
192         signal.signal(sig, handler)
193
194
195     # Create and register processors
196     chain_proc = BlockchainProcessor(config, shared)
197     dispatcher.register('blockchain', chain_proc)
198
199     server_proc = ServerProcessor(config)
200     dispatcher.register('server', server_proc)
201
202     transports = []
203     # Create various transports we need
204     if stratum_tcp_port:
205         tcp_server = TcpServer(dispatcher, host, stratum_tcp_port, False, None, None)
206         transports.append(tcp_server)
207
208     if stratum_tcp_ssl_port:
209         tcp_server = TcpServer(dispatcher, host, stratum_tcp_ssl_port, True, ssl_certfile, ssl_keyfile)
210         transports.append(tcp_server)
211
212     if stratum_http_port:
213         http_server = HttpServer(dispatcher, host, stratum_http_port, False, None, None)
214         transports.append(http_server)
215
216     if stratum_http_ssl_port:
217         http_server = HttpServer(dispatcher, host, stratum_http_ssl_port, True, ssl_certfile, ssl_keyfile)
218         transports.append(http_server)
219
220     for server in transports:
221         server.start()
222
223     
224
225     from SimpleXMLRPCServer import SimpleXMLRPCServer
226     server = SimpleXMLRPCServer(('localhost',7000), allow_none=True, logRequests=False)
227     server.register_function(lambda: os.getpid(), 'getpid')
228     server.register_function(shared.stop, 'stop')
229     server.register_function(cmd_info, 'info')
230     server.register_function(cmd_debug, 'debug')
231     server.socket.settimeout(1)
232  
233     while not shared.stopped():
234         try:
235             server.handle_request()
236         except socket.timeout:
237             continue
238         except:
239             shared.stop()
240
241     server_proc.join()
242     chain_proc.join()
243     print_log("Electrum Server stopped")