# Copyright © Albert Lane. All Rights Reserved. # Proprietary and Confidential — Albert Lane Proprietary Software License. # Unauthorized use, copying, modification, or distribution is prohibited. # License inquiries: https://albertlane.net """t1237 canonical 2-of-2 digests (SPEC.md §2-4). Must match host/host_main.cpp and worker/src/binding.js byte for byte.""" import hashlib import struct from eth_account import Account from eth_account.messages import encode_defunct import lane_isa_pb2 as pb CANONICAL_ROBOT_ADDRESS = "0xc136ab7d8775f58253f3c10c1af93cbb9ff16df6" DOMAIN_HUMAN = b"LANE-VM/2of2/v1/human" DELTA_STATE = b"PENDING_EXECUTION" SECP256K1_N = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 def sha256(b: bytes) -> bytes: return hashlib.sha256(b).digest() def instr_bytes(instr: "pb.Instruction") -> bytes: return instr.SerializeToString(deterministic=True) def h_human(packet_id: int, timestamp: int, instr: bytes, payload: bytes) -> bytes: return sha256(DOMAIN_HUMAN + struct.pack(">QQ", packet_id, timestamp) + sha256(instr) + sha256(payload)) def h_robot(human_sig: bytes, instr: bytes) -> bytes: return sha256(human_sig + instr + DELTA_STATE) def personal_sign(digest: bytes, private_key) -> bytes: """EIP-191 personal_sign over the 64-char lowercase hex of the digest. Returns 65 bytes r||s||v (v in 27/28).""" return bytes(Account.sign_message(encode_defunct(text=digest.hex()), private_key=private_key).signature) def recover(digest: bytes, sig: bytes) -> str: """Address (lowercase 0x…) that produced sig over hex(digest); raises on malformed input.""" if len(sig) != 65: raise ValueError("signature must be 65 bytes") if int.from_bytes(sig[32:64], "big") > SECP256K1_N // 2: raise ValueError("high-s signature rejected") if sig[64] not in (0, 1, 27, 28): raise ValueError("bad recovery id v") return Account.recover_message(encode_defunct(text=digest.hex()), signature=sig).lower() def normalize_address(a) -> str: s = (a or "").strip().lower() if s.startswith("0x"): s = s[2:] if len(s) != 40 or any(c not in "0123456789abcdef" for c in s) or s == "0" * 40: return "" return "0x" + s