Подписываемые данные — это 0x19 ‖ version ‖ version-specific data ‖ data. Версии: 0x00 (данные с адресом предполагаемого валидатора), 0x01 (структурированные данные, то есть EIP-712) и 0x45 (буква E) для personal_sign: за 0x19 следуют Ethereum Signed Message: и перевод строки, длина сообщения десятичными цифрами в ASCII и само сообщение. Транзакции в кодировке RLP не могут начинаться с 0x19 — в этом и состоит смысл префикса.
Кошелёк хеширует результат с помощью Keccak-256 и подписывает на secp256k1; подпись — это 65 байт r ‖ s ‖ v, где v равно 27 или 28. Для сообщения hello дайджест равен 0x50b2c43fd39106bafbba0da34fc430e1f91e3c96ea2acee2bc34119f92b37750. Проверяющая сторона восстанавливает адрес с помощью ecrecover (или verifyMessage из библиотеки) и сравнивает его с ожидаемым; для смарт-аккаунтов вместо этого нужен ERC-1271.
personal_sign показывает пользователям непонятный текст и не привязывает подпись ни к домену, ни к сети, поэтому приложениям следует включать эти данные в сообщение (так делает Sign-In with Ethereum) или выбирать EIP-712 для всего, что имеет структуру или денежную ценность.