XRPL Technology

How does transaction propagation work on XRPL?

Last updated:

Transaction propagation on the XRP Ledger uses an efficient flood-and-validate protocol that ensures rapid, reliable distribution of signed transactions across the entire network within seconds while preventing spam and maintaining security. **Propagation Mechanism** When a transaction is submitted to any XRPL node, it triggers a multi-phase propagation process: ```javascript // Transaction propagation lifecycle class TransactionPropagation { async handleNewTransaction(tx, source) { // Phase 1: Initial validation const validation = await this.validateTransaction(tx); if (!validation.valid) { return { status: 'rejected', reason: validation.error }; } // Phase 2: Check for duplicates if (this.transactionPool.has(tx.hash)) { return { status: 'already_seen' }; } // Phase 3: Add to local transaction pool this.transactionPool.add(tx); // Phase 4: Broadcast to peers (excluding source) await this.broadcastToPeers(tx, { exclude: source }); return { status: 'accepted', hash: tx.hash }; } async broadcastToPeers(tx, options = {}) { const peers = this.peerManager.getActivePeers(); const excludeSet = new Set(options.exclude ? [options.exclude] : []); const broadcasts = []; for (const peer of peers) { if (excludeSet.has(peer.id)) continue; broadcasts.push( this.sendToPeer(tx, peer).catch(err => { console.error(`Failed to send to ${peer.id}:`, err); }) ); } // Send in parallel await Promise.all(broadcasts); } } ``` **Flood Algorithm** XRPL uses a modified flooding algorithm optimized for consensus: ```python # Transaction flooding process class FloodProtocol: def __init__(self): self.seen_transactions = set() # Prevent loops self.propagation_times = {} # Track metrics def on_receive_transaction(self, tx, from_peer): tx_hash = compute_hash(tx) # Step 1: Check if already seen if tx_hash in self.seen_transactions: return # Don't re-propagate # Step 2: Validate transaction if not self.validate_transaction(tx): self.mark_peer_unreliable(from_peer) return # Step 3: Mark as seen self.seen_transactions.add(tx_hash) self.propagation_times[tx_hash] = time.now() # Step 4: Add to transaction pool self.transaction_pool.add(tx) # Step 5: Forward to all other peers for peer in self.peers: if peer != from_peer: # Don't send back to source self.send_transaction(peer, tx) def send_transaction(self, peer, tx): message = { 'type': 'TRANSACTION', 'transaction': serialize(tx), 'hash': compute_hash(tx) } peer.send(message) ``` **Propagation Speed** XRPL achieves sub-second transaction propagation: ```javascript // Typical propagation timeline const propagationTimeline = { t0: 'Transaction submitted to node A', t50ms: 'Node A validates and broadcasts to peers (10 nodes)', t100ms: 'First hop peers validate and re-broadcast to their peers (100 nodes)', t200ms: 'Second hop reaches most of network (1000+ nodes)', t500ms: 'Transaction reaches all well-connected nodes', t1000ms: 'Transaction known to entire network' }; // Propagation metrics async function measurePropagationSpeed(tx, network) { const startTime = Date.now(); const nodeReceipts = new Map(); // Submit transaction await network.submitTransaction(tx); // Monitor when each node receives it network.nodes.forEach(node => { node.on('transaction_received', (txHash) => { if (txHash === tx.hash) { const elapsed = Date.now() - startTime; nodeReceipts.set(node.id, elapsed); } }); }); // Wait for full propagation await new Promise(resolve => setTimeout(resolve, 2000)); // Calculate statistics const times = Array.from(nodeReceipts.values()); return { min: Math.min(...times), max: Math.max(...times), median: calculateMedian(times), p95: calculatePercentile(times, 95), coverage: (nodeReceipts.size / network.nodes.length) * 100 }; } // Typical results const results = { min: 20, // ms (direct peer) max: 800, // ms (distant peer, slow connection) median: 150, // ms p95: 400, // ms coverage: 99.5 // percent }; ``` **Validation During Propagation** Each node validates before re-broadcasting: ```javascript function validateBeforePropagation(tx) { const checks = [ // 1. Structure validation () => validateTransactionStructure(tx), // 2. Signature verification () => verifySignature(tx), // 3. Fee check () => validateFee(tx), // 4. Sequence number () => validateSequence(tx), // 5. Field validation () => validateTransactionFields(tx) ]; for (const check of checks) { const result = check(); if (!result.valid) { return result; } } return { valid: true }; } // Invalid transactions are NOT propagated function handleInvalidTransaction(tx, peer, reason) { console.log(`Rejected transaction ${tx.hash} from ${peer.id}: ${reason}`); // Update peer quality score peerManager.decreasePeerScore(peer.id, 10); // If peer sends too many invalid transactions, disconnect if (peerManager.getPeerScore(peer.id) < 20) { peerManager.disconnectPeer(peer.id); } // Do NOT propagate return; } ``` **Anti-Spam Measures** Propagation includes protection against spam: ```javascript class SpamPrevention { constructor() { this.transactionCounts = new Map(); // Per-account this.peerTransactionCounts = new Map(); // Per-peer this.windowSize = 60000; // 1 minute } checkSpamLimits(tx, peer) { const now = Date.now(); // Check per-account rate limit const accountKey = tx.Account; const accountCount = this.getRecentCount(accountKey, now); if (accountCount > 100) { // Max 100 tx/minute per account return { allowed: false, reason: 'Account rate limit exceeded' }; } // Check per-peer rate limit const peerCount = this.getRecentCount(peer.id, now); if (peerCount > 1000) { // Max 1000 tx/minute per peer return { allowed: false, reason: 'Peer rate limit exceeded' }; } // Check transaction fee (economic spam prevention) const minFee = this.calculateMinFee(); if (parseInt(tx.Fee) < minFee) { return { allowed: false, reason: `Fee too low: ${tx.Fee} < ${minFee}` }; } return { allowed: true }; } recordTransaction(tx, peer) { const now = Date.now(); // Record for account this.recordCount(tx.Account, now); // Record for peer this.recordCount(peer.id, now); // Clean old records this.cleanOldRecords(now); } } ``` **Transaction Pool Management** Nodes maintain a transaction pool for pending transactions: ```javascript class TransactionPool { constructor() { this.transactions = new Map(); // hash -> tx this.byAccount = new Map(); // account -> [hashes] this.byFee = new SortedSet(); // Sorted by fee this.maxSize = 10000; // Maximum transactions } add(tx) { // Check if pool is full if (this.transactions.size >= this.maxSize) { // Remove lowest fee transaction const lowestFeeTx = this.byFee.first(); if (parseInt(tx.Fee) <= parseInt(lowestFeeTx.Fee)) { return { accepted: false, reason: 'Pool full, fee too low' }; } this.remove(lowestFeeTx.hash); } // Add to pool this.transactions.set(tx.hash, { transaction: tx, received: Date.now(), attempts: 0 }); // Index by account if (!this.byAccount.has(tx.Account)) { this.byAccount.set(tx.Account, []); } this.byAccount.get(tx.Account).push(tx.hash); // Index by fee this.byFee.add(tx); return { accepted: true }; } getAccountTransactions(account) { const hashes = this.byAccount.get(account) || []; return hashes.map(hash => this.transactions.get(hash).transaction); } getHighestFeeTransactions(count) { return this.byFee.takeLast(count); } } ``` **Propagation Optimization** ```javascript // Selective propagation based on peer quality class SmartPropagation { selectPeersForBroadcast(tx, allPeers) { // Score peers based on multiple factors const scored = allPeers.map(peer => ({ peer, score: this.calculatePropagationScore(peer, tx) })); // Sort by score scored.sort((a, b) => b.score - a.score); // Select top peers (ensure redundancy but avoid waste) const redundancyFactor = 0.3; // Broadcast to 30% of peers const count = Math.max( 5, // Minimum 5 peers Math.ceil(allPeers.length * redundancyFactor) ); return scored.slice(0, count).map(s => s.peer); } calculatePropagationScore(peer, tx) { let score = 100; // Factor 1: Latency (prefer fast peers) score -= peer.metrics.avgLatency / 10; // Factor 2: Reliability score *= peer.metrics.reliability; // Factor 3: Bandwidth score *= (1 - peer.metrics.bandwidthUtilization); // Factor 4: Geographic diversity score *= this.getDiversityBonus(peer); // Factor 5: Validator status (prefer validators) if (peer.isValidator) { score *= 1.5; } return score; } } ``` **Monitoring and Metrics** ```javascript class PropagationMonitor { constructor() { this.metrics = { totalTransactions: 0, propagatedTransactions: 0, rejectedTransactions: 0, avgPropagationTime: 0, propagationTimes: [] }; } recordPropagation(tx, startTime) { const elapsed = Date.now() - startTime; this.metrics.totalTransactions++; this.metrics.propagatedTransactions++; this.metrics.propagationTimes.push(elapsed); // Update rolling average this.metrics.avgPropagationTime = ( this.metrics.avgPropagationTime * 0.95 + elapsed * 0.05 ); } getStatistics() { return { total: this.metrics.totalTransactions, propagated: this.metrics.propagatedTransactions, rejected: this.metrics.rejectedTransactions, successRate: this.metrics.propagatedTransactions / this.metrics.totalTransactions, avgTime: this.metrics.avgPropagationTime, p50: this.calculatePercentile(50), p95: this.calculatePercentile(95), p99: this.calculatePercentile(99) }; } } ``` **Comparison to Other Systems** **Bitcoin:** - INV/GETDATA pattern (request-response) - Slower propagation (~6-10 seconds) - Compact block relay for efficiency - No immediate validation before propagation **Ethereum:** - Direct transaction forwarding - Faster propagation (~2-5 seconds) - Transaction pool gossiping - Multiple propagation strategies (full, hash-only) **XRPL:** - Immediate flood with validation - Fastest propagation (~0.5-1 second) - Quality-based peer selection - Explicit spam prevention **Best Practices for Applications** ```javascript const xrpl = require('xrpl'); // Submit transaction with propagation awareness async function submitWithConfirmation(client, signedTx) { // Step 1: Submit const submitResult = await client.submit(signedTx.tx_blob); if (submitResult.result.engine_result !== 'tesSUCCESS') { throw new Error(`Submit failed: ${submitResult.result.engine_result}`); } console.log('Transaction propagated to initial node'); // Step 2: Monitor propagation // Wait for transaction to appear in validated ledger const validated = await client.request({ command: 'tx', transaction: signedTx.hash, min_ledger: submitResult.result.validated_ledger_index, max_ledger: submitResult.result.validated_ledger_index + 10 }); if (validated.result.validated) { console.log('Transaction validated and propagated network-wide'); return validated.result; } else { throw new Error('Transaction not validated'); } } ``` XRPL's transaction propagation system achieves rapid, reliable network-wide distribution while maintaining security and preventing spam, enabling the fast finality that distinguishes XRPL from traditional blockchain systems.
Was this helpful?

Related Questions

Go Deeper

Expand your knowledge with these related lessons

The XRP Ledger Consensus Protocol - Overview

55 minbeginner

XRPL vs. Other Fast Consensus Mechanisms

Comprehensive Consensus Comparison Matrix evaluating speed, security, decentralization, and energy efficiency

39 minadvanced

Comparing XRPL to Other BFT Systems

55 minintermediate

Have more questions?

Browse our complete FAQ or contact support.