tx.py 56 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596
  1. #!/usr/bin/env python3
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2019 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. tx.py: Transaction routines for the MMGen suite
  20. """
  21. import sys,os,json
  22. from stat import *
  23. from mmgen.common import *
  24. from mmgen.obj import *
  25. wmsg = lambda k: {
  26. 'addr_in_addrfile_only': """
  27. Warning: output address {} is not in the tracking wallet, which means
  28. its balance will not be tracked. You're strongly advised to import the address
  29. into your tracking wallet before broadcasting this transaction.
  30. """.strip(),
  31. 'addr_not_found': """
  32. No data for {pnm} address {{}} could be found in either the tracking
  33. wallet or the supplied address file. Please import this address into your
  34. tracking wallet, or supply an address file for it on the command line.
  35. """.strip().format(pnm=g.proj_name),
  36. 'addr_not_found_no_addrfile': """
  37. No data for {pnm} address {{}} could be found in the tracking wallet.
  38. Please import this address into your tracking wallet or supply an address file
  39. for it on the command line.
  40. """.strip().format(pnm=g.proj_name),
  41. }[k]
  42. def strfmt_locktime(num,terse=False):
  43. # Locktime itself is an unsigned 4-byte integer which can be parsed two ways:
  44. #
  45. # If less than 500 million, locktime is parsed as a block height. The transaction can be
  46. # added to any block which has this height or higher.
  47. # MMGen note: s/this height or higher/a higher block height/
  48. #
  49. # If greater than or equal to 500 million, locktime is parsed using the Unix epoch time
  50. # format (the number of seconds elapsed since 1970-01-01T00:00 UTC). The transaction can be
  51. # added to any block whose block time is greater than the locktime.
  52. if num == None:
  53. return '(None)'
  54. elif num >= 5 * 10**6:
  55. return ' '.join(time.strftime('%c',time.gmtime(num)).split()[1:])
  56. elif num > 0:
  57. return '{}{}'.format(('block height ','')[terse],num)
  58. else:
  59. die(2,"'{}': invalid locktime value!".format(num))
  60. def mmaddr2coinaddr(mmaddr,ad_w,ad_f):
  61. # assume mmaddr has already been checked
  62. coin_addr = ad_w.mmaddr2coinaddr(mmaddr)
  63. if not coin_addr:
  64. if ad_f:
  65. coin_addr = ad_f.mmaddr2coinaddr(mmaddr)
  66. if coin_addr:
  67. msg(wmsg('addr_in_addrfile_only').format(mmaddr))
  68. if not (opt.yes or keypress_confirm('Continue anyway?')):
  69. sys.exit(1)
  70. else:
  71. die(2,wmsg('addr_not_found').format(mmaddr))
  72. else:
  73. die(2,wmsg('addr_not_found_no_addrfile').format(mmaddr))
  74. return CoinAddr(coin_addr)
  75. def segwit_is_active(exit_on_error=False):
  76. d = g.rpch.getblockchaininfo()
  77. if d['chain'] == 'regtest':
  78. return True
  79. if ( 'bip9_softforks' in d
  80. and 'segwit' in d['bip9_softforks']
  81. and d['bip9_softforks']['segwit']['status'] == 'active'):
  82. return True
  83. if g.test_suite:
  84. return True
  85. if exit_on_error:
  86. die(2,'Segwit not active on this chain. Exiting')
  87. else:
  88. return False
  89. def addr2pubhash(addr):
  90. return g.proto.verify_addr(addr,addr.hex_width,return_dict=True)['hex']
  91. def addr2scriptPubKey(addr):
  92. return {
  93. 'p2pkh': '76a914' + addr2pubhash(addr) + '88ac',
  94. 'p2sh': 'a914' + addr2pubhash(addr) + '87',
  95. 'bech32': g.proto.witness_vernum_hex + '14' + addr2pubhash(addr)
  96. }[addr.addr_fmt]
  97. def scriptPubKey2addr(s):
  98. if len(s) == 50 and s[:6] == '76a914' and s[-4:] == '88ac':
  99. return g.proto.pubhash2addr(s[6:-4],p2sh=False),'p2pkh'
  100. elif len(s) == 46 and s[:4] == 'a914' and s[-2:] == '87':
  101. return g.proto.pubhash2addr(s[4:-2],p2sh=True),'p2sh'
  102. elif len(s) == 44 and s[:4] == g.proto.witness_vernum_hex + '14':
  103. return g.proto.pubhash2bech32addr(s[4:]),'bech32'
  104. else:
  105. raise NotImplementedError('Unknown scriptPubKey ({})'.format(s))
  106. class DeserializedTX(dict,MMGenObject):
  107. """
  108. Parse a serialized Bitcoin transaction
  109. For checking purposes, additionally reconstructs the raw (unsigned) tx hex from signed tx hex
  110. """
  111. def __init__(self,txhex):
  112. def bytes2int(bytes_le):
  113. if bytes_le[-1] & 0x80: # sign bit is set
  114. die(3,"{}: Negative values not permitted in transaction!".format(bytes_le[::-1].hex()))
  115. return int(bytes_le[::-1].hex(),16)
  116. def bytes2coin_amt(bytes_le):
  117. return g.proto.coin_amt(bytes2int(bytes_le) * g.proto.coin_amt.min_coin_unit)
  118. def bshift(n,skip=False,sub_null=False):
  119. ret = tx[self.idx:self.idx+n]
  120. self.idx += n
  121. if sub_null:
  122. self.raw_tx += b'\x00'
  123. elif not skip:
  124. self.raw_tx += ret
  125. return ret
  126. # https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
  127. # For example, the number 515 is encoded as 0xfd0302.
  128. def readVInt(skip=False):
  129. s = tx[self.idx]
  130. self.idx += 1
  131. if not skip: self.raw_tx.append(s)
  132. vbytes_len = 1 if s < 0xfd else 2 if s == 0xfd else 4 if s == 0xfe else 8
  133. if vbytes_len == 1:
  134. return s
  135. else:
  136. vbytes = tx[self.idx:self.idx+vbytes_len]
  137. self.idx += vbytes_len
  138. if not skip: self.raw_tx += vbytes
  139. return int(vbytes[::-1].hex(),16)
  140. def make_txid(tx_bytes):
  141. return sha256(sha256(tx_bytes).digest()).digest()[::-1].hex()
  142. self.idx = 0
  143. self.raw_tx = bytearray()
  144. tx = bytes.fromhex(txhex)
  145. d = { 'version': bytes2int(bshift(4)) }
  146. has_witness = tx[self.idx] == 0
  147. if has_witness:
  148. u = bshift(2,skip=True).hex()
  149. if u != '0001':
  150. raise IllegalWitnessFlagValue("'{}': Illegal value for flag in transaction!".format(u))
  151. d['num_txins'] = readVInt()
  152. d['txins'] = MMGenList([{
  153. 'txid': bshift(32)[::-1].hex(),
  154. 'vout': bytes2int(bshift(4)),
  155. 'scriptSig': bshift(readVInt(skip=True),sub_null=True).hex(),
  156. 'nSeq': bshift(4)[::-1].hex()
  157. } for i in range(d['num_txins'])])
  158. d['num_txouts'] = readVInt()
  159. d['txouts'] = MMGenList([{
  160. 'amount': bytes2coin_amt(bshift(8)),
  161. 'scriptPubKey': bshift(readVInt()).hex()
  162. } for i in range(d['num_txouts'])])
  163. for o in d['txouts']:
  164. o['address'] = scriptPubKey2addr(o['scriptPubKey'])[0]
  165. if has_witness:
  166. # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
  167. # A non-witness program (defined hereinafter) txin MUST be associated with an empty
  168. # witness field, represented by a 0x00.
  169. d['txid'] = make_txid(tx[:4] + tx[6:self.idx] + tx[-4:])
  170. d['witness_size'] = len(tx) - self.idx + 2 - 4 # add len(marker+flag), subtract len(locktime)
  171. for txin in d['txins']:
  172. if tx[self.idx] == 0:
  173. bshift(1,skip=True)
  174. continue
  175. txin['witness'] = [
  176. bshift(readVInt(skip=True),skip=True).hex() for item in range(readVInt(skip=True)) ]
  177. else:
  178. d['txid'] = make_txid(tx)
  179. d['witness_size'] = 0
  180. if len(tx) - self.idx != 4:
  181. raise TxHexParseError('TX hex has invalid length: {} extra bytes'.format(len(tx)-self.idx-4))
  182. d['lock_time'] = bytes2int(bshift(4))
  183. d['unsigned_hex'] = self.raw_tx.hex()
  184. dict.__init__(self,d)
  185. class MMGenTxIO(MMGenListItem):
  186. vout = MMGenListItemAttr('vout',int,typeconv=False)
  187. _amt = MMGenImmutableAttr('amt',None,no_type_check=True,typeconv=False)
  188. label = MMGenListItemAttr('label','TwComment',reassign_ok=True)
  189. mmid = MMGenListItemAttr('mmid','MMGenID')
  190. addr = MMGenImmutableAttr('addr','CoinAddr')
  191. confs = MMGenListItemAttr('confs',int,typeconv=True) # confs of type long exist in the wild, so convert
  192. txid = MMGenListItemAttr('txid','CoinTxID')
  193. have_wif = MMGenListItemAttr('have_wif',bool,typeconv=False,delete_ok=True)
  194. valid_attrs_extra = {'amt'}
  195. # Setting self.amt is runtime-dependent, so make it a property
  196. # Make underlying self._amt an MMGenImmutableAttr to prevent reassignment
  197. @property
  198. def amt(self):
  199. if type(self._amt) != g.proto.coin_amt:
  200. raise ValueError('{}: invalid coin_amt type (must be {})'.format(type(self._amt),g.proto.coin_amt))
  201. return self._amt
  202. @amt.setter
  203. def amt(self,val):
  204. if type(val) != g.proto.coin_amt:
  205. raise ValueError('{}: invalid coin_amt type (must be {})'.format(type(val),g.proto.coin_amt))
  206. self._amt = val
  207. class MMGenTxInput(MMGenTxIO):
  208. scriptPubKey = MMGenListItemAttr('scriptPubKey','HexStr')
  209. sequence = MMGenListItemAttr('sequence',int,typeconv=False)
  210. class MMGenTxOutput(MMGenTxIO):
  211. is_chg = MMGenListItemAttr('is_chg',bool,typeconv=False)
  212. class MMGenTxInputList(list,MMGenObject):
  213. desc = 'transaction inputs'
  214. member_type = 'MMGenTxInput'
  215. def convert_coin(self,verbose=False):
  216. if verbose:
  217. msg('{}:'.format(self.desc.capitalize()))
  218. for i in self:
  219. d = i.__dict__
  220. d['amt'] = g.proto.coin_amt(d['amt'])
  221. def check_coin_mismatch(self):
  222. for i in self:
  223. if type(i.amt) != g.proto.coin_amt:
  224. die(2,'Coin mismatch in transaction: amount {} not of type {}!'.format(i.amt,g.proto.coin_amt))
  225. class MMGenTxOutputList(MMGenTxInputList):
  226. desc = 'transaction outputs'
  227. member_type = 'MMGenTxOutput'
  228. class MMGenTX(MMGenObject):
  229. def __new__(cls,*args,**kwargs):
  230. return MMGenObject.__new__(altcoin_subclass(cls,'tx','MMGenTX'))
  231. ext = 'rawtx'
  232. raw_ext = 'rawtx'
  233. sig_ext = 'sigtx'
  234. txid_ext = 'txid'
  235. desc = 'transaction'
  236. fee_fail_fs = 'Network fee estimation for {c} confirmations failed ({t})'
  237. no_chg_msg = 'Warning: Change address will be deleted as transaction produces no change'
  238. rel_fee_desc = 'satoshis per byte'
  239. rel_fee_disp = 'satoshis per byte'
  240. txview_hdr_fs = 'TRANSACTION DATA\n\nID={i} ({a} {c}) UTC={t} RBF={r} Sig={s} Locktime={l}\n'
  241. txview_hdr_fs_short = 'TX {i} ({a} {c}) UTC={t} RBF={r} Sig={s} Locktime={l}\n'
  242. txview_ftr_fs = 'Total input: {i} {d}\nTotal output: {o} {d}\nTX fee: {a} {c}{r}\n'
  243. txview_ftr_fs_short = 'In {i} {d} - Out {o} {d}\nFee {a} {c}{r}\n'
  244. usr_fee_prompt = 'Enter transaction fee: '
  245. fee_is_approximate = False
  246. fn_fee_unit = 'satoshi'
  247. view_sort_orders = ('addr','raw')
  248. dfl_view_sort_order = 'addr'
  249. msg_low_coin = 'Selected outputs insufficient to fund this transaction ({} {} needed)'
  250. msg_no_change_output = """
  251. ERROR: No change address specified. If you wish to create a transaction with
  252. only one output, specify a single output address with no {} amount
  253. """.strip()
  254. msg_non_mmgen_inputs = """
  255. NOTE: This transaction includes non-{pnm} inputs, which makes the signing
  256. process more complicated. When signing the transaction, keys for non-{pnm}
  257. inputs must be supplied to '{pnl}-txsign' in a file with the '--keys-from-file'
  258. option.
  259. Selected non-{pnm} inputs: {{}}""".strip().format(pnm=g.proj_name,pnl=g.proj_name.lower())
  260. def __init__(self,filename=None,metadata_only=False,caller=None,quiet_open=False):
  261. self.inputs = MMGenTxInputList()
  262. self.outputs = MMGenTxOutputList()
  263. self.send_amt = g.proto.coin_amt('0') # total amt minus change
  264. self.fee = g.proto.coin_amt('0')
  265. self.hex = '' # raw serialized hex transaction
  266. self.label = MMGenTXLabel('')
  267. self.txid = ''
  268. self.coin_txid = ''
  269. self.timestamp = ''
  270. self.chksum = ''
  271. self.fmt_data = ''
  272. self.fn = ''
  273. self.blockcount = 0
  274. self.chain = None
  275. self.coin = None
  276. self.dcoin = None
  277. self.caller = caller
  278. self.locktime = None
  279. if filename:
  280. self.parse_tx_file(filename,metadata_only=metadata_only,quiet_open=quiet_open)
  281. if metadata_only: return
  282. self.check_pubkey_scripts()
  283. self.check_sigs() # marks the tx as signed
  284. # repeat with sign and send, because coin daemon could be restarted
  285. self.check_correct_chain(on_fail='die')
  286. def check_correct_chain(self,on_fail='return'):
  287. assert on_fail in ('return','die'),"'{}': invalid value for 'on_fail'".format(on_fail)
  288. m = 'Transaction is for {}, but current chain is {}!'.format(self.chain,g.chain)
  289. bad = self.chain and g.chain and self.chain != g.chain
  290. if bad and hasattr(g.proto,'chain_name'):
  291. bad = self.chain != g.proto.chain_name
  292. if bad:
  293. msg(m) if on_fail == 'return' else die(2,m)
  294. return not bad
  295. def add_output(self,coinaddr,amt,is_chg=None):
  296. self.outputs.append(MMGenTxOutput(addr=coinaddr,amt=amt,is_chg=is_chg))
  297. def get_chg_output_idx(self):
  298. ch_ops = [x.is_chg for x in self.outputs]
  299. try:
  300. return ch_ops.index(True)
  301. except ValueError:
  302. return None
  303. def update_output_amt(self,idx,amt):
  304. o = self.outputs[idx].__dict__
  305. o['amt'] = amt
  306. self.outputs[idx] = MMGenTxOutput(**o)
  307. def update_change_output(self,change_amt):
  308. chg_idx = self.get_chg_output_idx()
  309. if change_amt == 0:
  310. msg(self.no_chg_msg)
  311. self.del_output(chg_idx)
  312. else:
  313. self.update_output_amt(chg_idx,g.proto.coin_amt(change_amt))
  314. def del_output(self,idx):
  315. self.outputs.pop(idx)
  316. def sum_outputs(self,exclude=None):
  317. if not len(self.outputs): return g.proto.coin_amt('0')
  318. olist = self.outputs if exclude == None else \
  319. self.outputs[:exclude] + self.outputs[exclude+1:]
  320. return g.proto.coin_amt(sum(e.amt for e in olist))
  321. def add_mmaddrs_to_outputs(self,ad_w,ad_f):
  322. a = [e.addr for e in self.outputs]
  323. d = ad_w.make_reverse_dict(a)
  324. if ad_f:
  325. d.update(ad_f.make_reverse_dict(a))
  326. for e in self.outputs:
  327. if e.addr and e.addr in d:
  328. e.mmid,f = d[e.addr]
  329. if f: e.label = f
  330. def check_dup_addrs(self,io_str):
  331. assert io_str in ('inputs','outputs')
  332. addrs = [e.addr for e in getattr(self,io_str)]
  333. if len(addrs) != len(set(addrs)):
  334. die(2,'{}: duplicate address in transaction {}'.format(attr,io_str))
  335. def update_txid(self):
  336. self.txid = MMGenTxID(make_chksum_6(bytes.fromhex(self.hex)).upper())
  337. def create_raw(self):
  338. i = [{'txid':e.txid,'vout':e.vout} for e in self.inputs]
  339. if self.inputs[0].sequence:
  340. i[0]['sequence'] = self.inputs[0].sequence
  341. o = {e.addr:e.amt for e in self.outputs}
  342. self.hex = HexStr(g.rpch.createrawtransaction(i,o))
  343. self.update_txid()
  344. # returns true if comment added or changed
  345. def add_comment(self,infile=None):
  346. if infile:
  347. self.label = MMGenTXLabel(get_data_from_file(infile,'transaction comment'))
  348. else: # get comment from user, or edit existing comment
  349. m = ('Add a comment to transaction?','Edit transaction comment?')[bool(self.label)]
  350. if keypress_confirm(m,default_yes=False):
  351. while True:
  352. s = MMGenTXLabel(my_raw_input('Comment: ',insert_txt=self.label))
  353. if s:
  354. lbl_save = self.label
  355. self.label = s
  356. return (True,False)[lbl_save == self.label]
  357. else:
  358. msg('Invalid comment')
  359. return False
  360. def edit_comment(self):
  361. return self.add_comment(self)
  362. def get_fee_from_tx(self):
  363. return self.sum_inputs() - self.sum_outputs()
  364. def has_segwit_inputs(self):
  365. return any(i.mmid and i.mmid.mmtype in ('S','B') for i in self.inputs)
  366. def compare_size_and_estimated_size(self):
  367. est_vsize = self.estimate_size()
  368. d = g.rpch.decoderawtransaction(self.hex)
  369. vsize = d['vsize'] if 'vsize' in d else d['size']
  370. vmsg('\nSize: {}, Vsize: {} (true) {} (estimated)'.format(d['size'],vsize,est_vsize))
  371. m1 = 'Estimated transaction vsize is {:1.2f} times the true vsize\n'
  372. m2 = 'Your transaction fee estimates will be inaccurate\n'
  373. m3 = 'Please re-create and re-sign the transaction using the option --vsize-adj={:1.2f}'
  374. # allow for 5% error
  375. ratio = float(est_vsize) / vsize
  376. if not (0.95 < ratio < 1.05):
  377. raise BadTxSizeEstimate((m1+m2+m3).format(ratio,1/ratio))
  378. # https://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending
  379. # 180: uncompressed, 148: compressed
  380. def estimate_size_old(self):
  381. if not self.inputs or not self.outputs: return None
  382. return len(self.inputs)*180 + len(self.outputs)*34 + 10
  383. # https://bitcoincore.org/en/segwit_wallet_dev/
  384. # vsize: 3 times of the size with original serialization, plus the size with new
  385. # serialization, divide the result by 4 and round up to the next integer.
  386. # TODO: results differ slightly from actual transaction size
  387. def estimate_size(self):
  388. if not self.inputs or not self.outputs: return None
  389. sig_size = 72 # sig in DER format
  390. pubkey_size_uncompressed = 65
  391. pubkey_size_compressed = 33
  392. def get_inputs_size():
  393. # txid vout [scriptSig size (vInt)] scriptSig (<sig> <pubkey>) nSeq
  394. isize_common = 32 + 4 + 1 + 4 # txid vout [scriptSig size] nSeq = 41
  395. input_size = {
  396. 'L': isize_common + sig_size + pubkey_size_uncompressed, # = 180
  397. 'C': isize_common + sig_size + pubkey_size_compressed, # = 148
  398. 'S': isize_common + 23, # = 64
  399. 'B': isize_common + 0 # = 41
  400. }
  401. ret = sum(input_size[i.mmid.mmtype] for i in self.inputs if i.mmid)
  402. # We have no way of knowing whether a non-MMGen addr is compressed or uncompressed until
  403. # we see the key, so assume compressed for fee-estimation purposes. If fee estimate is
  404. # off by more than 5%, sign() aborts and user is instructed to use --vsize-adj option
  405. return ret + sum(input_size['C'] for i in self.inputs if not i.mmid)
  406. def get_outputs_size():
  407. # output bytes = amt: 8, byte_count: 1+, pk_script
  408. # pk_script bytes: p2pkh: 25, p2sh: 23, bech32: 22
  409. return sum({'p2pkh':34,'p2sh':32,'bech32':31}[o.addr.addr_fmt] for o in self.outputs)
  410. # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
  411. # The witness is a serialization of all witness data of the transaction. Each txin is
  412. # associated with a witness field. A witness field starts with a var_int to indicate the
  413. # number of stack items for the txin. It is followed by stack items, with each item starts
  414. # with a var_int to indicate the length. Witness data is NOT script.
  415. # A non-witness program txin MUST be associated with an empty witness field, represented
  416. # by a 0x00. If all txins are not witness program, a transaction's wtxid is equal to its txid.
  417. def get_witness_size():
  418. if not self.has_segwit_inputs(): return 0
  419. wf_size = 1 + 1 + sig_size + 1 + pubkey_size_compressed # vInt vInt sig vInt pubkey = 108
  420. return sum((1,wf_size)[bool(i.mmid) and i.mmid.mmtype in ('S','B')] for i in self.inputs)
  421. isize = get_inputs_size()
  422. osize = get_outputs_size()
  423. wsize = get_witness_size()
  424. # TODO: compute real varInt sizes instead of assuming 1 byte
  425. # old serialization: [nVersion] [vInt][txins][vInt][txouts] [nLockTime]
  426. old_size = 4 + 1 + isize + 1 + osize + 4
  427. # marker = 0x00, flag = 0x01
  428. # new serialization: [nVersion][marker][flag][vInt][txins][vInt][txouts][witness][nLockTime]
  429. new_size = 4 + 1 + 1 + 1 + isize + 1 + osize + wsize + 4 \
  430. if wsize else old_size
  431. ret = (old_size * 3 + new_size) // 4
  432. dmsg('\nData from estimate_size():')
  433. dmsg(' inputs size: {}, outputs size: {}, witness size: {}'.format(isize,osize,wsize))
  434. dmsg(' size: {}, vsize: {}, old_size: {}'.format(new_size,ret,old_size))
  435. return int(ret * float(opt.vsize_adj)) if hasattr(opt,'vsize_adj') and opt.vsize_adj else ret
  436. # coin-specific fee routines
  437. def get_relay_fee(self):
  438. kb_fee = g.proto.coin_amt(g.rpch.getnetworkinfo()['relayfee'])
  439. ret = kb_fee * self.estimate_size() // 1024
  440. vmsg('Relay fee: {} {c}/kB, for transaction: {} {c}'.format(kb_fee,ret,c=g.coin))
  441. return ret
  442. # convert absolute BTC fee to satoshis-per-byte using estimated size
  443. def fee_abs2rel(self,abs_fee,to_unit=None):
  444. unit = getattr(g.proto.coin_amt,to_unit or 'min_coin_unit')
  445. return int(abs_fee // unit // self.estimate_size())
  446. def get_rel_fee_from_network(self): # rel_fee is in BTC/kB
  447. try:
  448. ret = g.rpch.estimatesmartfee(opt.tx_confs)
  449. rel_fee = ret['feerate'] if 'feerate' in ret else -2
  450. fe_type = 'estimatesmartfee'
  451. except:
  452. rel_fee = g.rpch.estimatefee(opt.tx_confs)
  453. fe_type = 'estimatefee'
  454. return rel_fee,fe_type
  455. # given tx size, rel fee and units, return absolute fee
  456. def convert_fee_spec(self,tx_size,units,amt,unit):
  457. self.usr_rel_fee = None # TODO
  458. return g.proto.coin_amt(int(amt)*tx_size*getattr(g.proto.coin_amt,units[unit])) \
  459. if tx_size else None
  460. # given network fee estimate in BTC/kB, return absolute fee using estimated tx size
  461. def fee_est2abs(self,rel_fee,fe_type=None):
  462. tx_size = self.estimate_size()
  463. ret = g.proto.coin_amt(rel_fee) * opt.tx_fee_adj * tx_size // 1024
  464. if opt.verbose:
  465. msg('{} fee for {} confirmations: {} {}/kB'.format(fe_type.upper(),opt.tx_confs,rel_fee,g.coin))
  466. msg('TX size (estimated): {}'.format(tx_size))
  467. return ret
  468. def convert_and_check_fee(self,tx_fee,desc='Missing description'):
  469. abs_fee = self.process_fee_spec(tx_fee,self.estimate_size(),on_fail='return')
  470. if abs_fee == None:
  471. # we shouldn't be calling this if tx size is unknown
  472. m = "'{}': cannot convert {} to {} because transaction size is unknown"
  473. assert False, m.format(tx_fee,self.rel_fee_desc,g.coin)
  474. elif abs_fee == False:
  475. m = "'{}': invalid TX fee (not a {} amount or {} specification)"
  476. msg(m.format(tx_fee,g.coin,self.rel_fee_desc))
  477. return False
  478. elif abs_fee > g.proto.max_tx_fee:
  479. m = '{} {c}: {} fee too large (maximum fee: {} {c})'
  480. msg(m.format(abs_fee,desc,g.proto.max_tx_fee,c=g.coin))
  481. return False
  482. elif abs_fee < self.get_relay_fee():
  483. m = '{} {c}: {} fee too small (below relay fee of {} {c})'
  484. msg(m.format(str(abs_fee),desc,str(self.get_relay_fee()),c=g.coin))
  485. return False
  486. else:
  487. return abs_fee
  488. # non-coin-specific fee routines
  489. # given tx size and absolute fee or fee spec, return absolute fee
  490. # relative fee is N+<first letter of unit name>
  491. def process_fee_spec(self,tx_fee,tx_size,on_fail='throw'):
  492. import re
  493. units = {u[0]:u for u in g.proto.coin_amt.units}
  494. pat = r'([1-9][0-9]*)({})'.format('|'.join(units.keys()))
  495. if g.proto.coin_amt(tx_fee,on_fail='silent'):
  496. return g.proto.coin_amt(tx_fee)
  497. elif re.match(pat,tx_fee):
  498. return self.convert_fee_spec(tx_size,units,*re.match(pat,tx_fee).groups())
  499. else:
  500. if on_fail == 'return':
  501. return False
  502. elif on_fail == 'throw':
  503. assert False, "'{}': invalid tx-fee argument".format(tx_fee)
  504. def get_usr_fee_interactive(self,tx_fee=None,desc='Starting'):
  505. abs_fee = None
  506. while True:
  507. if tx_fee:
  508. abs_fee = self.convert_and_check_fee(tx_fee,desc)
  509. if abs_fee:
  510. m = ('',' (after {}x adjustment)'.format(opt.tx_fee_adj))[opt.tx_fee_adj != 1]
  511. p = '{} TX fee{}: {}{} {} ({} {})\n'.format(
  512. desc,
  513. m,
  514. ('','≈')[self.fee_is_approximate],
  515. abs_fee.hl(),
  516. g.coin,
  517. pink(str(self.fee_abs2rel(abs_fee))),
  518. self.rel_fee_disp)
  519. if opt.yes or keypress_confirm(p+'OK?',default_yes=True):
  520. if opt.yes: msg(p)
  521. return abs_fee
  522. tx_fee = my_raw_input(self.usr_fee_prompt)
  523. desc = 'User-selected'
  524. def get_fee_from_user(self,have_estimate_fail=[]):
  525. if opt.tx_fee:
  526. desc = 'User-selected'
  527. start_fee = opt.tx_fee
  528. else:
  529. desc = 'Network-estimated'
  530. rel_fee,fe_type = self.get_rel_fee_from_network()
  531. if rel_fee < 0:
  532. if not have_estimate_fail:
  533. msg(self.fee_fail_fs.format(c=opt.tx_confs,t=fe_type))
  534. have_estimate_fail.append(True)
  535. start_fee = None
  536. else:
  537. start_fee = self.fee_est2abs(rel_fee,fe_type)
  538. return self.get_usr_fee_interactive(start_fee,desc=desc)
  539. def delete_attrs(self,desc,attr):
  540. for e in getattr(self,desc):
  541. if hasattr(e,attr): delattr(e,attr)
  542. # inputs methods
  543. def copy_inputs_from_tw(self,tw_unspent_data):
  544. self.inputs = MMGenTxInputList()
  545. MMGenTxInput() # throwaway instance to initialize cls.valid_attrs
  546. for d in tw_unspent_data:
  547. t = MMGenTxInput(**{attr:getattr(d,attr) for attr in d.__dict__ if attr in MMGenTxInput.valid_attrs})
  548. if d.twmmid.type == 'mmgen': t.mmid = d.twmmid # twmmid -> mmid
  549. self.inputs.append(t)
  550. def get_input_sids(self):
  551. return set(e.mmid.sid for e in self.inputs if e.mmid)
  552. def get_output_sids(self):
  553. return set(e.mmid.sid for e in self.outputs if e.mmid)
  554. def sum_inputs(self):
  555. return sum(e.amt for e in self.inputs)
  556. def add_timestamp(self):
  557. self.timestamp = make_timestamp()
  558. def get_hex_locktime(self):
  559. return int(bytes.fromhex(self.hex[-8:])[::-1].hex(),16)
  560. def set_hex_locktime(self,val):
  561. assert type(val) == int,'locktime value not an integer'
  562. self.hex = self.hex[:-8] + bytes.fromhex('{:08x}'.format(val))[::-1].hex()
  563. def get_blockcount(self):
  564. return int(g.rpch.getblockcount())
  565. def add_blockcount(self):
  566. self.blockcount = self.get_blockcount()
  567. def format(self):
  568. self.inputs.check_coin_mismatch()
  569. self.outputs.check_coin_mismatch()
  570. def amt_to_str(d):
  571. return {k: (str(d[k]) if k == 'amt' else d[k]) for k in d}
  572. coin_id = '' if g.coin == 'BTC' else g.coin + ('' if g.coin == g.dcoin else ':'+g.dcoin)
  573. lines = [
  574. '{}{} {} {} {} {}{}'.format(
  575. (coin_id+' ' if coin_id else ''),
  576. self.chain.upper() if self.chain else 'Unknown',
  577. self.txid,
  578. self.send_amt,
  579. self.timestamp,
  580. self.blockcount,
  581. ('',' LT={}'.format(self.locktime))[bool(self.locktime)]
  582. ),
  583. self.hex,
  584. repr([amt_to_str(e.__dict__) for e in self.inputs]),
  585. repr([amt_to_str(e.__dict__) for e in self.outputs])
  586. ]
  587. if self.label:
  588. lines.append(baseconv.b58encode(self.label.encode()))
  589. if self.coin_txid:
  590. if not self.label: lines.append('-') # keep old tx files backwards compatible
  591. lines.append(self.coin_txid)
  592. self.chksum = make_chksum_6(' '.join(lines))
  593. self.fmt_data = '\n'.join([self.chksum] + lines)+'\n'
  594. assert len(self.fmt_data) <= g.max_tx_file_size,(
  595. 'Transaction file size exceeds limit ({} bytes)'.format(g.max_tx_file_size))
  596. def get_non_mmaddrs(self,desc):
  597. return {i.addr for i in getattr(self,desc) if not i.mmid}
  598. def sign(self,tx_num_str,keys): # return True or False; don't exit or raise exception
  599. if self.marked_signed():
  600. msg('Transaction is already signed!')
  601. return False
  602. if not self.check_correct_chain(on_fail='return'):
  603. return False
  604. if (self.has_segwit_inputs() or self.has_segwit_outputs()) and not g.proto.cap('segwit'):
  605. ymsg("TX has Segwit inputs or outputs, but {} doesn't support Segwit!".format(g.coin))
  606. return False
  607. self.check_pubkey_scripts()
  608. qmsg('Passing {} key{} to {}'.format(len(keys),suf(keys,'s'),g.proto.daemon_name))
  609. if self.has_segwit_inputs():
  610. from mmgen.addr import KeyGenerator,AddrGenerator
  611. kg = KeyGenerator('std')
  612. ag = AddrGenerator('segwit')
  613. keydict = MMGenDict([(d.addr,d.sec) for d in keys])
  614. sig_data = []
  615. for d in self.inputs:
  616. e = {k:getattr(d,k) for k in ('txid','vout','scriptPubKey','amt')}
  617. e['amount'] = e['amt']
  618. del e['amt']
  619. if d.mmid and d.mmid.mmtype == 'S':
  620. e['redeemScript'] = ag.to_segwit_redeem_script(kg.to_pubhex(keydict[d.addr]))
  621. sig_data.append(e)
  622. msg_r('Signing transaction{}...'.format(tx_num_str))
  623. wifs = [d.sec.wif for d in keys]
  624. try:
  625. ret = g.rpch.signrawtransactionwithkey(self.hex,wifs,sig_data,g.proto.sighash_type) \
  626. if 'sign_with_key' in g.rpch.caps else \
  627. g.rpch.signrawtransaction(self.hex,sig_data,wifs,g.proto.sighash_type)
  628. except Exception as e:
  629. msg(yellow('This is not the BCH chain.\nRe-run the script without the --coin=bch option.'
  630. if 'Invalid sighash param' in e.args[0] else e.args[0]))
  631. return False
  632. if not ret['complete']:
  633. msg('failed\n{} returned the following errors:'.format(g.proto.daemon_name.capitalize()))
  634. msg(repr(ret['errors']))
  635. return False
  636. try:
  637. self.hex = HexStr(ret['hex'])
  638. self.compare_size_and_estimated_size()
  639. dt = DeserializedTX(self.hex)
  640. self.check_hex_tx_matches_mmgen_tx(dt)
  641. self.coin_txid = CoinTxID(dt['txid'],on_fail='raise')
  642. self.check_sigs(dt)
  643. if not self.coin_txid == g.rpch.decoderawtransaction(ret['hex'])['txid']:
  644. raise BadMMGenTxID('txid mismatch (after signing)')
  645. msg('OK')
  646. return True
  647. except Exception as e:
  648. if g.traceback:
  649. import traceback
  650. ymsg('\n'+''.join(traceback.format_exception(*sys.exc_info())))
  651. try: m = '{}'.format(e.args[0])
  652. except: m = repr(e.args[0])
  653. msg('\n'+yellow(m))
  654. return False
  655. def mark_raw(self):
  656. self.desc = 'transaction'
  657. self.ext = self.raw_ext
  658. def mark_signed(self): # called ONLY by check_sigs()
  659. self.desc = 'signed transaction'
  660. self.ext = self.sig_ext
  661. def marked_signed(self,color=False):
  662. ret = self.desc == 'signed transaction'
  663. return (red,green)[ret](str(ret)) if color else ret
  664. # check that a malicious, compromised or malfunctioning coin daemon hasn't altered hex tx data:
  665. # does not check witness or signature data
  666. def check_hex_tx_matches_mmgen_tx(self,deserial_tx):
  667. m = 'A malicious or malfunctioning coin daemon or other program may have altered your data!'
  668. lt = deserial_tx['lock_time']
  669. if lt != int(self.locktime or 0):
  670. m2 = 'Transaction hex locktime ({}) does not match MMGen transaction locktime ({})\n{}'
  671. raise TxHexMismatch(m2.format(lt,self.locktime,m))
  672. def check_equal(desc,hexio,mmio):
  673. if mmio != hexio:
  674. msg('\nMMGen {}:\n{}'.format(desc,pformat(mmio)))
  675. msg('Hex {}:\n{}'.format(desc,pformat(hexio)))
  676. m2 = '{} in hex transaction data from coin daemon do not match those in MMGen transaction!\n'
  677. raise TxHexMismatch((m2+m).format(desc.capitalize()))
  678. seq_hex = [int(i['nSeq'],16) for i in deserial_tx['txins']]
  679. seq_mmgen = [i.sequence or g.max_int for i in self.inputs]
  680. check_equal('sequence numbers',seq_hex,seq_mmgen)
  681. d_hex = sorted((i['txid'],i['vout']) for i in deserial_tx['txins'])
  682. d_mmgen = sorted((i.txid,i.vout) for i in self.inputs)
  683. check_equal('inputs',d_hex,d_mmgen)
  684. d_hex = sorted((o['address'],g.proto.coin_amt(o['amount'])) for o in deserial_tx['txouts'])
  685. d_mmgen = sorted((o.addr,o.amt) for o in self.outputs)
  686. check_equal('outputs',d_hex,d_mmgen)
  687. uh = deserial_tx['unsigned_hex']
  688. if str(self.txid) != make_chksum_6(bytes.fromhex(uh)).upper():
  689. raise TxHexMismatch('MMGen TxID ({}) does not match hex transaction data!\n{}'.format(self.txid,m))
  690. def check_pubkey_scripts(self):
  691. for n,i in enumerate(self.inputs,1):
  692. addr,fmt = scriptPubKey2addr(i.scriptPubKey)
  693. if i.addr != addr:
  694. if fmt != i.addr.addr_fmt:
  695. m = 'Address format of scriptPubKey ({}) does not match that of address ({}) in input #{}'
  696. msg(m.format(fmt,i.addr.addr_fmt,n))
  697. m = 'ERROR: Address and scriptPubKey of transaction input #{} do not match!'
  698. die(3,(m+'\n {:23}{}'*3).format(n, 'address:',i.addr,
  699. 'scriptPubKey:',i.scriptPubKey,
  700. 'scriptPubKey->address:',addr ))
  701. # check signature and witness data
  702. def check_sigs(self,deserial_tx=None): # return False if no sigs, raise exception on error
  703. txins = (deserial_tx or DeserializedTX(self.hex))['txins']
  704. has_ss = any(ti['scriptSig'] for ti in txins)
  705. has_witness = any('witness' in ti and ti['witness'] for ti in txins)
  706. if not (has_ss or has_witness):
  707. return False
  708. fs = "Hex TX has {} scriptSig but input is of type '{}'!"
  709. for n in range(len(txins)):
  710. ti,mmti = txins[n],self.inputs[n]
  711. if ti['scriptSig'] == '' or ( len(ti['scriptSig']) == 46 and # native P2WPKH or P2SH-P2WPKH
  712. ti['scriptSig'][:6] == '16' + g.proto.witness_vernum_hex + '14' ):
  713. assert 'witness' in ti, 'missing witness'
  714. assert type(ti['witness']) == list and len(ti['witness']) == 2, 'malformed witness'
  715. assert len(ti['witness'][1]) == 66, 'incorrect witness pubkey length'
  716. assert mmti.mmid, fs.format('witness-type','non-MMGen')
  717. assert mmti.mmid.mmtype == ('S','B')[ti['scriptSig']==''],(
  718. fs.format('witness-type',mmti.mmid.mmtype))
  719. else: # non-witness
  720. if mmti.mmid:
  721. assert mmti.mmid.mmtype not in ('S','B'), fs.format('signature in',mmti.mmid.mmtype)
  722. assert not 'witness' in ti, 'non-witness input has witness'
  723. # sig_size 72 (DER format), pubkey_size 'compressed':33, 'uncompressed':65
  724. assert (200 < len(ti['scriptSig']) < 300), 'malformed scriptSig' # VERY rough check
  725. self.mark_signed()
  726. return True
  727. def has_segwit_outputs(self):
  728. return any(o.mmid and o.mmid.mmtype in ('S','B') for o in self.outputs)
  729. def is_in_mempool(self):
  730. return 'size' in g.rpch.getmempoolentry(self.coin_txid,on_fail='silent')
  731. def is_in_wallet(self):
  732. ret = g.rpch.gettransaction(self.coin_txid,on_fail='silent')
  733. if 'confirmations' in ret and ret['confirmations'] > 0:
  734. return ret['confirmations']
  735. else:
  736. return False
  737. def is_replaced(self):
  738. if self.is_in_mempool(): return False
  739. ret = g.rpch.gettransaction(self.coin_txid,on_fail='silent')
  740. if not 'bip125-replaceable' in ret or not 'confirmations' in ret or ret['confirmations'] > 0:
  741. return False
  742. return -ret['confirmations'] + 1,ret # 1: replacement in mempool, 2: replacement confirmed
  743. def is_in_utxos(self):
  744. return 'txid' in g.rpch.getrawtransaction(self.coin_txid,True,on_fail='silent')
  745. def get_status(self,status=False):
  746. if self.is_in_mempool():
  747. if status:
  748. d = g.rpch.gettransaction(self.coin_txid,on_fail='silent')
  749. brs = 'bip125-replaceable'
  750. r = '{}replaceable'.format(('NOT ','')[brs in d and d[brs]=='yes'])
  751. t = d['timereceived']
  752. m = 'Sent {} ({} h/m/s ago)'
  753. b = m.format(time.strftime('%c',time.gmtime(t)),secs_to_dhms(int(time.time()-t)))
  754. if opt.quiet:
  755. msg('Transaction is in mempool')
  756. else:
  757. msg('TX status: in mempool, {}\n{}'.format(r,b))
  758. else:
  759. msg('Warning: transaction is in mempool!')
  760. elif self.is_in_wallet():
  761. confs = self.is_in_wallet()
  762. die(0,'Transaction has {} confirmation{}'.format(confs,suf(confs,'s')))
  763. elif self.is_in_utxos():
  764. die(2,red('ERROR: transaction is in the blockchain (but not in the tracking wallet)!'))
  765. else:
  766. ret = self.is_replaced() # ret[0]==1: replacement in mempool, ret[0]==2: replacement confirmed
  767. if ret and ret[0]:
  768. m1 = 'Transaction has been replaced'
  769. m2 = ('',', and the replacement TX is confirmed')[ret[0]==2]
  770. msg('{}{}!'.format(m1,m2))
  771. if not opt.quiet:
  772. msg('Replacing transactions:')
  773. rt = ret[1]['walletconflicts']
  774. for t,s in [(tx,'size' in g.rpch.getmempoolentry(tx,on_fail='silent')) for tx in rt]:
  775. msg(' {}{}'.format(t,('',' in mempool')[s]))
  776. die(0,'')
  777. def confirm_send(self):
  778. m1 = ("Once this transaction is sent, there's no taking it back!",'')[bool(opt.quiet)]
  779. m2 = 'broadcast this transaction to the {} network'.format(g.chain.upper())
  780. m3 = ('YES, I REALLY WANT TO DO THIS','YES')[bool(opt.quiet or opt.yes)]
  781. confirm_or_raise(m1,m2,m3)
  782. msg('Sending transaction')
  783. def send(self,prompt_user=True,exit_on_fail=False):
  784. if not self.marked_signed():
  785. die(1,'Transaction is not signed!')
  786. self.check_correct_chain(on_fail='die')
  787. self.check_pubkey_scripts()
  788. self.check_hex_tx_matches_mmgen_tx(DeserializedTX(self.hex))
  789. if self.has_segwit_outputs() and not segwit_is_active() and not g.bogus_send:
  790. m = 'Transaction has MMGen Segwit outputs, but this blockchain does not support Segwit'
  791. die(2,m+' at the current height')
  792. if self.get_fee_from_tx() > g.proto.max_tx_fee:
  793. die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format(
  794. self.get_fee_from_tx(),g.proto.name.capitalize(),g.proto.max_tx_fee,g.coin))
  795. self.get_status()
  796. if prompt_user: self.confirm_send()
  797. ret = None if g.bogus_send else g.rpch.sendrawtransaction(self.hex,on_fail='return')
  798. from mmgen.rpc import rpc_error,rpc_errmsg
  799. if rpc_error(ret):
  800. errmsg = rpc_errmsg(ret)
  801. if 'Signature must use SIGHASH_FORKID' in errmsg:
  802. m = 'The Aug. 1 2017 UAHF has activated on this chain.'
  803. m += "\nRe-run the script with the --coin=bch option."
  804. elif 'Illegal use of SIGHASH_FORKID' in errmsg:
  805. m = 'The Aug. 1 2017 UAHF is not yet active on this chain.'
  806. m += "\nRe-run the script without the --coin=bch option."
  807. elif '64: non-final' in errmsg:
  808. m2 = "Transaction with locktime '{}' can't be included in this block!"
  809. m = m2.format(strfmt_locktime(self.get_hex_locktime()))
  810. else:
  811. m = errmsg
  812. msg(yellow(m))
  813. msg(red('Send of MMGen transaction {} failed'.format(self.txid)))
  814. if exit_on_fail: sys.exit(1)
  815. return False
  816. else:
  817. if g.bogus_send:
  818. m = 'BOGUS transaction NOT sent: {}'
  819. else:
  820. assert ret == self.coin_txid, 'txid mismatch (after sending)'
  821. m = 'Transaction sent: {}'
  822. self.desc = 'sent transaction'
  823. msg(m.format(self.coin_txid.hl()))
  824. self.add_timestamp()
  825. self.add_blockcount()
  826. return True
  827. def write_txid_to_file(self,ask_write=False,ask_write_default_yes=True):
  828. fn = '{}[{}].{}'.format(self.txid,self.send_amt,self.txid_ext)
  829. write_data_to_file(fn,self.coin_txid+'\n','transaction ID',
  830. ask_write=ask_write,
  831. ask_write_default_yes=ask_write_default_yes)
  832. def create_fn(self):
  833. tl = self.get_hex_locktime()
  834. tn = ('','.testnet')[g.proto.is_testnet()]
  835. self.fn = '{}{}[{!s}{}{}]{x}{}.{}'.format(
  836. self.txid,
  837. ('-'+g.dcoin,'')[g.coin=='BTC'],
  838. self.send_amt,
  839. ('',',{}'.format(self.fee_abs2rel(
  840. self.get_fee_from_tx(),to_unit=self.fn_fee_unit))
  841. )[self.is_replaceable()],
  842. ('',',tl={}'.format(tl))[bool(tl)],
  843. tn,self.ext,
  844. x='-α' if g.debug_utf8 else '')
  845. def write_to_file( self,
  846. add_desc='',
  847. ask_write=True,
  848. ask_write_default_yes=False,
  849. ask_tty=True,
  850. ask_overwrite=True):
  851. if ask_write == False: ask_write_default_yes = True
  852. if not self.fmt_data: self.format()
  853. if not self.fn: self.create_fn()
  854. write_data_to_file(self.fn,self.fmt_data,self.desc+add_desc,
  855. ask_overwrite=ask_overwrite,
  856. ask_write=ask_write,
  857. ask_tty=ask_tty,
  858. ask_write_default_yes=ask_write_default_yes)
  859. def view_with_prompt(self,prompt=''):
  860. prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view'
  861. reply = prompt_and_get_char(prompt,'YyNnVvTt',enter_ok=True)
  862. if reply and reply in 'YyVvTt':
  863. self.view(pager=reply in 'Vv',terse=reply in 'Tt')
  864. def view(self,pager=False,pause=True,terse=False):
  865. o = self.format_view(terse=terse)
  866. if pager: do_pager(o)
  867. else:
  868. msg_r(o)
  869. from mmgen.term import get_char
  870. if pause:
  871. get_char('Press any key to continue: ')
  872. msg('')
  873. # def is_replaceable_from_rpc(self):
  874. # dec_tx = g.rpch.decoderawtransaction(self.hex)
  875. # return None < dec_tx['vin'][0]['sequence'] <= g.max_int - 2
  876. def is_replaceable(self):
  877. return self.inputs[0].sequence == g.max_int - 2
  878. def format_view_body(self,blockcount,nonmm_str,max_mmwid,enl,terse,sort):
  879. if sort not in self.view_sort_orders:
  880. m = '{!r}: invalid transaction view sort order. Valid options: {}'
  881. die(1,m.format(sort,','.join(self.view_sort_orders)))
  882. def format_io(desc):
  883. io = getattr(self,desc)
  884. ip = desc == 'inputs'
  885. out = desc.capitalize() + ':\n' + enl
  886. addr_w = max(len(e.addr) for e in io)
  887. confs_per_day = 60*60*24 // g.proto.secs_per_block
  888. io_sorted = {
  889. # prepend '/' (sorts before '0') to ensure non-MMGen addrs are displayed first
  890. 'addr': lambda: sorted(io,key=lambda o: o.mmid.sort_key if o.mmid else '/'+o.addr),
  891. 'raw': lambda: io
  892. }[sort]
  893. for n,e in enumerate(io_sorted()):
  894. if ip and blockcount:
  895. confs = e.confs + blockcount - self.blockcount
  896. days = int(confs // confs_per_day)
  897. if e.mmid:
  898. mmid_fmt = e.mmid.fmt(
  899. width=max_mmwid,
  900. encl='()',
  901. color=True,
  902. append_chars=('',' (chg)')[bool(not ip and e.is_chg and terse)],
  903. append_color='green')
  904. else:
  905. mmid_fmt = MMGenID.fmtc(nonmm_str,width=max_mmwid,color=True)
  906. if terse:
  907. out += '{:3} {} {} {} {}\n'.format(n+1,
  908. e.addr.fmt(color=True,width=addr_w),
  909. mmid_fmt,e.amt.hl(),g.dcoin)
  910. else:
  911. icommon = [
  912. ((n+1,'')[ip],'address:',e.addr.fmt(color=True,width=addr_w) + ' '+mmid_fmt),
  913. ('','comment:',e.label.hl() if e.label else ''),
  914. ('','amount:','{} {}'.format(e.amt.hl(),g.dcoin))]
  915. items = [(n+1, 'tx,vout:','{},{}'.format(e.txid,e.vout))] + icommon + [
  916. ('','confirmations:','{} (around {} days)'.format(confs,days) if blockcount else '')
  917. ] if ip else icommon + [
  918. ('','change:',green('True') if e.is_chg else '')]
  919. out += '\n'.join([('{:>3} {:<8} {}'.format(*d)) for d in items if d[2]]) + '\n\n'
  920. return out
  921. md = {'raw':'raw','addr':'address'}
  922. m = 'Displaying inputs and outputs in {} sort order'.format(md[sort])
  923. return m + ('\n\n','\n')[terse] + format_io('inputs') + format_io('outputs')
  924. def format_view_rel_fee(self,terse):
  925. return ' ({} {})\n'.format(
  926. pink(str(self.fee_abs2rel(self.get_fee_from_tx()))),
  927. self.rel_fee_disp)
  928. def format_view_abs_fee(self):
  929. return g.proto.coin_amt(self.get_fee_from_tx()).hl()
  930. def format_view_verbose_footer(self):
  931. ts = len(self.hex)//2 if self.hex else 'unknown'
  932. out = 'Transaction size: Vsize {} (estimated), Total {}'.format(self.estimate_size(),ts)
  933. if self.marked_signed():
  934. ws = DeserializedTX(self.hex)['witness_size']
  935. out += ', Base {}, Witness {}'.format(ts-ws,ws)
  936. return out + '\n'
  937. def format_view(self,terse=False,sort=dfl_view_sort_order):
  938. blockcount = None
  939. if g.proto.base_coin != 'ETH':
  940. try:
  941. rpc_init()
  942. blockcount = self.get_blockcount()
  943. except:
  944. pass
  945. def get_max_mmwid(io):
  946. if io == self.inputs:
  947. sel_f = lambda o: len(o.mmid) + 2 # len('()')
  948. else:
  949. sel_f = lambda o: len(o.mmid) + (2,8)[bool(o.is_chg)] # + len(' (chg)')
  950. return max(max([sel_f(o) for o in io if o.mmid] or [0]),len(nonmm_str))
  951. nonmm_str = '(non-{} address)'.format(g.proj_name)
  952. max_mmwid = max(get_max_mmwid(self.inputs),get_max_mmwid(self.outputs))
  953. out = (self.txview_hdr_fs,self.txview_hdr_fs_short)[bool(terse)].format(
  954. i=self.txid.hl(),
  955. a=self.send_amt.hl(),
  956. c=g.dcoin,
  957. t=self.timestamp,
  958. r=(red('False'),green('True'))[self.is_replaceable()],
  959. s=self.marked_signed(color=True),
  960. l=(green('None'),orange(strfmt_locktime(self.locktime,terse=True)))[bool(self.locktime)])
  961. if self.chain != 'mainnet':
  962. out += green('Chain: {}\n'.format(self.chain.upper()))
  963. if self.coin_txid:
  964. out += '{} TxID: {}\n'.format(g.coin,self.coin_txid.hl())
  965. enl = ('\n','')[bool(terse)]
  966. out += enl
  967. if self.label:
  968. out += 'Comment: {}\n{}'.format(self.label.hl(),enl)
  969. out += self.format_view_body(blockcount,nonmm_str,max_mmwid,enl,terse=terse,sort=sort)
  970. out += (self.txview_ftr_fs,self.txview_ftr_fs_short)[bool(terse)].format(
  971. i=self.sum_inputs().hl(),
  972. o=self.sum_outputs().hl(),
  973. a=self.format_view_abs_fee(),
  974. r=self.format_view_rel_fee(terse),
  975. d=g.dcoin,c=g.coin)
  976. if opt.verbose: out += self.format_view_verbose_footer()
  977. return out # TX label might contain non-ascii chars
  978. def check_txfile_hex_data(self):
  979. self.hex = HexStr(self.hex,on_fail='raise')
  980. def parse_tx_file(self,infile,metadata_only=False,quiet_open=False):
  981. def eval_io_data(raw_data,desc):
  982. from ast import literal_eval
  983. try:
  984. d = literal_eval(raw_data)
  985. except:
  986. if desc == 'inputs' and not quiet_open:
  987. ymsg('Warning: transaction data appears to be in old format')
  988. import re
  989. d = literal_eval(re.sub(r"[A-Za-z]+?\(('.+?')\)",r'\1',raw_data))
  990. assert type(d) == list,'{} data not a list!'.format(desc)
  991. if not (desc == 'outputs' and g.proto.base_coin == 'ETH'): # ETH txs can have no outputs
  992. assert len(d),'no {}!'.format(desc)
  993. for e in d: e['amt'] = g.proto.coin_amt(e['amt'])
  994. io,io_list = (
  995. (MMGenTxOutput,MMGenTxOutputList),
  996. (MMGenTxInput,MMGenTxInputList)
  997. )[desc=='inputs']
  998. return io_list([io(**e) for e in d])
  999. tx_data = get_data_from_file(infile,self.desc+' data',quiet=quiet_open)
  1000. try:
  1001. desc = 'data'
  1002. assert len(tx_data) <= g.max_tx_file_size,(
  1003. 'Transaction file size exceeds limit ({} bytes)'.format(g.max_tx_file_size))
  1004. tx_data = tx_data.splitlines()
  1005. assert len(tx_data) >= 5,'number of lines less than 5'
  1006. assert len(tx_data[0]) == 6,'invalid length of first line'
  1007. self.chksum = HexStr(tx_data.pop(0),on_fail='raise')
  1008. assert self.chksum == make_chksum_6(' '.join(tx_data)),'file data does not match checksum'
  1009. if len(tx_data) == 6:
  1010. assert len(tx_data[-1]) == 64,'invalid coin TxID length'
  1011. desc = '{} TxID'.format(g.proto.name.capitalize())
  1012. self.coin_txid = CoinTxID(tx_data.pop(-1),on_fail='raise')
  1013. if len(tx_data) == 5:
  1014. # rough check: allow for 4-byte utf8 characters + base58 (4 * 11 / 8 = 6 (rounded up))
  1015. assert len(tx_data[-1]) < MMGenTXLabel.max_len*6,'invalid comment length'
  1016. c = tx_data.pop(-1)
  1017. if c != '-':
  1018. desc = 'encoded comment (not base58)'
  1019. comment = baseconv.b58decode(c).decode('utf8')
  1020. assert comment != False,'invalid comment'
  1021. desc = 'comment'
  1022. self.label = MMGenTXLabel(comment,on_fail='raise')
  1023. desc = 'number of lines' # four required lines
  1024. metadata,self.hex,inputs_data,outputs_data = tx_data
  1025. assert len(metadata) < 100,'invalid metadata length' # rough check
  1026. metadata = metadata.split()
  1027. if metadata[-1].find('LT=') == 0:
  1028. desc = 'locktime'
  1029. self.locktime = int(metadata.pop()[3:])
  1030. self.coin = metadata.pop(0) if len(metadata) == 6 else 'BTC'
  1031. if ':' in self.coin:
  1032. self.coin,self.dcoin = self.coin.split(':')
  1033. if len(metadata) == 5:
  1034. t = metadata.pop(0)
  1035. self.chain = (t.lower(),None)[t=='Unknown']
  1036. desc = 'metadata (4 items minimum required)'
  1037. txid,send_amt,self.timestamp,blockcount = metadata
  1038. desc = 'txid in metadata'
  1039. self.txid = MMGenTxID(txid,on_fail='raise')
  1040. desc = 'send amount in metadata'
  1041. self.send_amt = UnknownCoinAmt(send_amt) # temporary, for 'metadata_only'
  1042. desc = 'block count in metadata'
  1043. self.blockcount = int(blockcount)
  1044. if metadata_only: return
  1045. desc = 'send amount in metadata'
  1046. self.send_amt = g.proto.coin_amt(send_amt,on_fail='raise')
  1047. desc = 'transaction hex data'
  1048. self.check_txfile_hex_data()
  1049. # the following ops will all fail if g.coin doesn't match self.coin
  1050. desc = 'coin type in metadata'
  1051. assert self.coin == g.coin,self.coin
  1052. desc = 'inputs data'
  1053. self.inputs = eval_io_data(inputs_data,'inputs')
  1054. desc = 'outputs data'
  1055. self.outputs = eval_io_data(outputs_data,'outputs')
  1056. except Exception as e:
  1057. die(2,'Invalid {} in transaction file: {}'.format(desc,e.args[0]))
  1058. # test doesn't work for Ethereum: test and mainnet addrs have same format
  1059. if not self.chain and not self.inputs[0].addr.is_for_chain('testnet'):
  1060. self.chain = 'mainnet'
  1061. if self.dcoin: self.set_g_token()
  1062. def process_cmd_arg(self,arg,ad_f,ad_w):
  1063. def add_output_chk(addr,amt,err_desc):
  1064. if not amt and self.get_chg_output_idx() != None:
  1065. die(2,'ERROR: More than one change address listed on command line')
  1066. if is_mmgen_id(addr) or is_coin_addr(addr):
  1067. coin_addr = mmaddr2coinaddr(addr,ad_w,ad_f) if is_mmgen_id(addr) else CoinAddr(addr)
  1068. self.add_output(coin_addr,g.proto.coin_amt(amt or '0'),is_chg=not amt)
  1069. else:
  1070. die(2,"{}: invalid {} '{}'".format(addr,err_desc,','.join((addr,amt)) if amt else addr))
  1071. if ',' in arg:
  1072. addr,amt = arg.split(',',1)
  1073. add_output_chk(addr,amt,'coin argument in command-line argument')
  1074. else:
  1075. add_output_chk(arg,None,'command-line argument')
  1076. def process_cmd_args(self,cmd_args,ad_f,ad_w):
  1077. for a in cmd_args: self.process_cmd_arg(a,ad_f,ad_w)
  1078. if self.get_chg_output_idx() == None:
  1079. die(2,( 'ERROR: No change output specified',
  1080. self.msg_no_change_output.format(g.dcoin))[len(self.outputs) == 1])
  1081. if not segwit_is_active() and self.has_segwit_outputs():
  1082. fs = '{} Segwit address requested on the command line, but Segwit is not active on this chain'
  1083. rdie(2,fs.format(g.proj_name))
  1084. if not self.outputs:
  1085. die(2,'At least one output must be specified on the command line')
  1086. def get_outputs_from_cmdline(self,cmd_args):
  1087. from mmgen.addr import AddrList,AddrData
  1088. addrfiles = [a for a in cmd_args if get_extension(a) == AddrList.ext]
  1089. cmd_args = set(cmd_args) - set(addrfiles)
  1090. ad_f = AddrData()
  1091. for a in addrfiles:
  1092. check_infile(a)
  1093. ad_f.add(AddrList(a))
  1094. ad_w = AddrData(source='tw')
  1095. self.process_cmd_args(cmd_args,ad_f,ad_w)
  1096. self.add_mmaddrs_to_outputs(ad_w,ad_f)
  1097. self.check_dup_addrs('outputs')
  1098. def select_unspent(self,unspent):
  1099. prompt = 'Enter a range or space-separated list of outputs to spend: '
  1100. while True:
  1101. reply = my_raw_input(prompt).strip()
  1102. if reply:
  1103. selected = AddrIdxList(fmt_str=','.join(reply.split()),on_fail='return')
  1104. if selected:
  1105. if selected[-1] <= len(unspent):
  1106. return selected
  1107. msg('Unspent output number must be <= {}'.format(len(unspent)))
  1108. def check_sufficient_funds(self,inputs_sum,foo):
  1109. if self.send_amt > inputs_sum:
  1110. msg(self.msg_low_coin.format(self.send_amt-inputs_sum,g.coin))
  1111. return False
  1112. return True
  1113. def get_change_amt(self):
  1114. return self.sum_inputs() - self.send_amt - self.fee
  1115. def warn_insufficient_chg(self,change_amt):
  1116. msg(self.msg_low_coin.format(g.proto.coin_amt(-change_amt).hl(),g.coin))
  1117. def final_inputs_ok_msg(self,change_amt):
  1118. m = 'Transaction produces {} {} in change'
  1119. return m.format(g.proto.coin_amt(change_amt).hl(),g.coin)
  1120. def select_unspent_cmdline(self,unspent):
  1121. sel_nums = []
  1122. for i in opt.inputs.split(','):
  1123. ls = len(sel_nums)
  1124. if is_mmgen_id(i):
  1125. for j in range(len(unspent)):
  1126. if unspent[j].twmmid == i:
  1127. sel_nums.append(j+1)
  1128. elif is_coin_addr(i):
  1129. for j in range(len(unspent)):
  1130. if unspent[j].addr == i:
  1131. sel_nums.append(j+1)
  1132. else:
  1133. die(1,"'{}': not an MMGen ID or coin address".format(i))
  1134. ldiff = len(sel_nums) - ls
  1135. if ldiff:
  1136. sel_inputs = ','.join([str(i) for i in sel_nums[-ldiff:]])
  1137. ul = unspent[sel_nums[-1]-1]
  1138. mmid_disp = ' (' + ul.twmmid + ')' if ul.twmmid.type == 'mmgen' else ''
  1139. msg('Adding input{}: {} {}{}'.format(suf(ldiff),sel_inputs,ul.addr,mmid_disp))
  1140. else:
  1141. die(1,"'{}': address not found in tracking wallet".format(i))
  1142. return set(sel_nums) # silently discard duplicates
  1143. def get_inputs_from_user(self,tw):
  1144. while True:
  1145. us_f = ('select_unspent','select_unspent_cmdline')[bool(opt.inputs)]
  1146. sel_nums = getattr(self,us_f)(tw.unspent)
  1147. msg('Selected output{}: {}'.format(suf(sel_nums,'s'),' '.join(map(str,sel_nums))))
  1148. sel_unspent = tw.MMGenTwOutputList([tw.unspent[i-1] for i in sel_nums])
  1149. inputs_sum = sum(s.amt for s in sel_unspent)
  1150. if not self.check_sufficient_funds(inputs_sum,sel_unspent):
  1151. continue
  1152. non_mmaddrs = [i for i in sel_unspent if i.twmmid.type == 'non-mmgen']
  1153. if non_mmaddrs and self.caller != 'txdo':
  1154. msg(self.msg_non_mmgen_inputs.format(
  1155. ', '.join(sorted({a.addr.hl() for a in non_mmaddrs}))))
  1156. if not (opt.yes or keypress_confirm('Accept?')):
  1157. continue
  1158. self.copy_inputs_from_tw(sel_unspent) # makes self.inputs
  1159. self.fee = self.get_fee_from_user()
  1160. change_amt = self.get_change_amt()
  1161. if change_amt >= 0: # TODO: show both ETH and token amts remaining
  1162. p = self.final_inputs_ok_msg(change_amt)
  1163. if opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
  1164. if opt.yes: msg(p)
  1165. return change_amt
  1166. else:
  1167. self.warn_insufficient_chg(change_amt)
  1168. def check_fee(self):
  1169. assert self.sum_inputs() - self.sum_outputs() <= g.proto.max_tx_fee
  1170. def update_send_amt(self,change_amt):
  1171. if not self.send_amt:
  1172. self.send_amt = change_amt
  1173. def create(self,cmd_args,locktime,do_info=False):
  1174. assert type(locktime) == int
  1175. if opt.comment_file: self.add_comment(opt.comment_file)
  1176. if not do_info: self.get_outputs_from_cmdline(cmd_args)
  1177. do_license_msg()
  1178. from mmgen.tw import TwUnspentOutputs
  1179. tw = TwUnspentOutputs(minconf=opt.minconf)
  1180. if not opt.inputs:
  1181. tw.view_and_sort(self)
  1182. tw.display_total()
  1183. if do_info: sys.exit(0)
  1184. self.send_amt = self.sum_outputs()
  1185. msg('Total amount to spend: {}'.format(
  1186. ('Unknown','{} {}'.format(self.send_amt.hl(),g.dcoin))[bool(self.send_amt)]
  1187. ))
  1188. change_amt = self.get_inputs_from_user(tw)
  1189. self.update_change_output(change_amt)
  1190. self.update_send_amt(change_amt)
  1191. if g.proto.base_proto == 'Bitcoin':
  1192. if opt.rbf: self.inputs[0].sequence = g.max_int - 2 # handles the locktime case too
  1193. elif locktime: self.inputs[0].sequence = g.max_int - 1
  1194. if not opt.yes:
  1195. self.add_comment() # edits an existing comment
  1196. self.create_raw() # creates self.hex, self.txid
  1197. if g.proto.base_proto == 'Bitcoin' and locktime:
  1198. msg('Setting nlocktime to {}!'.format(strfmt_locktime(locktime)))
  1199. self.set_hex_locktime(locktime)
  1200. self.update_txid()
  1201. self.locktime = locktime
  1202. self.add_timestamp()
  1203. self.add_blockcount()
  1204. self.chain = g.chain
  1205. self.check_fee()
  1206. qmsg('Transaction successfully created')
  1207. if not opt.yes:
  1208. self.view_with_prompt('View decoded transaction?')
  1209. class MMGenBumpTX(MMGenTX):
  1210. def __new__(cls,*args,**kwargs):
  1211. return MMGenTX.__new__(altcoin_subclass(cls,'tx','MMGenBumpTX'),*args,**kwargs)
  1212. min_fee = None
  1213. bump_output_idx = None
  1214. def __init__(self,filename,send=False):
  1215. super(MMGenBumpTX,self).__init__(filename)
  1216. if not self.is_replaceable():
  1217. die(1,"Transaction '{}' is not replaceable".format(self.txid))
  1218. # If sending, require tx to have been signed
  1219. if send:
  1220. if not self.marked_signed():
  1221. die(1,"File '{}' is not a signed {} transaction file".format(filename,g.proj_name))
  1222. if not self.coin_txid:
  1223. die(1,"Transaction '{}' was not broadcast to the network".format(self.txid))
  1224. self.coin_txid = ''
  1225. self.mark_raw()
  1226. def check_bumpable(self):
  1227. if not [o.amt for o in self.outputs if o.amt >= self.min_fee]:
  1228. die(1,'Transaction cannot be bumped.' +
  1229. '\nAll outputs have less than the minimum fee ({} {})'.format(self.min_fee,g.coin))
  1230. def choose_output(self):
  1231. chg_idx = self.get_chg_output_idx()
  1232. init_reply = opt.output_to_reduce
  1233. while True:
  1234. if init_reply == None:
  1235. m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): '
  1236. reply = my_raw_input(m) or 'c'
  1237. else:
  1238. reply,init_reply = init_reply,None
  1239. if chg_idx == None and not is_int(reply):
  1240. msg("Output must be an integer")
  1241. elif chg_idx != None and not is_int(reply) and reply != 'c':
  1242. msg("Output must be an integer, or 'c' for the change output")
  1243. else:
  1244. idx = chg_idx if reply == 'c' else (int(reply) - 1)
  1245. if idx < 0 or idx >= len(self.outputs):
  1246. msg('Output must be in the range 1-{}'.format(len(self.outputs)))
  1247. else:
  1248. o_amt = self.outputs[idx].amt
  1249. cs = ('',' (change output)')[chg_idx == idx]
  1250. p = 'Fee will be deducted from output {}{} ({} {})'.format(idx+1,cs,o_amt,g.coin)
  1251. if o_amt < self.min_fee:
  1252. msg('Minimum fee ({} {c}) is greater than output amount ({} {c})'.format(
  1253. self.min_fee,o_amt,c=g.coin))
  1254. elif opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
  1255. if opt.yes: msg(p)
  1256. self.bump_output_idx = idx
  1257. return idx
  1258. def set_min_fee(self):
  1259. self.min_fee = self.sum_inputs() - self.sum_outputs() + self.get_relay_fee()
  1260. def update_fee(self,op_idx,fee):
  1261. self.update_output_amt(op_idx,self.sum_inputs()-self.sum_outputs(exclude=op_idx)-fee)
  1262. def convert_and_check_fee(self,tx_fee,desc):
  1263. ret = super(MMGenBumpTX,self).convert_and_check_fee(tx_fee,desc)
  1264. if ret < self.min_fee:
  1265. msg('{} {c}: {} fee too small. Minimum fee: {} {c} ({} {})'.format(
  1266. ret,desc,self.min_fee,self.fee_abs2rel(self.min_fee.hl()),self.rel_fee_desc,c=g.coin))
  1267. return False
  1268. output_amt = self.outputs[self.bump_output_idx].amt
  1269. if ret >= output_amt:
  1270. msg('{} {c}: {} fee too large. Maximum fee: <{} {c}'.format(ret.hl(),desc,output_amt.hl(),c=g.coin))
  1271. return False
  1272. return ret
  1273. class MMGenSplitTX(MMGenTX):
  1274. def get_outputs_from_cmdline(self,mmid): # TODO: check that addr is empty
  1275. from mmgen.addr import AddrData
  1276. ad_w = AddrData(source='tw')
  1277. if is_mmgen_id(mmid):
  1278. coin_addr = mmaddr2coinaddr(mmid,ad_w,None) if is_mmgen_id(mmid) else CoinAddr(mmid)
  1279. self.add_output(coin_addr,g.proto.coin_amt('0'),is_chg=True)
  1280. else:
  1281. die(2,'{}: invalid command-line argument'.format(mmid))
  1282. self.add_mmaddrs_to_outputs(ad_w,None)
  1283. if not segwit_is_active() and self.has_segwit_outputs():
  1284. fs = '{} Segwit address requested on the command line, but Segwit is not active on this chain'
  1285. rdie(2,fs.format(g.proj_name))
  1286. def get_split_fee_from_user(self):
  1287. if opt.rpc_host2:
  1288. g.rpc_host = opt.rpc_host2
  1289. if opt.tx_fees:
  1290. opt.tx_fee = opt.tx_fees.split(',')[1]
  1291. try:
  1292. rpc_init(reinit=True)
  1293. except:
  1294. ymsg('Connect to {} daemon failed. Network fee estimation unavailable'.format(g.coin))
  1295. return self.get_usr_fee_interactive(opt.tx_fee,'User-selected')
  1296. return super(type(self),self).get_fee_from_user()
  1297. def create_split(self,mmid):
  1298. self.outputs = self.MMGenTxOutputList()
  1299. self.get_outputs_from_cmdline(mmid)
  1300. while True:
  1301. change_amt = self.sum_inputs() - self.get_split_fee_from_user()
  1302. if change_amt >= 0:
  1303. p = 'Transaction produces {} {} in change'.format(change_amt.hl(),g.coin)
  1304. if opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
  1305. if opt.yes: msg(p)
  1306. break
  1307. else:
  1308. self.warn_insufficient_chg(change_amt)
  1309. self.update_output_amt(0,change_amt)
  1310. self.send_amt = change_amt
  1311. if not opt.yes:
  1312. self.add_comment() # edits an existing comment
  1313. self.create_raw() # creates self.hex, self.txid
  1314. self.add_timestamp()
  1315. self.add_blockcount() # TODO
  1316. self.chain = g.chain
  1317. assert self.sum_inputs() - self.sum_outputs() <= g.proto.max_tx_fee
  1318. qmsg('Transaction successfully created')
  1319. if not opt.yes:
  1320. self.view_with_prompt('View decoded transaction?')