Files
aitbc/.windsurf/skills/blockchain-operations/tx-tracer.py
oib 9b9c5beb23 ```
chore: enhance .gitignore and remove obsolete documentation files

- Reorganize .gitignore with categorized sections for better maintainability
- Add comprehensive ignore patterns for Python, Node.js, databases, logs, and build artifacts
- Add project-specific ignore rules for coordinator, explorer, and deployment files
- Remove outdated documentation: BITCOIN-WALLET-SETUP.md, LOCAL_ASSETS_SUMMARY.md, README-CONTAINER-DEPLOYMENT.md, README-DOMAIN-DEPLOYMENT.md
```
2026-01-24 14:44:51 +01:00

274 lines
11 KiB
Python

#!/usr/bin/env python3
"""
AITBC Transaction Tracer
Comprehensive transaction debugging and analysis tool
"""
import web3
import json
import sys
import argparse
from datetime import datetime
from typing import Dict, List, Optional, Any
class TransactionTracer:
def __init__(self, node_url: str = "http://localhost:8545"):
"""Initialize the transaction tracer"""
self.w3 = web3.Web3(web3.HTTPProvider(node_url))
if not self.w3.is_connected():
raise Exception("Failed to connect to AITBC node")
def trace_transaction(self, tx_hash: str) -> Dict[str, Any]:
"""Trace a transaction and return comprehensive information"""
try:
# Get transaction details
tx = self.w3.eth.get_transaction(tx_hash)
receipt = self.w3.eth.get_transaction_receipt(tx_hash)
# Build trace result
trace = {
'hash': tx_hash,
'status': 'success' if receipt.status == 1 else 'failed',
'block_number': tx.blockNumber,
'block_hash': tx.blockHash.hex(),
'transaction_index': receipt.transactionIndex,
'from_address': tx['from'],
'to_address': tx.get('to'),
'value': self.w3.from_wei(tx.value, 'ether'),
'gas_limit': tx.gas,
'gas_used': receipt.gasUsed,
'gas_price': self.w3.from_wei(tx.gasPrice, 'gwei'),
'effective_gas_price': self.w3.from_wei(receipt.effectiveGasPrice, 'gwei'),
'nonce': tx.nonce,
'max_fee_per_gas': None,
'max_priority_fee_per_gas': None,
'type': tx.get('type', 0)
}
# EIP-1559 transaction fields
if tx.get('type') == 2:
trace['max_fee_per_gas'] = self.w3.from_wei(tx.maxFeePerGas, 'gwei')
trace['max_priority_fee_per_gas'] = self.w3.from_wei(tx.maxPriorityFeePerGas, 'gwei')
# Calculate gas efficiency
trace['gas_efficiency'] = f"{(receipt.gasUsed / tx.gas * 100):.2f}%"
# Get logs
trace['logs'] = self._parse_logs(receipt.logs)
# Get contract creation info if applicable
if tx.get('to') is None:
trace['contract_created'] = receipt.contractAddress
trace['contract_code'] = self.w3.eth.get_code(receipt.contractAddress).hex()
# Get internal transfers (if tracing is available)
trace['internal_transfers'] = self._get_internal_transfers(tx_hash)
return trace
except Exception as e:
return {'error': str(e), 'hash': tx_hash}
def _parse_logs(self, logs: List) -> List[Dict]:
"""Parse transaction logs"""
parsed_logs = []
for log in logs:
parsed_logs.append({
'address': log.address,
'topics': [topic.hex() for topic in log.topics],
'data': log.data.hex(),
'log_index': log.logIndex,
'decoded': self._decode_log(log)
})
return parsed_logs
def _decode_log(self, log) -> Optional[Dict]:
"""Attempt to decode log events"""
# This would contain ABI decoding logic
# For now, return basic info
return {
'signature': log.topics[0].hex() if log.topics else None,
'event_name': 'Unknown' # Would be decoded from ABI
}
def _get_internal_transfers(self, tx_hash: str) -> List[Dict]:
"""Get internal ETH transfers (requires tracing)"""
try:
# Try debug_traceTransaction if available
trace = self.w3.provider.make_request('debug_traceTransaction', [tx_hash, {}])
transfers = []
# Parse trace for transfers
if trace and 'result' in trace:
# Implementation would parse the trace for CALL/DELEGATECALL with value
pass
return transfers
except:
return []
def analyze_gas_usage(self, tx_hash: str) -> Dict[str, Any]:
"""Analyze gas usage and provide optimization tips"""
trace = self.trace_transaction(tx_hash)
if 'error' in trace:
return trace
analysis = {
'gas_used': trace['gas_used'],
'gas_limit': trace['gas_limit'],
'efficiency': trace['gas_efficiency'],
'recommendations': []
}
# Gas efficiency recommendations
if trace['gas_used'] < trace['gas_limit'] * 0.5:
analysis['recommendations'].append(
f"Gas limit too high. Consider reducing to ~{int(trace['gas_used'] * 1.2)}"
)
# Gas price analysis
if trace['gas_price'] > 100: # High gas price threshold
analysis['recommendations'].append(
"High gas price detected. Consider using EIP-1559 or waiting for lower gas"
)
return analysis
def debug_failed_transaction(self, tx_hash: str) -> Dict[str, Any]:
"""Debug why a transaction failed"""
trace = self.trace_transaction(tx_hash)
if trace.get('status') == 'success':
return {'error': 'Transaction was successful', 'hash': tx_hash}
debug_info = {
'hash': tx_hash,
'failure_reason': 'Unknown',
'possible_causes': [],
'debug_steps': []
}
# Check for common failure reasons
debug_info['debug_steps'].append("1. Checking if transaction ran out of gas...")
if trace['gas_used'] == trace['gas_limit']:
debug_info['failure_reason'] = 'Out of gas'
debug_info['possible_causes'].append('Transaction required more gas than provided')
debug_info['debug_steps'].append(" ✓ Transaction ran out of gas")
debug_info['debug_steps'].append("2. Checking for revert reasons...")
# Would implement revert reason decoding here
debug_info['debug_steps'].append(" ✗ Could not decode revert reason")
debug_info['debug_steps'].append("3. Checking nonce issues...")
# Would check for nonce problems
debug_info['debug_steps'].append(" ✓ Nonce appears correct")
return debug_info
def monitor_mempool(self, address: str = None) -> Dict[str, Any]:
"""Monitor transaction mempool"""
try:
# Get pending transactions
pending_block = self.w3.eth.get_block('pending', full_transactions=True)
pending_txs = pending_block.transactions
mempool_info = {
'pending_count': len(pending_txs),
'pending_by_address': {},
'high_priority_txs': [],
'stuck_txs': []
}
# Analyze pending transactions
for tx in pending_txs:
from_addr = str(tx['from'])
if from_addr not in mempool_info['pending_by_address']:
mempool_info['pending_by_address'][from_addr] = 0
mempool_info['pending_by_address'][from_addr] += 1
# High priority transactions (high gas price)
if tx.gasPrice > web3.Web3.to_wei(50, 'gwei'):
mempool_info['high_priority_txs'].append({
'hash': tx.hash.hex(),
'gas_price': web3.Web3.from_wei(tx.gasPrice, 'gwei'),
'from': from_addr
})
return mempool_info
except Exception as e:
return {'error': str(e)}
def print_trace(self, trace: Dict[str, Any]):
"""Print formatted transaction trace"""
if 'error' in trace:
print(f"Error: {trace['error']}")
return
print(f"\n{'='*60}")
print(f"Transaction Trace: {trace['hash']}")
print(f"{'='*60}")
print(f"Status: {trace['status'].upper()}")
print(f"Block: #{trace['block_number']} ({trace['block_hash'][:10]}...)")
print(f"From: {trace['from_address']}")
print(f"To: {trace['to_address'] or 'Contract Creation'}")
print(f"Value: {trace['value']} ETH")
print(f"Gas Used: {trace['gas_used']:,} / {trace['gas_limit']:,} ({trace['gas_efficiency']})")
print(f"Gas Price: {trace['gas_price']} gwei")
if trace['max_fee_per_gas']:
print(f"Max Fee: {trace['max_fee_per_gas']} gwei")
print(f"Priority Fee: {trace['max_priority_fee_per_gas']} gwei")
if trace.get('contract_created'):
print(f"\nContract Created: {trace['contract_created']}")
if trace['logs']:
print(f"\nLogs ({len(trace['logs'])}):")
for log in trace['logs'][:5]: # Show first 5 logs
print(f" - {log['address']}: {log['decoded']['event_name'] or 'Unknown Event'}")
if trace['internal_transfers']:
print(f"\nInternal Transfers:")
for transfer in trace['internal_transfers']:
print(f" {transfer['from']} -> {transfer['to']}: {transfer['value']} ETH")
def main():
parser = argparse.ArgumentParser(description='AITBC Transaction Tracer')
parser.add_argument('command', choices=['trace', 'analyze', 'debug', 'mempool'])
parser.add_argument('--tx', help='Transaction hash')
parser.add_argument('--address', help='Address for mempool monitoring')
parser.add_argument('--node', default='http://localhost:8545', help='Node URL')
args = parser.parse_args()
tracer = TransactionTracer(args.node)
if args.command == 'trace':
if not args.tx:
print("Error: Transaction hash required for trace command")
sys.exit(1)
trace = tracer.trace_transaction(args.tx)
tracer.print_trace(trace)
elif args.command == 'analyze':
if not args.tx:
print("Error: Transaction hash required for analyze command")
sys.exit(1)
analysis = tracer.analyze_gas_usage(args.tx)
print(json.dumps(analysis, indent=2))
elif args.command == 'debug':
if not args.tx:
print("Error: Transaction hash required for debug command")
sys.exit(1)
debug = tracer.debug_failed_transaction(args.tx)
print(json.dumps(debug, indent=2))
elif args.command == 'mempool':
mempool = tracer.monitor_mempool(args.address)
print(json.dumps(mempool, indent=2))
if __name__ == "__main__":
main()