Browse Source

cmdtest.py: reimplement thornode server using WSGI framework

The MMGen Project 2 weeks ago
parent
commit
90544d677c
2 changed files with 55 additions and 55 deletions
  1. 10 8
      test/cmdtest_d/ct_swap.py
  2. 45 47
      test/cmdtest_d/httpd/thornode.py

+ 10 - 8
test/cmdtest_d/ct_swap.py

@@ -17,20 +17,16 @@ from pathlib import Path
 from mmgen.protocol import init_proto
 from ..include.common import make_burn_addr, gr_uc
 from .common import dfl_bip39_file
-from .thornode import run_thornode_server
+from .httpd.thornode import ThornodeServer
 
 from .ct_autosign import CmdTestAutosign, CmdTestAutosignThreaded
 from .ct_regtest import CmdTestRegtest, rt_data, dfl_wcls, rt_pw, cfg, strip_ansi_escapes
 
+thornode_server = ThornodeServer()
+
 sample1 = gr_uc[:24]
 sample2 = '00010203040506'
 
-def thornode_server_start():
-	import threading
-	t = threading.Thread(target=run_thornode_server, name='Thornode server thread')
-	t.daemon = True
-	t.start()
-
 class CmdTestSwap(CmdTestRegtest, CmdTestAutosignThreaded):
 	bdb_wallet = True
 	networks = ('btc',)
@@ -48,6 +44,7 @@ class CmdTestSwap(CmdTestRegtest, CmdTestAutosignThreaded):
 		('subgroup.signsend',     ['init_swap']),
 		('subgroup.signsend_bad', ['init_swap']),
 		('subgroup.autosign',     ['init_data', 'signsend']),
