diff --git a/.gitignore b/.gitignore index 58654f8..b89cd4e 100644 --- a/.gitignore +++ b/.gitignore @@ -54,6 +54,7 @@ chains # tox/pytest cache .cache +.pytest_cache # Test output logs logs @@ -83,9 +84,6 @@ logs # Mongo Explorer plugin: .idea/mongoSettings.xml -# VIM temp files -*.swp - ## File-based project format: *.iws @@ -106,3 +104,5 @@ crashlytics.properties crashlytics-build.properties fabric.properties +# virtualenv default location +venv* diff --git a/eth_account/account.py b/eth_account/account.py index d797005..bf662fe 100644 --- a/eth_account/account.py +++ b/eth_account/account.py @@ -27,6 +27,10 @@ to_bytes, to_int, ) +from eth_utils import ( + blake2b +) + from hexbytes import ( HexBytes, ) @@ -81,7 +85,7 @@ def create(self, extra_entropy=''): # but without the private key argument ''' extra_key_bytes = text_if_str(to_bytes, extra_entropy) - key_bytes = keccak(os.urandom(32) + extra_key_bytes) + key_bytes = blake2b(os.urandom(32) + extra_key_bytes) return self.privateKeyToAccount(key_bytes) @staticmethod @@ -433,8 +437,7 @@ def signTransaction(self, transaction_dict, private_key): s, rlp_encoded, ) = sign_transaction_dict(account._key_obj, sanitized_transaction) - - transaction_hash = keccak(rlp_encoded) + transaction_hash = blake2b(rlp_encoded, digest_size=32) return AttributeDict({ 'rawTransaction': HexBytes(rlp_encoded), diff --git a/eth_account/internal/signing.py b/eth_account/internal/signing.py index 5cad567..8a803b7 100644 --- a/eth_account/internal/signing.py +++ b/eth_account/internal/signing.py @@ -31,12 +31,12 @@ def sign_transaction_dict(eth_key, transaction_dict): chain_id = unsigned_transaction.v # sign with private key - (v, r, s) = sign_transaction_hash(eth_key, transaction_hash, chain_id) - + (_, r, s, sign_bytes) = sign_transaction_hash(eth_key, transaction_hash, chain_id) + pub_key = bytes(eth_key.public_key.to_bytes()) # serialize transaction with rlp - encoded_transaction = encode_transaction(unsigned_transaction, vrs=(v, r, s)) - - return (v, r, s, encoded_transaction) + sign_bytes = pub_key + sign_bytes + encoded_transaction = encode_transaction(unsigned_transaction, sig_bytes=sign_bytes) + return (1, r, s, encoded_transaction) # watch here for updates to signature format: https://github.com/ethereum/EIPs/issues/191 @@ -116,9 +116,9 @@ def to_eth_v(v_raw, chain_id=None): def sign_transaction_hash(account, transaction_hash, chain_id): signature = account.sign_msg_hash(transaction_hash) - (v_raw, r, s) = signature.vrs - v = to_eth_v(v_raw, chain_id) - return (v, r, s) + (_, r, s) = signature.vrs + sig_bytes = signature.signature_bytes + return (1, r, s, sig_bytes) def _pad_to_eth_word(bytes_val): diff --git a/eth_account/internal/transactions.py b/eth_account/internal/transactions.py index 6261a25..edd448e 100644 --- a/eth_account/internal/transactions.py +++ b/eth_account/internal/transactions.py @@ -1,4 +1,5 @@ import itertools +import time from cytoolz import ( curry, @@ -26,9 +27,11 @@ import rlp from rlp.sedes import ( Binary, + BigEndianInt, big_endian_int, binary, ) +import hexbytes def serializable_unsigned_transaction_from_dict(transaction_dict): @@ -47,10 +50,9 @@ def serializable_unsigned_transaction_from_dict(transaction_dict): return serializer.from_dict(filled_transaction) -def encode_transaction(unsigned_transaction, vrs): - (v, r, s) = vrs - chain_naive_transaction = dissoc(unsigned_transaction.as_dict(), 'v', 'r', 's') - signed_transaction = Transaction(v=v, r=r, s=s, **chain_naive_transaction) +def encode_transaction(unsigned_transaction, sig_bytes): + chain_naive_transaction = dissoc(unsigned_transaction.as_dict()) + signed_transaction = Transaction(s=sig_bytes, **chain_naive_transaction) return rlp.encode(signed_transaction) @@ -76,17 +78,27 @@ def is_none(val): return val is None +def int_to_hex(val): + return bytes(hex(val), encoding='utf-8') + + TRANSACTION_DEFAULTS = { 'to': b'', 'value': 0, 'data': b'', - 'chainId': None, + 'tx_type': b'\x01', + 'timestamp': bytes(hexbytes.HexBytes(int(time.time()*1000.0))), + 'gasPrice': b'R\x08' } TRANSACTION_FORMATTERS = { 'nonce': hexstr_if_str(to_int), - 'gasPrice': hexstr_if_str(to_int), - 'gas': hexstr_if_str(to_int), + # 'gas': hexstr_if_str(to_int), + # 'gasPrice': apply_one_of_formatters(( + # (is_string, hexstr_if_str(to_bytes)), + # (is_bytes, identity), + # (is_none, lambda val: b''), + # )), 'to': apply_one_of_formatters(( (is_string, hexstr_if_str(to_bytes)), (is_bytes, identity), @@ -96,13 +108,17 @@ def is_none(val): 'data': hexstr_if_str(to_bytes), 'v': hexstr_if_str(to_int), 'r': hexstr_if_str(to_int), - 's': hexstr_if_str(to_int), + 's': apply_one_of_formatters(( + (is_string, hexstr_if_str(to_bytes)), + (is_bytes, identity), + (is_none, lambda val: b''), + )), } TRANSACTION_VALID_VALUES = { - 'nonce': is_int_or_prefixed_hexstr, + 'nonce': lambda val: isinstance(val, (int, str, bytes, bytearray)), 'gasPrice': is_int_or_prefixed_hexstr, - 'gas': is_int_or_prefixed_hexstr, + 'gas': lambda val: isinstance(val, (int, str, bytes, bytearray)), 'to': is_empty_or_checksum_address, 'value': is_int_or_prefixed_hexstr, 'data': lambda val: isinstance(val, (int, str, bytes, bytearray)), @@ -116,6 +132,8 @@ def is_none(val): 'to', 'value', 'data', + 'tx_type', + 'timestamp', 'chainId', # set chainId to None if you want a transaction that can be replayed across networks } @@ -156,19 +174,19 @@ def fill_transaction_defaults(transaction): UNSIGNED_TRANSACTION_FIELDS = ( ('nonce', big_endian_int), - ('gasPrice', big_endian_int), - ('gas', big_endian_int), - ('to', Binary.fixed_length(20, allow_empty=True)), + ('to', Binary.fixed_length(32, allow_empty=True)), ('value', big_endian_int), ('data', binary), + ('timestamp', binary), + ('gas', binary), + ('gasPrice', binary), + ('tx_type', binary), ) class Transaction(HashableRLP): fields = UNSIGNED_TRANSACTION_FIELDS + ( - ('v', big_endian_int), - ('r', big_endian_int), - ('s', big_endian_int), + ('s', Binary.fixed_length(96, allow_empty=True)), ) diff --git a/tests/core/test_accounts.py b/tests/core/test_accounts.py index 5234470..946b320 100644 --- a/tests/core/test_accounts.py +++ b/tests/core/test_accounts.py @@ -54,13 +54,13 @@ ] -PRIVATE_KEY_AS_BYTES = b'unicorns' * 4 +PRIVATE_KEY_AS_BYTES = b'unicorns' * 8 PRIVATE_KEY_AS_HEXSTR = '0x756e69636f726e73756e69636f726e73756e69636f726e73756e69636f726e73' PRIVATE_KEY_AS_INT = 0x756e69636f726e73756e69636f726e73756e69636f726e73756e69636f726e73 PRIVATE_KEY_AS_OBJ = keys.PrivateKey(PRIVATE_KEY_AS_BYTES) ACCT_ADDRESS = '0xa79F6f349C853F9Ea0B29636779ae3Cb4E3BA729' -PRIVATE_KEY_AS_BYTES_ALT = b'rainbows' * 4 +PRIVATE_KEY_AS_BYTES_ALT = b'rainbows' * 8 PRIVATE_KEY_AS_HEXSTR_ALT = '0x7261696e626f77737261696e626f77737261696e626f77737261696e626f7773' PRIVATE_KEY_AS_INT_ALT = 0x7261696e626f77737261696e626f77737261696e626f77737261696e626f7773 PRIVATE_KEY_AS_OBJ_ALT = keys.PrivateKey(PRIVATE_KEY_AS_BYTES_ALT)