Skip to content

Commit

Permalink
Updates & Fix
Browse files Browse the repository at this point in the history
- use checksumed address
- return r and s as int
- Add instruction for ledger init using a well defined seed
- add ledger as a sessions fixture
- add tx_signed and addresses values related to the new seed
- update tests to use new defined seed
  • Loading branch information
bargst committed Jul 25, 2018
1 parent 7e10e29 commit cb03372
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 39 deletions.
13 changes: 7 additions & 6 deletions eth_account/signers/ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from eth_utils import (
to_bytes,
to_hex,
to_int,
)
from eth_utils.curried import (
keccak,
Expand Down Expand Up @@ -301,15 +302,15 @@ def get_address(self, account_id):
address = result[offset + 1: offset + 1 + result[offset]]
address = address.decode() # Use decode() to convert from bytearray

return eth_utils.to_normalized_address(address)
return eth_utils.to_checksum_address(address)

def get_account_id(self, address, search_limit=20, search_account_id=0):
'''
Convert an address to the HD wallet tree account_id
Start search at an account_id. This would allow to search deeper if required.
Default search_limit at 20 take about 5s to reach.
'''
address = eth_utils.to_normalized_address(address)
address = eth_utils.to_checksum_address(address)

for account_id in itertools.count(start=search_account_id):
if account_id > search_limit:
Expand Down Expand Up @@ -378,8 +379,8 @@ def signTransaction(self, transaction_dict):
'rawTransaction': HexBytes(rlp_encoded),
'hash': HexBytes(transaction_hash),
'v': v,
'r': to_hex(r),
's': to_hex(s),
'r': to_int(r),
's': to_int(s),
})

def defunctSignMessage(self, primitive=None, hexstr=None, text=None):
Expand Down Expand Up @@ -423,8 +424,8 @@ def defunctSignMessage(self, primitive=None, hexstr=None, text=None):
'messageHash': HexBytes(defunct_hash_message(message_bytes)),
'signature': HexBytes(to_bytes(r) + to_bytes(s) + to_bytes(v)),
'v': v,
'r': to_hex(r),
's': to_hex(s),
'r': to_int(r),
's': to_int(s),
})

def get_version(self):
Expand Down
11 changes: 10 additions & 1 deletion tests/integration/ledger/README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,15 @@
To run the test a ledger device must be connected and the Ethereum application running on it.

Install ledger requirement:
The following seed must be used to initilalize the ledger. Without it the tests
would fail on addresses, hash and signature assertion.
```
later galaxy arrange short length wisdom
impact balcony vague coast lunch promote
dust save enroll gain wreck welcome
rotate elder abandon grain master eager
```

