don't display traceback if client does not send version number
[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
28 logging.basicConfig()
29
30 if sys.maxsize <= 2**32:
31     print "Warning: it looks like you are using a 32bit system. You may experience crashes caused by mmap"
32
33
34 def attempt_read_config(config, filename):
35     try:
36         with open(filename, 'r') as f:
37             config.readfp(f)
38     except IOError:
39         pass
40
41
42 def create_config():
43     config = ConfigParser.ConfigParser()
44     # set some defaults, which will be overwritten by the config file
45     config.add_section('server')
46     config.set('server', 'banner', 'Welcome to Electrum!')
47     config.set('server', 'host', 'localhost')
48     config.set('server', 'report_host', '')
49     config.set('server', 'stratum_tcp_port', '50001')
50     config.set('server', 'stratum_http_port', '8081')
51     config.set('server', 'stratum_tcp_ssl_port', '')
52     config.set('server', 'stratum_http_ssl_port', '')
53     config.set('server', 'report_stratum_tcp_port', '')
54     config.set('server', 'report_stratum_http_port', '')
55     config.set('server', 'report_stratum_tcp_ssl_port', '')
56     config.set('server', 'report_stratum_http_ssl_port', '')
57     config.set('server', 'ssl_certfile', '')
58     config.set('server', 'ssl_keyfile', '')
59     config.set('server', 'password', '')
60     config.set('server', 'irc', 'no')
61     config.set('server', 'irc_nick', '')
62     config.set('server', 'coin', '')
63     config.set('server', 'datadir', '')
64
65     # use leveldb as default
66     config.set('server', 'backend', 'leveldb')
67     config.add_section('leveldb')
68     config.set('leveldb', 'path', '/dev/shm/electrum_db')
69     config.set('leveldb', 'pruning_limit', '100')
70
71     for path in ('/etc/', ''):
72         filename = path + 'electrum.conf'
73         attempt_read_config(config, filename)
74
75     try:
76         with open('/etc/electrum.banner', 'r') as f:
77             config.set('server', 'banner', f.read())
78     except IOError:
79         pass
80
81     return config
82
83
84 def run_rpc_command(command, stratum_tcp_port):
85     try:
86         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
87         s.connect((host, stratum_tcp_port))
88     except:
89         print "cannot connect to server."
90         return
91
92     method = 'server.' + command
93     params = [password]
94     if len(sys.argv) > 2:
95         params.append(sys.argv[2])
96     
97     request = json.dumps({'id': 0, 'method': method, 'params': params})
98     s.send(request + '\n')
99     msg = ''
100     while True:
101         o = s.recv(1024)
102         if not o: break
103         msg += o
104         if msg.find('\n') != -1:
105             break
106     s.close()
107     r = json.loads(msg).get('result')
108
109     if command == 'info':
110         now = time.time()
111         print 'type           address         sub  version  time'
112         for item in r:
113             print '%4s   %21s   %3s  %7s  %.2f' % (item.get('name'),
114                                                    item.get('address'),
115                                                    item.get('subscriptions'),
116                                                    item.get('version'),
117                                                    (now - item.get('time')),
118                                                    )
119     else:
120         print r
121
122
123 if __name__ == '__main__':
124     config = create_config()
125     password = config.get('server', 'password')
126     host = config.get('server', 'host')
127     stratum_tcp_port = config.getint('server', 'stratum_tcp_port')
128     stratum_http_port = config.getint('server', 'stratum_http_port')
129     stratum_tcp_ssl_port = config.getint('server', 'stratum_tcp_ssl_port')
130     stratum_http_ssl_port = config.getint('server', 'stratum_http_ssl_port')
131     ssl_certfile = config.get('server', 'ssl_certfile')
132     ssl_keyfile = config.get('server', 'ssl_keyfile')
133
134     if stratum_tcp_ssl_port or stratum_http_ssl_port:
135         assert ssl_certfile and ssl_keyfile
136
137     if len(sys.argv) > 1:
138         run_rpc_command(sys.argv[1], stratum_tcp_port)
139         sys.exit(0)
140
141     try:
142         s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
143         s.connect((host, stratum_tcp_port))
144         s.close()
145         is_running = True
146     except:
147         is_running = False
148
149     if is_running:
150         print "server already running"
151         sys.exit(1)
152
153
154     from processor import Dispatcher, print_log
155     from backends.irc import ServerProcessor
156
157     backend_name = config.get('server', 'backend')
158     if backend_name == 'libbitcoin':
159         from backends.libbitcoin import BlockchainProcessor
160     elif backend_name == 'leveldb':
161         from backends.bitcoind import BlockchainProcessor
162     else:
163         print "Unknown backend '%s' specified\n" % backend_name
164         sys.exit(1)
165
166     for i in xrange(5):
167         print ""
168     print_log("Starting Electrum server on", host)
169
170     # Create hub
171     dispatcher = Dispatcher(config)
172     shared = dispatcher.shared
173
174     # Create and register processors
175     chain_proc = BlockchainProcessor(config, shared)
176     dispatcher.register('blockchain', chain_proc)
177
178     server_proc = ServerProcessor(config)
179     dispatcher.register('server', server_proc)
180
181     transports = []
182     # Create various transports we need
183     if stratum_tcp_port:
184         from transports.stratum_tcp import TcpServer
185         tcp_server = TcpServer(dispatcher, host, stratum_tcp_port, False, None, None)
186         transports.append(tcp_server)
187
188     if stratum_tcp_ssl_port:
189         from transports.stratum_tcp import TcpServer
190         tcp_server = TcpServer(dispatcher, host, stratum_tcp_ssl_port, True, ssl_certfile, ssl_keyfile)
191         transports.append(tcp_server)
192
193     if stratum_http_port:
194         from transports.stratum_http import HttpServer
195         http_server = HttpServer(dispatcher, host, stratum_http_port, False, None, None)
196         transports.append(http_server)
197
198     if stratum_http_ssl_port:
199         from transports.stratum_http import HttpServer
200         http_server = HttpServer(dispatcher, host, stratum_http_ssl_port, True, ssl_certfile, ssl_keyfile)
201         transports.append(http_server)
202
203     for server in transports:
204         server.start()
205
206     while not shared.stopped():
207         try:
208             time.sleep(1)
209         except:
210             shared.stop()
211
212     server_proc.join()
213     chain_proc.join()
214     print_log("Electrum Server stopped")