help.py 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2022 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. help.py: help notes for MMGen suite commands
  20. """
  21. def help_notes_func(proto,k):
  22. from .globalvars import g
  23. def fee_spec_letters(use_quotes=False):
  24. cu = proto.coin_amt.units
  25. sep,conj = ((',',' or '),("','","' or '"))[use_quotes]
  26. return sep.join(u[0] for u in cu[:-1]) + ('',conj)[len(cu)>1] + cu[-1][0]
  27. def fee_spec_names():
  28. cu = proto.coin_amt.units
  29. return ', '.join(cu[:-1]) + ('',' and ')[len(cu)>1] + cu[-1] + ('',',\nrespectively')[len(cu)>1]
  30. def coind_exec():
  31. from .daemon import CoinDaemon
  32. return (
  33. CoinDaemon(proto.coin).exec_fn if proto.coin in CoinDaemon.coins else 'bitcoind' )
  34. class help_notes:
  35. def coind_exec():
  36. return coind_exec()
  37. def coin_daemon_network_ids():
  38. from .daemon import CoinDaemon
  39. from .util import fmt_list
  40. return fmt_list(CoinDaemon.get_network_ids(),fmt='bare')
  41. def rel_fee_desc():
  42. from .tx import MMGenTX
  43. return MMGenTX.Base().rel_fee_desc
  44. def fee_spec_letters():
  45. return fee_spec_letters()
  46. def fee():
  47. from .tx import MMGenTX
  48. return """
  49. FEE SPECIFICATION: Transaction fees, both on the command line and at the
  50. interactive prompt, may be specified as either absolute {c} amounts, using
  51. a plain decimal number, or as {r}, using an integer followed by
  52. '{l}', for {u}.
  53. """.format(
  54. c = proto.coin,
  55. r = MMGenTX.Base().rel_fee_desc,
  56. l = fee_spec_letters(use_quotes=True),
  57. u = fee_spec_names() )
  58. def passwd():
  59. return """
  60. PASSPHRASE NOTE:
  61. For passphrases all combinations of whitespace are equal, and leading and
  62. trailing space are ignored. This permits reading passphrase or brainwallet
  63. data from a multi-line file with free spacing and indentation.
  64. """.strip()
  65. def brainwallet():
  66. return """
  67. BRAINWALLET NOTE:
  68. To thwart dictionary attacks, it's recommended to use a strong hash preset
  69. with brainwallets. For a brainwallet passphrase to generate the correct
  70. seed, the same seed length and hash preset parameters must always be used.
  71. """.strip()
  72. def txcreate():
  73. return f"""
  74. The transaction's outputs are specified on the command line, while its inputs
  75. are chosen from a list of the user's unspent outputs via an interactive menu.
  76. If the transaction fee is not specified on the command line (see FEE
  77. SPECIFICATION below), it will be calculated dynamically using network fee
  78. estimation for the default (or user-specified) number of confirmations.
  79. If network fee estimation fails, the user will be prompted for a fee.
  80. Network-estimated fees will be multiplied by the value of '--tx-fee-adj',
  81. if specified.
  82. All addresses on the command line can be either {proto.name} addresses or {g.proj_name}
  83. addresses of the form <seed ID>:<index>.
  84. To send the value of all inputs (minus TX fee) to a single output, specify
  85. one address with no amount on the command line.
  86. """
  87. def txsign():
  88. from .protocol import CoinProtocol
  89. return """
  90. Transactions may contain both {pnm} or non-{pnm} input addresses.
  91. To sign non-{pnm} inputs, a {wd}flat key list is used
  92. as the key source ('--keys-from-file' option).
  93. To sign {pnm} inputs, key data is generated from a seed as with the
  94. {pnl}-addrgen and {pnl}-keygen commands. Alternatively, a key-address file
  95. may be used (--mmgen-keys-from-file option).
  96. Multiple wallets or other seed files can be listed on the command line in
  97. any order. If the seeds required to sign the transaction's inputs are not
  98. found in these files (or in the default wallet), the user will be prompted
  99. for seed data interactively.
  100. To prevent an attacker from crafting transactions with bogus {pnm}-to-{pnu}
  101. address mappings, all outputs to {pnm} addresses are verified with a seed
  102. source. Therefore, seed files or a key-address file for all {pnm} outputs
  103. must also be supplied on the command line if the data can't be found in the
  104. default wallet.
  105. """.format(
  106. wd = (f'{coind_exec()} wallet dump or ' if isinstance(proto,CoinProtocol.Bitcoin) else ''),
  107. pnm = g.proj_name,
  108. pnu = proto.name,
  109. pnl = g.proj_name.lower() )
  110. def seedsplit():
  111. from .seedsplit import SeedShareIdx,SeedShareCount,MasterShareIdx
  112. return """
  113. COMMAND NOTES:
  114. This command generates shares one at a time. Shares may be output to any
  115. MMGen wallet format, with one limitation: only one share in a given split may
  116. be in hidden incognito format, and it must be the master share in the case of
  117. a master-share split.
  118. If the command's optional first argument is omitted, the default wallet is
  119. used for the split.
  120. The last argument is a seed split specifier consisting of an optional split
  121. ID, a share index, and a share count, all separated by colons. The split ID
  122. must be a valid UTF-8 string. If omitted, the ID 'default' is used. The
  123. share index (the index of the share being generated) must be in the range
  124. {si.min_val}-{si.max_val} and the share count (the total number of shares in the split)
  125. in the range {sc.min_val}-{sc.max_val}.
  126. Master Shares
  127. Each seed has a total of {mi.max_val} master shares, which can be used as the first
  128. shares in multiple splits if desired. To generate a master share, use the
  129. --master-share (-M) option with an index in the range {mi.min_val}-{mi.max_val} and omit
  130. the last argument.
  131. When creating and joining a split using a master share, ensure that the same
  132. master share index is used in all split and join commands.
  133. EXAMPLES:
  134. Split a BIP39 seed phrase into two BIP39 shares. Rejoin the split:
  135. $ echo 'zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong' > sample.bip39
  136. $ mmgen-seedsplit -o bip39 sample.bip39 1:2
  137. BIP39 mnemonic data written to file '03BAE887-default-1of2[D51CB683][128].bip39'
  138. $ mmgen-seedsplit -o bip39 sample.bip39 2:2
  139. BIP39 mnemonic data written to file '03BAE887-default-2of2[67BFD36E][128].bip39'
  140. $ mmgen-seedjoin -o bip39 \\
  141. '03BAE887-default-2of2[67BFD36E][128].bip39' \\
  142. '03BAE887-default-1of2[D51CB683][128].bip39'
  143. BIP39 mnemonic data written to file '03BAE887[128].bip39'
  144. $ cat '03BAE887[128].bip39'
  145. zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo zoo wrong
  146. Create a 3-way default split of your default wallet, outputting all shares
  147. to default wallet format. Rejoin the split:
  148. $ mmgen-seedsplit 1:3 # Step A
  149. $ mmgen-seedsplit 2:3 # Step B
  150. $ mmgen-seedsplit 3:3 # Step C
  151. $ mmgen-seedjoin <output_of_step_A> <output_of_step_B> <output_of_step_C>
  152. Create a 2-way split of your default wallet with ID string 'alice',
  153. outputting shares to MMGen native mnemonic format. Rejoin the split:
  154. $ mmgen-seedsplit -o words alice:1:2 # Step D
  155. $ mmgen-seedsplit -o words alice:2:2 # Step E
  156. $ mmgen-seedjoin <output_of_step_D> <output_of_step_E>
  157. Create a 2-way split of your default wallet with ID string 'bob' using
  158. master share #7, outputting share #1 (the master share) to default wallet
  159. format and share #2 to BIP39 format. Rejoin the split:
  160. $ mmgen-seedsplit -M7 # Step X
  161. $ mmgen-seedsplit -M7 -o bip39 bob:2:2 # Step Y
  162. $ mmgen-seedjoin -M7 --id-str=bob <output_of_step_X> <output_of_step_Y>
  163. Create a 2-way split of your default wallet with ID string 'alice' using
  164. master share #7. Rejoin the split using master share #7 generated in the
  165. previous example:
  166. $ mmgen-seedsplit -M7 -o bip39 alice:2:2 # Step Z
  167. $ mmgen-seedjoin -M7 --id-str=alice <output_of_step_X> <output_of_step_Z>
  168. Create a 2-way default split of your default wallet with an incognito-format
  169. master share hidden in file 'my.hincog' at offset 1325. Rejoin the split:
  170. $ mmgen-seedsplit -M4 -o hincog -J my.hincog,1325 1:2 # Step M (share A)
  171. $ mmgen-seedsplit -M4 -o bip39 2:2 # Step N (share B)
  172. $ mmgen-seedjoin -M4 -H my.hincog,1325 <output_of_step_N>
  173. """.strip().format(
  174. si = SeedShareIdx,
  175. sc = SeedShareCount,
  176. mi = MasterShareIdx )
  177. def subwallet():
  178. from .subseed import SubSeedIdxRange
  179. return f"""
  180. SUBWALLETS:
  181. Subwallets (subseeds) are specified by a "Subseed Index" consisting of:
  182. a) an integer in the range 1-{SubSeedIdxRange.max_idx}, plus
  183. b) an optional single letter, 'L' or 'S'
  184. The letter designates the length of the subseed. If omitted, 'L' is assumed.
  185. Long ('L') subseeds are the same length as their parent wallet's seed
  186. (typically 256 bits), while short ('S') subseeds are always 128-bit.
  187. The long and short subseeds for a given index are derived independently,
  188. so both may be used.
  189. MMGen has no notion of "depth", and to an outside observer subwallets are
  190. identical to ordinary wallets. This is a feature rather than a bug, as it
  191. denies an attacker any way of knowing whether a given wallet has a parent.
  192. Since subwallets are just wallets, they may be used to generate other
  193. subwallets, leading to hierarchies of arbitrary depth. However, this is
  194. inadvisable in practice for two reasons: Firstly, it creates accounting
  195. complexity, requiring the user to independently keep track of a derivation
  196. tree. More importantly, however, it leads to the danger of Seed ID
  197. collisions between subseeds at different levels of the hierarchy, as
  198. MMGen checks and avoids ID collisions only among sibling subseeds.
  199. An exception to this caveat would be a multi-user setup where sibling
  200. subwallets are distributed to different users as their default wallets.
  201. Since the subseeds derived from these subwallets are private to each user,
  202. Seed ID collisions among them doesn't present a problem.
  203. A safe rule of thumb, therefore, is for *each user* to derive all of his/her
  204. subwallets from a single parent. This leaves each user with a total of two
  205. million subwallets, which should be enough for most practical purposes.
  206. """.strip()
  207. return getattr(help_notes,k)() + ('-α' if g.debug_utf8 else '')