Install ledger requirement:
```sh
# in the project root:
pip install -e .[ledger]
Expand Down
107 changes: 75 additions & 32 deletions tests/integration/ledger/test_ledger.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# Require a connected Ledger ...
# This test requires a connected Ledger
# For setup instructions see tests/integration/ledger/README.md

import pytest

Expand All @@ -16,13 +17,10 @@
LedgerAccount,
)

# Not a fixture because it does not support multiple LedgerAccount
ledger = LedgerAccount()


@pytest.fixture
def acct(request):
return Account
@pytest.fixture(scope="session")
def ledger():
return LedgerAccount()


@pytest.fixture
Expand All @@ -43,74 +41,119 @@ def transaction():


@pytest.fixture
def tx_hash():
return HexBytes('0xdd4998746632108893ed905116ec4c1839833f6f3c9ae276b6550e15bad308c8')
def tx_signed():
return {
'simple': {
'hash': HexBytes('0xddeb184cc6402d5b37fd457dd28bb1329b48114c3b2dd39cac1807262f80e07d'),
'rawTransaction': HexBytes('0xf8638083010000831e8480940000000000000000000000000000000000000000808026a0d216b59d9bb89085f9e3ca5624ffdcbea221509972968a64667eb8034640f4e4a075905d377e379a02a1e0499712ffbab3538bc0360714b8988e2c7d1f58b78956'), # noqa: E501
'v': 38,
'r': 95025822303110792506668203374001502991324810769163693561517488621294756885732,
's': 53175672620069794921739924167813457113862585588172053377671074506702685964630,
},
'small': {
'hash': HexBytes('0xd4a925de505cdc9bfb09a9f27c2d76f4237e311ff283d720534c9541621d62e3'),
'rawTransaction': HexBytes('0xf8e48083010000831e848094000000000000000000000000000000000000000080b880000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000026a0938d206f5467cec8242873868cf67f17ed9436d52189136a7363506f047c6abda075d73b2d222875ead6143f1e7b5700520ff5419fa5584ba496a156efb66d71f4'), # noqa: E501
'v': 38,
'r': 66739338035200025161165992676977142706893094614685038989477601075187882420925,
's': 53300883822998843183727929873017963769951929663427607731405578919018633196020,
},
'large': {
'hash': HexBytes('0x3eb0dcee10bfcbe57840677c20e5cafd51e0695fa5c35214c6be0a812e4ac371'),
'rawTransaction': HexBytes('0xf904778083010000831e848094000000000000000000000000000000000000000080b904120000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000025a0b523071692b885795b091b7ac9694dfcd2e6c46e0d74242b7d546e71348b7af5a00d07d6abb27550be3bee0ef7905fdd7ca5df5cd2404387e7ca229f623649ad4c'), # noqa: E501
'v': 37,
'r': 81930514161633216236135182913840266223031273831673753321895122440148646329077,
's': 5893916563698853677037385304030355005909465428868517353045734843684466830668,
},
'message': {
'messageHash': HexBytes('0x1476abb745d423bf09273f1afd887d951181d25adc66c4834a70491911b7f750'), # noqa: E501
'signature': HexBytes('0x1583c1cf809d2fc5294083980646afaaaf63a92acfbe5602696c31275d14b52444adcb417c25bd7d94b7726328c0fc58119b8e7581a6c0912ce05477f442290c1b'), # noqa: E501
'v': 27,
'r': 9731364417033089333147427310826421126925904544498616548944720677021727110436,
's': 31064341065847956331275303604190402864644327470418816746115010225653627889932,
}

}


def test_address():
@pytest.fixture
def addresses():
return ['0x27a1CC26f44c83660F766c024DfC8546Eff75A91',
'0xF0BE54E21F0A041f2Fb9F3F21AF9F90B15463453',
'0x341f603fDa3Df004c8Daa52ABe7f4073fe087255',
'0x8C0F878DC84B7Af40F4BD4313d522aEabA740774',
'0x9c13Bbe90eD988A0dB372eFEeA13AE1bcA53a307',
'0x242db61e0D66c33C415fd2D43323357D2A41ED0d',
'0x9ED7999715fdC8987578075e48EDaA3249EDe5aa',
'0xFa2e2461474600CC7185d259B7B3b7cD76E88861',
'0x4afE0CAC04B8E30093bAe2860Ac5eb2911ed48B0',
'0x206FC39DABA6Be1215E46E724939a13314d209C1',
'0xea8ADaF1797d422827d91Eb9fD5d5A8292A42ffA',
'0x37934f3968d67837951ceA11c4c95C39bD0761E7']


def test_address(ledger, addresses):
address = ledger.address
account_id = ledger.get_account_id(address)

assert address == ledger.get_address(account_id)
assert address == addresses[0]


def test_get_addresses():
def test_get_addresses(ledger, addresses):
accounts_1 = ledger.get_addresses(1)
assert len(accounts_1) == 1

accounts_5 = ledger.get_addresses(5)
assert len(accounts_5) == 5
assert accounts_1[0] == accounts_5[0]

accounts_10 = ledger.get_addresses(10)
assert len(accounts_10) == 10
assert accounts_5 == accounts_10[:5]
accounts_12 = ledger.get_addresses(12)
assert accounts_12 == addresses
assert accounts_5 == accounts_12[:5]

accounts_4p0 = ledger.get_addresses(limit=4, page=0)
accounts_4p1 = ledger.get_addresses(limit=4, page=1)
accounts_4p2 = ledger.get_addresses(limit=4, page=2)
assert len(accounts_4p1) == 4
assert accounts_4p0 == accounts_10[:4]
assert accounts_4p1 == accounts_10[4:8]
assert accounts_10 == (accounts_4p0 + accounts_4p1 + accounts_4p2[:2])
assert accounts_12 == (accounts_4p0 + accounts_4p1 + accounts_4p2)


def test_sign_transaction(transaction, tx_hash, acct):
def test_sign_transaction(ledger, transaction, tx_signed):
expected_sender = ledger.address
signed = ledger.signTransaction(transaction)

assert type(signed.v) is int
assert type(signed.r) is str
assert type(signed.s) is str

assert signed.hash == tx_hash

assert acct.recoverTransaction(signed.rawTransaction).lower() == expected_sender
assert type(signed.r) is int
assert type(signed.s) is int
assert signed == tx_signed['simple']
assert Account.recoverTransaction(signed.rawTransaction) == expected_sender

# Test transaction with a small payload
transaction['data'] = bytes([0x0] * 128)
signed = ledger.signTransaction(transaction)
assert acct.recoverTransaction(signed.rawTransaction).lower() == expected_sender
assert signed == tx_signed['small']
assert Account.recoverTransaction(signed.rawTransaction) == expected_sender

# Test transaction with a large payload
transaction['data'] = bytes([0x0] * 1042)
signed = ledger.signTransaction(transaction)
assert acct.recoverTransaction(signed.rawTransaction).lower() == expected_sender
assert signed == tx_signed['large']
assert Account.recoverTransaction(signed.rawTransaction) == expected_sender


def test_defunct_sign_message(acct):
def test_defunct_sign_message(ledger, tx_signed):
message = "I♥SF"
msghash = defunct_hash_message(text=message)

expected_signer = ledger.address

signed = ledger.defunctSignMessage(text=message)

assert signed == tx_signed['message']
assert signed.messageHash == msghash
assert type(signed.v) is int
assert type(signed.r) is str
assert type(signed.s) is str
assert type(signed.r) is int
assert type(signed.s) is int

vrs = (signed.v, signed.r, signed.s)
from_account = acct.recoverHash(signed.messageHash, vrs=vrs)
assert from_account.lower() == expected_signer
from_account = Account.recoverHash(signed.messageHash, vrs=vrs)
assert from_account == expected_signer

0 comments on commit cb03372

Please sign in to comment.