Skip to content

jimmybytes/phpblock

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 

Repository files navigation

Build a Simple Blockchain in PHP (PoW Demo)

Purpose: This is an educational demo to understand how a basic blockchain works (blocks, hashes, transactions, and Proof of Work).
Disclaimer: For learning and communication only. Do not use this code for illegal activity or to violate any local laws/regulations.


Prerequisites

  • PHP 5.6+

1. Block

What does a block look like?

Each block contains:

  • index — block height (starting from 1)
  • timestamp — Unix timestamp
  • transactions — list of transactions
  • proof — Proof of Work result
  • previous_hash — hash of the previous block

Example structure:

block = {
  "index": 2,
  "timestamp": 1506057125,
  "transactions": [
    {
      "sender": "8527147fe1f5426f9dd545de4b27ee00",
      "recipient": "a77f5cdfa2934df3954a5c7c7da5df1f",
      "amount": 5
    }
  ],
  "proof": 324984774000,
  "previous_hash": "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824"
}

Why is the hash link important?

A blockchain becomes tamper-evident because every new block stores the hash of the previous block.
If an attacker modifies an earlier block, its hash changes, and all subsequent blocks will contain an invalid previous_hash.


Create a Block class

A blockchain is made of many blocks. Value data (e.g., Bitcoin transactions) is stored inside blocks.
Blocks also contain technical metadata such as timestamps and the previous block hash.

<?php
/**
 * Created by PhpStorm.
 * User: Jimmy
 * Date: 2020/2/2
 * Time: 18:42
 */

class Block
{
    /**
     * @var int Block index
     */
    private $index;

    /**
     * @var int Unix timestamp
     */
    private $timestamp;

    /**
     * @var array Transaction list
     */
    private $transactions;

    /**
     * @var string Previous block hash
     */
    private $previousHash;

    /**
     * @var int Proof generated by PoW
     */
    private $proof;

    /**
     * @var string Current block hash
     */
    private $hash;

    /**
     * Expose hash via getter to prevent external modifications
     * @return string
     */
    public function getHash()
    {
        return $this->hash;
    }

    public function __construct($index, $timestamp, $transactions, $previousHash, $proof)
    {
        $this->index        = $index;
        $this->timestamp    = $timestamp;
        $this->transactions = $transactions;
        $this->previousHash = $previousHash;
        $this->proof        = $proof;
        $this->hash         = $this->blockHash();
    }

    /**
     * Calculate block hash (signature)
     * @return string
     */
    private function blockHash()
    {
        // Ensure consistent hashing by using a deterministic field order
        $blockArray = [
            'index'         => $this->index,
            'timestamp'     => $this->timestamp,
            'transactions'  => $this->transactions,
            'proof'         => $this->proof,
            'previous_hash' => $this->previousHash,
        ];

        $blockString = json_encode($blockArray);
        return hash('sha256', $blockString);
    }
}

Field meanings

  • index: current block index
  • timestamp: creation time
  • transactions: transaction list (one or many)
  • previousHash: previous block signature hash
  • hash: current block signature hash
  • proof: Proof of Work (mining result)

About proof (Proof of Work)

Proof of Work (PoW) explains how a new block is created (“mined”).

  • The goal is to find a number that satisfies a certain rule.
  • It should be computationally hard to find, but easy to verify.

Bitcoin’s PoW is based on a Hashcash-like puzzle (but with much higher difficulty), where miners compete to find a valid proof to earn rewards.


2. Create a Blockchain

We will create a Blockchain class. Its constructor:

  • initializes an empty chain
  • creates the genesis block
  • initializes the pending transaction list

Step 1: Initialize the chain & create the Genesis block

/**
 * @var array Chain (block list)
 */
private $chain;

/**
 * @var array Pending transactions (to be packed into the next block)
 */
private $currentTransactions;

public function __construct()
{
    $this->chain = [$this->createGenesisBlock()];
    $this->currentTransactions = [];
}

/**
 * Create genesis block
 * @return array
 */
