tobelisk - obelisk - Electrum server using libbitcoin as its backend
HTML git clone https://git.parazyd.org/obelisk
DIR Log
DIR Files
DIR Refs
DIR README
DIR LICENSE
---
tobelisk (3604B)
---
1 #!/usr/bin/env python3
2 # Copyright (C) 2020-2021 Ivan J. <parazyd@dyne.org>
3 #
4 # This file is part of obelisk
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU Affero General Public License version 3
8 # as published by the Free Software Foundation.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # GNU Affero General Public License for more details.
14 #
15 # You should have received a copy of the GNU Affero General Public License
16 # along with this program. If not, see <http://www.gnu.org/licenses/>.
17 import asyncio
18 import sys
19 from argparse import ArgumentParser
20 from configparser import RawConfigParser, NoSectionError
21 from logging import getLogger, FileHandler, Formatter, StreamHandler
22 from os import devnull
23
24 from obelisk.protocol import ElectrumProtocol, VERSION
25
26 # Used for destructor/cleanup
27 PROTOCOL = None
28
29
30 def logger_config(log, config):
31 """Setup logging"""
32 fmt = Formatter("%(asctime)s\t%(levelname)s\t%(message)s")
33 logstream = StreamHandler()
34 logstream.setFormatter(fmt)
35 debuglevel = config.get("obelisk", "log_level", fallback="INFO")
36 logstream.setLevel(debuglevel)
37 log.addHandler(logstream)
38 filename = config.get("obelisk", "log_file", fallback=devnull)
39 append_log = config.getboolean("obelisk", "append_log", fallback=False)
40 logfile = FileHandler(filename, mode=("a" if append_log else "w"))
41 logfile.setFormatter(fmt)
42 logfile.setLevel(debuglevel)
43 log.addHandler(logfile)
44 log.setLevel(debuglevel)
45 return log, filename
46
47
48 async def run_electrum_server(config, chain):
49 """Server coroutine"""
50 log = getLogger("obelisk")
51 host = config.get("obelisk", "host")
52 port = int(config.get("obelisk", "port"))
53
54 endpoints = {}
55 endpoints["query"] = config.get("obelisk", "query")
56 endpoints["heart"] = config.get("obelisk", "heart")
57 endpoints["block"] = config.get("obelisk", "block")
58 endpoints["trans"] = config.get("obelisk", "trans")
59
60 server_cfg = {}
61 hostname_list = config.get("obelisk", "hostname").split(",")
62 server_cfg["server_hostnames"] = hostname_list
63 server_cfg["server_port"] = port
64
65 global PROTOCOL
66 PROTOCOL = ElectrumProtocol(log, chain, endpoints, server_cfg)
67
68 server = await asyncio.start_server(PROTOCOL.recv, host, port)
69 async with server:
70 await server.serve_forever()
71
72
73 def main():
74 """Main orchestration"""
75 parser = ArgumentParser(description=f"obelisk {VERSION}")
76 parser.add_argument("config_file", help="Path to config file")
77 args = parser.parse_args()
78
79 try:
80 config = RawConfigParser()
81 config.read(args.config_file)
82 config.options("obelisk")
83 except NoSectionError:
84 print(f"error: Invalid config file {args.config_file}")
85 return 1
86
87 log = getLogger("obelisk")
88 log, logfilename = logger_config(log, config)
89 log.info(f"Starting obelisk {VERSION}")
90 log.info(f"Logging to {logfilename}")
91
92 chain = config.get("obelisk", "chain")
93 if chain not in ("mainnet", "testnet"):
94 log.error("chain is not 'mainnet' or 'testnet'")
95 return 1
96
97 try:
98 asyncio.run(run_electrum_server(config, chain))
99 except KeyboardInterrupt:
100 print("\r", end="")
101 log.debug("Caught KeyboardInterrupt, exiting...")
102 if PROTOCOL:
103 asyncio.run(PROTOCOL.stop())
104 return 0
105
106 return 1
107
108
109 if __name__ == "__main__":
110 sys.exit(main())