Skip to content
This repository has been archived by the owner on Sep 28, 2023. It is now read-only.

WIP: Feature/transaction rlp update #1

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
6 changes: 3 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ chains

# tox/pytest cache
.cache
.pytest_cache

# Test output logs
logs
Expand Down Expand Up @@ -83,9 +84,6 @@ logs
# Mongo Explorer plugin:
.idea/mongoSettings.xml

# VIM temp files
*.swp

## File-based project format:
*.iws

Expand All @@ -106,3 +104,5 @@ crashlytics.properties
crashlytics-build.properties
fabric.properties

# virtualenv default location
venv*
9 changes: 6 additions & 3 deletions eth_account/account.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,10 @@
to_bytes,
to_int,
)
from eth_utils import (
blake2b
)

from hexbytes import (
HexBytes,
)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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),
Expand Down
16 changes: 8 additions & 8 deletions eth_account/internal/signing.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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):
Expand Down
50 changes: 34 additions & 16 deletions eth_account/internal/transactions.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import itertools
import time

from cytoolz import (
curry,
Expand Down Expand Up @@ -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):
Expand All @@ -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)


Expand All @@ -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),
Expand All @@ -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)),
Expand All @@ -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
}

Expand Down Expand Up @@ -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)),
)


Expand Down
4 changes: 2 additions & 2 deletions tests/core/test_accounts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down