- Organized 252 files across project - Root directory: 187 → 2 files (98.9% reduction) - Moved configuration guides to docs/04-configuration/ - Moved troubleshooting guides to docs/09-troubleshooting/ - Moved quick start guides to docs/01-getting-started/ - Moved reports to reports/ directory - Archived temporary files - Generated comprehensive reports and documentation - Created maintenance scripts and guides All files organized according to established standards.
250 lines
11 KiB
Bash
Executable File
250 lines
11 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Check for stuck transactions in Besu transaction pool
|
|
# Usage: ./scripts/check-stuck-transactions.sh [rpc-url] [account-address]
|
|
|
|
set -euo pipefail
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m'
|
|
|
|
log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
|
|
log_success() { echo -e "${GREEN}[✓]${NC} $1"; }
|
|
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
|
|
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }
|
|
log_detail() { echo -e "${CYAN}[DETAIL]${NC} $1"; }
|
|
|
|
# Configuration
|
|
RPC_URL="${1:-http://192.168.11.250:8545}" # Use Core RPC (VMID 2500)
|
|
ACCOUNT_ADDRESS="${2:-}" # Optional: specific account to check
|
|
|
|
echo "========================================="
|
|
echo "Check for Stuck Transactions"
|
|
echo "========================================="
|
|
echo ""
|
|
log_info "RPC URL: $RPC_URL"
|
|
echo ""
|
|
|
|
# Check if RPC is accessible
|
|
log_info "Checking RPC connectivity..."
|
|
RPC_RESPONSE=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' \
|
|
"$RPC_URL" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$RPC_RESPONSE" ] || ! echo "$RPC_RESPONSE" | jq -e '.result' >/dev/null 2>&1; then
|
|
log_error "Cannot connect to RPC endpoint: $RPC_URL"
|
|
exit 1
|
|
fi
|
|
|
|
BLOCK_NUMBER=$(echo "$RPC_RESPONSE" | jq -r '.result' 2>/dev/null | sed 's/0x//' | xargs -I {} printf "%d\n" 0x{} 2>/dev/null || echo "unknown")
|
|
log_success "RPC is accessible (Current block: $BLOCK_NUMBER)"
|
|
echo ""
|
|
|
|
# Check if TXPOOL API is enabled
|
|
log_info "Checking if TXPOOL API is enabled..."
|
|
RPC_MODULES=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data '{"jsonrpc":"2.0","method":"rpc_modules","params":[],"id":1}' \
|
|
"$RPC_URL" 2>/dev/null || echo "")
|
|
|
|
TXPOOL_ENABLED=false
|
|
if echo "$RPC_MODULES" | jq -r '.result | keys[]' 2>/dev/null | grep -qi "txpool"; then
|
|
TXPOOL_ENABLED=true
|
|
log_success "TXPOOL API is enabled"
|
|
else
|
|
log_warn "TXPOOL API is not enabled - cannot check transaction pool"
|
|
log_info "Enable TXPOOL in RPC config: rpc-http-api=[\"ETH\",\"NET\",\"WEB3\",\"TXPOOL\"]"
|
|
exit 1
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Get transaction pool content
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
log_info "Fetching Transaction Pool"
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Try txpool_besuTransactions (Besu-specific)
|
|
TXPOOL=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data '{"jsonrpc":"2.0","method":"txpool_besuTransactions","params":[],"id":1}' \
|
|
"$RPC_URL" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$TXPOOL" ] || ! echo "$TXPOOL" | jq -e '.result' >/dev/null 2>&1; then
|
|
log_warn "Could not fetch transaction pool using txpool_besuTransactions"
|
|
|
|
# Try txpool_content (standard)
|
|
TXPOOL=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data '{"jsonrpc":"2.0","method":"txpool_content","params":[],"id":1}' \
|
|
"$RPC_URL" 2>/dev/null || echo "")
|
|
|
|
if [ -z "$TXPOOL" ] || ! echo "$TXPOOL" | jq -e '.result' >/dev/null 2>&1; then
|
|
log_error "Cannot fetch transaction pool content"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
# Parse transaction pool
|
|
TX_COUNT=$(echo "$TXPOOL" | jq -r '[.result | if type == "array" then .[] else . end] | length' 2>/dev/null || echo "0")
|
|
|
|
if [ "$TX_COUNT" = "0" ] || [ "$TX_COUNT" = "null" ]; then
|
|
log_success "✓ Transaction pool is empty - no stuck transactions"
|
|
echo ""
|
|
exit 0
|
|
fi
|
|
|
|
log_warn "⚠ Found $TX_COUNT transaction(s) in pool"
|
|
echo ""
|
|
|
|
# Display all transactions
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
log_info "Transaction Pool Details"
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Format and display transactions
|
|
# txpool_besuTransactions returns minimal data, so we fetch full details for each hash
|
|
TX_HASHES=$(echo "$TXPOOL" | jq -r '.result[]?.hash // empty' 2>/dev/null | grep -v "^null$" | grep -v "^$")
|
|
|
|
if [ -z "$TX_HASHES" ]; then
|
|
log_warn "Could not extract transaction hashes from pool"
|
|
log_detail "Raw pool data:"
|
|
echo "$TXPOOL" | jq '.result' 2>/dev/null | head -20
|
|
echo ""
|
|
else
|
|
TX_INDEX=0
|
|
echo "$TX_HASHES" | while IFS= read -r TX_HASH; do
|
|
if [ -z "$TX_HASH" ] || [ "$TX_HASH" = "null" ]; then
|
|
continue
|
|
fi
|
|
|
|
TX_INDEX=$((TX_INDEX + 1))
|
|
|
|
# Fetch full transaction details
|
|
TX_DETAILS=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionByHash\",\"params\":[\"$TX_HASH\"],\"id\":1}" \
|
|
"$RPC_URL" 2>/dev/null | jq -r '.result' 2>/dev/null)
|
|
|
|
if [ -z "$TX_DETAILS" ] || [ "$TX_DETAILS" = "null" ]; then
|
|
# Transaction not found in blockchain, still in pool
|
|
ADDED_AT=$(echo "$TXPOOL" | jq -r --arg hash "$TX_HASH" '.result[] | select(.hash == $hash) | .addedToPoolAt // "N/A"' 2>/dev/null)
|
|
log_detail "Transaction #$TX_INDEX:"
|
|
log_detail " Hash: $TX_HASH"
|
|
log_detail " Status: In pool (not yet mined)"
|
|
log_detail " Added to pool: $ADDED_AT"
|
|
echo ""
|
|
else
|
|
# Extract details from transaction
|
|
TX_FROM=$(echo "$TX_DETAILS" | jq -r '.from // "N/A"' 2>/dev/null)
|
|
TX_TO=$(echo "$TX_DETAILS" | jq -r '.to // "N/A"' 2>/dev/null)
|
|
TX_NONCE_HEX=$(echo "$TX_DETAILS" | jq -r '.nonce // "0x0"' 2>/dev/null)
|
|
TX_NONCE=$(printf "%d" $TX_NONCE_HEX 2>/dev/null || echo "N/A")
|
|
TX_GAS_PRICE_HEX=$(echo "$TX_DETAILS" | jq -r '.gasPrice // "0x0"' 2>/dev/null)
|
|
TX_GAS_PRICE_WEI=$(printf "%d" $TX_GAS_PRICE_HEX 2>/dev/null || echo "0")
|
|
TX_GAS_PRICE_GWEI=$(echo "scale=2; $TX_GAS_PRICE_WEI / 1000000000" | bc 2>/dev/null || echo "N/A")
|
|
TX_GAS=$(echo "$TX_DETAILS" | jq -r '.gas // "N/A"' 2>/dev/null)
|
|
TX_VALUE_HEX=$(echo "$TX_DETAILS" | jq -r '.value // "0x0"' 2>/dev/null)
|
|
TX_VALUE=$(printf "%d" $TX_VALUE_HEX 2>/dev/null || echo "0")
|
|
|
|
log_detail "Transaction #$TX_INDEX:"
|
|
log_detail " Hash: $TX_HASH"
|
|
log_detail " From: $TX_FROM"
|
|
log_detail " To: $TX_TO"
|
|
log_detail " Nonce: $TX_NONCE"
|
|
log_detail " Gas Price: $TX_GAS_PRICE_GWEI gwei ($TX_GAS_PRICE_WEI wei)"
|
|
log_detail " Gas Limit: $TX_GAS"
|
|
log_detail " Value: $TX_VALUE wei"
|
|
|
|
# Check if this transaction matches current nonce (stuck)
|
|
if [ "$TX_FROM" != "N/A" ] && [ "$TX_NONCE" != "N/A" ]; then
|
|
CURRENT_NONCE_TX=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$TX_FROM\",\"latest\"],\"id\":1}" \
|
|
"$RPC_URL" 2>/dev/null | jq -r '.result' 2>/dev/null | sed 's/0x//' | xargs -I {} printf "%d\n" 0x{} 2>/dev/null || echo "")
|
|
|
|
if [ -n "$CURRENT_NONCE_TX" ] && [ "$TX_NONCE" = "$CURRENT_NONCE_TX" ]; then
|
|
log_warn " ⚠ STUCK: Nonce $TX_NONCE matches current nonce (transaction blocking subsequent transactions)"
|
|
elif [ -n "$CURRENT_NONCE_TX" ] && [ "$TX_NONCE" -lt "$CURRENT_NONCE_TX" ]; then
|
|
log_warn " ⚠ OUTDATED: Nonce $TX_NONCE is below current nonce $CURRENT_NONCE_TX (transaction may be rejected)"
|
|
fi
|
|
fi
|
|
echo ""
|
|
fi
|
|
done <<< "$TX_HASHES"
|
|
fi
|
|
|
|
echo ""
|
|
|
|
# Check specific account if provided
|
|
if [ -n "$ACCOUNT_ADDRESS" ]; then
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
log_info "Checking Account: $ACCOUNT_ADDRESS"
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
|
|
# Normalize address (remove 0x prefix for comparison)
|
|
NORMALIZED_ACCOUNT=$(echo "$ACCOUNT_ADDRESS" | tr '[:upper:]' '[:lower:]' | sed 's/^0x//')
|
|
|
|
# Find transactions for this account
|
|
ACCOUNT_TXS=$(echo "$TXPOOL" | jq -r --arg addr "$NORMALIZED_ACCOUNT" \
|
|
'.result | if type == "array" then .[] else . end |
|
|
select((.from // "") | ascii_downcase | gsub("^0x"; "") == $addr)' 2>/dev/null || echo "")
|
|
|
|
if [ -z "$ACCOUNT_TXS" ] || [ "$ACCOUNT_TXS" = "null" ]; then
|
|
log_success "✓ No transactions found for account $ACCOUNT_ADDRESS"
|
|
else
|
|
ACCOUNT_TX_COUNT=$(echo "$ACCOUNT_TXS" | jq -s 'length' 2>/dev/null || echo "1")
|
|
log_warn "⚠ Found $ACCOUNT_TX_COUNT transaction(s) for account $ACCOUNT_ADDRESS"
|
|
echo ""
|
|
|
|
# Get current nonce for account
|
|
CURRENT_NONCE=$(curl -s -X POST -H "Content-Type: application/json" \
|
|
--data "{\"jsonrpc\":\"2.0\",\"method\":\"eth_getTransactionCount\",\"params\":[\"$ACCOUNT_ADDRESS\",\"latest\"],\"id\":1}" \
|
|
"$RPC_URL" 2>/dev/null | jq -r '.result' 2>/dev/null | sed 's/0x//' | xargs -I {} printf "%d\n" 0x{} 2>/dev/null || echo "unknown")
|
|
|
|
log_info "Current on-chain nonce: $CURRENT_NONCE"
|
|
echo ""
|
|
|
|
# Display account transactions with nonce info
|
|
echo "$ACCOUNT_TXS" | jq -r '
|
|
"Transaction Details:
|
|
Hash: \(.hash // .transactionHash // "N/A")
|
|
From: \(.from // "N/A")
|
|
Nonce: \(.nonce // "N/A")
|
|
Gas Price: \(.gasPrice // "N/A")
|
|
---"' 2>/dev/null | while IFS= read -r line; do
|
|
if [[ "$line" == "---" ]]; then
|
|
echo ""
|
|
else
|
|
log_detail "$line"
|
|
fi
|
|
done
|
|
|
|
# Check for stuck nonces (nonce matches current nonce)
|
|
echo "$ACCOUNT_TXS" | jq -r --arg nonce "$CURRENT_NONCE" \
|
|
'select((.nonce // "" | tostring) == $nonce) | .hash // .transactionHash' 2>/dev/null | while read -r hash; do
|
|
if [ -n "$hash" ] && [ "$hash" != "null" ]; then
|
|
log_warn "⚠ STUCK TRANSACTION: Hash $hash has nonce $CURRENT_NONCE (matches current nonce)"
|
|
fi
|
|
done
|
|
fi
|
|
echo ""
|
|
fi
|
|
|
|
# Summary
|
|
echo "========================================="
|
|
echo "Summary"
|
|
echo "========================================="
|
|
echo ""
|
|
log_warn "Total transactions in pool: $TX_COUNT"
|
|
|
|
if [ "$TX_COUNT" -gt 0 ]; then
|
|
log_info ""
|
|
log_info "Next steps:"
|
|
log_info " 1. Review transactions above to identify stuck ones"
|
|
log_info " 2. Flush mempools: ./scripts/flush-all-mempools-proxmox.sh"
|
|
log_info " 3. Or wait for transactions to be processed"
|
|
fi
|
|
|
|
echo ""
|
|
|