dazman72's picture
feature rich, interactive demonstration on how the block chain works. lots of interactive elements and feature rich visuals. use your best UI/IX skills when developing the ui. use the latest js libraries to help - Initial Deployment
6c39938 verified
Raw History Blame Contribute Delete
48.6 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Blockchain Explorer</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/particles.js@2.0.0/particles.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.blockchain-node {
transition: all 0.3s ease;
}
.blockchain-node:hover {
transform: scale(1.05);
box-shadow: 0 10px 25px rgba(59, 130, 246, 0.3);
}
.block-highlight {
animation: pulse 2s infinite;
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0.7); }
70% { box-shadow: 0 0 0 10px rgba(59, 130, 246, 0); }
100% { box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }
}
#particles-js {
position: absolute;
width: 100%;
height: 100%;
z-index: -1;
background: linear-gradient(135deg, #1e3a8a 0%, #0f172a 100%);
}
.transaction-card {
transition: all 0.3s ease;
}
.transaction-card:hover {
transform: translateY(-5px);
}
.mining-animation {
animation: mining 1.5s infinite;
}
@keyframes mining {
0% { opacity: 0.5; }
50% { opacity: 1; }
100% { opacity: 0.5; }
}
.blockchain-visualization {
perspective: 1000px;
}
.block-3d {
transform-style: preserve-3d;
transition: transform 0.5s;
}
.block-3d:hover {
transform: rotateY(15deg) rotateX(5deg);
}
</style>
</head>
<body class="bg-gray-900 text-gray-100 min-h-screen overflow-x-hidden">
<div id="particles-js"></div>
<div class="container mx-auto px-4 py-8">
<!-- Header -->
<header class="text-center mb-12">
<h1 class="text-4xl md:text-6xl font-bold bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-purple-600 mb-4">
Interactive Blockchain Explorer
</h1>
<p class="text-xl text-blue-200 max-w-3xl mx-auto">
Visualize how blockchain technology works through this interactive demonstration. Create transactions, mine blocks, and see the decentralized network in action.
</p>
</header>
<!-- Main Dashboard -->
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8 mb-12">
<!-- Control Panel -->
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700">
<h2 class="text-2xl font-bold mb-6 text-blue-400 flex items-center">
<i class="fas fa-sliders-h mr-3"></i> Control Panel
</h2>
<div class="space-y-6">
<div>
<h3 class="text-lg font-semibold mb-3 text-blue-300">Create Transaction</h3>
<div class="grid grid-cols-2 gap-3 mb-3">
<div>
<label class="block text-sm text-gray-300 mb-1">From</label>
<select id="fromAddress" class="w-full bg-gray-700 rounded px-3 py-2 text-sm">
<option value="Alice">Alice</option>
<option value="Bob">Bob</option>
<option value="Charlie">Charlie</option>
<option value="Diana">Diana</option>
</select>
</div>
<div>
<label class="block text-sm text-gray-300 mb-1">To</label>
<select id="toAddress" class="w-full bg-gray-700 rounded px-3 py-2 text-sm">
<option value="Bob">Bob</option>
<option value="Alice">Alice</option>
<option value="Charlie">Charlie</option>
<option value="Diana">Diana</option>
</select>
</div>
</div>
<div class="mb-3">
<label class="block text-sm text-gray-300 mb-1">Amount</label>
<input type="number" id="transactionAmount" class="w-full bg-gray-700 rounded px-3 py-2 text-sm" placeholder="0.00" min="0.01" step="0.01">
</div>
<button id="createTransactionBtn" class="w-full bg-blue-600 hover:bg-blue-700 text-white py-2 px-4 rounded-lg transition flex items-center justify-center">
<i class="fas fa-paper-plane mr-2"></i> Create Transaction
</button>
</div>
<div>
<h3 class="text-lg font-semibold mb-3 text-blue-300">Mine Block</h3>
<div class="flex items-center mb-3">
<div class="w-full bg-gray-700 rounded-lg h-2 mr-3">
<div id="miningProgress" class="bg-yellow-500 h-2 rounded-lg" style="width: 0%"></div>
</div>
<span id="miningDifficulty" class="text-sm text-gray-300">Difficulty: 4</span>
</div>
<button id="mineBlockBtn" class="w-full bg-yellow-600 hover:bg-yellow-700 text-white py-2 px-4 rounded-lg transition flex items-center justify-center">
<i class="fas fa-hammer mr-2"></i> Mine Block
</button>
</div>
<div>
<h3 class="text-lg font-semibold mb-3 text-blue-300">Network Settings</h3>
<div class="mb-3">
<label class="flex items-center">
<input type="checkbox" id="autoMineToggle" class="mr-2">
<span class="text-sm text-gray-300">Auto-mine transactions</span>
</label>
</div>
<div class="mb-3">
<label class="block text-sm text-gray-300 mb-1">Network Nodes</label>
<div class="flex flex-wrap gap-2">
<span class="bg-gray-700 px-3 py-1 rounded-full text-xs flex items-center">
<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span> Node 1
</span>
<span class="bg-gray-700 px-3 py-1 rounded-full text-xs flex items-center">
<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span> Node 2
</span>
<span class="bg-gray-700 px-3 py-1 rounded-full text-xs flex items-center">
<span class="w-2 h-2 bg-green-500 rounded-full mr-2"></span> Node 3
</span>
<button class="bg-gray-700 hover:bg-gray-600 px-3 py-1 rounded-full text-xs flex items-center transition">
<i class="fas fa-plus mr-1"></i> Add Node
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Blockchain Visualization -->
<div class="lg:col-span-2">
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700 h-full">
<div class="flex justify-between items-center mb-6">
<h2 class="text-2xl font-bold text-blue-400 flex items-center">
<i class="fas fa-link mr-3"></i> Blockchain
</h2>
<div class="text-sm bg-gray-700 px-3 py-1 rounded-full">
<span id="blockCount" class="font-semibold">1</span> Blocks
</div>
</div>
<div class="blockchain-visualization">
<div id="blockchainContainer" class="space-y-4">
<!-- Genesis Block -->
<div class="block-3d bg-gray-700 rounded-lg p-5 shadow-lg border-l-4 border-blue-500 relative overflow-hidden">
<div class="absolute top-0 right-0 bg-blue-600 text-white text-xs px-2 py-1 rounded-bl-lg">
Genesis Block
</div>
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">Block #0</h3>
<p class="text-xs text-gray-400">0000000000000000000000000000000000000000</p>
</div>
<div class="text-right">
<div class="text-xs text-gray-400">Nonce: 0</div>
<div class="text-xs text-gray-400">Timestamp: 0</div>
</div>
</div>
<div class="bg-gray-800 rounded p-3 mb-3">
<div class="text-xs text-gray-400 mb-1">Merkle Root:</div>
<div class="text-xs font-mono break-all">00000000000000000000000000000000</div>
</div>
<div class="text-xs text-gray-400">Previous Hash: None</div>
</div>
</div>
</div>
<div id="pendingTransactions" class="mt-8">
<h3 class="text-lg font-semibold mb-3 text-blue-300 flex items-center">
<i class="fas fa-clock mr-2"></i> Pending Transactions
<span id="pendingCount" class="ml-2 bg-gray-700 px-2 py-0.5 rounded-full text-xs">0</span>
</h3>
<div id="transactionsContainer" class="space-y-3">
<div class="text-center py-4 text-gray-500">
No pending transactions
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Network Visualization -->
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700 mb-12">
<h2 class="text-2xl font-bold mb-6 text-blue-400 flex items-center">
<i class="fas fa-network-wired mr-3"></i> Network Visualization
</h2>
<div class="relative h-64 md:h-96 bg-gray-900 rounded-lg overflow-hidden">
<canvas id="networkCanvas" class="w-full h-full"></canvas>
<div class="absolute bottom-4 left-4 bg-gray-800 bg-opacity-80 px-3 py-2 rounded-lg text-sm">
<div class="flex items-center mb-1">
<div class="w-3 h-3 bg-blue-500 rounded-full mr-2"></div>
<span>Nodes: <span id="nodeCount">4</span></span>
</div>
<div class="flex items-center">
<div class="w-3 h-3 bg-green-500 rounded-full mr-2"></div>
<span>Connections: <span id="connectionCount">6</span></span>
</div>
</div>
</div>
</div>
<!-- Statistics and Charts -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-8 mb-12">
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700">
<h2 class="text-2xl font-bold mb-6 text-blue-400 flex items-center">
<i class="fas fa-chart-line mr-3"></i> Blockchain Statistics
</h2>
<div class="grid grid-cols-2 gap-4 mb-6">
<div class="bg-gray-700 p-4 rounded-lg">
<div class="text-sm text-gray-400 mb-1">Total Blocks</div>
<div class="text-2xl font-bold" id="statsBlocks">1</div>
</div>
<div class="bg-gray-700 p-4 rounded-lg">
<div class="text-sm text-gray-400 mb-1">Total Transactions</div>
<div class="text-2xl font-bold" id="statsTransactions">0</div>
</div>
<div class="bg-gray-700 p-4 rounded-lg">
<div class="text-sm text-gray-400 mb-1">Average Block Time</div>
<div class="text-2xl font-bold" id="statsBlockTime">0s</div>
</div>
<div class="bg-gray-700 p-4 rounded-lg">
<div class="text-sm text-gray-400 mb-1">Network Hashrate</div>
<div class="text-2xl font-bold" id="statsHashrate">0 H/s</div>
</div>
</div>
<div>
<h3 class="text-lg font-semibold mb-3 text-blue-300">Block Time History</h3>
<canvas id="blockTimeChart" height="200"></canvas>
</div>
</div>
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700">
<h2 class="text-2xl font-bold mb-6 text-blue-400 flex items-center">
<i class="fas fa-coins mr-3"></i> Wallet Balances
</h2>
<div class="mb-6">
<canvas id="balanceChart" height="200"></canvas>
</div>
<div class="overflow-x-auto">
<table class="w-full">
<thead>
<tr class="text-left text-sm text-gray-400 border-b border-gray-700">
<th class="pb-2">Wallet</th>
<th class="pb-2">Balance</th>
<th class="pb-2">Transactions</th>
</tr>
</thead>
<tbody id="walletBalances">
<tr class="border-b border-gray-700">
<td class="py-3 flex items-center">
<div class="w-2 h-2 bg-blue-500 rounded-full mr-2"></div>
Alice
</td>
<td class="py-3">100.00</td>
<td class="py-3">0</td>
</tr>
<tr class="border-b border-gray-700">
<td class="py-3 flex items-center">
<div class="w-2 h-2 bg-green-500 rounded-full mr-2"></div>
Bob
</td>
<td class="py-3">100.00</td>
<td class="py-3">0</td>
</tr>
<tr class="border-b border-gray-700">
<td class="py-3 flex items-center">
<div class="w-2 h-2 bg-purple-500 rounded-full mr-2"></div>
Charlie
</td>
<td class="py-3">100.00</td>
<td class="py-3">0</td>
</tr>
<tr>
<td class="py-3 flex items-center">
<div class="w-2 h-2 bg-yellow-500 rounded-full mr-2"></div>
Diana
</td>
<td class="py-3">100.00</td>
<td class="py-3">0</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<!-- Educational Section -->
<div class="bg-gray-800 rounded-xl p-6 shadow-xl border border-gray-700 mb-12">
<h2 class="text-2xl font-bold mb-6 text-blue-400 flex items-center">
<i class="fas fa-graduation-cap mr-3"></i> How Blockchain Works
</h2>
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-cube"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Blocks</h3>
<p class="text-sm text-gray-300">
Blocks are containers that hold transactions. Each block is cryptographically linked to the previous one, forming a chain.
</p>
</div>
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-lock"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Cryptography</h3>
<p class="text-sm text-gray-300">
Blockchain uses cryptographic hashes to secure data. Changing any information would require changing all subsequent blocks.
</p>
</div>
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-users"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Decentralization</h3>
<p class="text-sm text-gray-300">
The blockchain is maintained by a network of nodes that all have a copy of the ledger, making it resistant to tampering.
</p>
</div>
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-hammer"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Mining</h3>
<p class="text-sm text-gray-300">
Miners solve complex mathematical problems to add new blocks to the chain and are rewarded with cryptocurrency.
</p>
</div>
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-handshake"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Consensus</h3>
<p class="text-sm text-gray-300">
Blockchain networks use consensus mechanisms like Proof of Work or Proof of Stake to agree on the state of the ledger.
</p>
</div>
<div class="bg-gray-700 p-5 rounded-lg hover:bg-gray-600 transition cursor-pointer">
<div class="text-blue-400 text-4xl mb-3">
<i class="fas fa-shield-alt"></i>
</div>
<h3 class="text-lg font-semibold mb-2">Immutability</h3>
<p class="text-sm text-gray-300">
Once data is recorded in a block and added to the chain, it becomes extremely difficult to alter, creating a permanent record.
</p>
</div>
</div>
</div>
</div>
<script>
// Initialize particles.js
document.addEventListener('DOMContentLoaded', function() {
particlesJS('particles-js', {
"particles": {
"number": {
"value": 80,
"density": {
"enable": true,
"value_area": 800
}
},
"color": {
"value": "#3b82f6"
},
"shape": {
"type": "circle",
"stroke": {
"width": 0,
"color": "#000000"
},
"polygon": {
"nb_sides": 5
}
},
"opacity": {
"value": 0.5,
"random": false,
"anim": {
"enable": false,
"speed": 1,
"opacity_min": 0.1,
"sync": false
}
},
"size": {
"value": 3,
"random": true,
"anim": {
"enable": false,
"speed": 40,
"size_min": 0.1,
"sync": false
}
},
"line_linked": {
"enable": true,
"distance": 150,
"color": "#3b82f6",
"opacity": 0.4,
"width": 1
},
"move": {
"enable": true,
"speed": 2,
"direction": "none",
"random": false,
"straight": false,
"out_mode": "out",
"bounce": false,
"attract": {
"enable": false,
"rotateX": 600,
"rotateY": 1200
}
}
},
"interactivity": {
"detect_on": "canvas",
"events": {
"onhover": {
"enable": true,
"mode": "grab"
},
"onclick": {
"enable": true,
"mode": "push"
},
"resize": true
},
"modes": {
"grab": {
"distance": 140,
"line_linked": {
"opacity": 1
}
},
"bubble": {
"distance": 400,
"size": 40,
"duration": 2,
"opacity": 8,
"speed": 3
},
"repulse": {
"distance": 200,
"duration": 0.4
},
"push": {
"particles_nb": 4
},
"remove": {
"particles_nb": 2
}
}
},
"retina_detect": true
});
});
// Blockchain simulation
class Blockchain {
constructor() {
this.chain = [this.createGenesisBlock()];
this.pendingTransactions = [];
this.difficulty = 4;
this.miningReward = 10;
this.wallets = {
'Alice': { balance: 100, transactions: 0 },
'Bob': { balance: 100, transactions: 0 },
'Charlie': { balance: 100, transactions: 0 },
'Diana': { balance: 100, transactions: 0 }
};
this.blockTimes = [];
this.nodes = 4;
this.connections = 6;
}
createGenesisBlock() {
return {
index: 0,
timestamp: new Date().toISOString(),
transactions: [],
previousHash: '0',
hash: this.calculateHash(0, '0', [], 0),
nonce: 0
};
}
getLatestBlock() {
return this.chain[this.chain.length - 1];
}
createTransaction(from, to, amount) {
if (this.wallets[from].balance < amount) {
alert('Insufficient balance!');
return false;
}
const transaction = {
from,
to,
amount,
timestamp: new Date().toISOString()
};
this.pendingTransactions.push(transaction);
return true;
}
minePendingTransactions(miningRewardAddress) {
const block = {
index: this.chain.length,
previousHash: this.getLatestBlock().hash,
transactions: this.pendingTransactions,
timestamp: new Date().toISOString(),
nonce: 0
};
// Process transactions
this.pendingTransactions.forEach(tx => {
this.wallets[tx.from].balance -= tx.amount;
this.wallets[tx.to].balance += tx.amount;
this.wallets[tx.from].transactions++;
this.wallets[tx.to].transactions++;
});
// Add mining reward
this.wallets[miningRewardAddress].balance += this.miningReward;
// Calculate hash with proof of work
block.hash = this.proofOfWork(block);
// Add block to chain
this.chain.push(block);
// Record block time
if (this.chain.length > 1) {
const prevTime = new Date(this.chain[this.chain.length - 2].timestamp);
const currTime = new Date(block.timestamp);
this.blockTimes.push((currTime - prevTime) / 1000);
}
// Reset pending transactions
this.pendingTransactions = [];
}
proofOfWork(block) {
let nonce = 0;
let hash;
const startTime = Date.now();
do {
nonce++;
hash = this.calculateHash(block.index, block.previousHash, block.transactions, nonce);
// Update UI with mining progress
const progress = Math.min(99, (nonce / Math.pow(10, this.difficulty)) * 100);
document.getElementById('miningProgress').style.width = `${progress}%`;
// Small delay for animation
if (nonce % 1000 === 0) {
return new Promise(resolve => setTimeout(resolve, 0)).then(() => {});
}
} while (!hash.startsWith('0'.repeat(this.difficulty)));
block.nonce = nonce;
const endTime = Date.now();
console.log(`Block mined in ${(endTime - startTime)/1000}s with nonce ${nonce}`);
return hash;
}
calculateHash(index, previousHash, transactions, nonce) {
return CryptoJS.SHA256(index + previousHash + JSON.stringify(transactions) + nonce).toString();
}
isChainValid() {
for (let i = 1; i < this.chain.length; i++) {
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
if (currentBlock.hash !== this.calculateHash(
currentBlock.index,
currentBlock.previousHash,
currentBlock.transactions,
currentBlock.nonce
)) {
return false;
}
if (currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
}
// Initialize blockchain
const blockchain = new Blockchain();
// DOM elements
const createTransactionBtn = document.getElementById('createTransactionBtn');
const mineBlockBtn = document.getElementById('mineBlockBtn');
const fromAddress = document.getElementById('fromAddress');
const toAddress = document.getElementById('toAddress');
const transactionAmount = document.getElementById('transactionAmount');
const blockchainContainer = document.getElementById('blockchainContainer');
const transactionsContainer = document.getElementById('transactionsContainer');
const pendingCount = document.getElementById('pendingCount');
const blockCount = document.getElementById('blockCount');
const statsBlocks = document.getElementById('statsBlocks');
const statsTransactions = document.getElementById('statsTransactions');
const statsBlockTime = document.getElementById('statsBlockTime');
const statsHashrate = document.getElementById('statsHashrate');
const walletBalances = document.getElementById('walletBalances');
const nodeCount = document.getElementById('nodeCount');
const connectionCount = document.getElementById('connectionCount');
// Initialize charts
const blockTimeCtx = document.getElementById('blockTimeChart').getContext('2d');
const blockTimeChart = new Chart(blockTimeCtx, {
type: 'line',
data: {
labels: [],
datasets: [{
label: 'Block Time (seconds)',
data: [],
borderColor: '#3b82f6',
backgroundColor: 'rgba(59, 130, 246, 0.1)',
borderWidth: 2,
tension: 0.4,
fill: true
}]
},
options: {
responsive: true,
plugins: {
legend: {
display: false
}
},
scales: {
y: {
beginAtZero: true,
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'rgba(255, 255, 255, 0.7)'
}
},
x: {
grid: {
color: 'rgba(255, 255, 255, 0.1)'
},
ticks: {
color: 'rgba(255, 255, 255, 0.7)'
}
}
}
}
});
const balanceCtx = document.getElementById('balanceChart').getContext('2d');
const balanceChart = new Chart(balanceCtx, {
type: 'doughnut',
data: {
labels: ['Alice', 'Bob', 'Charlie', 'Diana'],
datasets: [{
data: [100, 100, 100, 100],
backgroundColor: [
'#3b82f6',
'#10b981',
'#8b5cf6',
'#f59e0b'
],
borderWidth: 0
}]
},
options: {
responsive: true,
plugins: {
legend: {
position: 'right',
labels: {
color: 'rgba(255, 255, 255, 0.7)'
}
}
},
cutout: '70%'
}
});
// Initialize network visualization
const networkCanvas = document.getElementById('networkCanvas');
networkCanvas.width = networkCanvas.offsetWidth;
networkCanvas.height = networkCanvas.offsetHeight;
const networkCtx = networkCanvas.getContext('2d');
function drawNetwork() {
networkCtx.clearRect(0, 0, networkCanvas.width, networkCanvas.height);
// Draw connections
networkCtx.strokeStyle = 'rgba(16, 185, 129, 0.3)';
networkCtx.lineWidth = 1;
// Draw nodes
const nodePositions = [];
for (let i = 0; i < blockchain.nodes; i++) {
const x = 50 + Math.random() * (networkCanvas.width - 100);
const y = 50 + Math.random() * (networkCanvas.height - 100);
nodePositions.push({x, y});
// Draw connections
for (let j = 0; j < i; j++) {
if (Math.random() > 0.5) {
networkCtx.beginPath();
networkCtx.moveTo(x, y);
networkCtx.lineTo(nodePositions[j].x, nodePositions[j].y);
networkCtx.stroke();
}
}
// Draw node
const gradient = networkCtx.createRadialGradient(x, y, 5, x, y, 15);
gradient.addColorStop(0, '#3b82f6');
gradient.addColorStop(1, '#1e40af');
networkCtx.beginPath();
networkCtx.arc(x, y, 15, 0, Math.PI * 2);
networkCtx.fillStyle = gradient;
networkCtx.fill();
networkCtx.strokeStyle = '#1e3a8a';
networkCtx.stroke();
// Draw node label
networkCtx.fillStyle = 'white';
networkCtx.font = 'bold 10px Arial';
networkCtx.textAlign = 'center';
networkCtx.fillText(`Node ${i+1}`, x, y + 30);
}
// Update connection count
connectionCount.textContent = Math.floor(blockchain.nodes * (blockchain.nodes - 1) / 2 * 0.6);
}
drawNetwork();
// Event listeners
createTransactionBtn.addEventListener('click', () => {
const from = fromAddress.value;
const to = toAddress.value;
const amount = parseFloat(transactionAmount.value);
if (isNaN(amount) || amount <= 0) {
alert('Please enter a valid amount');
return;
}
if (from === to) {
alert('Sender and receiver cannot be the same');
return;
}
if (blockchain.createTransaction(from, to, amount)) {
updatePendingTransactions();
transactionAmount.value = '';
// Add transaction to UI
const transactionDiv = document.createElement('div');
transactionDiv.className = 'transaction-card bg-gray-700 rounded-lg p-3 border-l-4 border-blue-500';
transactionDiv.innerHTML = `
<div class="flex justify-between items-center">
<div>
<div class="font-semibold">${from} → ${to}</div>
<div class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</div>
</div>
<div class="text-lg font-bold">${amount.toFixed(2)}</div>
</div>
`;
transactionsContainer.prepend(transactionDiv);
// Animation
setTimeout(() => {
transactionDiv.classList.add('opacity-100');
}, 10);
}
});
mineBlockBtn.addEventListener('click', async () => {
if (blockchain.pendingTransactions.length === 0) {
alert('No pending transactions to mine');
return;
}
mineBlockBtn.disabled = true;
mineBlockBtn.innerHTML = `<i class="fas fa-cog fa-spin mr-2"></i> Mining...`;
// Random miner
const miners = ['Alice', 'Bob', 'Charlie', 'Diana'];
const miner = miners[Math.floor(Math.random() * miners.length)];
// Simulate mining with progress
const miningInterval = setInterval(() => {
const progress = parseInt(document.getElementById('miningProgress').style.width) || 0;
if (progress < 90) {
document.getElementById('miningProgress').style.width = `${progress + 1}%`;
}
}, 100);
// Mine block (simulated delay for UI)
await new Promise(resolve => setTimeout(resolve, 2000));
blockchain.minePendingTransactions(miner);
clearInterval(miningInterval);
document.getElementById('miningProgress').style.width = '0%';
updateBlockchain();
updatePendingTransactions();
updateStats();
updateWalletBalances();
drawNetwork();
mineBlockBtn.disabled = false;
mineBlockBtn.innerHTML = `<i class="fas fa-hammer mr-2"></i> Mine Block`;
});
// Update functions
function updateBlockchain() {
blockchainContainer.innerHTML = '';
blockchain.chain.forEach((block, index) => {
const isGenesis = index === 0;
const blockDiv = document.createElement('div');
blockDiv.className = `block-3d bg-gray-700 rounded-lg p-5 shadow-lg border-l-4 ${isGenesis ? 'border-blue-500' : 'border-green-500'} mb-4 relative overflow-hidden`;
if (isGenesis) {
blockDiv.innerHTML += `
<div class="absolute top-0 right-0 bg-blue-600 text-white text-xs px-2 py-1 rounded-bl-lg">
Genesis Block
</div>
`;
} else if (index === blockchain.chain.length - 1) {
blockDiv.classList.add('block-highlight');
}
const transactionsCount = block.transactions.length;
const transactionsSummary = transactionsCount > 0 ?
`${transactionsCount} transaction${transactionsCount !== 1 ? 's' : ''}` :
'No transactions';
blockDiv.innerHTML += `
<div class="flex justify-between items-start mb-3">
<div>
<h3 class="font-bold text-lg">Block #${block.index}</h3>
<p class="text-xs text-gray-400">${block.hash.substring(0, 20)}...</p>
</div>
<div class="text-right">
<div class="text-xs text-gray-400">Nonce: ${block.nonce}</div>
<div class="text-xs text-gray-400">${new Date(block.timestamp).toLocaleTimeString()}</div>
</div>
</div>
<div class="bg-gray-800 rounded p-3 mb-3">
<div class="text-xs text-gray-400 mb-1">${transactionsSummary}</div>
${transactionsCount > 0 ? `
<div class="text-xs font-mono break-all">
${block.transactions[0].from} → ${block.transactions[0].to}: ${block.transactions[0].amount}
${transactionsCount > 1 ? `<br>+ ${transactionsCount - 1} more...` : ''}
</div>
` : ''}
</div>
<div class="text-xs text-gray-400">Previous Hash: ${block.previousHash.substring(0, 10)}...</div>
`;
blockchainContainer.appendChild(blockDiv);
});
blockCount.textContent = blockchain.chain.length;
}
function updatePendingTransactions() {
pendingCount.textContent = blockchain.pendingTransactions.length;
if (blockchain.pendingTransactions.length === 0) {
transactionsContainer.innerHTML = `
<div class="text-center py-4 text-gray-500">
No pending transactions
</div>
`;
}
}
function updateStats() {
statsBlocks.textContent = blockchain.chain.length;
statsTransactions.textContent = blockchain.chain.reduce((acc, block) => acc + block.transactions.length, 0);
if (blockchain.blockTimes.length > 0) {
const avgBlockTime = blockchain.blockTimes.reduce((a, b) => a + b, 0) / blockchain.blockTimes.length;
statsBlockTime.textContent = `${avgBlockTime.toFixed(2)}s`;
// Update block time chart
blockTimeChart.data.labels = Array.from({length: blockchain.blockTimes.length}, (_, i) => `Block ${i+1}`);
blockTimeChart.data.datasets[0].data = blockchain.blockTimes;
blockTimeChart.update();
}
// Simulated hashrate
const hashrate = Math.floor(Math.random() * 1000) + 500;
statsHashrate.textContent = `${hashrate} H/s`;
}
function updateWalletBalances() {
walletBalances.innerHTML = '';
for (const [name, wallet] of Object.entries(blockchain.wallets)) {
const colors = {
'Alice': 'blue-500',
'Bob': 'green-500',
'Charlie': 'purple-500',
'Diana': 'yellow-500'
};
const row = document.createElement('tr');
row.className = 'border-b border-gray-700';
row.innerHTML = `
<td class="py-3 flex items-center">
<div class="w-2 h-2 bg-${colors[name]} rounded-full mr-2"></div>
${name}
</td>
<td class="py-3">${wallet.balance.toFixed(2)}</td>
<td class="py-3">${wallet.transactions}</td>
`;
walletBalances.appendChild(row);
}
// Update balance chart
balanceChart.data.datasets[0].data = [
blockchain.wallets.Alice.balance,
blockchain.wallets.Bob.balance,
blockchain.wallets.Charlie.balance,
blockchain.wallets.Diana.balance
];
balanceChart.update();
}
// Initial updates
updateBlockchain();
updatePendingTransactions();
updateStats();
updateWalletBalances();
// Add some demo transactions
setTimeout(() => {
blockchain.createTransaction('Alice', 'Bob', 5);
blockchain.createTransaction('Bob', 'Charlie', 3);
blockchain.createTransaction('Diana', 'Alice', 7);
updatePendingTransactions();
// Add to UI
transactionsContainer.innerHTML = `
<div class="transaction-card bg-gray-700 rounded-lg p-3 border-l-4 border-blue-500">
<div class="flex justify-between items-center">
<div>
<div class="font-semibold">Alice → Bob</div>
<div class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</div>
</div>
<div class="text-lg font-bold">5.00</div>
</div>
</div>
<div class="transaction-card bg-gray-700 rounded-lg p-3 border-l-4 border-green-500">
<div class="flex justify-between items-center">
<div>
<div class="font-semibold">Bob → Charlie</div>
<div class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</div>
</div>
<div class="text-lg font-bold">3.00</div>
</div>
</div>
<div class="transaction-card bg-gray-700 rounded-lg p-3 border-l-4 border-purple-500">
<div class="flex justify-between items-center">
<div>
<div class="font-semibold">Diana → Alice</div>
<div class="text-xs text-gray-400">${new Date().toLocaleTimeString()}</div>
</div>
<div class="text-lg font-bold">7.00</div>
</div>
</div>
`;
pendingCount.textContent = '3';
}, 1000);
// Simulate network changes
setInterval(() => {
// Randomly add/remove nodes
if (Math.random() > 0.7) {
blockchain.nodes += Math.random() > 0.5 ? 1 : -1;
blockchain.nodes = Math.max(2, Math.min(8, blockchain.nodes));
nodeCount.textContent = blockchain.nodes;
drawNetwork();
}
// Update hashrate
const hashrate = Math.floor(Math.random() * 1000) + 500;
statsHashrate.textContent = `${hashrate} H/s`;
}, 5000);
</script>
<!-- Include CryptoJS for hashing -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js"></script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=dazman72/interactive-blockchain-explorer" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>