+		('thornode_server_stop',  'stopping the Thornode server'),
 		('stop',                  'stopping regtest daemons'),
 	)
 	cmd_subgroups = {
@@ -175,7 +172,7 @@ class CmdTestSwap(CmdTestRegtest, CmdTestAutosignThreaded):
 
 		self.protos = [init_proto(cfg, k, network='regtest', need_amt=True) for k in ('btc', 'ltc', 'bch')]
 
-		thornode_server_start() # TODO: stop server when test group finishes executing
+		thornode_server.start()
 
 		self.opts.append('--bob')
 
@@ -732,3 +729,8 @@ class CmdTestSwap(CmdTestRegtest, CmdTestAutosignThreaded):
 		data = self._do_cli(['getrawmempool'], add_opts=[f'--coin={self.protos[proto_idx].coin}'])
 		assert data
 		return 'ok'
+
+	def thornode_server_stop(self):
+		self.spawn(msg_only=True)
+		thornode_server.stop()
+		return 'ok'

+ 45 - 47
test/cmdtest_d/thornode.py → test/cmdtest_d/httpd/thornode.py

@@ -1,21 +1,29 @@
 #!/usr/bin/env python3
+#
+# MMGen Wallet, a terminal-based cryptocurrency wallet
+# Copyright (C)2013-2025 The MMGen Project <mmgen@tuta.io>
+# Licensed under the GNU General Public License, Version 3:
+#   https://www.gnu.org/licenses
+# Public project repositories:
+#   https://github.com/mmgen/mmgen-wallet
+#   https://gitlab.com/mmgen/mmgen-wallet
 
-import json, re, time
-from http.server import HTTPServer, BaseHTTPRequestHandler
+"""
+test.cmdtest_d.httpd.thornode: Thornode WSGI http server
+"""
+
+import time, re, json
 
 from mmgen.cfg import Config
-from mmgen.util import msg, make_timestr
+
+from . import HTTPD
 
 cfg = Config()
 
-def make_inbound_addr(proto, mmtype):
-	from mmgen.tool.coin import tool_cmd
-	n = int(time.time()) // (60 * 60 * 24) # increments once every 24 hrs
-	return tool_cmd(
-		cfg     = cfg,
-		cmdname = 'pubhash2addr',
-		proto   = proto,
-		mmtype  = mmtype).pubhash2addr(f'{n:040x}')
+# https://thornode.ninerealms.com/thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000
+sample_request = 'GET /thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000000'
+request_pat = r'/thorchain/quote/swap\?from_asset=(\S+)\.(\S+)&to_asset=(\S+)\.(\S+)&amount=(\d+)'
+prices = { 'BTC': 97000, 'LTC': 115, 'BCH': 330 }
 
 data_template = {
 	'inbound_address': None,
@@ -45,45 +53,35 @@ data_template = {
 	'total_swap_seconds': 2430
 }
 
-# https://thornode.ninerealms.com/thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000
-
-sample_request = 'GET /thorchain/quote/swap?from_asset=BCH.BCH&to_asset=LTC.LTC&amount=1000000000 HTTP/1.1'
+def make_inbound_addr(proto, mmtype):
+	from mmgen.tool.coin import tool_cmd
+	n = int(time.time()) // (60 * 60 * 24) # increments once every 24 hrs
+	return tool_cmd(
+		cfg     = cfg,
+		cmdname = 'pubhash2addr',
+		proto   = proto,
+		mmtype  = mmtype).pubhash2addr(f'{n:040x}')
 
-request_pat = r'/thorchain/quote/swap\?from_asset=(\S+)\.(\S+)&to_asset=(\S+)\.(\S+)&amount=(\d+) HTTP/'
+class ThornodeServer(HTTPD):
+	name = 'thornode server'
+	port = 18800
+	content_type = 'application/json'
 
-prices = { 'BTC': 97000, 'LTC': 115, 'BCH': 330 }
+	def make_response_body(self, method, environ):
+		from wsgiref.util import request_uri
 
-def create_data(request_line):
-	m = re.search(request_pat, request_line)
-	try:
+		m = re.search(request_pat, request_uri(environ))
 		_, send_coin, _, recv_coin, amt_atomic = m.groups()
-	except Exception as e:
-		msg(f'{type(e)}: {e}')
-		return {}
-
-	from mmgen.protocol import init_proto
-	send_proto = init_proto(cfg, send_coin, network='regtest', need_amt=True)
-	in_amt = send_proto.coin_amt(int(amt_atomic), from_unit='satoshi')
-	out_amt = in_amt * (prices[send_coin] / prices[recv_coin])
-
-	addr = make_inbound_addr(send_proto, send_proto.preferred_mmtypes[0])
-	expiry = int(time.time()) + (10 * 60)
-	return data_template | {
-		'expected_amount_out': str(out_amt.to_unit('satoshi')),
-		'expiry': expiry,
-		'inbound_address': addr,
-	}
-
-class handler(BaseHTTPRequestHandler):
-	header = b'HTTP/1.1 200 OK\nContent-type: application/json\n\n'
 
-	def do_GET(self):
-		# print(f'Thornode server received:\n  {self.requestline}')
-		self.wfile.write(self.header + json.dumps(create_data(self.requestline)).encode())
+		from mmgen.protocol import init_proto
+		send_proto = init_proto(cfg, send_coin, network='regtest', need_amt=True)
+		in_amt = send_proto.coin_amt(int(amt_atomic), from_unit='satoshi')
+		out_amt = in_amt * (prices[send_coin] / prices[recv_coin])
 
-def run_thornode_server(server_class=HTTPServer, handler_class=handler):
-	print('Thornode server listening on port 18800')
-	server_address = ('localhost', 18800)
-	httpd = server_class(server_address, handler_class)
-	httpd.serve_forever()
-	print('Thornode server exiting')
+		addr = make_inbound_addr(send_proto, send_proto.preferred_mmtypes[0])
+		data = data_template | {
+			'expected_amount_out': str(out_amt.to_unit('satoshi')),
+			'expiry': int(time.time()) + (10 * 60),
+			'inbound_address': addr,
+		}
+		return json.dumps(data).encode()