XE uses standard cryptographic primitives with cross-implementation compatibility between Go and JavaScript (tweetnacl/blakejs).
Key Generation
Ed25519 via crypto/rand or deterministic from 32-byte seed.
Signing Contexts
- Account address —
sha256("xe/account/v1" ‖ pubkey). The address commits to the key rather than being it, so a credential can be rotated without the identity changing. Multisig addresses are the SHA-256 of the canonical keyset instead. - Block hashing —
SHA-256(networkID ‖ canonical bytes ‖ aux bytes). The aux tail is a sequence of 8-byte-big-endian-length-prefixed sections: the opening block'spub_keyunder the tagxe/block/pubkey/v1(ASCII hex, present only whenprevious == "0"), then, on lease-family blocks, the certificate hash and the timekeeper attestations sorted by public key. It is empty for any other block, so a plain non-opening send hashes exactly as it did before keys were split from addresses. Genesis blocks are hashed with the network-ID prefix cleared — they are created before the network ID is known. - Block signing —
ed25519.Sign(privateKey, hash) - Vote signing — ed25519 over canonical vote encoding
- Attestation signing — ed25519 over
SHA-256(leaseHash || timestamp) - Chat signing — ed25519 over the envelope
id = sha256(f(from) ‖ f(pub_key) ‖ f(to) ‖ f(message) ‖ u64be(timestamp))wheref(x)is a 4-byte big-endian length followed by the UTF-8 bytes; the sameidis the PoW target - Chat read proof —
ed25519.Sign(priv, SHA-256("xe/chat-read-auth/v1\0" ‖ challenge_bytes))over a single-use, 120-second challenge fromGET /chat/auth/challenge - Directory signing —
ed25519.Sign(priv, SHA-256("xe/directory-registration/v1\x00" ‖ len‖networkID ‖ len‖account ‖ len‖pubKey ‖ len‖nodePeer ‖ len‖timestamp)), where eachlenis an 8-byte big-endian length prefix framing the field that follows; the node checkspubKeyderivesaccountbefore verifying
Proof of Work
Anti-spam only, not consensus. Always computed client-side.
result = blake2b_8(nonce_LE || blockHash)
valid = result >= difficulty- Block DefaultDifficulty:
0xfffff80000000000(~2²¹ attempts, ~1s) - Chat DefaultPoWDifficulty:
0xffffc00000000000(~2¹⁸ attempts — chat spam pricing is tuned independently of block mining) - TestDifficulty:
0x0000000000000002(instant) - Nonce: little-endian; result compared as big-endian
- Both difficulties are advertised in
GET /node
Functions: ComputePoW, ComputePoWConcurrent, ComputePoWWithContext, ValidatePoW