Merge branch 'master' of github.com:spesmilo/electrum-server
[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', 'yes')
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, int(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.get('server', 'stratum_tcp_port')
128     stratum_http_port = config.get('server', 'stratum_http_port')
129     stratum_tcp_ssl_port = config.get('server', 'stratum_tcp_ssl_port')
130     stratum_http_ssl_port = config.get('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     from processor import Dispatcher, print_log
142     from backends.irc import ServerProcessor
143
144     backend_name = config.get('server', 'backend')
145     if backend_name == 'libbitcoin':
146         from backends.libbitcoin import BlockchainProcessor
147     elif backend_name == 'leveldb':
148         from backends.bitcoind import BlockchainProcessor
149     else:
150         print "Unknown backend '%s' specified\n" % backend_name
151         sys.exit(1)
152
153     for i in xrange(5):
154         print ""
155     print_log("Starting Electrum server on", host)
156
157     # Create hub
158     dispatcher = Dispatcher(config)
159     shared = dispatcher.shared
160
161     # Create and register processors
162     chain_proc = BlockchainProcessor(config, shared)
163     dispatcher.register('blockchain', chain_proc)
164
165     server_proc = ServerProcessor(config)
166     dispatcher.register('server', server_proc)
167
168     transports = []
169     # Create various transports we need
170     if stratum_tcp_port:
171         from transports.stratum_tcp import TcpServer
172         tcp_server = TcpServer(dispatcher, host, int(stratum_tcp_port), False, None, None)
173         transports.append(tcp_server)
174
175     if stratum_tcp_ssl_port:
176         from transports.stratum_tcp import TcpServer
177         tcp_server = TcpServer(dispatcher, host, int(stratum_tcp_ssl_port), True, ssl_certfile, ssl_keyfile)
178         transports.append(tcp_server)
179
180     if stratum_http_port:
181         from transports.stratum_http import HttpServer
182         http_server = HttpServer(dispatcher, host, int(stratum_http_port), False, None, None)
183         transports.append(http_server)
184
185     if stratum_http_ssl_port:
186         from transports.stratum_http import HttpServer
187         http_server = HttpServer(dispatcher, host, int(stratum_http_ssl_port), True, ssl_certfile, ssl_keyfile)
188         transports.append(http_server)
189
190     for server in transports:
191         server.start()
192
193     while not shared.stopped():
194         try:
195             time.sleep(1)
196         except:
197             shared.stop()
198
199     print_log("Electrum Server stopped")