diff --git a/crypto_dev_signer/eth/signer/defaultsigner.py b/crypto_dev_signer/eth/signer/defaultsigner.py index 18b881d..0c4b7f3 100644 --- a/crypto_dev_signer/eth/signer/defaultsigner.py +++ b/crypto_dev_signer/eth/signer/defaultsigner.py @@ -4,6 +4,10 @@ import logging # external imports import sha3 import coincurve +from hexathon import int_to_minbytes + +# local imports +from crypto_dev_signer.eth.encoding import chain_id_to_v logg = logging.getLogger().getChild(__name__) @@ -31,28 +35,18 @@ class ReferenceSigner(Signer): h = sha3.keccak_256() h.update(s) message_to_sign = h.digest() - z = self.sign(tx.sender, message_to_sign, password) - - vnum = int.from_bytes(tx.v, 'big') - v = (vnum * 2) + 35 + z[64] - byts = ((v.bit_length()-1)/8)+1 - tx.v = v.to_bytes(int(byts), 'big') - tx.r = z[:32] - tx.s = z[32:64] - - for i in range(len(tx.r)): - if tx.r[i] > 0: - tx.r = tx.r[i:] - break - - for i in range(len(tx.s)): - if tx.s[i] > 0: - tx.s = tx.s[i:] - break + z = self.sign_pure(tx.sender, message_to_sign, password) return z + def sign_transaction_to_rlp(self, tx, password=None): + chain_id = int.from_bytes(tx.v, byteorder='big') + sig = self.sign_transaction(tx, password) + tx.apply_signature(chain_id, sig) + return tx.rlp_serialize() + + def sign_ethereum_message(self, address, message, password=None): #k = keys.PrivateKey(self.keyGetter.get(address, password)) @@ -73,12 +67,12 @@ class ReferenceSigner(Signer): h.update(ethereumed_message_header + message) message_to_sign = h.digest() - z = self.sign(address, message_to_sign, password) + z = self.sign_pure(address, message_to_sign, password) return z # TODO: generic sign should be moved to non-eth context - def sign(self, address, message, password=None): + def sign_pure(self, address, message, password=None): pk = coincurve.PrivateKey(secret=self.keyGetter.get(address, password)) z = pk.sign_recoverable(hasher=None, message=message) return z diff --git a/crypto_dev_signer/eth/transaction.py b/crypto_dev_signer/eth/transaction.py index 8fb9304..2e2cc92 100644 --- a/crypto_dev_signer/eth/transaction.py +++ b/crypto_dev_signer/eth/transaction.py @@ -1,15 +1,20 @@ # standard imports import logging import binascii +import re # external imports from rlp import encode as rlp_encode from hexathon import ( strip_0x, add_0x, + int_to_minbytes, ) -logg = logging.getLogger(__name__) +# local imports +from crypto_dev_signer.eth.encoding import chain_id_to_v + +logg = logging.getLogger().getChild(__name__) class Transaction: @@ -23,54 +28,55 @@ class Transaction: class EIP155Transaction: - def __init__(self, tx, nonce, chainId=1): - to = None - data = None - if tx['to'] != None: - #to = binascii.unhexlify(strip_0x(tx['to'], allow_empty=True)) + def __init__(self, tx, nonce_in, chainId_in=1): + to = b'' + data = b'' + if tx.get('to') != None: to = bytes.fromhex(strip_0x(tx['to'], allow_empty=True)) - if tx['data'] != None: - #data = binascii.unhexlify(strip_0x(tx['data'], allow_empty=True)) + if tx.get('data') != None: data = bytes.fromhex(strip_0x(tx['data'], allow_empty=True)) gas_price = None start_gas = None value = None + nonce = None + chainId = None + # TODO: go directly from hex to bytes try: gas_price = int(tx['gasPrice']) + byts = ((gas_price.bit_length()-1)/8)+1 + gas_price = gas_price.to_bytes(int(byts), 'big') except ValueError: - gas_price = int(tx['gasPrice'], 16) - byts = ((gas_price.bit_length()-1)/8)+1 - gas_price = gas_price.to_bytes(int(byts), 'big') + gas_price = bytes.fromhex(strip_0x(tx['gasPrice'], allow_empty=True)) try: start_gas = int(tx['gas']) + byts = ((start_gas.bit_length()-1)/8)+1 + start_gas = start_gas.to_bytes(int(byts), 'big') except ValueError: - start_gas = int(tx['gas'], 16) - byts = ((start_gas.bit_length()-1)/8)+1 - start_gas = start_gas.to_bytes(int(byts), 'big') + start_gas = bytes.fromhex(strip_0x(tx['gas'], allow_empty=True)) try: value = int(tx['value']) + byts = ((value.bit_length()-1)/8)+1 + value = value.to_bytes(int(byts), 'big') except ValueError: - value = int(tx['value'], 16) - byts = ((value.bit_length()-1)/8)+1 - value = value.to_bytes(int(byts), 'big') + value = bytes.fromhex(strip_0x(tx['value'], allow_empty=True)) try: - nonce = int(nonce) + nonce = int(nonce_in) + byts = ((nonce.bit_length()-1)/8)+1 + nonce = nonce.to_bytes(int(byts), 'big') except ValueError: - nonce = int(nonce, 16) - byts = ((nonce.bit_length()-1)/8)+1 - nonce = nonce.to_bytes(int(byts), 'big') + nonce = bytes.fromhex(strip_0x(nonce_in, allow_empty=True)) try: - chainId = int(chainId) + chainId = int(chainId_in) + byts = ((chainId.bit_length()-1)/8)+1 + chainId = chainId.to_bytes(int(byts), 'big') except ValueError: - chainId = int(chainId, 16) - byts = ((chainId.bit_length()-1)/8)+1 - chainId = chainId.to_bytes(int(byts), 'big') + chainId = bytes.fromhex(strip_0x(chainId_in, allow_empty=True)) self.nonce = nonce self.gas_price = gas_price @@ -120,7 +126,7 @@ class EIP155Transaction: 'gas': add_0x(self.start_gas.hex()), 'to': add_0x(self.to.hex()), 'value': add_0x(self.value.hex(), allow_empty=True), - 'data': add_0x(self.data.hex()), + 'data': add_0x(self.data.hex(), allow_empty=True), 'v': add_0x(self.v.hex(), allow_empty=True), 'r': add_0x(self.r.hex(), allow_empty=True), 's': add_0x(self.s.hex(), allow_empty=True), @@ -135,3 +141,24 @@ class EIP155Transaction: tx['nonce'] = '0x00' return tx + + + def apply_signature(self, chain_id, signature): + if len(self.r + self.s) > 0: + raise AttributeError('signature already set') + if len(signature) < 65: + raise ValueError('invalid signature length') + v = chain_id_to_v(chain_id, signature) + self.v = int_to_minbytes(v) + self.r = signature[:32] + self.s = signature[32:64] + + for i in range(len(self.r)): + if self.r[i] > 0: + self.r = self.r[i:] + break + + for i in range(len(self.s)): + if self.s[i] > 0: + self.s = self.s[i:] + break diff --git a/crypto_dev_signer/runnable/signer.py b/crypto_dev_signer/runnable/signer.py index b786524..9014cc2 100755 --- a/crypto_dev_signer/runnable/signer.py +++ b/crypto_dev_signer/runnable/signer.py @@ -102,12 +102,13 @@ def personal_new_account(p): return r -def personal_sign_transaction(p): +def personal_signTransaction(p): logg.debug('got {} to sign'.format(p[0])) #t = EIP155Transaction(p[0], p[0]['nonce'], 8995) t = EIP155Transaction(p[0], p[0]['nonce'], int(p[0]['chainId'])) - z = signer.sign_transaction(t, p[1]) - raw_signed_tx = t.rlp_serialize() + # z = signer.sign_transaction(t, p[1]) + # raw_signed_tx = t.rlp_serialize() + raw_signed_tx = signer.sign_transaction_to_rlp(t, p[1]) o = { 'raw': '0x' + raw_signed_tx.hex(), 'tx': t.serialize(), @@ -116,9 +117,9 @@ def personal_sign_transaction(p): return o -# TODO: temporary workaround for platform, since personal_sign_transaction is missing from web3.py -def eth_sign_transaction(tx): - return personal_sign_transaction([tx[0], '']) +# TODO: temporary workaround for platform, since personal_signTransaction is missing from web3.py +def eth_signTransaction(tx): + return personal_signTransaction([tx[0], '']) def eth_sign(p): @@ -132,8 +133,8 @@ def eth_sign(p): methods = { 'personal_newAccount': personal_new_account, - 'personal_sign_transaction': personal_sign_transaction, - 'eth_sign_transaction': eth_sign_transaction, + 'personal_signTransaction': personal_signTransaction, + 'eth_signTransaction': eth_signTransaction, 'eth_sign': eth_sign, } diff --git a/requirements.txt b/requirements.txt index 6a7153f..2353faf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,9 +3,10 @@ cryptography==3.2.1 pysha3==1.0.2 #simple-rlp==0.1.2 rlp==2.0.1 +eth-utils==1.10.0 json-rpc==1.13.0 confini~=0.3.6a3 sqlalchemy==1.3.20 coincurve==15.0.0 pycrypto==2.6.1 -hexathon~=0.0.1a5 +hexathon~=0.0.1a6 diff --git a/setup.py b/setup.py index da73111..b278bd8 100644 --- a/setup.py +++ b/setup.py @@ -24,7 +24,7 @@ f.close() setup( name="crypto-dev-signer", - version="0.4.14a10", + version="0.4.14a14", description="A signer and keystore daemon and library for cryptocurrency software development", author="Louis Holbrook", author_email="dev@holbrook.no",