mirror of
https://github.com/Such-Software/wownerogue.git
synced 2026-10-21 08:08:32 +02:00
17 KiB
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.jsstill referencesthis.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
walletRPCServicenever clean up expired addresses addressToUserMap 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):
- ✅ COMPLETED - Fix
walletServicereference in socketHandlers.js - ✅ COMPLETED - Remove all MoneroPay references
- ✅ COMPLETED - Fix RPC method names for Wownero
- ✅ COMPLETED - Add proper error handling for wallet-rpc failures
- ✅ COMPLETED - Fix method signature mismatches between WalletRPCService and GameModeManager
- ✅ COMPLETED - Add missing startPaymentMonitoring/stopPaymentMonitoring methods to WalletRPCService
- ✅ COMPLETED - Fix property name inconsistencies (inMempool vs in_mempool)
- ✅ COMPLETED - Fix database query methods (db.run vs db.query)
- ✅ COMPLETED - Update README.md to reflect wallet-rpc integration
- ✅ COMPLETED - Remove MoneroPay configuration from .env files
HIGH (Do soon):
- ✅ Implement payment expiration and cleanup
- ✅ Add address validation before creating subaddress
- ✅ Batch payment status checks
- ✅ Add reconnection logic for wallet-rpc
MEDIUM (Nice to have):
- ✅ Split socketHandlers into smaller modulee
- ✅ Add payment history tracking
- ✅ Implement refund logic
- ✅ Add QR codes for payment addresses
LOW (Future improvements):
- ✅ Add comprehensive tests
- ✅ Create admin interface
- ✅ Add metrics and monitoring
- ✅ Implement payment webhooks
Would you like me to start implementing these fixes, beginning with the critical issues?