simplification
[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', '50001')
51     config.set('server', 'stratum_http_port', '8081')
52     config.set('server', 'stratum_tcp_ssl_port', '')
53     config.set('server', 'stratum_http_ssl_port', '')
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', 'coin', '')
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', '/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:8000')
89     func = getattr(server, cmd)
90     try:
91         r = func(*params[1:])
92     except socket.error:
93         print "server not running"
94         sys.exit(1)
95
96     if cmd == 'info':
97         now = time.time()
98         print 'type           address         sub  version  time'
99         for item in r:
100             print '%4s   %21s   %3s  %7s  %.2f' % (item.get('name'),
101                                                    item.get('address'),
102                                                    item.get('subscriptions'),
103                                                    item.get('version'),
104                                                    (now - item.get('time')),
105                                                    )
106     else:
107         print r
108
109
110 def cmd_info():
111     return map(lambda s: {"time": s.time,
112                           "name": s.name,
113                           "address": s.address,
114                           "version": s.version,
115                           "subscriptions": len(s.subscriptions)},
116                dispatcher.request_dispatcher.get_sessions())
117
118 def cmd_debug(s):
119     if s:
120         from guppy import hpy
121         h = hpy()
122         bp = dispatcher.request_dispatcher.processors['blockchain']
123         try:
124             result = str(eval(s))
125         except:
126             result = "error"
127         return result
128
129
130 if __name__ == '__main__':
131     config = create_config()
132     password = config.get('server', 'password')
133     host = config.get('server', 'host')
134     stratum_tcp_port = config.getint('server', 'stratum_tcp_port')
135     stratum_http_port = config.getint('server', 'stratum_http_port')
136     stratum_tcp_ssl_port = config.getint('server', 'stratum_tcp_ssl_port')
137     stratum_http_ssl_port = config.getint('server', 'stratum_http_ssl_port')
138     ssl_certfile = config.get('server', 'ssl_certfile')
139     ssl_keyfile = config.get('server', 'ssl_keyfile')
140
141     if stratum_tcp_ssl_port or stratum_http_ssl_port:
142         assert ssl_certfile and ssl_keyfile
143
144     if len(sys.argv) > 1:
145         run_rpc_command(sys.argv[1:])
146         sys.exit(0)
147
148     try:
149         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
150         s.connect((host, stratum_tcp_port))
151         s.close()
152         is_running = True
153     except:
154         is_running = False
155
156     if is_running:
157         print "server already running"
158         sys.exit(1)
159
160
161     from processor import Dispatcher, print_log
162     from backends.irc import ServerProcessor
163     from transports.stratum_tcp import TcpServer
164     from transports.stratum_http import HttpServer
165
166     backend_name = config.get('server', 'backend')
167     if backend_name == 'libbitcoin':
168         from backends.libbitcoin import BlockchainProcessor
169     elif backend_name == 'leveldb':
170         from backends.bitcoind import BlockchainProcessor
171     else:
172         print "Unknown backend '%s' specified\n" % backend_name
173         sys.exit(1)
174
175     print "\n\n\n\n\n"
176     print_log("Starting Electrum server on", host)
177
178     # Create hub
179     dispatcher = Dispatcher(config)
180     shared = dispatcher.shared
181
182     # Create and register processors
183     chain_proc = BlockchainProcessor(config, shared)
184     dispatcher.register('blockchain', chain_proc)
185
186     server_proc = ServerProcessor(config)
187     dispatcher.register('server', server_proc)
188
189     transports = []
190     # Create various transports we need
191     if stratum_tcp_port:
192         tcp_server = TcpServer(dispatcher, host, stratum_tcp_port, False, None, None)
193         transports.append(tcp_server)
194
195     if stratum_tcp_ssl_port:
196         tcp_server = TcpServer(dispatcher, host, stratum_tcp_ssl_port, True, ssl_certfile, ssl_keyfile)
197         transports.append(tcp_server)
198
199     if stratum_http_port:
200         http_server = HttpServer(dispatcher, host, stratum_http_port, False, None, None)
201         transports.append(http_server)
202
203     if stratum_http_ssl_port:
204         http_server = HttpServer(dispatcher, host, stratum_http_ssl_port, True, ssl_certfile, ssl_keyfile)
205         transports.append(http_server)
206
207     for server in transports:
208         server.start()
209
210
211
212     from SimpleXMLRPCServer import SimpleXMLRPCServer
213     
214
215     server = SimpleXMLRPCServer(('localhost',8000), allow_none=True, logRequests=False)
216     server.register_function(lambda: os.getpid(), 'getpid')
217     server.register_function(shared.stop, 'stop')
218     server.register_function(cmd_info, 'info')
219     server.register_function(cmd_debug, 'debug')
220  
221     while not shared.stopped():
222         try:
223             server.handle_request()
224         except:
225             shared.stop()
226
227     server_proc.join()
228     chain_proc.join()
229     print_log("Electrum Server stopped")