Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix client ecrecover #546

Merged
merged 4 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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:])
Loading