private function createGenesisBlock()
{
    $block = [
        'index'         => 1,
        'timestamp'     => time(),
        'transactions'  => [],
        'proof'         => 100,
        // Similar to Bitcoin genesis style: previous hash is all zeros
        'previous_hash' => str_attach('', 64, '0'),
    ];

    $block['hash'] = (new Block(
        $block['index'],
        $block['timestamp'],
        $block['transactions'],
        $block['previous_hash'],
        $block['proof']
    ))->getHash();

    return $block;
}

Note: str_attach('', 64, '0') is a placeholder to represent 64 zeros.
If you don’t have str_attach, simply hardcode the 64-zero string.


Step 2: Add a new transaction

A transaction is added to the pending list, waiting to be included in the next block.
After a new block is created, the pending list will be cleared.

/**
 * Create a new transaction
 * @param string $senderPrivateKey
 * @param string $senderAddress
 * @param string $recipientAddress
 * @param int|float $amount
 * @return bool
 */
public function createTransaction($senderPrivateKey, $senderAddress, $recipientAddress, $amount)
{
    $row = [
        'from'      => $senderAddress,
        'to'        => $recipientAddress,
        'amount'    => $amount,
        'timestamp' => time(),
    ];

    // TODO: Sign the transaction with the sender's private key (like signing a check)
    // TODO: Nodes can derive the public key and verify signature integrity.
    $this->currentTransactions[] = $row;

    return true;
}

Step 3: Create a new block

In this demo, only a miner that finds a valid proof can add a new block (see Step 4).

/**
 * Add a new block
 * @param int $proof
 * @return bool
 */
public function addBlock(int $proof)
{
    // Get previous block
    $preBlockInfo = $this->chain[count($this->chain) - 1];

    // Validate proof
    if ($this->checkProof((string)$proof, (string)$preBlockInfo['proof']) === false) {
        return false;
    }

    // TODO: Reward miner (as a special transaction)

    $block = [
        'index'         => count($this->chain) + 1,
        'timestamp'     => time(),
        'transactions'  => $this->currentTransactions,
        'proof'         => $proof,
        'previous_hash' => $preBlockInfo['hash'],
        'hash'          => '',
    ];

    $block['hash'] = (new Block(
        $block['index'],
        $block['timestamp'],
        $block['transactions'],
        $block['previous_hash'],
        $block['proof']
    ))->getHash();

    // Append block to chain
    $this->chain[] = $block;

    // Reset pending transactions
    $this->currentTransactions = [];

    return true;
}

/**
 * Validate PoW
 * Rule: sha256(proof + previousProof) starts with '0000'
 * @param string $proof
 * @param string $preProof
 * @return bool
 */
private function checkProof(string $proof, string $preProof)
{
    $string = $proof . $preProof;
    $hash   = hash('sha256', $string);

    return substr($hash, 0, 4) === '0000';
}

Step 4: Mining (Proof of Work)

Mining is the “magic” part. In this simplified demo, it does three things:

  1. Compute PoW
  2. (Optional) reward the miner
  3. Create a new block and append it to the chain

PoW rule in this demo

Find a number P such that:

sha256(str(P) + str(previousProof)) starts with four zeros (0000)

Implementation:

/**
 * Mine a new block
 * @return void
 */
public function mine()
{
    $proof = 0;

    // Latest block
    $blockInfo = $this->chain[count($this->chain) - 1];
    $preProof  = $blockInfo['proof'];

    while (true) {
        $string = $proof . $preProof;
        $hash   = hash('sha256', $string);

        if (substr($hash, 0, 4) === '0000') {
            // Add new block
            $this->addBlock($proof);
            break;
        }

        $proof++;
    }
}

Step 5: Run a Test

$blockChainObj = new Blockchain();

// Add a transaction
$blockChainObj->createTransaction(
    '',
    '8527147fe1f5426f9dd545de4b27ee00',
    'a77f5cdfa2934df3954a5c7c7da5df1f',
    1
);

// Start mining (will create a new block once found)
$blockChainObj->mine();

// Print current chain
$blockList = $blockChainObj->getChainList();
var_dump($blockList);

Example output (simplified):

array(2) {
  [0]=> genesis block ...
  [1]=> block #2 with 1 transaction ...
}

Step 6: Full Source Code


Author

Jimmy (@jimmybytes)
Full-Cycle Programmer · AI-Assisted Engineering

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages