tx.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. #!/usr/bin/env python
  2. #
  3. # mmgen = Multi-Mode GENerator, command-line Bitcoin cold storage solution
  4. # Copyright (C)2013-2017 Philemon <mmgen-py@yandex.com>
  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
  22. from stat import *
  23. from binascii import unhexlify
  24. from mmgen.common import *
  25. from mmgen.obj import *
  26. def is_mmgen_seed_id(s): return SeedID(sid=s,on_fail='silent')
  27. def is_mmgen_idx(s): return AddrIdx(s,on_fail='silent')
  28. def is_mmgen_id(s): return MMGenID(s,on_fail='silent')
  29. def is_btc_addr(s): return BTCAddr(s,on_fail='silent')
  30. def is_addrlist_id(s): return AddrListID(s,on_fail='silent')
  31. def is_tw_label(s): return TwLabel(s,on_fail='silent')
  32. def is_wif(s):
  33. if s == '': return False
  34. from mmgen.bitcoin import wif2hex
  35. return bool(wif2hex(s))
  36. def segwit_is_active(exit_on_error=False):
  37. d = bitcoin_connection().getblockchaininfo()
  38. if d['chain'] == 'regtest':
  39. return True
  40. if 'segwit' in d['bip9_softforks'] and d['bip9_softforks']['segwit']['status'] == 'active':
  41. return True
  42. if g.skip_segwit_active_check:
  43. return True
  44. if exit_on_error:
  45. die(2,'Segwit not active on this chain. Exiting')
  46. else:
  47. return False
  48. def bytes2int(hex_bytes):
  49. r = hexlify(unhexlify(hex_bytes)[::-1])
  50. if r[0] in '89abcdef':
  51. die(3,"{}: Negative values not permitted in transaction!".format(hex_bytes))
  52. return int(r,16)
  53. def bytes2btc(hex_bytes):
  54. return bytes2int(hex_bytes) * g.satoshi
  55. from collections import OrderedDict
  56. class DeserializedTX(OrderedDict,MMGenObject): # need to add MMGen types
  57. def __init__(self,txhex):
  58. tx = list(unhexlify(txhex))
  59. tx_copy = tx[:]
  60. def hshift(l,n,reverse=False):
  61. ret = l[:n]
  62. del l[:n]
  63. return hexlify(''.join(ret[::-1] if reverse else ret))
  64. # https://bitcoin.org/en/developer-reference#compactsize-unsigned-integers
  65. # For example, the number 515 is encoded as 0xfd0302.
  66. def readVInt(l):
  67. s = int(hexlify(l[0]),16)
  68. bytes_len = 1 if s < 0xfd else 2 if s == 0xfd else 4 if s == 0xfe else 8
  69. if bytes_len != 1: del l[0]
  70. ret = int(hexlify(''.join(l[:bytes_len][::-1])),16)
  71. del l[:bytes_len]
  72. return ret
  73. d = { 'version': bytes2int(hshift(tx,4)) }
  74. has_witness = (False,True)[hexlify(tx[0])=='00']
  75. if has_witness:
  76. u = hshift(tx,2)[2:]
  77. if u != '01':
  78. die(2,"'{}': Illegal value for flag in transaction!".format(u))
  79. del tx_copy[-len(tx)-2:-len(tx)]
  80. d['num_txins'] = readVInt(tx)
  81. d['txins'] = MMGenList([OrderedDict((
  82. ('txid', hshift(tx,32,reverse=True)),
  83. ('vout', bytes2int(hshift(tx,4))),
  84. ('scriptSig', hshift(tx,readVInt(tx))),
  85. ('nSeq', hshift(tx,4,reverse=True))
  86. )) for i in range(d['num_txins'])])
  87. d['num_txouts'] = readVInt(tx)
  88. d['txouts'] = MMGenList([OrderedDict((
  89. ('amount', bytes2btc(hshift(tx,8))),
  90. ('scriptPubKey', hshift(tx,readVInt(tx)))
  91. )) for i in range(d['num_txouts'])])
  92. d['witness_size'] = 0
  93. if has_witness:
  94. # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
  95. # A non-witness program (defined hereinafter) txin MUST be associated with an empty
  96. # witness field, represented by a 0x00.
  97. del tx_copy[-len(tx):-4]
  98. wd,tx = tx[:-4],tx[-4:]
  99. d['witness_size'] = len(wd) + 2 # add marker and flag
  100. for i in range(len(d['txins'])):
  101. if hexlify(wd[0]) == '00':
  102. hshift(wd,1)
  103. continue
  104. d['txins'][i]['witness'] = [hshift(wd,readVInt(wd)) for item in range(readVInt(wd))]
  105. if wd:
  106. die(3,'More witness data than inputs with witnesses!')
  107. d['lock_time'] = bytes2int(hshift(tx,4))
  108. d['txid'] = hexlify(sha256(sha256(''.join(tx_copy)).digest()).digest()[::-1])
  109. keys = 'txid','version','lock_time','witness_size','num_txins','txins','num_txouts','txouts'
  110. return OrderedDict.__init__(self, ((k,d[k]) for k in keys))
  111. class MMGenTX(MMGenObject):
  112. ext = 'rawtx'
  113. raw_ext = 'rawtx'
  114. sig_ext = 'sigtx'
  115. txid_ext = 'txid'
  116. desc = 'transaction'
  117. class MMGenTxInput(MMGenListItem):
  118. attrs = 'txid','vout','amt','label','mmid','addr','confs','scriptPubKey','have_wif','sequence'
  119. txid = MMGenListItemAttr('txid','BitcoinTxID')
  120. scriptPubKey = MMGenListItemAttr('scriptPubKey','HexStr')
  121. class MMGenTxOutput(MMGenListItem):
  122. attrs = 'txid','vout','amt','label','mmid','addr','have_wif','is_chg'
  123. class MMGenTxInputOldFmt(MMGenListItem): # for converting old tx files only
  124. tr = {'amount':'amt', 'address':'addr', 'confirmations':'confs','comment':'label'}
  125. attrs = 'txid','vout','amt','label','mmid','addr','confs','scriptPubKey','wif'
  126. attrs_priv = 'tr',
  127. class MMGenTxInputList(list,MMGenObject): pass
  128. class MMGenTxOutputList(list,MMGenObject): pass
  129. def __init__(self,filename=None):
  130. self.inputs = self.MMGenTxInputList()
  131. self.outputs = self.MMGenTxOutputList()
  132. self.send_amt = BTCAmt('0') # total amt minus change
  133. self.hex = '' # raw serialized hex transaction
  134. self.label = MMGenTXLabel('')
  135. self.txid = ''
  136. self.btc_txid = ''
  137. self.timestamp = ''
  138. self.chksum = ''
  139. self.fmt_data = ''
  140. self.blockcount = 0
  141. self.chain = None
  142. if filename:
  143. self.parse_tx_file(filename)
  144. self.check_sigs() # marks the tx as signed
  145. # repeat with sign and send, because bitcoind could be restarted
  146. self.die_if_incorrect_chain()
  147. def die_if_incorrect_chain(self):
  148. if self.chain and g.chain and self.chain != g.chain:
  149. die(2,'Transaction is for {}, but current chain is {}!'.format(self.chain,g.chain))
  150. def add_output(self,btcaddr,amt,is_chg=None):
  151. self.outputs.append(self.MMGenTxOutput(addr=btcaddr,amt=amt,is_chg=is_chg))
  152. def get_chg_output_idx(self):
  153. for i in range(len(self.outputs)):
  154. if self.outputs[i].is_chg == True:
  155. return i
  156. return None
  157. def update_output_amt(self,idx,amt):
  158. o = self.outputs[idx].__dict__
  159. o['amt'] = amt
  160. self.outputs[idx] = self.MMGenTxOutput(**o)
  161. def del_output(self,idx):
  162. self.outputs.pop(idx)
  163. def sum_outputs(self,exclude=None):
  164. olist = self.outputs if exclude == None else \
  165. self.outputs[:exclude] + self.outputs[exclude+1:]
  166. return BTCAmt(sum(e.amt for e in olist))
  167. def add_mmaddrs_to_outputs(self,ad_w,ad_f):
  168. a = [e.addr for e in self.outputs]
  169. d = ad_w.make_reverse_dict(a)
  170. d.update(ad_f.make_reverse_dict(a))
  171. for e in self.outputs:
  172. if e.addr and e.addr in d:
  173. e.mmid,f = d[e.addr]
  174. if f: e.label = f
  175. # def encode_io(self,desc):
  176. # tr = getattr((self.MMGenTxOutput,self.MMGenTxInput)[desc=='inputs'],'tr')
  177. # tr_rev = dict([(v,k) for k,v in tr.items()])
  178. # return [dict([(tr_rev[e] if e in tr_rev else e,getattr(d,e)) for e in d.__dict__])
  179. # for d in getattr(self,desc)]
  180. #
  181. def create_raw(self,c):
  182. i = [{'txid':e.txid,'vout':e.vout} for e in self.inputs]
  183. if self.inputs[0].sequence:
  184. i[0]['sequence'] = self.inputs[0].sequence
  185. o = dict([(e.addr,e.amt) for e in self.outputs])
  186. self.hex = c.createrawtransaction(i,o)
  187. self.txid = MMGenTxID(make_chksum_6(unhexlify(self.hex)).upper())
  188. # returns true if comment added or changed
  189. def add_comment(self,infile=None):
  190. if infile:
  191. self.label = MMGenTXLabel(get_data_from_file(infile,'transaction comment'))
  192. else: # get comment from user, or edit existing comment
  193. m = ('Add a comment to transaction?','Edit transaction comment?')[bool(self.label)]
  194. if keypress_confirm(m,default_yes=False):
  195. while True:
  196. s = MMGenTXLabel(my_raw_input('Comment: ',insert_txt=self.label))
  197. if s:
  198. lbl_save = self.label
  199. self.label = s
  200. return (True,False)[lbl_save == self.label]
  201. else:
  202. msg('Invalid comment')
  203. return False
  204. def edit_comment(self):
  205. return self.add_comment(self)
  206. def has_segwit_inputs(self):
  207. return any(i.mmid and i.mmid.mmtype == 'S' for i in self.inputs)
  208. # https://bitcoin.stackexchange.com/questions/1195/how-to-calculate-transaction-size-before-sending
  209. # 180: uncompressed, 148: compressed
  210. def estimate_size_old(self):
  211. if not self.inputs or not self.outputs: return None
  212. return len(self.inputs)*180 + len(self.outputs)*34 + 10
  213. # https://bitcoincore.org/en/segwit_wallet_dev/
  214. # vsize: 3 times of the size with original serialization, plus the size with new
  215. # serialization, divide the result by 4 and round up to the next integer.
  216. # TODO: results differ slightly from actual transaction size
  217. def estimate_vsize(self):
  218. if not self.inputs or not self.outputs: return None
  219. sig_size = 72 # sig in DER format
  220. pubkey_size = { 'compressed':33, 'uncompressed':65 }
  221. outpoint_size = 36 # txid + vout
  222. def get_inputs_size():
  223. segwit_isize = outpoint_size + 1 + 23 + 4 # (txid,vout) [scriptSig size] scriptSig nSeq # = 64
  224. # txid vout [scriptSig size] scriptSig (<sig> <pubkey>) nSeq
  225. legacy_isize = outpoint_size + 1 + 2 + sig_size + pubkey_size['uncompressed'] + 4 # = 180
  226. compressed_isize = outpoint_size + 1 + 2 + sig_size + pubkey_size['compressed'] + 4 # = 148
  227. ret = sum((legacy_isize,segwit_isize)[i.mmid.mmtype=='S'] for i in self.inputs if i.mmid)
  228. # assume all non-MMGen pubkeys are compressed (we have no way of knowing
  229. # until we see the key). TODO: add user option to specify this?
  230. return ret + sum(compressed_isize for i in self.inputs if not i.mmid)
  231. def get_outputs_size():
  232. return sum((34,32)[o.addr.addr_fmt=='p2sh'] for o in self.outputs)
  233. # https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki
  234. # The witness is a serialization of all witness data of the transaction. Each txin is
  235. # associated with a witness field. A witness field starts with a var_int to indicate the
  236. # number of stack items for the txin. It is followed by stack items, with each item starts
  237. # with a var_int to indicate the length. Witness data is NOT script.
  238. # A non-witness program txin MUST be associated with an empty witness field, represented
  239. # by a 0x00. If all txins are not witness program, a transaction's wtxid is equal to its txid.
  240. def get_witness_size():
  241. if not self.has_segwit_inputs(): return 0
  242. wf_size = 1 + 1 + sig_size + 1 + pubkey_size['compressed'] # vInt vInt sig vInt pubkey = 108
  243. return sum((1,wf_size)[bool(i.mmid) and i.mmid.mmtype=='S'] for i in self.inputs)
  244. isize = get_inputs_size()
  245. osize = get_outputs_size()
  246. wsize = get_witness_size()
  247. # pmsg([i.mmid and i.mmid.mmtype == 'S' for i in self.inputs])
  248. # pmsg([i.mmid for i in self.inputs])
  249. # pmsg([i.mmid for i in self.outputs])
  250. # pmsg('isize',isize)
  251. # pmsg('osize',osize)
  252. # pmsg('wsize',wsize)
  253. # TODO: compute real varInt sizes instead of assuming 1 byte
  254. # old serialization: [nVersion] [vInt][txins][vInt][txouts] [nLockTime]
  255. old_size = 4 + 1 + isize + 1 + osize + 4
  256. # new serialization: [nVersion][marker][flag][vInt][txins][vInt][txouts][witness][nLockTime]
  257. new_size = 4 + 1 + 1 + 1 + isize + 1 + osize + wsize + 4 \
  258. if wsize else old_size
  259. ret = (old_size * 3 + new_size) / 4
  260. # pmsg('old_size',old_size) # This should be equal to the size of serialized signed tx
  261. # pmsg('ret',ret)
  262. # pmsg('estimate_size_old',self.estimate_size_old())
  263. return ret
  264. estimate_size = estimate_vsize
  265. def get_fee(self):
  266. return self.sum_inputs() - self.sum_outputs()
  267. def btc2spb(self,btc_fee):
  268. return int(btc_fee/g.satoshi/self.estimate_size())
  269. def get_relay_fee(self):
  270. assert self.estimate_size()
  271. kb_fee = BTCAmt(bitcoin_connection().getnetworkinfo()['relayfee'])
  272. vmsg('Relay fee: {} BTC/kB'.format(kb_fee))
  273. return kb_fee * self.estimate_size() / 1024
  274. def convert_fee_spec(self,tx_fee,tx_size,on_fail='throw'):
  275. if BTCAmt(tx_fee,on_fail='silent'):
  276. return BTCAmt(tx_fee)
  277. elif len(tx_fee) >= 2 and tx_fee[-1] == 's' and is_int(tx_fee[:-1]) and int(tx_fee[:-1]) >= 1:
  278. if tx_size:
  279. return BTCAmt(int(tx_fee[:-1]) * tx_size * g.satoshi)
  280. else:
  281. return None
  282. else:
  283. if on_fail == 'return':
  284. return False
  285. elif on_fail == 'throw':
  286. assert False, "'{}': invalid tx-fee argument".format(tx_fee)
  287. def get_usr_fee(self,tx_fee,desc='Missing description'):
  288. btc_fee = self.convert_fee_spec(tx_fee,self.estimate_size(),on_fail='return')
  289. if btc_fee == None:
  290. msg("'{}': cannot convert satoshis-per-byte to BTC because transaction size is unknown".format(tx_fee))
  291. assert False # because we shouldn't be calling this if tx size is unknown
  292. elif btc_fee == False:
  293. msg("'{}': invalid TX fee (not a BTC amount or satoshis-per-byte specification)".format(tx_fee))
  294. return False
  295. elif btc_fee > g.max_tx_fee:
  296. msg('{} BTC: {} fee too large (maximum fee: {} BTC)'.format(btc_fee,desc,g.max_tx_fee))
  297. return False
  298. elif btc_fee < self.get_relay_fee():
  299. msg('{} BTC: {} fee too small (below relay fee of {} BTC)'.format(str(btc_fee),desc,str(self.get_relay_fee())))
  300. return False
  301. else:
  302. return btc_fee
  303. def get_usr_fee_interactive(self,tx_fee=None,desc='Starting'):
  304. btc_fee = None
  305. while True:
  306. if tx_fee:
  307. btc_fee = self.get_usr_fee(tx_fee,desc)
  308. if btc_fee:
  309. m = ('',' (after {}x adjustment)'.format(opt.tx_fee_adj))[opt.tx_fee_adj != 1]
  310. p = '{} TX fee{}: {} BTC ({} satoshis per byte)'.format(desc,m,
  311. btc_fee.hl(),pink(str(self.btc2spb(btc_fee))))
  312. if opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
  313. if opt.yes: msg(p)
  314. return btc_fee
  315. tx_fee = my_raw_input('Enter transaction fee: ')
  316. desc = 'User-selected'
  317. # inputs methods
  318. def list_wifs(self,desc,mmaddrs_only=False):
  319. return [e.wif for e in getattr(self,desc) if e.mmid] if mmaddrs_only \
  320. else [e.wif for e in getattr(self,desc)]
  321. def delete_attrs(self,desc,attr):
  322. for e in getattr(self,desc):
  323. if hasattr(e,attr): delattr(e,attr)
  324. def decode_io(self,desc,data):
  325. io,il = (
  326. (self.MMGenTxOutput,self.MMGenTxOutputList),
  327. (self.MMGenTxInput,self.MMGenTxInputList)
  328. )[desc=='inputs']
  329. return il([io(**dict([(k,d[k]) for k in io.attrs
  330. if k in d and d[k] not in ('',None)])) for d in data])
  331. def decode_io_oldfmt(self,data):
  332. io = self.MMGenTxInputOldFmt
  333. tr_rev = dict([(v,k) for k,v in io.tr.items()])
  334. copy_keys = [tr_rev[k] if k in tr_rev else k for k in io.attrs]
  335. return [io(**dict([(io.tr[k] if k in io.tr else k,d[k])
  336. for k in copy_keys if k in d and d[k] != ''])) for d in data]
  337. def copy_inputs_from_tw(self,tw_unspent_data):
  338. txi,self.inputs = self.MMGenTxInput,self.MMGenTxInputList()
  339. for d in tw_unspent_data:
  340. t = txi(**dict([(attr,getattr(d,attr)) for attr in d.__dict__ if attr in txi.attrs]))
  341. if d.twmmid.type == 'mmgen': t.mmid = d.twmmid # twmmid -> mmid
  342. self.inputs.append(t)
  343. def get_input_sids(self):
  344. return set(e.mmid.sid for e in self.inputs if e.mmid)
  345. def get_output_sids(self):
  346. return set(e.mmid.sid for e in self.outputs if e.mmid)
  347. def sum_inputs(self):
  348. return sum(e.amt for e in self.inputs)
  349. def add_timestamp(self):
  350. self.timestamp = make_timestamp()
  351. def add_blockcount(self,c):
  352. self.blockcount = int(c.getblockcount())
  353. def format(self):
  354. from mmgen.bitcoin import b58encode
  355. lines = [
  356. '{} {} {} {} {}'.format(
  357. self.chain.upper() if self.chain else 'Unknown',
  358. self.txid,
  359. self.send_amt,
  360. self.timestamp,
  361. self.blockcount
  362. ),
  363. self.hex,
  364. repr([e.__dict__ for e in self.inputs]),
  365. repr([e.__dict__ for e in self.outputs])
  366. ]
  367. if self.label:
  368. lines.append(b58encode(self.label.encode('utf8')))
  369. if self.btc_txid:
  370. if not self.label: lines.append('-') # keep old tx files backwards compatible
  371. lines.append(self.btc_txid)
  372. self.chksum = make_chksum_6(' '.join(lines))
  373. self.fmt_data = '\n'.join([self.chksum] + lines)+'\n'
  374. def get_non_mmaddrs(self,desc):
  375. return list(set(i.addr for i in getattr(self,desc) if not i.mmid))
  376. # return true or false, don't exit
  377. def sign(self,c,tx_num_str,keys):
  378. self.die_if_incorrect_chain()
  379. if opt.aug1hf and self.has_segwit_inputs():
  380. die(2,yellow("'--aug1hf' option is incompatible with Segwit transaction inputs!"))
  381. if not keys:
  382. msg('No keys. Cannot sign!')
  383. return False
  384. qmsg('Passing %s key%s to bitcoind' % (len(keys),suf(keys,'s')))
  385. sig_data = []
  386. for d in self.inputs:
  387. e = dict([(k,getattr(d,k)) for k in ('txid','vout','scriptPubKey','amt')])
  388. e['amount'] = e['amt']
  389. del e['amt']
  390. wif = keys[d.addr]
  391. if d.mmid and d.mmid.mmtype == 'S':
  392. from mmgen.bitcoin import pubhex2redeem_script
  393. from mmgen.addr import keygen_wif2pubhex,keygen_selector
  394. pubhex = keygen_wif2pubhex(wif,keygen_selector())
  395. e['redeemScript'] = pubhex2redeem_script(pubhex)
  396. sig_data.append(e)
  397. from mmgen.bitcoin import hash256
  398. msg_r('Signing transaction{}...'.format(tx_num_str))
  399. ht = ('ALL','ALL|FORKID')[bool(opt.aug1hf)] # sighashtype defaults to 'ALL'
  400. ret = c.signrawtransaction(self.hex,sig_data,keys.values(),ht,on_fail='return')
  401. from mmgen.rpc import rpc_error,rpc_errmsg
  402. if rpc_error(ret):
  403. errmsg = rpc_errmsg(ret)
  404. if 'Invalid sighash param' in errmsg:
  405. m = 'This chain does not support the Aug. 1 2017 UAHF.'
  406. m += "\nRe-run 'mmgen-txsign' or 'mmgen-txdo' without the --aug1hf option."
  407. else:
  408. m = errmsg
  409. msg(yellow(m))
  410. return False
  411. else:
  412. if ret['complete']:
  413. self.hex = ret['hex']
  414. vmsg('Signed transaction size: {}'.format(len(self.hex)/2))
  415. dt = DeserializedTX(self.hex)
  416. txid = dt['txid']
  417. self.check_sigs(dt)
  418. assert txid == c.decoderawtransaction(self.hex)['txid'], 'txid mismatch (after signing)'
  419. self.btc_txid = BitcoinTxID(txid,on_fail='return')
  420. msg('OK')
  421. return True
  422. else:
  423. msg('failed\nBitcoind returned the following errors:')
  424. msg(repr(ret['errors']))
  425. return False
  426. def mark_raw(self):
  427. self.desc = 'transaction'
  428. self.ext = self.raw_ext
  429. def mark_signed(self): # called ONLY by check_sigs()
  430. self.desc = 'signed transaction'
  431. self.ext = self.sig_ext
  432. def marked_signed(self,color=False):
  433. ret = self.desc == 'signed transaction'
  434. return (red,green)[ret](str(ret)) if color else ret
  435. def check_sigs(self,deserial_tx=None): # return False if no sigs, die on error
  436. txins = (deserial_tx or DeserializedTX(self.hex))['txins']
  437. has_ss = any(ti['scriptSig'] for ti in txins)
  438. has_witness = any('witness' in ti and ti['witness'] for ti in txins)
  439. if not (has_ss or has_witness):
  440. return False
  441. for ti in txins:
  442. if ti['scriptSig'][:6] == '160014' and len(ti['scriptSig']) == 46: # P2SH-P2WPKH
  443. assert 'witness' in ti, 'missing witness'
  444. assert type(ti['witness']) == list and len(ti['witness']) == 2, 'malformed witness'
  445. assert len(ti['witness'][1]) == 66, 'incorrect witness pubkey length'
  446. elif ti['scriptSig'] == '': # native P2WPKH
  447. die(3,('TX has missing signature','Native P2WPKH not implemented')['witness' in ti])
  448. else: # non-witness
  449. assert not 'witness' in ti, 'non-witness input has witness'
  450. # sig_size 72 (DER format), pubkey_size 'compressed':33, 'uncompressed':65
  451. assert (200 < len(ti['scriptSig']) < 300), 'malformed scriptSig' # VERY rough check
  452. self.mark_signed()
  453. return True
  454. def has_segwit_outputs(self):
  455. return any(o.mmid and o.mmid.mmtype == 'S' for o in self.outputs)
  456. def is_in_mempool(self,c):
  457. return 'size' in c.getmempoolentry(self.btc_txid,on_fail='silent')
  458. def is_in_wallet(self,c):
  459. ret = c.gettransaction(self.btc_txid,on_fail='silent')
  460. return 'confirmations' in ret and ret['confirmations'] > 0
  461. def is_replaced(self,c):
  462. if self.is_in_mempool(c): return False
  463. ret = c.gettransaction(self.btc_txid,on_fail='silent')
  464. if not 'bip125-replaceable' in ret or not 'confirmations' in ret or ret['confirmations'] > 0:
  465. return False
  466. return -ret['confirmations'] + 1 # 1: replacement in mempool, 2: replacement confirmed
  467. def is_in_utxos(self,c):
  468. return 'txid' in c.getrawtransaction(self.btc_txid,True,on_fail='silent')
  469. def send(self,c,prompt_user=True):
  470. self.die_if_incorrect_chain()
  471. bogus_send = os.getenv('MMGEN_BOGUS_SEND')
  472. if self.has_segwit_outputs() and not segwit_is_active() and not bogus_send:
  473. m = 'Transaction has MMGen Segwit outputs, but this blockchain does not support Segwit'
  474. die(2,m+' at the current height')
  475. if self.get_fee() > g.max_tx_fee:
  476. die(2,'Transaction fee ({}) greater than max_tx_fee ({})!'.format(self.get_fee(),g.max_tx_fee))
  477. if self.is_in_mempool(c):
  478. msg('Warning: transaction is in mempool!')
  479. elif self.is_in_wallet(c):
  480. die(1,'Transaction has been confirmed!')
  481. elif self.is_in_utxos(c):
  482. die(2,red('ERROR: transaction is in the blockchain (but not in the tracking wallet)!'))
  483. ret = self.is_replaced(c) # 1: replacement in mempool, 2: replacement confirmed
  484. if ret:
  485. die(1,'Transaction has been replaced'+('',', and the replacement TX is confirmed')[ret==2]+'!')
  486. if prompt_user:
  487. m1 = ("Once this transaction is sent, there's no taking it back!",'')[bool(opt.quiet)]
  488. m2 = 'broadcast this transaction to the {} network'.format(g.chain.upper())
  489. m3 = ('YES, I REALLY WANT TO DO THIS','YES')[bool(opt.quiet or opt.yes)]
  490. confirm_or_exit(m1,m2,m3)
  491. msg('Sending transaction')
  492. if bogus_send:
  493. ret = 'deadbeef' * 8
  494. m = 'BOGUS transaction NOT sent: %s'
  495. else:
  496. ret = c.sendrawtransaction(self.hex,on_fail='return')
  497. m = 'Transaction sent: %s'
  498. from mmgen.rpc import rpc_error,rpc_errmsg
  499. if rpc_error(ret):
  500. errmsg = rpc_errmsg(ret)
  501. if 'Signature must use SIGHASH_FORKID' in errmsg:
  502. m = 'The Aug. 1 2017 UAHF has activated on this chain.'
  503. m += "\nRe-run 'mmgen-txsign' or 'mmgen-txdo' with the --aug1hf option."
  504. elif 'Illegal use of SIGHASH_FORKID' in errmsg:
  505. m = 'The Aug. 1 2017 UAHF is not yet active on this chain.'
  506. m += "\nRe-run 'mmgen-txsign' or 'mmgen-txdo' without the --aug1hf option."
  507. else:
  508. m = errmsg
  509. msg(yellow(m))
  510. msg(red('Send of MMGen transaction {} failed'.format(self.txid)))
  511. return False
  512. else:
  513. if not bogus_send:
  514. assert ret == self.btc_txid, 'txid mismatch (after sending)'
  515. self.desc = 'sent transaction'
  516. msg(m % self.btc_txid.hl())
  517. self.add_timestamp()
  518. self.add_blockcount(c)
  519. return True
  520. def write_txid_to_file(self,ask_write=False,ask_write_default_yes=True):
  521. fn = '%s[%s].%s' % (self.txid,self.send_amt,self.txid_ext)
  522. write_data_to_file(fn,self.btc_txid+'\n','transaction ID',
  523. ask_write=ask_write,
  524. ask_write_default_yes=ask_write_default_yes)
  525. def write_to_file(self,add_desc='',ask_write=True,ask_write_default_yes=False,ask_tty=True,ask_overwrite=True):
  526. if ask_write == False:
  527. ask_write_default_yes=True
  528. self.format()
  529. spbs = ('',',{}'.format(self.btc2spb(self.get_fee())))[self.is_rbf()]
  530. fn = '{}[{}{}].{}'.format(self.txid,self.send_amt,spbs,self.ext)
  531. write_data_to_file(fn,self.fmt_data,self.desc+add_desc,
  532. ask_overwrite=ask_overwrite,
  533. ask_write=ask_write,
  534. ask_tty=ask_tty,
  535. ask_write_default_yes=ask_write_default_yes)
  536. def view_with_prompt(self,prompt=''):
  537. prompt += ' (y)es, (N)o, pager (v)iew, (t)erse view'
  538. reply = prompt_and_get_char(prompt,'YyNnVvTt',enter_ok=True)
  539. if reply and reply in 'YyVvTt':
  540. self.view(pager=reply in 'Vv',terse=reply in 'Tt')
  541. def view(self,pager=False,pause=True,terse=False):
  542. o = self.format_view(terse=terse).encode('utf8')
  543. if pager: do_pager(o)
  544. else:
  545. Msg_r(o)
  546. from mmgen.term import get_char
  547. if pause:
  548. get_char('Press any key to continue: ')
  549. msg('')
  550. # def is_rbf_fromhex(self,color=False):
  551. # try:
  552. # dec_tx = bitcoin_connection().decoderawtransaction(self.hex)
  553. # except:
  554. # return yellow('Unknown') if color else None
  555. # rbf = bool(dec_tx['vin'][0]['sequence'] == g.max_int - 2)
  556. # return (red,green)[rbf](str(rbf)) if color else rbf
  557. def is_rbf(self,color=False):
  558. ret = None < self.inputs[0].sequence <= g.max_int - 2
  559. return (red,green)[ret](str(ret)) if color else ret
  560. def signal_for_rbf(self):
  561. self.inputs[0].sequence = g.max_int - 2
  562. def format_view(self,terse=False):
  563. # self.pdie()
  564. try:
  565. blockcount = bitcoin_connection().getblockcount()
  566. except:
  567. blockcount = None
  568. hdr_fs = (
  569. 'TRANSACTION DATA\n\nHeader: [ID:{}] [{} BTC] [{} UTC] [RBF:{}] [Signed:{}]\n',
  570. 'Transaction {} {} BTC ({} UTC) RBF={} Signed={}\n'
  571. )[bool(terse)]
  572. out = hdr_fs.format(self.txid.hl(),self.send_amt.hl(),self.timestamp,
  573. self.is_rbf(color=True),self.marked_signed(color=True))
  574. enl = ('\n','')[bool(terse)]
  575. if self.chain in ('testnet','regtest'): out += green('Chain: {}\n'.format(self.chain.upper()))
  576. if self.btc_txid: out += 'Bitcoin TxID: {}\n'.format(self.btc_txid.hl())
  577. out += enl
  578. if self.label:
  579. out += 'Comment: %s\n%s' % (self.label.hl(),enl)
  580. out += 'Inputs:\n' + enl
  581. nonmm_str = '(non-{pnm} address){s} '.format(pnm=g.proj_name,s=('',' ')[terse])
  582. for n,e in enumerate(sorted(self.inputs,key=lambda o: o.mmid.sort_key if o.mmid else o.addr)):
  583. if blockcount:
  584. confs = e.confs + blockcount - self.blockcount
  585. days = int(confs * g.mins_per_block / (60*24))
  586. mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True) if e.mmid \
  587. else MMGenID.hlc(nonmm_str)
  588. if terse:
  589. out += '%3s: %s %s %s BTC' % (n+1, e.addr.fmt(color=True),mmid_fmt, e.amt.hl())
  590. else:
  591. for d in (
  592. (n+1, 'tx,vout:', '%s,%s' % (e.txid, e.vout)),
  593. ('', 'address:', e.addr.fmt(color=True) + ' ' + mmid_fmt),
  594. ('', 'comment:', e.label.hl() if e.label else ''),
  595. ('', 'amount:', '%s BTC' % e.amt.hl()),
  596. ('', 'confirmations:', '%s (around %s days)' % (confs,days) if blockcount else '')
  597. ):
  598. if d[2]: out += ('%3s %-8s %s\n' % d)
  599. out += '\n'
  600. out += 'Outputs:\n' + enl
  601. for n,e in enumerate(sorted(self.outputs,key=lambda o: o.mmid.sort_key if o.mmid else o.addr)):
  602. if e.mmid:
  603. app=('',' (chg)')[bool(e.is_chg and terse)]
  604. mmid_fmt = e.mmid.fmt(width=len(nonmm_str),encl='()',color=True,
  605. app=app,appcolor='green')
  606. else:
  607. mmid_fmt = MMGenID.hlc(nonmm_str)
  608. if terse:
  609. out += '%3s: %s %s %s BTC' % (n+1, e.addr.fmt(color=True),mmid_fmt, e.amt.hl())
  610. else:
  611. for d in (
  612. (n+1, 'address:', e.addr.fmt(color=True) + ' ' + mmid_fmt),
  613. ('', 'comment:', e.label.hl() if e.label else ''),
  614. ('', 'amount:', '%s BTC' % e.amt.hl()),
  615. ('', 'change:', green('True') if e.is_chg else '')
  616. ):
  617. if d[2]: out += ('%3s %-8s %s\n' % d)
  618. out += '\n'
  619. fs = (
  620. 'Total input: %s BTC\nTotal output: %s BTC\nTX fee: %s BTC (%s satoshis per byte)\n',
  621. 'In %s BTC - Out %s BTC - Fee %s BTC (%s satoshis/byte)\n'
  622. )[bool(terse)]
  623. total_in = self.sum_inputs()
  624. total_out = self.sum_outputs()
  625. out += fs % (
  626. total_in.hl(),
  627. total_out.hl(),
  628. (total_in-total_out).hl(),
  629. pink(str(self.btc2spb(total_in-total_out))),
  630. )
  631. if opt.verbose:
  632. ts = len(self.hex)/2 if self.hex else 'unknown'
  633. out += 'Transaction size: Vsize={} Actual={}'.format(self.estimate_size(),ts)
  634. if self.marked_signed():
  635. ws = DeserializedTX(self.hex)['witness_size']
  636. out += ' Base={} Witness={}'.format(ts-ws,ws)
  637. out += '\n'
  638. # TX label might contain non-ascii chars
  639. return out
  640. def parse_tx_file(self,infile):
  641. self.parse_tx_data(get_lines_from_file(infile,self.desc+' data'))
  642. def parse_tx_data(self,tx_data):
  643. def do_err(s): die(2,'Invalid %s in transaction file' % s)
  644. if len(tx_data) < 5: do_err('number of lines')
  645. self.chksum = HexStr(tx_data.pop(0))
  646. if self.chksum != make_chksum_6(' '.join(tx_data)):
  647. do_err('checksum')
  648. if len(tx_data) == 6:
  649. self.btc_txid = BitcoinTxID(tx_data.pop(-1),on_fail='return')
  650. if not self.btc_txid:
  651. do_err('Bitcoin TxID')
  652. if len(tx_data) == 5:
  653. c = tx_data.pop(-1)
  654. if c != '-':
  655. from mmgen.bitcoin import b58decode
  656. comment = b58decode(c)
  657. if comment == False:
  658. do_err('encoded comment (not base58)')
  659. else:
  660. self.label = MMGenTXLabel(comment,on_fail='return')
  661. if not self.label:
  662. do_err('comment')
  663. else:
  664. comment = ''
  665. if len(tx_data) == 4:
  666. metadata,self.hex,inputs_data,outputs_data = tx_data
  667. else:
  668. do_err('number of lines')
  669. metadata = metadata.split()
  670. if len(metadata) not in (4,5): do_err('metadata')
  671. if len(metadata) == 5:
  672. t = metadata.pop(0)
  673. self.chain = (t.lower(),None)[t=='Unknown']
  674. self.txid,send_amt,self.timestamp,blockcount = metadata
  675. self.txid = MMGenTxID(self.txid)
  676. self.send_amt = BTCAmt(send_amt)
  677. self.blockcount = int(blockcount)
  678. self.hex = HexStr(self.hex)
  679. try: unhexlify(self.hex)
  680. except: do_err('hex data')
  681. try: self.inputs = self.decode_io('inputs',eval(inputs_data))
  682. except: do_err('inputs data')
  683. if not self.chain and not self.inputs[0].addr.testnet:
  684. self.chain = 'mainnet'
  685. try: self.outputs = self.decode_io('outputs',eval(outputs_data))
  686. except: do_err('btc-to-mmgen address map data')
  687. class MMGenBumpTX(MMGenTX):
  688. min_fee = None
  689. bump_output_idx = None
  690. def __init__(self,filename,send=False):
  691. super(type(self),self).__init__(filename)
  692. if not self.is_rbf():
  693. die(1,"Transaction '{}' is not replaceable (RBF)".format(self.txid))
  694. # If sending, require tx to have been signed
  695. if send:
  696. if not self.marked_signed():
  697. die(1,"File '{}' is not a signed {} transaction file".format(filename,g.proj_name))
  698. if not self.btc_txid:
  699. die(1,"Transaction '{}' was not broadcast to the network".format(self.txid,g.proj_name))
  700. self.btc_txid = ''
  701. self.mark_raw()
  702. def choose_output(self):
  703. chg_idx = self.get_chg_output_idx()
  704. init_reply = opt.output_to_reduce
  705. while True:
  706. if init_reply == None:
  707. m = 'Choose an output to deduct the fee from (Hit ENTER for the change output): '
  708. reply = my_raw_input(m) or 'c'
  709. else:
  710. reply,init_reply = init_reply,None
  711. if chg_idx == None and not is_int(reply):
  712. msg("Output must be an integer")
  713. elif chg_idx != None and not is_int(reply) and reply != 'c':
  714. msg("Output must be an integer, or 'c' for the change output")
  715. else:
  716. idx = chg_idx if reply == 'c' else (int(reply) - 1)
  717. if idx < 0 or idx >= len(self.outputs):
  718. msg('Output must be in the range 1-{}'.format(len(self.outputs)))
  719. else:
  720. o_amt = self.outputs[idx].amt
  721. cs = ('',' (change output)')[chg_idx == idx]
  722. p = 'Fee will be deducted from output {}{} ({} BTC)'.format(idx+1,cs,o_amt)
  723. if o_amt < self.min_fee:
  724. msg('Minimum fee ({} BTC) is greater than output amount ({} BTC)'.format(
  725. self.min_fee,o_amt))
  726. elif opt.yes or keypress_confirm(p+'. OK?',default_yes=True):
  727. if opt.yes: msg(p)
  728. self.bump_output_idx = idx
  729. return idx
  730. def set_min_fee(self):
  731. self.min_fee = self.sum_inputs() - self.sum_outputs() + self.get_relay_fee()
  732. def get_usr_fee(self,tx_fee,desc):
  733. ret = super(type(self),self).get_usr_fee(tx_fee,desc)
  734. if ret < self.min_fee:
  735. msg('{} BTC: {} fee too small. Minimum fee: {} BTC ({} satoshis per byte)'.format(
  736. ret,desc,self.min_fee,self.btc2spb(self.min_fee)))
  737. return False
  738. output_amt = self.outputs[self.bump_output_idx].amt
  739. if ret >= output_amt:
  740. msg('{} BTC: {} fee too large. Maximum fee: <{} BTC'.format(ret,desc,output_amt))
  741. return False
  742. return ret