From 9d694488d9065a8858f71786de4c87ddb51e6808 Mon Sep 17 00:00:00 2001 From: MMGen Date: Wed, 31 Oct 2018 18:13:27 +0000 Subject: [PATCH] py3port: Ethereum-related changes --- mmgen/altcoins/eth/contract.py | 33 +++++++++++------- mmgen/altcoins/eth/tx.py | 62 +++++++++++++++------------------- 2 files changed, 49 insertions(+), 46 deletions(-) diff --git a/mmgen/altcoins/eth/contract.py b/mmgen/altcoins/eth/contract.py index 843acf19..190683c2 100755 --- a/mmgen/altcoins/eth/contract.py +++ b/mmgen/altcoins/eth/contract.py @@ -42,10 +42,10 @@ class Token(MMGenObject): # ERC20 def __init__(self,addr,decimals=None): self.addr = TokenAddr(addr) if decimals is None: - ret_hex = self.do_call('decimals()') - try: decimals = int(ret_hex,16) - except: raise TokenNotInBlockchain("Token '{}' not in blockchain".format(addr)) - self.base_unit = Decimal(10) ** -decimals + decimals = self.decimals() + if not decimals: + raise TokenNotInBlockchain("Token '{}' not in blockchain".format(addr)) + self.base_unit = Decimal('10') ** -decimals def transferdata2amt(self,data): # online return ETHAmt(int(parse_abi(data)[-1],16) * self.base_unit) @@ -61,10 +61,17 @@ class Token(MMGenObject): # ERC20 return self.do_call('balanceOf(address)',acct_addr.rjust(64,'0'),toUnit=True) def strip(self,s): - return ''.join(ch for ch in s if 32 <= ord(ch) <= 127).strip() + return ''.join([chr(b) for b in s if 32 <= b <= 127]).strip() def total_supply(self): return self.do_call('totalSupply()',toUnit=True) - def decimals(self): return int(self.do_call('decimals()'),16) + def decimals(self): + ret = self.do_call('decimals()') + try: + a,b = ret[:2],ret[2:] + assert a == '0x' and is_hex_str_lc(b) + except: + "RPC call to decimals() failed (returned '{}')".format(ret) + return int(b,16) if b else None def name(self): return self.strip(unhexlify(self.do_call('name()')[2:])) def symbol(self): return self.strip(unhexlify(self.do_call('symbol()')[2:])) @@ -79,12 +86,6 @@ class Token(MMGenObject): # ERC20 def code(self): return g.rpch.eth_getCode('0x'+self.addr)[2:] - def transfer_from(self,from_addr,to_addr,amt,key,start_gas,gasPrice): - raise NotImplementedError('method not implemented') - return self.transfer( from_addr,to_addr,amt,key,start_gas,gasPrice, - method_sig='transferFrom(address,address,uint256)', - from_addr2=from_addr) - def create_data(self,to_addr,amt,method_sig='transfer(address,uint256)',from_addr=None): from_arg = from_addr.rjust(64,'0') if from_addr else '' to_arg = to_addr.rjust(64,'0') @@ -118,6 +119,8 @@ class Token(MMGenObject): # ERC20 pmsg(tx.to_dict()) return hex_tx,coin_txid +# The following are used for token deployment only: + def txsend(self,hex_tx): return g.rpch.eth_sendRawTransaction('0x'+hex_tx.decode()).replace('0x','',1).encode() @@ -132,3 +135,9 @@ class Token(MMGenObject): # ERC20 from_addr2=from_addr2) (hex_tx,coin_txid) = self.txsign(tx_in,key,from_addr) return self.txsend(hex_tx) + + def transfer_from(self,from_addr,to_addr,amt,key,start_gas,gasPrice): + raise NotImplementedError('method not implemented') + return self.transfer( from_addr,to_addr,amt,key,start_gas,gasPrice, + method_sig='transferFrom(address,address,uint256)', + from_addr2=from_addr) diff --git a/mmgen/altcoins/eth/tx.py b/mmgen/altcoins/eth/tx.py index 80a6c534..3e9468b7 100755 --- a/mmgen/altcoins/eth/tx.py +++ b/mmgen/altcoins/eth/tx.py @@ -151,7 +151,8 @@ class EthereumMMGenTX(MMGenTX): o_num = len(self.outputs) assert o_num in o_ok,'Transaction has invalid number of outputs!'.format(o_num) self.make_txobj() - self.hex = json.dumps(dict([(k,str(v))for k,v in list(self.txobj.items())])) + ol = [(k,v.decode() if issubclass(type(v),bytes) else str(v)) for k,v in self.txobj.items()] + self.hex = json.dumps(dict(ol)).encode() self.update_txid() def del_output(self,idx): pass @@ -278,7 +279,6 @@ class EthereumMMGenTX(MMGenTX): return m.format(ETHAmt(chg).hl(),g.coin) def do_sign(self,d,wif,tx_num_str): - d_in = {'to': unhexlify(d['to']), 'startgas': d['startGas'].toWei(), 'gasprice': d['gasPrice'].toWei(), @@ -286,24 +286,15 @@ class EthereumMMGenTX(MMGenTX): 'nonce': d['nonce'], 'data': unhexlify(d['data'])} - msg_r('Signing transaction{}...'.format(tx_num_str)) - - try: - from ethereum.transactions import Transaction - etx = Transaction(**d_in) - etx.sign(wif,d['chainId']) - import rlp - self.hex = hexlify(rlp.encode(etx)) - self.coin_txid = CoinTxID(hexlify(etx.hash)) - msg('OK') - if d['data']: - self.token_addr = TokenAddr(hexlify(etx.creates)) - except Exception as e: - m = "{!r}: transaction signing failed!" - msg(m.format(e.args[0])) - return False - - return self.check_sigs() + from ethereum.transactions import Transaction + etx = Transaction(**d_in) + etx.sign(wif,d['chainId']) + import rlp + self.hex = hexlify(rlp.encode(etx)) + self.coin_txid = CoinTxID(hexlify(etx.hash)) + if d['data']: + self.token_addr = TokenAddr(hexlify(etx.creates).decode()) + assert self.check_sigs(),'Signature check failed' def sign(self,tx_num_str,keys): # return True or False; don't exit or raise exception @@ -314,10 +305,21 @@ class EthereumMMGenTX(MMGenTX): if not self.check_correct_chain(on_fail='return'): return False - return self.do_sign(self.txobj,keys[0].sec.wif,tx_num_str) + msg_r('Signing transaction{}...'.format(tx_num_str)) + + try: + self.do_sign(self.txobj,keys[0].sec.wif,tx_num_str) + msg('OK') + return True + except Exception as e: + if os.getenv('MMGEN_TRACEBACK'): + import traceback + ymsg('\n'+''.join(traceback.format_exception(*sys.exc_info()))) + m = "{!r}: transaction signing failed!" + msg(m.format(e.args[0])) + return False def is_in_mempool(self): -# pmsg(g.rpch.parity_pendingTransactions()) return '0x'+self.coin_txid.decode() in [x['hash'] for x in g.rpch.parity_pendingTransactions()] def is_in_wallet(self): @@ -454,18 +456,10 @@ class EthereumTokenMMGenTX(EthereumMMGenTX): def do_sign(self,d,wif,tx_num_str): d = self.txobj - msg_r('Signing transaction{}...'.format(tx_num_str)) - try: - t = Token(d['token_addr'],decimals=d['decimals']) - tx_in = t.txcreate(d['from'],d['to'],d['amt'],self.start_gas,d['gasPrice'],nonce=d['nonce']) - (self.hex,self.coin_txid) = t.txsign(tx_in,wif,d['from'],chain_id=d['chainId']) - msg('OK') - except Exception as e: - m = "{!r}: transaction signing failed!" - msg(m.format(e.args[0])) - return False - - return self.check_sigs() + t = Token(d['token_addr'],decimals=d['decimals']) + tx_in = t.txcreate(d['from'],d['to'],d['amt'],self.start_gas,d['gasPrice'],nonce=d['nonce']) + (self.hex,self.coin_txid) = t.txsign(tx_in,wif,d['from'],chain_id=d['chainId']) + assert self.check_sigs(),'Signature check failed' class EthereumMMGenBumpTX(EthereumMMGenTX,MMGenBumpTX):