mmgen-autosign: support signing of TXs with non-MMGen inputs
This commit is contained in:
parent
2df03306f2
commit
b12fd879bf
8 changed files with 111 additions and 10 deletions
|
|
@ -275,6 +275,7 @@ class Signable:
|
|||
self.cfg,
|
||||
tx1,
|
||||
seedfiles = self.parent.wallet_files[:],
|
||||
keylist = self.parent.keylist,
|
||||
passwdfile = str(self.parent.keyfile),
|
||||
autosign = True).keys)
|
||||
if tx2:
|
||||
|
|
@ -424,6 +425,7 @@ class Autosign:
|
|||
macOS_ramdisk_name = 'AutosignRamDisk'
|
||||
wallet_subdir = 'autosign'
|
||||
linux_blkid_cmd = 'sudo blkid -s LABEL -o value'
|
||||
keylist_fn = 'keylist.mmenc'
|
||||
|
||||
cmds = ('setup', 'xmr_setup', 'sign', 'wait')
|
||||
|
||||
|
|
@ -672,6 +674,7 @@ class Autosign:
|
|||
self.led.set('busy')
|
||||
self.do_mount()
|
||||
key_ok = self.decrypt_wallets()
|
||||
self.init_non_mmgen_keys()
|
||||
if key_ok:
|
||||
if self.cfg.stealth_led:
|
||||
self.led.set('busy')
|
||||
|
|
@ -780,6 +783,9 @@ class Autosign:
|
|||
ss_out = Wallet(self.cfg, ss=ss_in, passwd_file=str(self.keyfile))
|
||||
ss_out.write_to_file(desc='autosign wallet', outdir=self.wallet_dir)
|
||||
|
||||
if self.cfg.keys_from_file:
|
||||
self.setup_non_mmgen_keys()
|
||||
|
||||
@property
|
||||
def xmrwallet_cfg(self):
|
||||
if not hasattr(self, '_xmrwallet_cfg'):
|
||||
|
|
@ -908,3 +914,34 @@ class Autosign:
|
|||
enabled = self.cfg.led,
|
||||
simulate = self.cfg.test_suite_autosign_led_simulate)
|
||||
self.led.set('off')
|
||||
|
||||
def setup_non_mmgen_keys(self):
|
||||
from .fileutil import get_lines_from_file, write_data_to_file
|
||||
from .crypto import Crypto
|
||||
lines = get_lines_from_file(self.cfg, self.cfg.keys_from_file, desc='keylist data')
|
||||
write_data_to_file(
|
||||
self.cfg,
|
||||
str(self.wallet_dir / self.keylist_fn),
|
||||
Crypto(self.cfg).mmgen_encrypt(
|
||||
data = '\n'.join(lines).encode(),
|
||||
passwd = self.keyfile.read_text()),
|
||||
desc = 'encrypted keylist data',
|
||||
binary = True)
|
||||
if keypress_confirm(self.cfg, 'Securely delete original keylist file?'):
|
||||
shred_file(self.cfg, self.cfg.keys_from_file)
|
||||
|
||||
def init_non_mmgen_keys(self):
|
||||
if not hasattr(self, 'keylist'):
|
||||
path = self.wallet_dir / self.keylist_fn
|
||||
if path.exists():
|
||||
from .crypto import Crypto
|
||||
from .fileutil import get_data_from_file
|
||||
self.keylist = Crypto(self.cfg).mmgen_decrypt(
|
||||
get_data_from_file(
|
||||
self.cfg,
|
||||
path,
|
||||
desc = 'encrypted keylist data',
|
||||
binary = True),
|
||||
passwd = self.keyfile.read_text()).decode().split()
|
||||
else:
|
||||
self.keylist = None
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
15.1.dev48
|
||||
15.1.dev49
|
||||
|
|
|
|||
|
|
@ -36,6 +36,12 @@ opts_data = {
|
|||
--, --longhelp Print help message for long (global) options
|
||||
-c, --coins=c Coins to sign for (comma-separated list)
|
||||
-I, --no-insert-check Don’t check for device insertion
|
||||
-k, --keys-from-file=F Use wif keys listed in file ‘F’ for signing non-MMGen
|
||||
inputs. The file may be MMGen encrypted if desired. The
|
||||
‘setup’ operation creates a temporary encrypted copy of
|
||||
the file in volatile memory for use during the signing
|
||||
session, thus permitting the deletion of the original
|
||||
file for increased security.
|
||||
-l, --seed-len=N Specify wallet seed length of ‘N’ bits (for setup only)
|
||||
-L, --led Use status LED to signal standby, busy and error
|
||||
-m, --mountpoint=M Specify an alternate mountpoint 'M'
|
||||
|
|
|
|||
|
|
@ -95,8 +95,7 @@ class Base(MMGenObject):
|
|||
This transaction includes inputs with non-{gc.proj_name} addresses. When
|
||||
signing the transaction, private keys for the addresses listed below must
|
||||
be supplied using the --keys-from-file option. The key file must contain
|
||||
one key per line. Please note that this transaction cannot be autosigned,
|
||||
as autosigning does not support the use of key files.
|
||||
one key per line.
|
||||
|
||||
Non-{gc.proj_name} addresses found in inputs:
|
||||
{{}}
|
||||
|
|
@ -213,6 +212,7 @@ class Base(MMGenObject):
|
|||
quiet = True)
|
||||
|
||||
def check_non_mmgen_inputs(self, *, caller, non_mmaddrs=None):
|
||||
assert caller in ('txcreate', 'txdo', 'txsign', 'autosign')
|
||||
non_mmaddrs = non_mmaddrs or self.get_non_mmaddrs('inputs')
|
||||
if non_mmaddrs:
|
||||
indent = ' '
|
||||
|
|
@ -223,7 +223,7 @@ class Base(MMGenObject):
|
|||
die('UserOptError', f'\n{indent}ERROR: {m}\n')
|
||||
else:
|
||||
msg(f'\n{indent}WARNING: {m}\n')
|
||||
if not self.cfg.yes:
|
||||
if not (caller == 'autosign' or self.cfg.yes):
|
||||
from ..ui import keypress_confirm
|
||||
keypress_confirm(self.cfg, 'Continue?', default_yes=True, do_exit=True)
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ class TxKeys:
|
|||
self.keylist = keylist if autosign else keylist or get_keylist(cfg)
|
||||
self.keyaddrlist = keyaddrlist if autosign else keyaddrlist or get_keyaddrlist(cfg, tx.proto)
|
||||
self.passwdfile = passwdfile
|
||||
self.autosign = autosign
|
||||
self.saved_seeds = {}
|
||||
|
||||
def get_keys_for_non_mmgen_inputs(self):
|
||||
|
|
@ -96,7 +97,7 @@ class TxKeys:
|
|||
sep = '\n '
|
||||
if addrs := self.tx.get_non_mmaddrs('inputs'):
|
||||
self.tx.check_non_mmgen_inputs(
|
||||
caller = 'txsign',
|
||||
caller = 'autosign' if self.autosign and self.keylist else 'txsign',
|
||||
non_mmaddrs = addrs)
|
||||
kal = KeyAddrList(
|
||||
cfg = self.cfg,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import time
|
|||
|
||||
from .autosign import CmdTestAutosignThreaded
|
||||
from .regtest import CmdTestRegtest, rt_pw
|
||||
from ..include.common import gr_uc
|
||||
from ..include.common import gr_uc, create_addrpairs
|
||||
|
||||
class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
|
||||
'automounted transacting operations via regtest mode'
|
||||
|
|
@ -23,21 +23,29 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
|
|||
networks = ('btc', 'bch', 'ltc')
|
||||
tmpdir_nums = [49]
|
||||
bdb_wallet = True
|
||||
keylist_passwd = 'abc'
|
||||
|
||||
rt_data = {
|
||||
'rtFundAmt': {'btc':'500', 'bch':'500', 'ltc':'5500'},
|
||||
}
|
||||
bal1_chk = {
|
||||
'btc': '502.46',
|
||||
'bch': '502.46',
|
||||
'ltc': '5502.46'}
|
||||
bal2_chk = {
|
||||
'btc': '491.11002204',
|
||||
'bch': '498.7653392',
|
||||
'ltc': '5491.11002204'}
|
||||
'btc': '493.56992828',
|
||||
'bch': '501.22524576',
|
||||
'ltc': '5493.56992828'}
|
||||
|
||||
cmd_group = (
|
||||
('setup', 'regtest mode setup'),
|
||||
('walletgen_alice', 'wallet generation (Alice)'),
|
||||
('addrgen_alice', 'address generation (Alice)'),
|
||||
('addrimport_alice', 'importing Alice’s addresses'),
|
||||
('addrimport_alice_non_mmgen', 'importing Alice’s non-MMGen addresses'),
|
||||
('fund_alice', 'funding Alice’s wallet'),
|
||||
('fund_alice_non_mmgen1', 'funding Alice’s wallet (non-MMGen addr #1)'),
|
||||
('fund_alice_non_mmgen2', 'funding Alice’s wallet (non-MMGen addr #2)'),
|
||||
('generate', 'mining a block'),
|
||||
('alice_bal1', 'checking Alice’s balance'),
|
||||
('alice_txcreate1', 'creating a transaction'),
|
||||
|
|
@ -92,9 +100,30 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
|
|||
|
||||
self.opts.append('--alice')
|
||||
|
||||
self.non_mmgen_addrs = create_addrpairs(self.proto, 'C', 2)
|
||||
|
||||
def addrimport_alice_non_mmgen(self):
|
||||
self.write_to_tmpfile(
|
||||
'non_mmgen_addrs',
|
||||
'\n'.join(e.addr for e in self.non_mmgen_addrs))
|
||||
return self.spawn(
|
||||
'mmgen-addrimport',
|
||||
['--alice', '--quiet', '--addrlist', f'{self.tmpdir}/non_mmgen_addrs'])
|
||||
|
||||
def fund_alice_non_mmgen1(self):
|
||||
return self.fund_wallet('alice', '1.23', addr=self.non_mmgen_addrs[0].addr)
|
||||
|
||||
def fund_alice_non_mmgen2(self):
|
||||
return self.fund_wallet('alice', '1.23', addr=self.non_mmgen_addrs[1].addr)
|
||||
|
||||
def alice_bal1(self):
|
||||
return self._user_bal_cli('alice', chk=self.bal1_chk[self.coin])
|
||||
|
||||
def alice_txcreate1(self):
|
||||
return self._user_txcreate(
|
||||
'alice',
|
||||
inputs = '1-3',
|
||||
tweaks = ['confirm_non_mmgen'],
|
||||
chg_addr = 'C:5',
|
||||
data_arg = 'data:'+gr_uc[:24])
|
||||
|
||||
|
|
@ -147,7 +176,18 @@ class CmdTestAutosignAutomount(CmdTestAutosignThreaded, CmdTestRegtest):
|
|||
return self._user_txcreate('alice', chg_addr='C:5', exit_val=2, expect_str='unsent transaction')
|
||||
|
||||
def alice_run_autosign_setup(self):
|
||||
return self.run_setup(mn_type='default', use_dfl_wallet=True, passwd=rt_pw)
|
||||
from mmgen.crypto import Crypto
|
||||
from mmgen.cfg import Config
|
||||
new_cfg = Config({'_clone': self.cfg, 'usr_randchars': 0, 'hash_preset': '1'})
|
||||
enc_data = Crypto(new_cfg).mmgen_encrypt(
|
||||
'\n'.join(e.wif for e in self.non_mmgen_addrs).encode(), passwd=self.keylist_passwd)
|
||||
self.write_to_tmpfile('non_mmgen_keys.mmenc', enc_data, binary=True)
|
||||
return self.run_setup(
|
||||
mn_type = 'default',
|
||||
use_dfl_wallet = True,
|
||||
wallet_passwd = rt_pw,
|
||||
add_opts = [f'--keys-from-file={self.tmpdir}/non_mmgen_keys.mmenc'],
|
||||
keylist_passwd = self.keylist_passwd)
|
||||
|
||||
def alice_txsend1(self):
|
||||
return self._user_txsend('alice', comment='This one’s worth a comment', no_wait=True)
|
||||
|
|
|
|||
|
|
@ -197,6 +197,7 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
seed_len = None,
|
||||
usr_entry_modes = False,
|
||||
wallet_passwd = None,
|
||||
keylist_passwd = None,
|
||||
add_opts = [],
|
||||
expect_args = []):
|
||||
|
||||
|
|
@ -244,6 +245,10 @@ class CmdTestAutosignBase(CmdTestBase):
|
|||
if expect_args:
|
||||
t.expect(*expect_args)
|
||||
|
||||
if keylist_passwd:
|
||||
t.passphrase('keylist data', keylist_passwd)
|
||||
t.expect('(y/N): ', 'y')
|
||||
|
||||
t.read()
|
||||
self.remove_device()
|
||||
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ test.include.common: Shared routines and data for the MMGen test suites
|
|||
import sys, os, re, atexit
|
||||
from subprocess import run, PIPE, DEVNULL
|
||||
from pathlib import Path
|
||||
from collections import namedtuple
|
||||
|
||||
from mmgen.cfg import gv
|
||||
from mmgen.color import yellow, green, orange
|
||||
|
|
@ -362,6 +363,17 @@ def make_burn_addr(proto, mmtype='compressed', hexdata=None):
|
|||
proto = proto,
|
||||
mmtype = mmtype).pubhash2addr(hexdata or '00'*20)
|
||||
|
||||
def create_addrpairs(proto, mmtype, num):
|
||||
ap = namedtuple('addrpair', ['wif', 'addr'])
|
||||
from mmgen.tool.coin import tool_cmd
|
||||
n = 123456789123456789
|
||||
return [ap(*tool_cmd(
|
||||
cfg = cfg,
|
||||
cmdname = 'privhex2pair',
|
||||
proto = proto,
|
||||
mmtype = mmtype).privhex2pair(f'{n+m:064x}'))
|
||||
for m in range(num)]
|
||||
|
||||
def VirtBlockDevice(img_path, size):
|
||||
if sys.platform == 'linux':
|
||||
return VirtBlockDeviceLinux(img_path, size)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue