macOS: support autosign/automount for BTC
Testing:
$ test/cmdtest.py autosign_clean autosign_automount autosign_btc
This commit is contained in:
parent
ee35ab0683
commit
a24eed0826
10 changed files with 117 additions and 9 deletions
|
|
@ -325,6 +325,7 @@ class Autosign:
|
|||
|
||||
dev_label = 'MMGEN_TX'
|
||||
linux_mount_subdir = 'mmgen_autosign'
|
||||
macOS_ramdisk_name = 'AutosignRamDisk'
|
||||
wallet_subdir = 'autosign'
|
||||
|
||||
mn_fmts = {
|
||||
|
|
@ -374,6 +375,9 @@ class Autosign:
|
|||
to a directory! Please create the mountpoint and add an entry
|
||||
to your fstab as described in this script’s help text.
|
||||
"""
|
||||
elif sys.platform == 'darwin':
|
||||
self.dfl_mountpoint = f'/Volumes/{self.dev_label}'
|
||||
self.dfl_shm_dir = f'/Volumes/{self.macOS_ramdisk_name}'
|
||||
|
||||
self.cfg = cfg
|
||||
|
||||
|
|
@ -385,12 +389,18 @@ class Autosign:
|
|||
if sys.platform == 'linux':
|
||||
self.mount_cmd = f'mount {self.mountpoint}'
|
||||
self.umount_cmd = f'umount {self.mountpoint}'
|
||||
elif sys.platform == 'darwin':
|
||||
self.mount_cmd = f'diskutil mount {self.dev_label}'
|
||||
self.umount_cmd = f'diskutil eject {self.dev_label}'
|
||||
|
||||
self.init_fixup()
|
||||
|
||||
# these use the ‘fixed-up’ values:
|
||||
if sys.platform == 'linux':
|
||||
self.dev_label_path = self.dev_label_dir / self.dev_label
|
||||
elif sys.platform == 'darwin':
|
||||
from .platform.darwin.util import MacOSRamDisk
|
||||
self.ramdisk = MacOSRamDisk(cfg, self.macOS_ramdisk_name, 10, path=self.shm_dir)
|
||||
|
||||
self.keyfile = self.mountpoint / 'autosign.key'
|
||||
|
||||
|
|
@ -621,6 +631,9 @@ class Autosign:
|
|||
except:
|
||||
die(2,f"Unable to create wallet directory '{self.wallet_dir}'")
|
||||
|
||||
if sys.platform == 'darwin':
|
||||
self.ramdisk.create()
|
||||
|
||||
remove_wallet_dir()
|
||||
create_wallet_dir()
|
||||
self.gen_key(no_unmount=True)
|
||||
|
|
@ -717,6 +730,8 @@ class Autosign:
|
|||
return True
|
||||
if sys.platform == 'linux':
|
||||
return self.dev_label_path.exists()
|
||||
elif sys.platform == 'darwin':
|
||||
return self.mountpoint.exists()
|
||||
|
||||
async def main_loop(self):
|
||||
if not self.cfg.stealth_led:
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
July 2024
|
||||
August 2024
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
15.0.dev2
|
||||
15.0.dev3
|
||||
|
|
|
|||
|
|
@ -87,12 +87,13 @@ On supported platforms (currently Orange Pi, Rock Pi and Raspberry Pi boards),
|
|||
the status LED indicates whether the program is busy or in standby mode, i.e.
|
||||
ready for device insertion or removal.
|
||||
|
||||
The removable device must have a partition labeled MMGEN_TX with a user-
|
||||
writable root directory.
|
||||
The removable device must have a partition with a filesystem labeled MMGEN_TX
|
||||
and a user-writable root directory. For interoperability between OS-es, it’s
|
||||
recommended to use the exFAT file system.
|
||||
|
||||
On both the signing and online machines the mountpoint ‘{asi.mountpoint}’
|
||||
(as currently configured) must exist and ‘/etc/fstab’ must contain the
|
||||
following entry:
|
||||
(as currently configured) must exist. Linux (not macOS) machines must have
|
||||
an ‘/etc/fstab’ with the following entry:
|
||||
|
||||
LABEL=MMGEN_TX {asi.mountpoint} auto noauto,user 0 0
|
||||
|
||||
|
|
@ -118,7 +119,7 @@ file path to the end of the command line. Multiple temporary wallets may
|
|||
be created in this way and used for signing (note, however, that for XMR
|
||||
operations only one wallet is supported).
|
||||
|
||||
Autosigning is currently available only on Linux-based platforms.
|
||||
Autosigning is currently supported on Linux and macOS only.
|
||||
|
||||
|
||||
SECURITY NOTE
|
||||
|
|
|
|||
0
mmgen/platform/__init__.py
Executable file
0
mmgen/platform/__init__.py
Executable file
0
mmgen/platform/darwin/__init__.py
Executable file
0
mmgen/platform/darwin/__init__.py
Executable file
54
mmgen/platform/darwin/util.py
Executable file
54
mmgen/platform/darwin/util.py
Executable file
|
|
@ -0,0 +1,54 @@
|
|||
#!/usr/bin/env python3
|
||||
#
|
||||
# mmgen = Multi-Mode GENerator, a command-line cryptocurrency wallet
|
||||
# Copyright (C)2013-2024 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
|
||||
|
||||
"""
|
||||
platform.darwin.util: utilities for the macOS platform
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from subprocess import run, PIPE, DEVNULL
|
||||
|
||||
from ...color import cyan
|
||||
from ...obj import MMGenLabel
|
||||
|
||||
class RamDiskLabel(MMGenLabel):
|
||||
max_len = 24
|
||||
desc = 'ramdisk label'
|
||||
|
||||
class MacOSRamDisk:
|
||||
|
||||
desc = 'macOS ramdisk'
|
||||
|
||||
def __init__(self, cfg, label, size_in_MB, path=None):
|
||||
self.cfg = cfg
|
||||
self.label = RamDiskLabel(label)
|
||||
self.size_in_MB = size_in_MB
|
||||
self.dfl_path = Path('/Volumes') / self.label
|
||||
self.path = Path(path) if path else self.dfl_path
|
||||
|
||||
def create(self, quiet=False):
|
||||
redir = DEVNULL if quiet else None
|
||||
if self.path.exists():
|
||||
self.cfg._util.qmsg('{} {} [{}] already exists'.format(self.desc, self.label.hl(), self.path))
|
||||
return
|
||||
cp = run(['hdiutil', 'attach', '-nomount', f'ram://{2048 * self.size_in_MB}'], stdout=PIPE, check=True)
|
||||
self.dev_name = cp.stdout.decode().strip()
|
||||
self.cfg._util.qmsg('{} {} [{}]'.format(cyan(f'Created {self.desc}'), self.label.hl(), self.dev_name))
|
||||
run(['diskutil', 'eraseVolume', 'APFS', self.label, self.dev_name], stdout=redir, check=True)
|
||||
if self.path != self.dfl_path:
|
||||
run(['diskutil', 'umount', self.label], stdout=redir, check=True)
|
||||
self.path.mkdir(parents=True, exist_ok=True)
|
||||
run(['diskutil', 'mount', '-mountPoint', str(self.path.absolute()), self.label], stdout=redir, check=True)
|
||||
|
||||
def destroy(self, quiet=False):
|
||||
redir = DEVNULL if quiet else None
|
||||
run(['diskutil', 'eject', self.label], stdout=redir, check=True)
|
||||
if not quiet:
|
||||
self.cfg._util.qmsg('{} {} [{}]'.format(cyan(f'Destroyed {self.desc}'), self.label.hl(), self.path))
|
||||
|
|
@ -56,6 +56,8 @@ packages =
|
|||
mmgen.contrib
|
||||
mmgen.data
|
||||
mmgen.help
|
||||
mmgen.platform
|
||||
mmgen.platform.darwin
|
||||
mmgen.proto
|
||||
mmgen.proto.bch
|
||||
mmgen.proto.btc
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
test.cmdtest_py_d.ct_autosign: Autosign tests for the cmdtest.py test suite
|
||||
"""
|
||||
|
||||
import sys, os, time, shutil
|
||||
import sys, os, time, shutil, atexit
|
||||
from subprocess import run,DEVNULL
|
||||
from pathlib import Path
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
networks = ('btc',)
|
||||
tmpdir_nums = [18]
|
||||
color = True
|
||||
platform_skip = ('win32', 'darwin')
|
||||
platform_skip = ('win32',)
|
||||
threaded = False
|
||||
daemon_coins = []
|
||||
|
||||
|
|
@ -72,6 +72,9 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
if not (cfg.skipping_deps or self.live):
|
||||
self._create_removable_device()
|
||||
|
||||
if sys.platform == 'darwin' and not cfg.no_daemon_stop:
|
||||
atexit.register(self._macOS_eject_disk, self.asi.dev_label)
|
||||
|
||||
self.opts = ['--coins='+','.join(self.coins)]
|
||||
|
||||
if not self.live:
|
||||
|
|
@ -83,6 +86,9 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
def __del__(self):
|
||||
if hasattr(self,'have_dummy_control_files'):
|
||||
LEDControl.delete_dummy_control_files()
|
||||
if sys.platform == 'darwin' and not cfg.no_daemon_stop:
|
||||
for label in (self.asi.dev_label, self.asi.ramdisk.label):
|
||||
self._macOS_eject_disk(label)
|
||||
|
||||
def _create_autosign_instances(self,create_dirs):
|
||||
d = {'offline': {'name':'asi'}}
|
||||
|
|
@ -105,7 +111,12 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
for k in ('mountpoint', 'shm_dir', 'wallet_dir', 'dev_label_dir'):
|
||||
if subdir == 'online' and k in ('shm_dir', 'wallet_dir'):
|
||||
continue
|
||||
if sys.platform == 'darwin' and k != 'mountpoint':
|
||||
continue
|
||||
getattr(asi, k).mkdir(parents=True, exist_ok=True)
|
||||
if sys.platform == 'darwin' and k == 'mountpoint':
|
||||
asi.mountpoint.rmdir()
|
||||
continue
|
||||
|
||||
setattr(self, data['name'], asi)
|
||||
|
||||
|
|
@ -116,6 +127,20 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
run(f'truncate --size=10M {img}'.split(), check=True)
|
||||
run(f'/sbin/mkfs.ext2 -E root_owner={os.getuid()}:{os.getgid()} {img}'.split(),
|
||||
stdout=redir, stderr=redir, check=True)
|
||||
elif sys.platform == 'darwin':
|
||||
redir = None if cfg.exact_output or cfg.verbose else DEVNULL
|
||||
run(f'hdiutil create -size 10M -fs exFAT -volname {self.asi.dev_label} {img}'.split(),
|
||||
stdout=redir, check=True)
|
||||
|
||||
def _macOS_mount_fs_image(self, loc):
|
||||
time.sleep(0.2)
|
||||
run(f'hdiutil attach -mountpoint {loc.mountpoint} {loc.fs_image_path}.dmg'.split(), stdout=DEVNULL, check=True)
|
||||
|
||||
def _macOS_eject_disk(self, label):
|
||||
try:
|
||||
run(['diskutil' ,'eject', label], stdout=DEVNULL, stderr=DEVNULL)
|
||||
except:
|
||||
pass
|
||||
|
||||
def start_daemons(self):
|
||||
self.spawn('',msg_only=True)
|
||||
|
|
@ -140,6 +165,9 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
mn_desc = mn_type or 'default'
|
||||
mn_type = mn_type or 'mmgen'
|
||||
|
||||
if sys.platform == 'darwin' and not cfg.no_daemon_stop:
|
||||
self._macOS_eject_disk(self.asi.ramdisk.label)
|
||||
|
||||
self.insert_device()
|
||||
|
||||
t = self.spawn(
|
||||
|
|
@ -181,6 +209,9 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
t.read()
|
||||
self.remove_device()
|
||||
|
||||
if sys.platform == 'darwin' and not cfg.no_daemon_stop:
|
||||
atexit.register(self._macOS_eject_disk, self.asi.ramdisk.label)
|
||||
|
||||
return t
|
||||
|
||||
def insert_device(self, asi='asi'):
|
||||
|
|
@ -189,6 +220,8 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
loc = getattr(self, asi)
|
||||
if sys.platform == 'linux':
|
||||
loc.dev_label_path.touch()
|
||||
elif sys.platform == 'darwin':
|
||||
self._macOS_mount_fs_image(loc)
|
||||
|
||||
def remove_device(self, asi='asi'):
|
||||
if self.live:
|
||||
|
|
@ -197,6 +230,8 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
if sys.platform == 'linux':
|
||||
if loc.dev_label_path.exists():
|
||||
loc.dev_label_path.unlink()
|
||||
elif sys.platform == 'darwin':
|
||||
loc.do_umount(silent=True)
|
||||
|
||||
def _mount_ops(self, loc, cmd, *args, **kwargs):
|
||||
return getattr(getattr(self,loc),cmd)(*args, silent=self.silent_mount, **kwargs)
|
||||
|
|
|
|||
|
|
@ -18,4 +18,5 @@ class overlay_fake_Autosign:
|
|||
|
||||
Autosign.dev_label = 'MMGEN_TS_TX'
|
||||
Autosign.linux_mount_subdir = 'mmgen_ts_autosign'
|
||||
Autosign.macOS_ramdisk_name = 'TestAutosignRamDisk'
|
||||
Autosign.init_fixup = overlay_fake_Autosign.init_fixup
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue