From 607893f9ac0b37fbdd93f211677f70fcc5b3c79e Mon Sep 17 00:00:00 2001 From: Jochen Hoenicke Date: Sun, 22 Jan 2017 13:51:07 +0100 Subject: [PATCH] Ethereum: EIP-155 replay protection Added chain_id parameter to sign tx (and updated protobuf). Added a unit test with chain_id for Ropsten testnet. trezorctl: - Fixed compatibility with new ethjsonrpc - added chain_id parameter --- .../device_tests/test_msg_ethereum_signtx.py | 27 +++++++++++++++++++ trezorctl | 14 ++++++---- trezorlib/client.py | 5 +++- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/tests/device_tests/test_msg_ethereum_signtx.py b/tests/device_tests/test_msg_ethereum_signtx.py index 85ac47ef7..88fa96a45 100644 --- a/tests/device_tests/test_msg_ethereum_signtx.py +++ b/tests/device_tests/test_msg_ethereum_signtx.py @@ -153,5 +153,32 @@ class TestMsgEthereumSigntx(common.TrezorTest): to=binascii.unhexlify('1d1c328764a41bda0492b66baa30c4a339ff85ef'), value=12345678901234567890) + def test_ethereum_signtx_nodata_eip155(self): + self.setup_mnemonic_allallall() + + sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( + n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0], + nonce=0, + gas_price=20000000000, + gas_limit=21000, + to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'), + value=100000000000000000, + chain_id=3) + self.assertEqual(sig_v, 41) + self.assertEqual(binascii.hexlify(sig_r), 'a90d0bc4f8d63be69453dd62f2bb5fff53c610000abf956672564d8a654d401a') + self.assertEqual(binascii.hexlify(sig_s), '544a2e57bc8b4da18660a1e6036967ea581cc635f5137e3ba97a750867c27cf2') + + sig_v, sig_r, sig_s = self.client.ethereum_sign_tx( + n=[0x80000000 | 44, 0x80000000 | 1, 0x80000000, 0, 0], + nonce=1, + gas_price=20000000000, + gas_limit=21000, + to=binascii.unhexlify('8ea7a3fccc211ed48b763b4164884ddbcf3b0a98'), + value=100000000000000000, + chain_id=3) + self.assertEqual(sig_v, 42) + self.assertEqual(binascii.hexlify(sig_r), '699428a6950e23c6843f1bf3754f847e64e047e829978df80d55187d19a401ce') + self.assertEqual(binascii.hexlify(sig_s), '087343d0a3a2f10842218ffccb146b59a8431b6245ab389fde22dc833f171e6e') + if __name__ == '__main__': unittest.main() diff --git a/trezorctl b/trezorctl index fb1dea5b8..99de41d0c 100755 --- a/trezorctl +++ b/trezorctl @@ -182,6 +182,8 @@ class Commands(object): host, port = args.host.split(':') eth = EthJsonRpc(host, int(port)) + if not args.data: + args.gata = '' if args.data.startswith('0x'): args.data = args.data[2:] data = binascii.unhexlify(args.data) @@ -189,12 +191,12 @@ class Commands(object): if not gas_price: gas_price = eth.eth_gasPrice() - if args.data: - gas_limit = hex_to_dec(eth.eth_estimateGas( + if not gas_limit: + gas_limit = eth.eth_estimateGas( to_address=args.to, from_address=address, - value=value, - data="0x"+args.data)) + value=("0x%x" % value), + data="0x"+args.data) if not nonce: nonce = eth.eth_getTransactionCount(address) @@ -206,7 +208,8 @@ class Commands(object): gas_limit=gas_limit, to=to_address, value=value, - data=data) + data=data, + chain_id=args.chain_id) transaction = rlp.encode( (nonce, gas_price, gas_limit, to_address, value, data) + sig) @@ -411,6 +414,7 @@ class Commands(object): ethereum_sign_tx.arguments = ( (('-a', '--host'), {'type': str, 'default': 'localhost:8545'}), + (('-c', '--chain-id'), {'type' : int, 'help': 'EIP-155 chain id (replay protection)', 'default': None}), (('-n', '-address'), {'type': str}), (('-v', '--value'), {'type': str, 'default': "0"}), (('-g', '--gas'), {'type': int, 'help': 'Required for offline signing'}), diff --git a/trezorlib/client.py b/trezorlib/client.py index be3ecbd2f..540510e67 100644 --- a/trezorlib/client.py +++ b/trezorlib/client.py @@ -503,7 +503,7 @@ class ProtocolMixin(object): return self.call(proto.EthereumGetAddress(address_n=n, show_display=show_display)) @session - def ethereum_sign_tx(self, n, nonce, gas_price, gas_limit, to, value, data=None): + def ethereum_sign_tx(self, n, nonce, gas_price, gas_limit, to, value, data=None, chain_id=None): def int_to_big_endian(value): import rlp.utils if value == 0: @@ -527,6 +527,9 @@ class ProtocolMixin(object): data, chunk = data[1024:], data[:1024] msg.data_initial_chunk = chunk + if chain_id: + msg.chain_id = chain_id + response = self.call(msg) while response.HasField('data_length'):