Digital Timestamping Service BLOCK CHAIN APP IN HTML CSS JAVASCRIPT , PHP AND MY SQL

Introduction

Project Name: BlockStamp: Decentralized Document Timestamping

Concept: In a digital world, proving that a document existed at a specific point in time (without revealing the document's secrets) is crucial for copyright, patents, and legal disputes. Traditional methods involve notaries or trusted third parties.

The Blockchain Solution: This project uses the blockchain as a immutable timestamping authority. Instead of uploading the actual document (which reveals contents), we upload a unique digital fingerprint of the document called a SHA-256 Hash. This hash is then anchored to the blockchain. If someone later claims to have the original document, you can generate its hash and compare it to the hash stored on the blockchain. If they match, the document is proven to be unchanged and existed at the time the block was mined.

Features

  • Content Privacy: Documents never leave the user's browser. Only the cryptographic hash is transmitted.
  • Immutability: Timestamps are stored on a simulated blockchain, making them tamper-proof and auditable.
  • Verification: Anyone can verify the authenticity and timestamp of a document by re-calculating its hash and checking the chain.
  • User-Friendly Interface: Simple drag-and-drop or file selection for hashing and verification.
  • Blockchain Simulation: A custom PHP class simulates the structure of a blockchain (Block, Hash, Previous Hash, Timestamp) and stores it in a MySQL database.
  • Live Demo: Input a document title and content to generate a unique fingerprint and "mine" it onto the chain.

Project File Structure

blockchain-timestamping/
│
├── index.html          # Main UI (HTML + Tailwind CSS + JS)
├── style.css           # (Optional) Custom styles if not using Tailwind
├── api.php             # PHP endpoint to handle mining and verification requests
├── Blockchain.php      # PHP Class for Blockchain logic (PoW, Hashing, DB Connection)
├── config.php          # Database configuration
├── db.sql              # SQL file to create the database and table
└── README.md           # Project Documentation

Database Setup (MySQL)

1. Create a database named blockchain_db.

2. Run the db.sql script:

CREATE DATABASE IF NOT EXISTS blockchain_db;
USE blockchain_db;
CREATE TABLE IF NOT EXISTS blocks (
id INT AUTO_INCREMENT PRIMARY KEY,
block_index INT NOT NULL,
timestamp BIGINT NOT NULL,
data_hash VARCHAR(255) NOT NULL,
previous_hash VARCHAR(255) NOT NULL,
hash VARCHAR(255) NOT NULL UNIQUE,
nonce INT NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
-- Insert the Genesis Block (First block)
INSERT INTO blocks (block_index, timestamp, data_hash, previous_hash, hash, nonce)
VALUES (0, UNIX_TIMESTAMP(), 'Genesis Data Hash', '0', '0000GenesisBlockHashPlaceholderForDemo', 0);

Code Implementation

1. config.php (Database Configuration)

<?php
define('DB_HOST', 'localhost');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_NAME', 'blockchain_db');
function getDBConnection() {
$conn = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME);
if ($conn->connect_error) {
die(json_encode(['success' => false, 'message' => 'Database connection failed: ' . $conn->connect_error]));
}
return $conn;
}
?>

2. Blockchain.php (Core Blockchain Logic)

<?php
require_once 'config.php';
class Blockchain {
private $conn;
private $difficulty = 2; // Number of leading zeros required in hash (Proof of Work)
public function __construct() {
$this->conn = getDBConnection();
}
// Calculate SHA-256 hash
public static function calculateHash($index, $timestamp, $dataHash, $previousHash, $nonce) {
return hash('sha256', $index . $timestamp . $dataHash . $previousHash . $nonce);
}
// Get the last block from the chain
public function getLastBlock() {
$sql = "SELECT * FROM blocks ORDER BY block_index DESC LIMIT 1";
$result = $this->conn->query($sql);
if ($result && $result->num_rows > 0) {
return $result->fetch_assoc();
}
return null;
}
// Proof of Work: Find a nonce that produces a hash with 'difficulty' leading zeros
public function proofOfWork($index, $timestamp, $dataHash, $previousHash) {
$nonce = 0;
$prefix = str_repeat('0', $this->difficulty);
while (true) {
$hash = $this->calculateHash($index, $timestamp, $dataHash, $previousHash, $nonce);
if (substr($hash, 0, $this->difficulty) === $prefix) {
return ['nonce' => $nonce, 'hash' => $hash];
}
$nonce++;
}
}
// Add a new block to the chain
public function addBlock($documentTitle, $documentContent) {
// 1. Create a unique data hash from the document (privacy preserved)
$dataHash = hash('sha256', $documentTitle . $documentContent . time());
// 2. Get the last block
$lastBlock = $this->getLastBlock();
if (!$lastBlock) {
return ['success' => false, 'message' => 'Blockchain corrupted. No genesis block found.'];
}
$newIndex = $lastBlock['block_index'] + 1;
$timestamp = time();
$previousHash = $lastBlock['hash'];
// 3. Perform Proof of Work
$powResult = $this->proofOfWork($newIndex, $timestamp, $dataHash, $previousHash);
// 4. Insert new block into database
$sql = "INSERT INTO blocks (block_index, timestamp, data_hash, previous_hash, hash, nonce) 
VALUES (?, ?, ?, ?, ?, ?)";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("iisssi", $newIndex, $timestamp, $dataHash, $previousHash, $powResult['hash'], $powResult['nonce']);
if ($stmt->execute()) {
return [
'success' => true,
'message' => 'Block mined successfully!',
'block' => [
'index' => $newIndex,
'timestamp' => $timestamp,
'data_hash' => $dataHash,
'previous_hash' => $previousHash,
'hash' => $powResult['hash'],
'nonce' => $powResult['nonce']
]
];
} else {
return ['success' => false, 'message' => 'Failed to add block: ' . $stmt->error];
}
}
// Verify if a document existed (by checking its hash)
public function verifyDocument($documentTitle, $documentContent) {
$searchHash = hash('sha256', $documentTitle . $documentContent);
$sql = "SELECT * FROM blocks WHERE data_hash = ? ORDER BY block_index DESC LIMIT 1";
$stmt = $this->conn->prepare($sql);
$stmt->bind_param("s", $searchHash);
$stmt->execute();
$result = $stmt->get_result();
if ($result && $result->num_rows > 0) {
$block = $result->fetch_assoc();
return [
'success' => true,
'message' => 'Document found in blockchain.',
'timestamp' => date('Y-m-d H:i:s', $block['timestamp']),
'block_index' => $block['block_index'],
'data_hash' => $block['data_hash']
];
} else {
return ['success' => false, 'message' => 'Document not found in blockchain.'];
}
}
public function __destruct() {
$this->conn->close();
}
}
?>

3. api.php (API Endpoint)

<?php
header('Content-Type: application/json');
require_once 'Blockchain.php';
$blockchain = new Blockchain();
$action = $_POST['action'] ?? '';
if ($action === 'mine') {
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
if (empty($title) || empty($content)) {
echo json_encode(['success' => false, 'message' => 'Title and content are required.']);
exit;
}
$result = $blockchain->addBlock($title, $content);
echo json_encode($result);
} elseif ($action === 'verify') {
$title = $_POST['title'] ?? '';
$content = $_POST['content'] ?? '';
if (empty($title) || empty($content)) {
echo json_encode(['success' => false, 'message' => 'Title and content are required.']);
exit;
}
$result = $blockchain->verifyDocument($title, $content);
echo json_encode($result);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid action.']);
}
?>

4. index.html (Frontend UI)

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>BlockStamp | Blockchain Timestamping</title>
<script src="https://cdn.tailwindcss.com"></script>
<style>
body { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
.glass-morphism {
background: rgba(255, 255, 255, 0.25);
box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
backdrop-filter: blur(4px);
-webkit-backdrop-filter: blur(4px);
border-radius: 10px;
border: 1px solid rgba(255, 255, 255, 0.18);
}
</style>
</head>
<body class="min-h-screen flex items-center justify-center p-4">
<div class="w-full max-w-4xl">
<!-- Header -->
<div class="text-center mb-8">
<h1 class="text-5xl font-bold text-white mb-2">🔗 BlockStamp</h1>
<p class="text-xl text-purple-200">Decentralized Proof of Existence. Privacy First.</p>
</div>
<!-- Main Card -->
<div class="glass-morphism p-8 rounded-2xl">
<!-- Feature Highlights -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-8 text-white">
<div class="text-center p-4 bg-purple-800 bg-opacity-50 rounded-lg">
<span class="text-3xl block mb-2">🔒</span>
<h3 class="font-bold">Privacy Preserved</h3>
<p class="text-sm">Only hash is stored, never the content.</p>
</div>
<div class="text-center p-4 bg-purple-800 bg-opacity-50 rounded-lg">
<span class="text-3xl block mb-2">⛓️</span>
<h3 class="font-bold">Immutable Chain</h3>
<p class="text-sm">Linked blocks with Proof of Work.</p>
</div>
<div class="text-center p-4 bg-purple-800 bg-opacity-50 rounded-lg">
<span class="text-3xl block mb-2">✅</span>
<h3 class="font-bold">Instant Verify</h3>
<p class="text-sm">Check authenticity in seconds.</p>
</div>
</div>
<!-- Input Form -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Mine Block -->
<div class="bg-white rounded-xl p-6 shadow-lg">
<h2 class="text-2xl font-bold text-gray-800 mb-4">⛏️ Timestamp Document</h2>
<p class="text-sm text-gray-600 mb-4">Create a unique fingerprint and anchor it to the chain.</p>
<input type="text" id="title" placeholder="Document Title" class="w-full p-3 border rounded-lg mb-3 focus:outline-none focus:ring-2 focus:ring-purple-600">
<textarea id="content" rows="4" placeholder="Document Content (this never leaves your browser without hashing)" class="w-full p-3 border rounded-lg mb-3 focus:outline-none focus:ring-2 focus:ring-purple-600"></textarea>
<button onclick="mineBlock()" class="w-full bg-gradient-to-r from-green-400 to-blue-500 text-white font-bold py-3 px-4 rounded-lg hover:opacity-90 transition">
🔒 Hash & Mine Block
</button>
</div>
<!-- Verify Document -->
<div class="bg-white rounded-xl p-6 shadow-lg">
<h2 class="text-2xl font-bold text-gray-800 mb-4">🔍 Verify Document</h2>
<p class="text-sm text-gray-600 mb-4">Check if this document existed before.</p>
<input type="text" id="verify_title" placeholder="Document Title" class="w-full p-3 border rounded-lg mb-3 focus:outline-none focus:ring-2 focus:ring-purple-600">
<textarea id="verify_content" rows="4" placeholder="Document Content" class="w-full p-3 border rounded-lg mb-3 focus:outline-none focus:ring-2 focus:ring-purple-600"></textarea>
<button onclick="verifyDocument()" class="w-full bg-gradient-to-r from-purple-400 to-pink-500 text-white font-bold py-3 px-4 rounded-lg hover:opacity-90 transition">
🔎 Check Authenticity
</button>
</div>
</div>
<!-- Result Display -->
<div id="result" class="mt-8 p-4 bg-gray-900 text-green-300 rounded-xl font-mono text-sm border border-gray-700 hidden">
<pre id="resultContent" class="whitespace-pre-wrap"></pre>
</div>
</div>
</div>
<script>
async function mineBlock() {
const title = document.getElementById('title').value;
const content = document.getElementById('content').value;
if (!title || !content) {
alert('Please fill in both fields');
return;
}
const formData = new FormData();
formData.append('action', 'mine');
formData.append('title', title);
formData.append('content', content);
try {
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
const data = await response.json();
displayResult(data);
} catch (error) {
console.error('Error:', error);
alert('An error occurred');
}
}
async function verifyDocument() {
const title = document.getElementById('verify_title').value;
const content = document.getElementById('verify_content').value;
if (!title || !content) {
alert('Please fill in both fields');
return;
}
const formData = new FormData();
formData.append('action', 'verify');
formData.append('title', title);
formData.append('content', content);
try {
const response = await fetch('api.php', {
method: 'POST',
body: formData
});
const data = await response.json();
displayResult(data);
} catch (error) {
console.error('Error:', error);
alert('An error occurred');
}
}
function displayResult(data) {
const resultDiv = document.getElementById('result');
const resultContent = document.getElementById('resultContent');
resultDiv.classList.remove('hidden');
resultContent.textContent = JSON.stringify(data, null, 2);
}
</script>
</body>
</html>

How to Run

  1. Setup Environment: Ensure you have a local server like XAMPP, WAMP, or Laragon running with PHP and MySQL.
  2. Database: Create the database blockchain_db and run the db.sql script to create the table and insert the genesis block.
  3. Place Files: Put all the PHP and HTML files in your web server's root directory (e.g., htdocs for XAMPP) inside a folder named blockchain-timestamping.
  4. Configure: Update config.php with your database credentials.
  5. Access: Open your browser and navigate to http://localhost/blockchain-timestamping/index.html.

You can now timestamp your documents securely and verify them anytime without revealing their contents

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper