main_seedjoin.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2023 The MMGen Project <mmgen@tuta.io>
  5. #
  6. # This program is free software: you can redistribute it and/or modify
  7. # it under the terms of the GNU General Public License as published by
  8. # the Free Software Foundation, either version 3 of the License, or
  9. # (at your option) any later version.
  10. #
  11. # This program is distributed in the hope that it will be useful,
  12. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. # GNU General Public License for more details.
  15. #
  16. # You should have received a copy of the GNU General Public License
  17. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. """
  19. mmgen-seedjoin: Regenerate an MMGen deterministic wallet from seed shares
  20. created by 'mmgen-seedsplit'
  21. """
  22. import mmgen.opts as opts
  23. from .cfg import gc,Config
  24. from .util import msg,msg_r,die
  25. from .color import yellow
  26. from .obj import MMGenWalletLabel
  27. from .seed import Seed
  28. from .seedsplit import SeedSplitIDString,MasterShareIdx,SeedShareMasterJoining
  29. from .wallet import Wallet
  30. opts_data = {
  31. 'text': {
  32. 'desc': """Regenerate an MMGen deterministic wallet from seed shares
  33. created by 'mmgen-seedsplit'""",
  34. 'usage': '[options] share1 share2 [...shareN]',
  35. 'options': """
  36. -h, --help Print this help message
  37. --, --longhelp Print help message for long options (common options)
  38. -d, --outdir= d Output file to directory 'd' instead of working dir
  39. -e, --echo-passphrase Echo passphrases and other user input to screen
  40. -i, --id-str= s ID String of split (required for master share join only)
  41. -H, --hidden-incog-input-params=f,o Read hidden incognito data from file
  42. 'f' at offset 'o' (comma-separated). NOTE: only the
  43. first share may be in hidden incognito format!
  44. -J, --hidden-incog-output-params=f,o Write hidden incognito data to file
  45. 'f' at offset 'o' (comma-separated). File 'f' will be
  46. created if necessary and filled with random data.
  47. -o, --out-fmt= f Output to wallet format 'f' (see FMT CODES below)
  48. -O, --old-incog-fmt Specify old-format incognito input
  49. -L, --label= l Specify a label 'l' for output wallet
  50. -M, --master-share=i Use a master share with index 'i' (min:{ms_min}, max:{ms_max})
  51. -p, --hash-preset= p Use the scrypt hash parameters defined by preset 'p'
  52. for password hashing (default: '{gc.dfl_hash_preset}')
  53. -z, --show-hash-presets Show information on available hash presets
  54. -P, --passwd-file= f Get wallet passphrase from file 'f'
  55. -q, --quiet Produce quieter output; suppress some warnings
  56. -r, --usr-randchars=n Get 'n' characters of additional randomness from user
  57. (min={cfg.min_urandchars}, max={cfg.max_urandchars}, default={cfg.usr_randchars})
  58. -S, --stdout Write wallet data to stdout instead of file
  59. -v, --verbose Produce more verbose output
  60. """,
  61. 'notes': """
  62. COMMAND NOTES:
  63. When joining with a master share, the master share must be listed first.
  64. The remaining shares may be listed in any order.
  65. The --id-str option is required only for master share joins. For ordinary
  66. joins it will be ignored.
  67. For usage examples, see the help screen for the 'mmgen-seedsplit' command.
  68. {n_pw}
  69. FMT CODES:
  70. {f}
  71. """
  72. },
  73. 'code': {
  74. 'options': lambda cfg,s: s.format(
  75. ms_min=MasterShareIdx.min_val,
  76. ms_max=MasterShareIdx.max_val,
  77. cfg=cfg,
  78. gc=gc,
  79. ),
  80. 'notes': lambda cfg,help_notes,s: s.format(
  81. f=help_notes('fmt_codes'),
  82. n_pw=help_notes('passwd'),
  83. )
  84. }
  85. }
  86. def print_shares_info():
  87. si,out = 0,'\nComputed shares:\n'
  88. if cfg.master_share:
  89. fs = '{:3}: {}->{} ' + yellow('(master share #{}, split id ') + '{}' + yellow(', share count {})\n')
  90. out += fs.format(
  91. 1,
  92. shares[0].sid,
  93. share1.sid,
  94. master_idx,
  95. id_str.hl2(encl='‘’'),
  96. len(shares) )
  97. si = 1
  98. for n,s in enumerate(shares[si:],si+1):
  99. out += f'{n:3}: {s.sid}\n'
  100. cfg._util.qmsg(out)
  101. cfg = Config(opts_data=opts_data)
  102. if len(cfg._args) + bool(cfg.hidden_incog_input_params) < 2:
  103. cfg._opts.usage()
  104. if cfg.master_share:
  105. master_idx = MasterShareIdx(cfg.master_share)
  106. id_str = SeedSplitIDString(cfg.id_str or 'default')
  107. if cfg.id_str and not cfg.master_share:
  108. die(1,'--id-str option meaningless in context of non-master-share join')
  109. from .fileutil import check_infile
  110. from .wallet import check_wallet_extension
  111. for arg in cfg._args:
  112. check_wallet_extension(arg)
  113. check_infile(arg)
  114. from .ui import do_license_msg
  115. do_license_msg(cfg)
  116. cfg._util.qmsg('Input files:\n {}\n'.format('\n '.join(cfg._args)))
  117. shares = [Wallet(cfg).seed] if cfg.hidden_incog_input_params else []
  118. shares += [Wallet(cfg,fn).seed for fn in cfg._args]
  119. if cfg.master_share:
  120. share1 = SeedShareMasterJoining( cfg, master_idx, shares[0], id_str, len(shares) ).derived_seed
  121. else:
  122. share1 = shares[0]
  123. print_shares_info()
  124. msg_r('Joining {n}-of-{n} XOR split...'.format(n=len(shares)))
  125. seed_out = Seed.join_shares( cfg, [share1] + shares[1:] )
  126. msg(f'OK\nJoined Seed ID: {seed_out.sid.hl()}')
  127. Wallet(cfg,seed=seed_out).write_to_file()