Skip to content

Commit

Permalink
Merge pull request #546 from LedgerHQ/fix/apa/client_ecrecover
Browse files Browse the repository at this point in the history
Fix client ecrecover
  • Loading branch information
apaillier-ledger authored Mar 12, 2024
2 parents ee10ca2 + d1ea89b commit e15fa0f
Show file tree
Hide file tree
Showing 2 changed files with 42 additions and 12 deletions.
6 changes: 6 additions & 0 deletions client/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.3.1] - 2024-03-12

### Fixed

- `recover_transaction` & `recover_message` util functions

## [0.3.0] - 2024-02-13

### Added
Expand Down
48 changes: 36 additions & 12 deletions client/src/ledger_app_clients/ethereum/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,14 @@
import rlp


# eth_account requires it for some reason
def normalize_vrs(vrs: tuple) -> tuple:
vrs_l = list()
for elem in vrs:
vrs_l.append(elem.lstrip(b'\x00'))
return tuple(vrs_l)


def get_selector_from_data(data: str) -> bytes:
raw_data = bytes.fromhex(data[2:])
return raw_data[:4]
Expand All @@ -13,7 +21,7 @@ def recover_message(msg, vrs: tuple) -> bytes:
smsg = encode_typed_data(full_message=msg)
else: # EIP-191
smsg = encode_defunct(primitive=msg)
addr = Account.recover_message(smsg, vrs)
addr = Account.recover_message(smsg, normalize_vrs(vrs))
return bytes.fromhex(addr[2:])


Expand All @@ -23,17 +31,33 @@ def recover_transaction(tx_params, vrs: tuple) -> bytes:
if raw_tx[0] in [0x01, 0x02]:
prefix = raw_tx[:1]
raw_tx = raw_tx[len(prefix):]
# v is returned on one byte only so it might have overflowed
# in that case, we will reconstruct it to its full value
if "chainId" in tx_params:
trunc_chain_id = tx_params["chainId"]
while trunc_chain_id.bit_length() > 32:
trunc_chain_id >>= 8
target = tx_params["chainId"] * 2 + 35
trunc_target = trunc_chain_id * 2 + 35
diff = vrs[0][0] - (trunc_target & 0xff)
vrs = (target + diff, vrs[1], vrs[2])
else:
if "chainId" in tx_params:
# v is returned on one byte only so it might have overflowed
# in that case, we will reconstruct it to its full value
trunc_chain_id = tx_params["chainId"]
while trunc_chain_id.bit_length() > 32:
trunc_chain_id >>= 8

trunc_target = trunc_chain_id * 2 + 35
trunc_v = int.from_bytes(vrs[0], "big")

if (trunc_target & 0xff) == trunc_v:
parity = 0
elif ((trunc_target + 1) & 0xff) == trunc_v:
parity = 1
else:
# should have matched with a previous if
assert False

# https://github.com/ethereum/EIPs/blob/master/EIPS/eip-155.md
full_v = parity + tx_params["chainId"] * 2 + 35
# 9 bytes would be big enough even for the biggest chain ID
vrs = (int(full_v).to_bytes(9, "big"), vrs[1], vrs[2])
else:
# Pre EIP-155 TX
assert False
decoded = rlp.decode(raw_tx)
reencoded = rlp.encode(decoded[:-3] + list(vrs))
reencoded = rlp.encode(decoded[:-3] + list(normalize_vrs(vrs)))
addr = Account.recover_transaction(prefix + reencoded)
return bytes.fromhex(addr[2:])

0 comments on commit e15fa0f

Please sign in to comment.