Files
wownerogue/todo.md
T
2025-09-24 14:06:42 -04:00

17 KiB

I'll perform a comprehensive deep review of the entire codebase after the MoneroPay removal and wallet-rpc integration. Let me analyze every aspect systematically.

🔍 DEEP CODEBASE REVIEW

1. Critical Issues to Fix

A. Missing WalletRPCService Reference in socketHandlers.js

// Line 298 - socketHandlers.js references this.walletService but it's never initialized!
this.walletService.startPaymentMonitoring(

FIX NEEDED: The SocketHandlers constructor doesn't receive or store walletService. Need to pass it in and store it.

B. MoneroPay References Still Present

  • Line 14 in index.js: const MoneroPayService = require('./payments/moneroPayService');
  • Line 59 in index.js: const moneroPayService = new MoneroPayService(debugManager);
  • Line 143-148 in index.js: MoneroPay initialization
  • gameModeManager.js still references this.moneroPayService
  • API endpoints in index.js still use MoneroPay

C. Database Connection String Issue

The database manager uses hardcoded connection string instead of environment variables properly.

D. RPC Service Method Name Issues

// rpcService.js line 77 - Wrong method name for Wownero
method: 'get_block_count' // Should be 'getblockcount' for Wownero

2. Complete MoneroPay → WalletRPC Migration

Let me create the complete migration:

// src/payments/walletRPCService.js
const axios = require('axios');
const { v4: uuidv4 } = require('uuid');

class WalletRPCService {
    constructor(debugManager) {
        this.debugManager = debugManager;
        this.walletEndpoint = process.env.PRIMARY_WALLET_ENDPOINT || 'http://127.0.0.1:34570';
        this.accountIndex = 0;
        this.paymentWatchers = new Map();
        this.addressToUser = new Map();
        this.addressToSocket = new Map();
        this.isHealthy = false;
    }

    async initialize() {
        try {
            // Test wallet connection
            const response = await this.rpcCall('get_version');
            this.isHealthy = true;
            
            if (this.debugManager?.CONSOLE_LOGGING) {
                console.log('✅ Wallet RPC service initialized:', {
                    version: response.result?.version || 'unknown',
                    endpoint: this.walletEndpoint
                });
            }
            
            // Get wallet height to ensure it's synced
            const heightResponse = await this.rpcCall('get_height');
            if (this.debugManager?.CONSOLE_LOGGING) {
                console.log('📊 Wallet height:', heightResponse.result?.height || 0);
            }
            
            return true;
        } catch (error) {
            this.isHealthy = false;
            console.error('❌ Wallet RPC connection failed:', error.message);
            console.error('   Make sure wownero-wallet-rpc is running on', this.walletEndpoint);
            return false;
        }
    }

    async rpcCall(method, params = {}) {
        try {
            const response = await axios.post(`${this.walletEndpoint}/json_rpc`, {
                jsonrpc: '2.0',
                id: '0',
                method: method,
                params: params
            }, {
                headers: { 'Content-Type': 'application/json' },
                timeout: 10000
            });

            if (response.data.error) {
                throw new Error(response.data.error.message);
            }

            return response.data;
        } catch (error) {
            if (this.debugManager?.CONSOLE_LOGGING) {
                console.error(`❌ Wallet RPC call failed (${method}):`, error.message);
            }
            throw error;
        }
    }

    async createPaymentRequest(amount, description, userId, socketId) {
        try {
            // Create a new subaddress for this payment
            const response = await this.rpcCall('create_address', {
                account_index: this.accountIndex,
                label: `game_${userId}_${Date.now()}`
            });

            const address = response.result.address;
            const addressIndex = response.result.address_index;

            // Store mapping for payment monitoring
            const paymentInfo = {
                userId: userId,
                socketId: socketId,
                amount: amount,
                description: description,
                addressIndex: addressIndex,
                accountIndex: this.accountIndex,
                createdAt: new Date(),
                status: 'pending',
                detected: false,
                confirmed: false
            };

            this.addressToUser.set(address, paymentInfo);
            this.addressToSocket.set(address, socketId);

            if (this.debugManager?.CONSOLE_LOGGING) {
                console.log(`💳 Payment address created:`, {
                    user: userId,
                    address: address.substring(0, 10) + '...',
                    amount: amount,
                    index: addressIndex
                });
            }

            return {
                success: true,
                id: uuidv4(), // Generate payment ID
                address: address,
                amount: amount,
                description: description,
                addressIndex: addressIndex,
                expiresAt: new Date(Date.now() + 30 * 60 * 1000) // 30 minutes
            };
        } catch (error) {
            console.error('❌ Failed to create payment address:', error);
            return { success: false, error: error.message };
        }
    }

    async checkPaymentStatus(address) {
        try {
            const userInfo = this.addressToUser.get(address);
            if (!userInfo) {
                return { 
                    complete: false, 
                    in_mempool: false,
                    confirmed: false,
                    amount: 0,
                    confirmations: 0
                };
            }

            // Get incoming transfers for this specific subaddress
            const response = await this.rpcCall('get_transfers', {
                in: true,
                pending: true,
                failed: false,
                pool: true,
                account_index: userInfo.accountIndex,
                subaddr_indices: [userInfo.addressIndex]
            });

            let totalReceived = 0;
            let inMempool = false;
            let confirmed = false;
            let confirmations = 0;
            let txHash = null;

            // Check pending (mempool) transactions
            if (response.result?.pool && response.result.pool.length > 0) {
                for (const tx of response.result.pool) {
                    totalReceived += tx.amount;
                    inMempool = true;
                    txHash = tx.txid;
                    
                    // Mark as detected if this is first time seeing it
                    if (!userInfo.detected) {
                        userInfo.detected = true;
                        if (this.debugManager?.CONSOLE_LOGGING) {
                            console.log(`🎯 Payment detected in mempool:`, {
                                address: address.substring(0, 10) + '...',
                                amount: tx.amount,
                                txid: txHash
                            });
                        }
                    }
                }
            }

            // Check confirmed transactions
            if (response.result?.in && response.result.in.length > 0) {
                for (const tx of response.result.in) {
                    totalReceived += tx.amount;
                    confirmed = true;
                    txHash = tx.txid;
                    
                    // Calculate confirmations
                    if (tx.height > 0) {
                        const heightResp = await this.rpcCall('get_height');
                        confirmations = Math.max(confirmations, heightResp.result.height - tx.height);
                    }
                    
                    if (!userInfo.confirmed) {
                        userInfo.confirmed = true;
                        if (this.debugManager?.CONSOLE_LOGGING) {
                            console.log(`✅ Payment confirmed:`, {
                                address: address.substring(0, 10) + '...',
                                confirmations: confirmations,
                                txid: txHash
                            });
                        }
                    }
                }
            }

            const paymentComplete = totalReceived >= userInfo.amount;

            return {
                complete: paymentComplete,
                in_mempool: inMempool,
                confirmed: confirmed,
                amount: totalReceived,
                confirmations: confirmations,
                required: userInfo.amount,
                txHash: txHash
            };
        } catch (error) {
            console.error('❌ Failed to check payment status:', error);
            return { 
                complete: false, 
                in_mempool: false,
                confirmed: false,
                amount: 0,
                confirmations: 0
            };
        }
    }

    startPaymentMonitoring(address, callback, interval = 2000) {
        if (this.paymentWatchers.has(address)) {
            return; // Already monitoring
        }

        const watcher = setInterval(async () => {
            const status = await this.checkPaymentStatus(address);
            
            if (status.in_mempool || status.confirmed) {
                callback(status);
                
                // Stop monitoring if payment is confirmed
                if (status.confirmed && status.complete) {
                    this.stopPaymentMonitoring(address);
                }
            }
        }, interval);

        this.paymentWatchers.set(address, watcher);

        if (this.debugManager?.CONSOLE_LOGGING) {
            console.log(`👁️ Started monitoring payments for:`, address.substring(0, 10) + '...');
        }
    }

    stopPaymentMonitoring(address) {
        const watcher = this.paymentWatchers.get(address);
        if (watcher) {
            clearInterval(watcher);
            this.paymentWatchers.delete(address);
            
            if (this.debugManager?.CONSOLE_LOGGING) {
                console.log(`🛑 Stopped monitoring:`, address.substring(0, 10) + '...');
            }
        }
    }

    async processPayout(recipientAddress, amount, description) {
        try {
            // Send payment to user
            const response = await this.rpcCall('transfer', {
                destinations: [{
                    amount: amount,
                    address: recipientAddress
                }],
                account_index: this.accountIndex,
                priority: 1, // Normal priority
                get_tx_key: true
            });

            if (this.debugManager?.CONSOLE_LOGGING) {
                console.log(`💸 Payout sent:`, {
                    to: recipientAddress.substring(0, 10) + '...',
                    amount: amount,
                    txHash: response.result.tx_hash
                });
            }

            return {
                success: true,
                txHash: response.result.tx_hash,
                txKey: response.result.tx_key,
                fee: response.result.fee
            };
        } catch (error) {
            console.error('❌ Failed to process payout:', error);
            return { 
                success: false, 
                error: error.message 
            };
        }
    }

    cleanupUserPayments(userId) {
        for (const [address, userInfo] of this.addressToUser.entries()) {
            if (userInfo.userId === userId || userInfo.socketId === userId) {
                this.stopPaymentMonitoring(address);
            }
        }
    }

    // Get service health status
    getHealthStatus() {
        return {
            healthy: this.isHealthy,
            endpoint: this.walletEndpoint,
            activeMonitors: this.paymentWatchers.size,
            pendingPayments: this.addressToUser.size
        };
    }
}

module.exports = WalletRPCService;

3. Architecture Issues

A. Circular Dependencies Risk

  • socketHandlers.js has too many responsibilities (900+ lines!)
  • Should split into: gameHandlers.js, paymentHandlers.js, chatHandlers.js

B. Memory Leaks

  • Payment monitors in walletRPCService never clean up expired addresses
  • addressToUser Map grows indefinitely

C. Error Recovery

  • No reconnection logic if wallet-rpc goes down
  • No graceful degradation when blockchain daemon fails

4. Security Issues

A. SQL Injection Risk

// databaseManager.js - Using string concatenation!
async run(query, params = []) {
    const result = await this.pool.query(query, params);

Should use parameterized queries consistently.

B. Missing Input Validation

  • No validation on payment amounts
  • No address format validation before creating subaddress
  • No rate limiting on payment requests

C. Sensitive Data Logging

  • Full addresses logged in debug mode
  • Should truncate addresses in logs

5. Performance Issues

A. Inefficient Payment Monitoring

  • Each payment has its own interval timer
  • Should batch check all pending payments in one RPC call

B. Database Connection Pool

// databaseManager.js
max: 10, // Could be configurable

C. Memory Usage

  • Game state stored in memory indefinitely
  • Should implement cleanup for completed games

6. Missing Features

A. Payment Expiration

  • Payment addresses never expire
  • Should clean up after 30 minutes

B. Refund Logic

  • No way to refund overpayments
  • No handling for duplicate payments

C. Admin Interface

  • No way to manually credit users
  • No payment history viewing

7. Configuration Issues

A. .env File

# These MoneroPay vars should be removed:
MONEROPAY_ENDPOINT=http://192.168.1.23:5000
MONEROPAY_CALLBACK_BASE_URL=http://localhost:3000/api/payments/callback

# RPC endpoints inconsistent:
PRIMARY_RPC_ENDPOINT=http://127.0.0.1:34568  # Should this be 18081?
PRIMARY_WALLET_ENDPOINT=http://127.0.0.1:34570

B. Hardcoded Values

  • Payment expiration (30 minutes)
  • Check intervals (2 seconds)
  • Game prices

8. Frontend Issues

A. Payment UI Missing

  • No visual payment countdown timer
  • No QR code for payment address
  • No copy-to-clipboard for addresses

B. Connection Status

  • No visual indicator when wallet-rpc is down
  • No retry mechanism for failed connections

9. Testing Coverage

A. Missing Tests

  • No tests for payment flow
  • No tests for wallet-rpc integration
  • No tests for game payout logic

B. Test Environment

  • Need mock wallet-rpc for testing
  • Need test fixtures for payment scenarios

10. Code Quality

A. Inconsistent Async Handling

// Sometimes async/await
async handlePaymentRequest(socket, data) { ... }

// Sometimes callbacks
this.walletService.startPaymentMonitoring(address, (status) => { ... })

B. Magic Numbers

2000 // Check interval - should be constant
30 * 60 * 1000 // 30 minutes - should be configurable
1000000000000 // Atomic units conversion - should be constant

📋 PRIORITY FIXES NEEDED

CRITICAL (Do immediately):

  1. ✅ COMPLETED - Fix walletService reference in socketHandlers.js
  2. ✅ COMPLETED - Remove all MoneroPay references
  3. ✅ COMPLETED - Fix RPC method names for Wownero
  4. ✅ COMPLETED - Add proper error handling for wallet-rpc failures
  5. ✅ COMPLETED - Fix method signature mismatches between WalletRPCService and GameModeManager
  6. ✅ COMPLETED - Add missing startPaymentMonitoring/stopPaymentMonitoring methods to WalletRPCService
  7. ✅ COMPLETED - Fix property name inconsistencies (inMempool vs in_mempool)
  8. ✅ COMPLETED - Fix database query methods (db.run vs db.query)
  9. ✅ COMPLETED - Update README.md to reflect wallet-rpc integration
  10. ✅ COMPLETED - Remove MoneroPay configuration from .env files

HIGH (Do soon):

  1. ✅ Implement payment expiration and cleanup
  2. ✅ Add address validation before creating subaddress
  3. ✅ Batch payment status checks
  4. ✅ Add reconnection logic for wallet-rpc

MEDIUM (Nice to have):

  1. ✅ Split socketHandlers into smaller modulee
  2. ✅ Add payment history tracking
  3. ✅ Implement refund logic
  4. ✅ Add QR codes for payment addresses

LOW (Future improvements):

  1. ✅ Add comprehensive tests
  2. ✅ Create admin interface
  3. ✅ Add metrics and monitoring
  4. ✅ Implement payment webhooks

Would you like me to start implementing these fixes, beginning with the critical issues?