From d165eadc2279c0895507cad20087ea3a6cd0a893 Mon Sep 17 00:00:00 2001 From: Konrad Feldmeier Date: Tue, 1 Nov 2016 09:37:49 +0100 Subject: [PATCH] Add more tests #417 --- ethereum/tests/test_transactions.py | 49 +++++++++++++++++++++++++---- ethereum/transactions.py | 34 +++++++++++++++----- 2 files changed, 70 insertions(+), 13 deletions(-) diff --git a/ethereum/tests/test_transactions.py b/ethereum/tests/test_transactions.py index 83ce53eed..931542c23 100644 --- a/ethereum/tests/test_transactions.py +++ b/ethereum/tests/test_transactions.py @@ -1,3 +1,4 @@ +from operator import attrgetter import ethereum.transactions as transactions import ethereum.utils as utils import rlp @@ -14,8 +15,6 @@ # hint: use 'py.test' with the '-s' option to dump logs to the console # configure_logging(':trace') -encode_hex('') - def test_eip155_transaction(): """Replicate the example from https://github.com/ethereum/eips/issues/155 @@ -28,6 +27,7 @@ def test_eip155_transaction(): value = 10 ** 18 data = '' private_key = "4646464646464646464646464646464646464646464646464646464646464646" + sender = utils.privtoaddr(private_key) old_style = transactions.Transaction(nonce, gasprice, startgas, to, value, data) new_style = transactions.EIP155Transaction(nonce, gasprice, startgas, to, value, data) @@ -45,7 +45,7 @@ def test_eip155_transaction(): 0 ) - assert rlp.encode(new_signing_data_sedes, transactions.Transaction) == decode_hex(new_signing_data) + assert rlp.encode(new_signing_data_sedes, transactions.EIP155Transaction) == decode_hex(new_signing_data) new_signing_hash = "ac9813f00ec955e65a50cc778243f6c22dcfe9d64253462b16187f1c99e0a8fa" @@ -58,16 +58,53 @@ def test_eip155_transaction(): ) new_style_signed = "f86e098504a817c800825208943535353535353535353535353535353535353535880de0b6b3a7640000801ca1a019ae791bb8378a38bb83f5b930fe78a0320cec27d86e5e258c69f0fa9541eb8da1a02bd8e0c5bde4c0800238ce5a59d2f3ce723f1e84a62cab53d961fe3b019d19fc" + new_deserialized = rlp.decode(decode_hex(new_style_signed), transactions.EIP155Transaction) + + old_style = old_style.sign(private_key) + new_style = new_style.sign(private_key) - old_style.sign(private_key) + assert not encode_hex(rlp.encode(old_style, transactions.Transaction)) == new_style_signed - new_style.sign(private_key) + # Check roundtrip serialization + roundtrip = rlp.decode( + decode_hex( + encode_hex( + rlp.encode(new_style, transactions.EIP155Transaction) + ) + ), transactions.EIP155Transaction) + for field, _ in transactions.EIP155Transaction.fields: + getter = attrgetter(field) + assert getter(new_style) == getter(roundtrip), field + # Check object values assert new_style.v == new_v assert new_style.r == new_r assert new_style.s == new_s - assert encode_hex(rlp.encode(new_style)) == new_style_signed + # Check hex rlp against provided hex + assert encode_hex(rlp.encode(new_style, transactions.EIP155Transaction)) == new_style_signed + + # Check against deserialized values + assert new_deserialized.v == new_style.v + assert new_deserialized.r == new_style.r + assert new_deserialized.s == new_style.s + + # Test sender recovery + assert old_style.sender == sender + assert new_style.sender == sender + assert new_deserialized.sender == sender + + # Check rlp against deserialized + new_rlp = rlp.decode(rlp.encode(new_style, transactions.EIP155Transaction)) + deserialized_rlp = rlp.decode(decode_hex(new_style_signed)) + assert len(new_rlp) == len(deserialized_rlp) + + for num, assert_ in enumerate( + [new_rlp[i] == deserialized_rlp[i] for i in range(len(new_rlp))]): + assert assert_, (new_rlp[num], deserialized_rlp[num], num) + + # Check hex rlp against provided hex + assert encode_hex(rlp.encode(new_style, transactions.EIP155Transaction)) == new_style_signed # TODO: test deserialize new style rlp fails with old_style Transaction class diff --git a/ethereum/transactions.py b/ethereum/transactions.py index c913a148a..6fc9abd37 100644 --- a/ethereum/transactions.py +++ b/ethereum/transactions.py @@ -2,7 +2,7 @@ import rlp from bitcoin import encode_pubkey, N, encode_privkey from rlp.sedes import big_endian_int, binary -from rlp.utils import encode_hex, str_to_bytes, ascii_chr, int_to_big_endian +from rlp.utils import encode_hex, str_to_bytes, ascii_chr from secp256k1 import PublicKey, ALL_FLAGS, PrivateKey from ethereum.exceptions import InvalidTransaction @@ -90,7 +90,10 @@ def sender(self): pk.public_key = pk.ecdsa_recover( rawhash, pk.ecdsa_recoverable_deserialize( - zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.r)), 32) + zpad(utils.bytearray_to_bytestr(int_to_32bytearray(self.s)), 32), + zpad(utils.bytearray_to_bytestr( + int_to_32bytearray(self.r)), 32) + + zpad(utils.bytearray_to_bytestr( + int_to_32bytearray(self.s)), 32), self.normalized_v ), raw=True @@ -112,7 +115,9 @@ def sender(self): def sender(self, value): self._sender = value - def sign(self, key, backwards_compatible=True): + def sign(self, key, + backwards_compatible=True, + ): """Sign this transaction with a private key. A potentially already existing signature would be overridden. @@ -132,7 +137,7 @@ def sign(self, key, backwards_compatible=True): self.to, self.value, self.data, - 18, + self.__class__.chain_id(), 0, 0), Transaction)) @@ -148,7 +153,7 @@ def sign(self, key, backwards_compatible=True): if backwards_compatible: self.v = utils.safe_ord(signature[64]) + 27 else: - self.v = utils.safe_ord(signature[64]) + (self.__class__._chain_id * 2 + 1) + self.v = utils.safe_ord(signature[64]) + (self.__class__.chain_id() * 2 + 1) self.r = big_endian_to_int(signature[0:32]) self.s = big_endian_to_int(signature[32:64]) @@ -166,7 +171,7 @@ def normalized_v(self): if self.v in (27, 28): return self.v - 27 else: - return self.v - (self.__class__._chain_id * 2 + 1) + return self.v - (self.__class__.chain_id() * 2 + 1) def log_bloom(self): "returns int" @@ -228,6 +233,10 @@ def check_low_s(self): if self.s > N // 2 or self.s == 0: raise InvalidTransaction("Invalid signature S value!") + @classmethod + def chain_id(cls): + return cls._chain_id + class EIP155Transaction(Transaction): # TODO: ensure EIP155Transaction is used after SPURIOUS_DRAGON_FORK_NUMBER @@ -237,7 +246,18 @@ def __init__(self, *args, **kwargs): super(EIP155Transaction, self).__init__(*args, **kwargs) def sign(self, key, backwards_compatible=False): - return super(self.__class__, self).sign(key, backwards_compatible) + return super(self.__class__, self).sign( + key, + backwards_compatible=backwards_compatible, + ) + + @classmethod + def chain_id(cls): + assert cls._chain_id == EIP155Transaction._chain_id + return cls._chain_id + + def __repr__(self): + return '' % encode_hex(self.hash)[:4] UnsignedTransaction = Transaction.exclude(['v', 'r', 's'])