"""An independent BIP-143-style sighash for Handshake, for cross-checking the device's signatures. Deliberately a second implementation rather than a port of the device's: a test that reuses the code under test proves only that it is self-consistent. Field order and lengths follow `shd/Sources/Script/SigHash.swift::compute`. """ from hashlib import blake2b from typing import List, Sequence, Tuple def encode_varint(n: int) -> bytes: """Deliberately duplicated rather than imported: this module is meant to be an independent check, and it keeps the cross-check importable without ragger.""" if n < 0xFD: return bytes([n]) if n <= 0xFFFF: return b"\xfd" + n.to_bytes(2, "little") if n <= 0xFFFFFFFF: return b"\xfe" + n.to_bytes(4, "little") return b"\xff" + n.to_bytes(8, "little") def blake2b_256(data: bytes) -> bytes: return blake2b(data, digest_size=32).digest() def blake2b_160(data: bytes) -> bytes: return blake2b(data, digest_size=20).digest() def p2wpkh_script_code(compressed_pubkey: bytes) -> bytes: """OP_DUP OP_BLAKE160 OP_EQUALVERIFY OP_CHECKSIG. Handshake uses OP_BLAKE160 (0xc0) where Bitcoin uses OP_HASH160 (0xa9). """ return (b"\x76\xc0\x14" + blake2b_160(compressed_pubkey) + b"\x88\xac") def serialize_output(value: int, addr_hash: bytes, addr_version: int = 0, covenant_kind: int = 0, covenant_items: Sequence[bytes] = ()) -> bytes: items = b"".join(encode_varint(len(i)) + i for i in covenant_items) return (value.to_bytes(8, "little") + bytes([addr_version, len(addr_hash)]) + addr_hash + bytes([covenant_kind]) + encode_varint(len(covenant_items)) + items) def sighash_all(version: int, inputs: List[Tuple[bytes, int, int]], outputs: List[bytes], locktime: int, index: int, script_code: bytes, value: int) -> bytes: """SIGHASH_ALL only, which is the one type the device signs. `inputs` are (txid, vout, sequence); `outputs` are pre-serialized. """ hash_prevouts = blake2b_256(b"".join( txid + vout.to_bytes(4, "little") for txid, vout, _ in inputs)) hash_sequence = blake2b_256(b"".join( seq.to_bytes(4, "little") for _, _, seq in inputs)) hash_outputs = blake2b_256(b"".join(outputs)) txid, vout, sequence = inputs[index] preimage = (version.to_bytes(4, "little") + hash_prevouts + hash_sequence + txid + vout.to_bytes(4, "little") + encode_varint(len(script_code)) + script_code + value.to_bytes(8, "little") + sequence.to_bytes(4, "little") + hash_outputs + locktime.to_bytes(4, "little") + (0x01).to_bytes(4, "little")) return blake2b_256(preimage)