Chapter 03 · Foundations
The login-shaped ancestors of the generic envelope show the one detail that has to be right: what exactly gets hashed, in which order, for which signature version.
FIGURE 3.1 · Two hash orders, selected by signatureVersion.
Request predates the generic envelope. It carries four things: system_id
(the chain), signing_id (the identity proving itself), an optional
signature, and a challenge. Response mirrors it with the
decision and the responder's signature.
The whole security story of a login is in getChallengeHash(): bind the signer to a
specific block height, so a signature cannot be replayed against a different chain state.
VERUS_DATA_SIGNATURE_PREFIX is a domain separator. In version 1 it is hashed
first; in version 2 it moves after signing_id. The order is not cosmetic — the
moved prefix prevents a signature made for one Verus address type from validating as another.
When you verify someone else's signature, you must know which version they used, or you will
compute a hash that never matches.
v1: sha256(PREFIX ‖ system_id ‖ height ‖ signing_id ‖ challenge_hash) v2: sha256(system_id ‖ height ‖ signing_id ‖ PREFIX ‖ challenge_hash)
Challenge is an arbitrary payload with a toSha256(); it exists so a
request is single-use. Two identical requests with different challenges produce different hashes.Hash160 wraps a 20-byte identifier with i-address encoding. It is the common
currency between "human readable i-address" and "bytes on the wire".system_id and signing_id are converted with
fromBase58Check(...).hash before hashing — passing the string through unchanged is
the classic mistake.src/vdxf/classes/Request.ts # getChallengeHash, toJson, length calculation src/vdxf/classes/Response.ts # decision + responder signature src/vdxf/classes/Challenge.ts # toSha256 src/vdxf/classes/Hash160.ts # address ⇄ hash160 src/constants/vdxf.ts # VERUS_DATA_SIGNATURE_PREFIX