def add_label(self, arg1, label='', addr=None, silent=False, on_fail='return'): from mmgen.tx import is_mmgen_id, is_coin_addr mmaddr, coinaddr = None, None if is_coin_addr(addr or arg1): coinaddr = CoinAddr(addr or arg1, on_fail='return') if is_mmgen_id(arg1): mmaddr = TwMMGenID(arg1) if mmaddr and not coinaddr: from mmgen.addr import AddrData coinaddr = AddrData(source='tw').mmaddr2coinaddr(mmaddr) try: if not is_mmgen_id(arg1): assert coinaddr, u"Invalid coin address for this chain: {}".format( arg1) assert coinaddr, u"{pn} address '{ma}' not found in tracking wallet" assert self.is_in_wallet( coinaddr), u"Address '{ca}' not found in tracking wallet" except Exception as e: msg(e.message.format(pn=g.proj_name, ma=mmaddr, ca=coinaddr)) return False # Allow for the possibility that BTC addr of MMGen addr was entered. # Do reverse lookup, so that MMGen addr will not be marked as non-MMGen. if not mmaddr: from mmgen.addr import AddrData mmaddr = AddrData(source='tw').coinaddr2mmaddr(coinaddr) if not mmaddr: mmaddr = '{}:{}'.format(g.proto.base_coin.lower(), coinaddr) mmaddr = TwMMGenID(mmaddr) cmt = TwComment(label, on_fail=on_fail) if cmt in (False, None): return False lbl = TwLabel(mmaddr + ('', ' ' + cmt)[bool(cmt)], on_fail=on_fail) ret = self.set_label(coinaddr, lbl) from mmgen.rpc import rpc_error, rpc_errmsg if rpc_error(ret): msg('From {}: {}'.format(g.proto.daemon_name, rpc_errmsg(ret))) if not silent: msg('Label could not be {}'.format( ('removed', 'added')[bool(label)])) return False else: m = mmaddr.type.replace('mmg', 'MMG') a = mmaddr.replace(g.proto.base_coin.lower() + ':', '') s = '{} address {} in tracking wallet'.format(m, a) if label: msg(u"Added label '{}' to {}".format(label, s)) else: msg(u'Removed label from {}'.format(s)) return True
def send(self,prompt_user=True,exit_on_fail=False): if not self.marked_signed(): die(1,'Transaction is not signed!') self.check_correct_chain(on_fail='die') fee = self.fee_rel2abs(self.txobj['gasPrice'].toWei()) if not self.disable_fee_check and (fee > g.proto.max_tx_fee): die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format( fee,g.proto.name.capitalize(),g.proto.max_tx_fee,g.coin)) self.get_status() if prompt_user: self.confirm_send() ret = None if g.bogus_send else g.rpch.eth_sendRawTransaction('0x'+self.hex,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): msg(yellow(rpc_errmsg(ret))) msg(red('Send of MMGen transaction {} failed'.format(self.txid))) if exit_on_fail: sys.exit(1) return False else: m = 'BOGUS transaction NOT sent: {}' if g.bogus_send else 'Transaction sent: {}' if not g.bogus_send: assert ret == '0x'+self.coin_txid,'txid mismatch (after sending)' self.desc = 'sent transaction' msg(m.format(self.coin_txid.hl())) self.add_timestamp() self.add_blockcount() return True
def send(self,prompt_user=True,exit_on_fail=False): if not self.marked_signed(): die(1,'Transaction is not signed!') self.die_if_incorrect_chain() self.check_hex_tx_matches_mmgen_tx(DeserializedTX(self.hex)) bogus_send = os.getenv('MMGEN_BOGUS_SEND') if self.has_segwit_outputs() and not segwit_is_active() and not bogus_send: m = 'Transaction has MMGen Segwit outputs, but this blockchain does not support Segwit' die(2,m+' at the current height') if self.get_fee() > g.proto.max_tx_fee: die(2,'Transaction fee ({}) greater than {} max_tx_fee ({} {})!'.format( self.get_fee(),g.proto.name.capitalize(),g.proto.max_tx_fee,g.coin.upper())) self.get_status() if prompt_user: m1 = ("Once this transaction is sent, there's no taking it back!",'')[bool(opt.quiet)] m2 = 'broadcast this transaction to the {} network'.format(g.chain.upper()) m3 = ('YES, I REALLY WANT TO DO THIS','YES')[bool(opt.quiet or opt.yes)] confirm_or_exit(m1,m2,m3) msg('Sending transaction') ret = None if bogus_send else g.rpch.sendrawtransaction(self.hex,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): errmsg = rpc_errmsg(ret) if 'Signature must use SIGHASH_FORKID' in errmsg: m = 'The Aug. 1 2017 UAHF has activated on this chain.' m += "\nRe-run the script with the --coin=bch option." elif 'Illegal use of SIGHASH_FORKID' in errmsg: m = 'The Aug. 1 2017 UAHF is not yet active on this chain.' m += "\nRe-run the script without the --coin=bch option." elif '64: non-final' in errmsg: m2 = "Transaction with locktime '{}' can't be included in this block!" m = m2.format(strfmt_locktime(self.get_hex_locktime())) else: m = errmsg msg(yellow(m)) msg(red('Send of MMGen transaction {} failed'.format(self.txid))) if exit_on_fail: sys.exit(1) return False else: if bogus_send: m = 'BOGUS transaction NOT sent: {}' else: assert ret == self.coin_txid, 'txid mismatch (after sending)' m = 'Transaction sent: {}' self.desc = 'sent transaction' msg(m.format(self.coin_txid.hl())) self.add_timestamp() self.add_blockcount() return True
def sign(self,tx_num_str,keys): if self.marked_signed(): die(1,'Transaction is already signed!') self.die_if_incorrect_chain() if (self.has_segwit_inputs() or self.has_segwit_outputs()) and not g.proto.cap('segwit'): die(2,yellow("TX has Segwit inputs or outputs, but {} doesn't support Segwit!".format(g.coin))) qmsg('Passing {} key{} to {}'.format(len(keys),suf(keys,'s'),g.proto.daemon_name)) if self.has_segwit_inputs(): from mmgen.addr import KeyGenerator,AddrGenerator kg = KeyGenerator('std') ag = AddrGenerator('segwit') keydict = MMGenDict([(d.addr,d.sec) for d in keys]) sig_data = [] for d in self.inputs: e = dict([(k,getattr(d,k)) for k in ('txid','vout','scriptPubKey','amt')]) e['amount'] = e['amt'] del e['amt'] if d.mmid and d.mmid.mmtype == 'S': e['redeemScript'] = ag.to_segwit_redeem_script(kg.to_pubhex(keydict[d.addr])) sig_data.append(e) msg_r('Signing transaction{}...'.format(tx_num_str)) wifs = [d.sec.wif for d in keys] ret = g.rpch.signrawtransaction(self.hex,sig_data,wifs,g.proto.sighash_type,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): errmsg = rpc_errmsg(ret) if 'Invalid sighash param' in errmsg: m = 'This is not the BCH chain.' m += "\nRe-run the script without the --coin=bch option." else: m = errmsg msg(yellow(m)) return False else: if ret['complete']: # Msg(pretty_hexdump(unhexlify(self.hex),cols=16)) # DEBUG # pmsg(make_chksum_6(unhexlify(self.hex)).upper()) self.hex = ret['hex'] self.compare_size_and_estimated_size() dt = DeserializedTX(self.hex) self.check_hex_tx_matches_mmgen_tx(dt) self.coin_txid = CoinTxID(dt['txid'],on_fail='return') self.check_sigs(dt) assert self.coin_txid == g.rpch.decoderawtransaction(self.hex)['txid'],( 'txid mismatch (after signing)') msg('OK') return True else: msg('failed\n{} returned the following errors:'.format(g.proto.daemon_name.capitalize())) msg(repr(ret['errors'])) return False
def send(self, c, prompt_user=True): self.die_if_incorrect_chain() bogus_send = os.getenv('MMGEN_BOGUS_SEND') if self.has_segwit_outputs( ) and not segwit_is_active() and not bogus_send: m = 'Transaction has MMGen Segwit outputs, but this blockchain does not support Segwit' die(2, m + ' at the current height') if self.get_fee() > g.max_tx_fee: die( 2, 'Transaction fee ({}) greater than max_tx_fee ({})!'.format( self.get_fee(), g.max_tx_fee)) self.get_status(c) if prompt_user: m1 = ("Once this transaction is sent, there's no taking it back!", '')[bool(opt.quiet)] m2 = 'broadcast this transaction to the {} network'.format( g.chain.upper()) m3 = ('YES, I REALLY WANT TO DO THIS', 'YES')[bool(opt.quiet or opt.yes)] confirm_or_exit(m1, m2, m3) msg('Sending transaction') ret = None if bogus_send else c.sendrawtransaction(self.hex, on_fail='return') from mmgen.rpc import rpc_error, rpc_errmsg if rpc_error(ret): errmsg = rpc_errmsg(ret) if 'Signature must use SIGHASH_FORKID' in errmsg: m = 'The Aug. 1 2017 UAHF has activated on this chain.' m += "\nRe-run the script with the --coin=bch option." elif 'Illegal use of SIGHASH_FORKID' in errmsg: m = 'The Aug. 1 2017 UAHF is not yet active on this chain.' m += "\nRe-run the script without the --aug1hf or --coin=bch option." else: m = errmsg msg(yellow(m)) msg(red('Send of MMGen transaction {} failed'.format(self.txid))) return False else: if bogus_send: m = 'BOGUS transaction NOT sent: {}' else: assert ret == self.btc_txid, 'txid mismatch (after sending)' m = 'Transaction sent: {}' self.desc = 'sent transaction' msg(m.format(self.btc_txid.hl())) self.add_timestamp() self.add_blockcount(c) return True
def add_label(self,arg1,label='',addr=None,silent=False,on_fail='return'): from mmgen.tx import is_mmgen_id,is_coin_addr mmaddr,coinaddr = None,None if is_coin_addr(addr or arg1): coinaddr = CoinAddr(addr or arg1,on_fail='return') if is_mmgen_id(arg1): mmaddr = TwMMGenID(arg1) if mmaddr and not coinaddr: from mmgen.addr import AddrData coinaddr = AddrData(source='tw').mmaddr2coinaddr(mmaddr) try: if not is_mmgen_id(arg1): assert coinaddr,"Invalid coin address for this chain: {}".format(arg1) assert coinaddr,"{pn} address '{ma}' not found in tracking wallet" assert self.is_in_wallet(coinaddr),"Address '{ca}' not found in tracking wallet" except Exception as e: msg(e.args[0].format(pn=g.proj_name,ma=mmaddr,ca=coinaddr)) return False # Allow for the possibility that BTC addr of MMGen addr was entered. # Do reverse lookup, so that MMGen addr will not be marked as non-MMGen. if not mmaddr: from mmgen.addr import AddrData mmaddr = AddrData(source='tw').coinaddr2mmaddr(coinaddr) if not mmaddr: mmaddr = '{}:{}'.format(g.proto.base_coin.lower(),coinaddr) mmaddr = TwMMGenID(mmaddr) cmt = TwComment(label,on_fail=on_fail) if cmt in (False,None): return False lbl = TwLabel(mmaddr + ('',' '+cmt)[bool(cmt)],on_fail=on_fail) ret = self.set_label(coinaddr,lbl) from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): msg('From {}: {}'.format(g.proto.daemon_name,rpc_errmsg(ret))) if not silent: msg('Label could not be {}'.format(('removed','added')[bool(label)])) return False else: m = mmaddr.type.replace('mmg','MMG') a = mmaddr.replace(g.proto.base_coin.lower()+':','') s = '{} address {} in tracking wallet'.format(m,a) if label: msg("Added label '{}' to {}".format(label,s)) else: msg('Removed label from {}'.format(s)) return True
def add_label(cls, arg1, label='', addr=None, silent=False): from mmgen.tx import is_mmgen_id, is_btc_addr mmaddr, btcaddr = None, None if is_btc_addr(addr or arg1): btcaddr = BTCAddr(addr or arg1, on_fail='return') if is_mmgen_id(arg1): mmaddr = TwMMGenID(arg1) if not btcaddr and not mmaddr: msg("Address '{}' invalid or not found in tracking wallet".format( addr or arg1)) return False if not btcaddr: from mmgen.addr import AddrData btcaddr = AddrData(source='tw').mmaddr2btcaddr(mmaddr) if not btcaddr: msg("{} address '{}' not found in tracking wallet".format( g.proj_name, mmaddr)) return False # Checked that the user isn't importing a random address if not btcaddr.is_in_tracking_wallet(): msg("Address '{}' not in tracking wallet".format(btcaddr)) return False c = rpc_connection() if not btcaddr.is_for_current_chain(): msg("Address '{}' not valid for chain {}".format( btcaddr, g.chain.upper())) return False # Allow for the possibility that BTC addr of MMGen addr was entered. # Do reverse lookup, so that MMGen addr will not be marked as non-MMGen. if not mmaddr: from mmgen.addr import AddrData ad = AddrData(source='tw') mmaddr = ad.btcaddr2mmaddr(btcaddr) if not mmaddr: mmaddr = 'btc:' + btcaddr mmaddr = TwMMGenID(mmaddr) cmt = TwComment(label, on_fail='return') if cmt in (False, None): return False lbl = TwLabel(mmaddr + ('', ' ' + cmt)[bool(cmt)]) # label is ASCII for now # NOTE: this works because importaddress() removes the old account before # associating the new account with the address. # Will be replaced by setlabel() with new RPC label API # RPC args: addr,label,rescan[=true],p2sh[=none] ret = c.importaddress(btcaddr, lbl, False, on_fail='return') from mmgen.rpc import rpc_error, rpc_errmsg if rpc_error(ret): msg('From bitcoind: ' + rpc_errmsg(ret)) if not silent: msg('Label could not be {}'.format( ('removed', 'added')[bool(label)])) return False else: m = mmaddr.type.replace('mmg', 'MMG') a = mmaddr.replace('btc:', '') s = '{} address {} in tracking wallet'.format(m, a) if label: msg("Added label '{}' to {}".format(label, s)) else: msg('Removed label from {}'.format(s)) return True
def sign(self, c, tx_num_str, keys): self.die_if_incorrect_chain() if g.coin == 'BCH' and (self.has_segwit_inputs() or self.has_segwit_outputs()): die( 2, yellow( "Segwit inputs cannot be spent or spent to on the BCH chain!" )) qmsg('Passing {} key{} to bitcoind'.format(len(keys), suf(keys, 's'))) if self.has_segwit_inputs(): from mmgen.addr import KeyGenerator, AddrGenerator kg = KeyGenerator() ag = AddrGenerator('segwit') keydict = MMGenDict([(d.addr, d.sec) for d in keys]) sig_data = [] for d in self.inputs: e = dict([(k, getattr(d, k)) for k in ('txid', 'vout', 'scriptPubKey', 'amt')]) e['amount'] = e['amt'] del e['amt'] if d.mmid and d.mmid.mmtype == 'S': e['redeemScript'] = ag.to_segwit_redeem_script( kg.to_pubhex(keydict[d.addr])) sig_data.append(e) msg_r('Signing transaction{}...'.format(tx_num_str)) ht = ('ALL', 'ALL|FORKID')[g.coin == 'BCH'] # sighashtype defaults to 'ALL' wifs = [d.sec.wif for d in keys] # keys.pmsg() # pmsg(wifs) ret = c.signrawtransaction(self.hex, sig_data, wifs, ht, on_fail='return') from mmgen.rpc import rpc_error, rpc_errmsg if rpc_error(ret): errmsg = rpc_errmsg(ret) if 'Invalid sighash param' in errmsg: m = 'This is not the BCH chain.' m += "\nRe-run the script without the --aug1hf or --coin=bch option." else: m = errmsg msg(yellow(m)) return False else: if ret['complete']: self.hex = ret['hex'] vmsg('Signed transaction size: {}'.format(len(self.hex) / 2)) dt = DeserializedTX(self.hex) txid = dt['txid'] self.check_sigs(dt) assert txid == c.decoderawtransaction( self.hex)['txid'], 'txid mismatch (after signing)' self.btc_txid = BitcoinTxID(txid, on_fail='return') msg('OK') return True else: msg('failed\nBitcoind returned the following errors:') msg(repr(ret['errors'])) return False
def add_label(cls, arg1, label='', addr=None, silent=False, on_fail='return'): from mmgen.tx import is_mmgen_id, is_coin_addr mmaddr, coinaddr = None, None if is_coin_addr(addr or arg1): coinaddr = CoinAddr(addr or arg1, on_fail='return') if is_mmgen_id(arg1): mmaddr = TwMMGenID(arg1) if mmaddr and not coinaddr: from mmgen.addr import AddrData coinaddr = AddrData(source='tw').mmaddr2coinaddr(mmaddr) try: if not is_mmgen_id(arg1): assert coinaddr, "Invalid coin address for this chain: {}".format( arg1) assert coinaddr, "{pn} address '{ma}' not found in tracking wallet" assert coinaddr.is_in_tracking_wallet( ), "Address '{ca}' not found in tracking wallet" except Exception as e: msg(e[0].format(pn=g.proj_name, ma=mmaddr, ca=coinaddr)) return False # Allow for the possibility that BTC addr of MMGen addr was entered. # Do reverse lookup, so that MMGen addr will not be marked as non-MMGen. if not mmaddr: from mmgen.addr import AddrData mmaddr = AddrData(source='tw').coinaddr2mmaddr(coinaddr) if not mmaddr: mmaddr = '{}:{}'.format(g.proto.base_coin.lower(), coinaddr) mmaddr = TwMMGenID(mmaddr) cmt = TwComment(label, on_fail=on_fail) if cmt in (False, None): return False lbl = TwLabel(mmaddr + ('', ' ' + cmt)[bool(cmt)], on_fail=on_fail) # NOTE: this works because importaddress() removes the old account before # associating the new account with the address. # Will be replaced by setlabel() with new RPC label API # RPC args: addr,label,rescan[=true],p2sh[=none] ret = g.rpch.importaddress(coinaddr, lbl, False, on_fail='return') from mmgen.rpc import rpc_error, rpc_errmsg if rpc_error(ret): msg('From {}: {}'.format(g.proto.daemon_name, rpc_errmsg(ret))) if not silent: msg('Label could not be {}'.format( ('removed', 'added')[bool(label)])) return False else: m = mmaddr.type.replace('mmg', 'MMG') a = mmaddr.replace(g.proto.base_coin.lower() + ':', '') s = '{} address {} in tracking wallet'.format(m, a) if label: msg(u"Added label '{}' to {}".format(label, s)) else: msg(u'Removed label from {}'.format(s)) return True
def add_label(cls,arg1,label='',addr=None,silent=False): from mmgen.tx import is_mmgen_id,is_coin_addr mmaddr,coinaddr = None,None if is_coin_addr(addr or arg1): coinaddr = CoinAddr(addr or arg1,on_fail='return') if is_mmgen_id(arg1): mmaddr = TwMMGenID(arg1) if not coinaddr and not mmaddr: msg("Address '{}' invalid or not found in tracking wallet".format(addr or arg1)) return False if not coinaddr: from mmgen.addr import AddrData coinaddr = AddrData(source='tw').mmaddr2coinaddr(mmaddr) if not coinaddr: msg("{} address '{}' not found in tracking wallet".format(g.proj_name,mmaddr)) return False # Checked that the user isn't importing a random address if not coinaddr.is_in_tracking_wallet(): msg("Address '{}' not in tracking wallet".format(coinaddr)) return False if not coinaddr.is_for_chain(g.chain): msg("Address '{}' not valid for chain {}".format(coinaddr,g.chain.upper())) return False # Allow for the possibility that BTC addr of MMGen addr was entered. # Do reverse lookup, so that MMGen addr will not be marked as non-MMGen. if not mmaddr: from mmgen.addr import AddrData ad = AddrData(source='tw') mmaddr = ad.coinaddr2mmaddr(coinaddr) if not mmaddr: mmaddr = '{}:{}'.format(g.proto.base_coin.lower(),coinaddr) mmaddr = TwMMGenID(mmaddr) cmt = TwComment(label,on_fail='return') if cmt in (False,None): return False lbl = TwLabel(mmaddr + ('',' '+cmt)[bool(cmt)]) # label is ASCII for now # NOTE: this works because importaddress() removes the old account before # associating the new account with the address. # Will be replaced by setlabel() with new RPC label API # RPC args: addr,label,rescan[=true],p2sh[=none] ret = g.rpch.importaddress(coinaddr,lbl,False,on_fail='return') from mmgen.rpc import rpc_error,rpc_errmsg if rpc_error(ret): msg('From {}: {}'.format(g.proto.daemon_name,rpc_errmsg(ret))) if not silent: msg('Label could not be {}'.format(('removed','added')[bool(label)])) return False else: m = mmaddr.type.replace('mmg','MMG') a = mmaddr.replace(g.proto.base_coin.lower()+':','') s = '{} address {} in tracking wallet'.format(m,a) if label: msg("Added label '{}' to {}".format(label,s)) else: msg('Removed label from {}'.format(s)) return True