From bcff2e4fa3e41212e6e7d0dc48dfcc0ec5b70416 Mon Sep 17 00:00:00 2001 From: The MMGen Project Date: Thu, 29 Aug 2024 11:17:24 +0000 Subject: [PATCH] full macOS support All coins and features supported on Linux are now supported on macOS Testing: $ test/test-release.sh -F --- mmgen/data/version | 2 +- mmgen/filename.py | 8 ++++++-- mmgen/fileutil.py | 2 +- mmgen/platform/darwin/util.py | 10 ++++++++++ mmgen/proto/btc/daemon.py | 2 ++ mmgen/proto/eth/daemon.py | 9 ++++++--- mmgen/proto/xmr/daemon.py | 9 +++++++-- mmgen/proto/xmr/rpc.py | 2 +- test/cmdtest_py_d/ct_wallet.py | 2 +- test/cmdtest_py_d/ct_xmrwallet.py | 2 +- test/include/common.py | 30 ++++++++++++++++++++++++++++++ test/unit_tests_d/ut_rpc.py | 5 ++++- 12 files changed, 70 insertions(+), 13 deletions(-) diff --git a/mmgen/data/version b/mmgen/data/version index db4e727a..f483d2dc 100644 --- a/mmgen/data/version +++ b/mmgen/data/version @@ -1 +1 @@ -15.0.dev3 +15.0.dev4 diff --git a/mmgen/filename.py b/mmgen/filename.py index d07913e8..02d3cac4 100755 --- a/mmgen/filename.py +++ b/mmgen/filename.py @@ -42,7 +42,7 @@ class File: import stat if stat.S_ISBLK(st.st_mode): - if sys.platform in ('win32', 'darwin'): + if sys.platform in ('win32',): die(2, 'Access to raw block devices not supported on platform {sys.platform!r}') mode = (os.O_RDONLY,os.O_RDWR)[bool(write)] try: @@ -52,7 +52,11 @@ class File: die(2,f'{fn!r}: permission denied') # if e.errno != 17: raise else: - self.size = os.lseek(fd, 0, os.SEEK_END) + if sys.platform == 'linux': + self.size = os.lseek(fd, 0, os.SEEK_END) + elif sys.platform == 'darwin': + from .platform.darwin.util import get_device_size + self.size = get_device_size(fn) os.close(fd) else: self.size = st.st_size diff --git a/mmgen/fileutil.py b/mmgen/fileutil.py index 92eb8a46..be8beaa7 100755 --- a/mmgen/fileutil.py +++ b/mmgen/fileutil.py @@ -86,7 +86,7 @@ def _check_file_type_and_access(fname,ftype,blkdev_ok=False): (stat.S_ISLNK,'symbolic link') ] if blkdev_ok: - if not sys.platform in ('win32', 'darwin'): + if not sys.platform in ('win32',): ok_types.append((stat.S_ISBLK, 'block device')) try: diff --git a/mmgen/platform/darwin/util.py b/mmgen/platform/darwin/util.py index 60520634..4500ae8d 100755 --- a/mmgen/platform/darwin/util.py +++ b/mmgen/platform/darwin/util.py @@ -18,6 +18,16 @@ from subprocess import run, PIPE, DEVNULL from ...color import cyan from ...obj import MMGenLabel +def get_device_size(fn): + import re + cp = run(['diskutil', 'info', fn], text=True, stdout=PIPE, check=True) + res = [e for e in cp.stdout.splitlines() if 'Disk Size' in e] + errmsg = '‘diskutil info’ output could not be parsed for device size' + assert len(res) == 1, f'{errmsg}:\n{cp.stdout}' + m = re.search(r'\((\d+) Bytes\)', res[0]) + assert m, f'{errmsg}:\n{res[0]}' + return int(m[1]) + class RamDiskLabel(MMGenLabel): max_len = 24 desc = 'ramdisk label' diff --git a/mmgen/proto/btc/daemon.py b/mmgen/proto/btc/daemon.py index 06b057f0..d3546c84 100755 --- a/mmgen/proto/btc/daemon.py +++ b/mmgen/proto/btc/daemon.py @@ -133,6 +133,7 @@ class bitcoin_cash_node_daemon(bitcoin_core_daemon): nonstd_datadir = True datadirs = { 'linux': [gc.home_dir,'.bitcoin-bchn'], + 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Bitcoin-Cash-Node'], 'win32': [os.getenv('APPDATA'),'Bitcoin-Cash-Node'] } @@ -162,5 +163,6 @@ class litecoin_core_daemon(bitcoin_core_daemon): cfg_file_hdr = '# Litecoin Core config file\n' datadirs = { 'linux': [gc.home_dir,'.litecoin'], + 'darwin': [gc.home_dir, 'Library', 'Application Support', 'Litecoin'], 'win32': [os.getenv('APPDATA'),'Litecoin'] } diff --git a/mmgen/proto/eth/daemon.py b/mmgen/proto/eth/daemon.py index 81b9fc7a..9fd58f26 100755 --- a/mmgen/proto/eth/daemon.py +++ b/mmgen/proto/eth/daemon.py @@ -63,12 +63,14 @@ class openethereum_daemon(ethereum_daemon): cfg_file = 'parity.conf' datadirs = { 'linux': [gc.home_dir,'.local','share','io.parity.ethereum'], + 'darwin': [gc.home_dir, 'Library', 'Application Support', 'io.parity.ethereum'], 'win32': [os.getenv('LOCALAPPDATA'),'Parity','Ethereum'] } def init_subclass(self): - ld = self.platform == 'linux' and not self.opt.no_daemonize + self.use_pidfile = self.platform == 'linux' and not self.opt.no_daemonize + self.use_threads = self.platform in ('win32', 'darwin') self.coind_args = list_gen( ['--no-ws'], @@ -81,8 +83,8 @@ class openethereum_daemon(ethereum_daemon): ['--config=dev', self.network=='regtest'], # no presets for mainnet or testnet ['--mode=offline', self.test_suite or self.network=='regtest'], [f'--log-file={self.logfile}', self.non_dfl_datadir], - ['daemon', ld], - [self.pidfile, ld], + ['daemon', self.use_pidfile], + [self.pidfile, self.use_pidfile], ) class parity_daemon(openethereum_daemon): @@ -104,6 +106,7 @@ class geth_daemon(ethereum_daemon): version_info_arg = 'version' datadirs = { 'linux': [gc.home_dir,'.ethereum','geth'], + 'darwin': [gc.home_dir, 'Library', 'Ethereum', 'geth'], 'win32': [os.getenv('LOCALAPPDATA'),'Geth'] # FIXME } diff --git a/mmgen/proto/xmr/daemon.py b/mmgen/proto/xmr/daemon.py index 73dae244..5b296aaf 100755 --- a/mmgen/proto/xmr/daemon.py +++ b/mmgen/proto/xmr/daemon.py @@ -12,7 +12,7 @@ proto.xmr.daemon: Monero base protocol daemon classes """ -import os +import sys, os from ...cfg import gc from ...util import list_gen,die,contains_any @@ -28,6 +28,7 @@ class monero_daemon(CoinDaemon): cfg_file = 'bitmonero.conf' datadirs = { 'linux': [gc.home_dir,'.bitmonero'], + 'darwin': [gc.home_dir,'.bitmonero'], 'win32': ['/','c','ProgramData','bitmonero'] } @@ -53,6 +54,8 @@ class monero_daemon(CoinDaemon): test_connection = False, daemon = self ) + self.use_pidfile = sys.platform == 'linux' + self.shared_args = list_gen( ['--no-zmq'], [f'--p2p-bind-port={self.p2p_port}', self.p2p_port], @@ -64,7 +67,7 @@ class monero_daemon(CoinDaemon): ['--hide-my-port'], ['--no-igd'], [f'--data-dir={self.datadir}', self.non_dfl_datadir], - [f'--pidfile={self.pidfile}', self.platform == 'linux'], + [f'--pidfile={self.pidfile}', self.use_pidfile], ['--detach', not (self.opt.no_daemonize or self.platform=='win32')], ['--offline', not self.opt.online], ) @@ -132,6 +135,8 @@ class MoneroWalletDaemon(RPCDaemon): self.pidfile = os.path.join(self.datadir,id_str+'.pid') self.logfile = os.path.join(self.datadir,id_str+'.log') + self.use_pidfile = sys.platform == 'linux' + self.proxy = proxy self.monerod_addr = monerod_addr self.monerod_port = ( diff --git a/mmgen/proto/xmr/rpc.py b/mmgen/proto/xmr/rpc.py index da451bba..80e0c49d 100755 --- a/mmgen/proto/xmr/rpc.py +++ b/mmgen/proto/xmr/rpc.py @@ -91,7 +91,7 @@ class MoneroRPCClient(RPCClient): ),json_rpc=False) async def do_stop_daemon(self,silent=False): - return self.call_raw('stop_daemon') + return self.call_raw('stop_daemon') # unreliable on macOS (daemon stops, but closes connection) rpcmethods = ( 'get_info', ) rpcmethods_raw = ( 'get_height', 'send_raw_transaction', 'stop_daemon' ) diff --git a/test/cmdtest_py_d/ct_wallet.py b/test/cmdtest_py_d/ct_wallet.py index 8511edcb..d638b462 100755 --- a/test/cmdtest_py_d/ct_wallet.py +++ b/test/cmdtest_py_d/ct_wallet.py @@ -172,7 +172,7 @@ class CmdTestWalletConv(CmdTestBase,CmdTestShared): b = VirtBlockDevice(os.path.join(self.tmpdir, 'hincog_blkdev_img'), '1K') b.create() - b.attach(dev_mode='0666') + b.attach(dev_mode='0666' if sys.platform == 'linux' else None) self.ref_hincog_conv_out(ic_f=b.dev) b.detach() diff --git a/test/cmdtest_py_d/ct_xmrwallet.py b/test/cmdtest_py_d/ct_xmrwallet.py index 939119ce..26aa744e 100755 --- a/test/cmdtest_py_d/ct_xmrwallet.py +++ b/test/cmdtest_py_d/ct_xmrwallet.py @@ -48,7 +48,7 @@ from .ct_base import CmdTestBase # atexit functions: def stop_daemons(self): for v in self.users.values(): - if '--restricted-rpc' in v.md.start_cmd: + if sys.platform == 'darwin' or '--restricted-rpc' in v.md.start_cmd: v.md.stop() else: async_run(v.md_rpc.stop_daemon()) diff --git a/test/include/common.py b/test/include/common.py index 95380694..d076bfd0 100755 --- a/test/include/common.py +++ b/test/include/common.py @@ -421,3 +421,33 @@ class VirtBlockDeviceLinux(VirtBlockDeviceBase): def do_detach(self, dev, check=True): do_run(['/sbin/losetup', '-d', dev], check=check) + +class VirtBlockDeviceMacOS(VirtBlockDeviceBase): + + def __init__(self, img_path, size): + self.img_path = Path(img_path + '.dmg').resolve() + self.size = size + + def _get_associations(self): + cp = run(['hdiutil', 'info'], stdout=PIPE, stderr=PIPE, text=True, check=False) + if cp.returncode == 0: + lines = cp.stdout.splitlines() + out = [re.sub('.* ', '', s.strip()) for s in lines if re.match(r'image-path|/dev/', s)] + def gen_pairs(): + for n in range(len(out) // 2): + yield(out[n*2], out[(n*2)+1]) + return [dev for path, dev in gen_pairs() if path == str(self.img_path)] + else: + return [] + + def get_new_dev(self): + return None + + def do_create(self, size, path): + do_run(['hdiutil', 'create', '-size', size, '-layout', 'NONE', str(path)]) + + def do_attach(self, path, dev=None): + do_run(['hdiutil', 'attach', '-mount', 'suppressed', str(path)]) + + def do_detach(self, dev, check=True): + do_run(['hdiutil', 'detach', dev], check=check) diff --git a/test/unit_tests_d/ut_rpc.py b/test/unit_tests_d/ut_rpc.py index 1f831b68..df97b31e 100755 --- a/test/unit_tests_d/ut_rpc.py +++ b/test/unit_tests_d/ut_rpc.py @@ -240,7 +240,10 @@ class unit_tests: await c.stop_daemon() if not cfg.no_daemon_stop: - await md.rpc.stop_daemon() + if sys.platform == 'darwin': + md.stop() + else: + await md.rpc.stop_daemon() gmsg('OK')