What hash functions does XRPL use?
Last updated:
The XRP Ledger employs a carefully selected suite of cryptographic hash functions, each chosen for specific purposes within the protocol. Understanding these hash functions is essential for grasping XRPL's security model and implementation details.
Primary Hash Functions
1. SHA-512Half (Most Common)
The predominant hash function in XRPL, SHA-512Half is the first 256 bits of SHA-512:
```javascript const crypto = require('crypto');
function sha512Half(data) { // Compute full SHA-512 hash const fullHash = crypto.createHash('sha512') .update(data) .digest(); // Return first 32 bytes (256 bits) return fullHash.slice(0, 32); }
// Example usage const input = Buffer.from('Hello XRPL'); const hash = sha512Half(input); console.log('SHA-512Half:', hash.toString('hex')); // Output: 64 hex characters (256 bits) ```
Uses of SHA-512Half: - Transaction identification (transaction hashes) - Ledger identification (ledger hashes) - Account ID computation - Merkle tree construction - State tree hashing - Object identification
Why SHA-512Half Instead of SHA-256?
The choice of SHA-512Half over SHA-256 is performance-driven:
```python # Performance comparison on 64-bit processors
# SHA-256: Operates on 32-bit words # - Requires 64 rounds # - Less efficient on 64-bit processors # - Slower on modern hardware
# SHA-512: Operates on 64-bit words # - Requires 80 rounds (more rounds) # - Native 64-bit operations # - Actually FASTER on 64-bit processors despite more rounds # - Truncating to 256 bits maintains same security level
# Result: SHA-512Half is ~30-50% faster than SHA-256 on 64-bit systems ```
Security Properties: - Output size: 256 bits (same as SHA-256) - Collision resistance: 2^256 security - Preimage resistance: 2^256 security - Second preimage resistance: 2^256 security
2. RIPEMD-160
Used specifically in address generation:
```javascript const crypto = require('crypto');
function generateAddress(publicKey) { // Step 1: SHA-256 of public key const sha256Hash = crypto.createHash('sha256') .update(publicKey) .digest(); // Step 2: RIPEMD-160 of the SHA-256 hash const ripemd160Hash = crypto.createHash('ripemd160') .update(sha256Hash) .digest(); console.log('Account ID (hex):', ripemd160Hash.toString('hex')); // Output: 40 hex characters (160 bits) return ripemd160Hash; } ```
Why RIPEMD-160?
1. Shorter addresses: 160 bits vs 256 bits = more compact addresses 2. Bitcoin compatibility: Same scheme as Bitcoin P2PKH addresses 3. Double hashing: Additional security layer (must break both SHA-256 and RIPEMD-160) 4. Quantum resistance: Breaking requires inverting two different hash functions
Uses of RIPEMD-160: - Account address generation only - Not used elsewhere in protocol
3. SHA-256
Used in specific contexts, particularly in the address generation pipeline:
```javascript // SHA-256 as intermediate step function addressGeneration(publicKey) { // SHA-256 is used here const sha256 = crypto.createHash('sha256') .update(publicKey) .digest(); // Then RIPEMD-160 const ripemd160 = crypto.createHash('ripemd160') .update(sha256) .digest(); return ripemd160; }
// SHA-256 for checksum in Base58Check encoding function base58CheckEncode(payload) { // Double SHA-256 for checksum const hash1 = crypto.createHash('sha256').update(payload).digest(); const hash2 = crypto.createHash('sha256').update(hash1).digest(); const checksum = hash2.slice(0, 4); return Buffer.concat([payload, checksum]); } ```
Uses of SHA-256: - Intermediate step in address generation - Checksum computation in Base58Check - Compatibility with Bitcoin-derived tools
Hash Function Applications
Transaction Hashing
Transaction hashes uniquely identify each transaction:
```javascript const { encode } = require('ripple-binary-codec');
function computeTransactionHash(transaction) { // Step 1: Remove signature-related fields const txCopy = { ...transaction }; delete txCopy.Signers; delete txCopy.TxnSignature; // Step 2: Add hash prefix const prefix = Buffer.from('54584E00', 'hex'); // 'TXN\0' // Step 3: Encode transaction canonically const encoded = encode(txCopy); // Step 4: SHA-512Half of (prefix + encoded transaction) const toHash = Buffer.concat([prefix, Buffer.from(encoded, 'hex')]); const hash = sha512Half(toHash); return hash.toString('hex').toUpperCase(); }
// Example const tx = { TransactionType: 'Payment', Account: 'rN7n7otQDd6FczFgLdlqtyMVrn3HMfgnZh', Destination: 'rLHzPsX6oXkzU9rFkyJ5R8J4HqBzZT4Sfp', Amount: '1000000', Fee: '12', Sequence: 1 };
const txHash = computeTransactionHash(tx); console.log('Transaction Hash:', txHash); // Result: 64-character hex string ```
Ledger Hashing
Each ledger has a unique hash computed from its contents:
```javascript function computeLedgerHash(ledger) { // Ledger hash includes: const components = [ ledger.ledger_index, // Sequence number ledger.total_coins, // Total XRP ledger.parent_hash, // Previous ledger hash ledger.transaction_hash, // Hash of all transactions ledger.account_hash, // Hash of account state tree ledger.parent_close_time, // Previous close time ledger.close_time, // Current close time ledger.close_time_resolution, ledger.close_flags ]; // Encode all components const encoded = encodeLedgerComponents(components); // Add ledger hash prefix const prefix = Buffer.from('4C575200', 'hex'); // 'LWR\0' // SHA-512Half const toHash = Buffer.concat([prefix, encoded]); return sha512Half(toHash).toString('hex').toUpperCase(); } ```
State Tree Hashing
XRPL uses Merkle-like trees for state representation:
```javascript // Account state tree uses SHA-512Half class StateTree { constructor() { this.root = null; } computeNodeHash(node) { if (node.isLeaf) { // Leaf: hash of account data const prefix = Buffer.from('4D4C4E00', 'hex'); // 'MLN\0' const encoded = encodeAccount(node.account); return sha512Half(Buffer.concat([prefix, encoded])); } else { // Inner node: hash of (left_hash + right_hash) const prefix = Buffer.from('4D494E00', 'hex'); // 'MIN\0' const combined = Buffer.concat([ prefix, node.leftHash, node.rightHash ]); return sha512Half(combined); } } getRootHash() { return this.computeNodeHash(this.root); } } ```
Hash Prefixes in XRPL
XRPL uses hash prefixes to prevent collision attacks:
```javascript const HASH_PREFIXES = { // Transaction identifiers transactionID: Buffer.from('54584E00', 'hex'), // 'TXN\0' // Ledger identifiers ledgerHeader: Buffer.from('4C575200', 'hex'), // 'LWR\0' // State tree nodes innerNode: Buffer.from('4D494E00', 'hex'), // 'MIN\0' leafNode: Buffer.from('4D4C4E00', 'hex'), // 'MLN\0' // Transaction tree nodes txInnerNode: Buffer.from('544D4E00', 'hex'), // 'TMN\0' txLeafNode: Buffer.from('544C4E00', 'hex'), // 'TLN\0' // Validation validation: Buffer.from('56414C00', 'hex'), // 'VAL\0' proposal: Buffer.from('50525000', 'hex') // 'PRP\0' };
// Why prefixes? // Prevents collision attacks where: // hash(type1 || data1) = hash(type2 || data2) // With prefixes, this is computationally infeasible ```
Performance Considerations
Benchmarks on typical server hardware:
``` Hash Function Speed (MB/s) Output Size Use Case ──────────────────────────────────────────────────────── SHA-512Half 450-600 256 bits General purpose SHA-256 300-400 256 bits Address generation RIPEMD-160 200-300 160 bits Address only ```
SHA-512Half's superior performance makes it ideal for high-throughput applications.
Security Analysis
Collision Resistance:
```python # Birthday paradox for hash collisions
# For 256-bit hashes (SHA-512Half, SHA-256): collision_probability = 2^128 # operations needed # = 340,282,366,920,938,463,463,374,607,431,768,211,456 operations # Computationally infeasible with current or foreseeable technology
# For 160-bit hashes (RIPEMD-160): collision_probability = 2^80 # operations needed # = 1,208,925,819,614,629,174,706,176 operations # Still secure, but lower margin # Acceptable for addresses (collision only affects address generation) ```
Quantum Resistance:
```python # Grover's algorithm (quantum attack)
# Classical security → Quantum security # 256-bit hash → 128-bit quantum security # 160-bit hash → 80-bit quantum security
# XRPL's use of 256-bit hashes (SHA-512Half) provides: # - 128-bit quantum security # - Adequate for foreseeable quantum computers
# RIPEMD-160 in address generation: # - 80-bit quantum security # - Lower but acceptable (only affects address collision) # - Double hashing (SHA-256 + RIPEMD-160) adds complexity ```
Comparison to Other Blockchains
Bitcoin: - Uses SHA-256 for everything - Double SHA-256 for many operations - RIPEMD-160 for address generation - Same address generation as XRPL
Ethereum: - Uses Keccak-256 (SHA-3) - Different from Bitcoin and XRPL - 256-bit output - No RIPEMD-160 (addresses are Keccak-256 of public key)
XRPL: - Uses SHA-512Half (unique choice) - Performance-optimized for 64-bit processors - RIPEMD-160 for addresses (Bitcoin-compatible) - Hash prefixes for collision prevention
Practical Usage
```javascript const xrpl = require('xrpl'); const { hashes } = require('ripple-hashes');
// Compute transaction hash async function getTransactionHash(client, txBlob) { // Transaction hash is automatically computed const decoded = xrpl.decode(txBlob); const hash = hashes.computeTransactionHash(decoded); console.log('Transaction hash:', hash); // Verify on ledger const tx = await client.request({ command: 'tx', transaction: hash }); return tx; }
// Compute ledger hash async function getLedgerHash(client, ledgerIndex) { const ledger = await client.request({ command: 'ledger', ledger_index: ledgerIndex, transactions: false, expand: false }); console.log('Ledger hash:', ledger.result.ledger_hash); console.log('Ledger index:', ledger.result.ledger_index); return ledger.result.ledger_hash; } ```
Best Practices
1. Use library functions: Don't implement hash functions manually 2. Verify hashes: Always verify transaction hashes match expected values 3. Use correct prefixes: When implementing low-level code, use proper hash prefixes 4. Understand truncation: SHA-512Half is truncated, not a separate algorithm 5. Security margins: 256-bit hashes provide adequate security for all use cases
XRPL's hash function choices represent careful engineering: SHA-512Half provides excellent performance on modern hardware while maintaining strong security, and RIPEMD-160 enables Bitcoin-compatible address generation.