Skip to content

Instantly share code, notes, and snippets.

@tonysm
Last active December 18, 2022 02:57
Show Gist options
  • Save tonysm/66a2f1159c39bb03c34eb2d72b9e68c1 to your computer and use it in GitHub Desktop.
Save tonysm/66a2f1159c39bb03c34eb2d72b9e68c1 to your computer and use it in GitHub Desktop.
Simple Blockchain in PHP
<?php
// This is the blockchain example from The Imposter's Handbook Season 2 book.
class Block
{
public static function mine(array $transactions, float $timestamp, int $difficulty, ?Block $previous = null)
{
if ($difficulty < 2) throw new \Exception("This isn't much of a challenge");
if ($difficulty > 18) throw new \Exception("Do polar icecaps matter to you?");
$lookingFor = str_repeat("0", $difficulty);
$mined = false;
$start = microtime(true);
$nonce = 0;
do {
$possibleHash = static::hashData($transactions, $timestamp, $nonce, $previous);
$mined = str_starts_with($possibleHash, $lookingFor);
if (! $mined) {
$nonce++;
} else {
$end = microtime(true);
$elapsed = $end - $start;
echo sprintf("Found it: %s\nThe nonce is: %s\nIt took exactly %s seconds\n\n", $possibleHash, $nonce, $elapsed);
return new Block(
$possibleHash,
$transactions,
$timestamp,
$nonce,
$previous,
);
}
} while (! $mined);
}
private static function hashData(array $transactions, float $timestamp, int $nonce, ?Block $previous = null)
{
return hash('sha256', json_encode([
'transactions' => $transactions,
'timestamp' => $timestamp,
'nonce' => $nonce,
'previous' => $previous,
]));
}
private function __construct(
private string $key,
private array $transactions,
private float $timestamp,
private int $nonce,
private ?Block $previous = null,
) {
}
public function isValid()
{
return $this->key === static::hashData($this->transactions, $this->timestamp, $this->nonce, $this->previous);
}
public function getKey(): string
{
return $this->key;
}
public function getPrevious(): ?Block
{
return $this->previous;
}
}
class BlockChain
{
public function __construct(
private array $blocks = [],
private ?Block $head = null,
) {}
public function createBlock($transactions, $timestamp)
{
$block = Block::mine($transactions, $timestamp, $difficulty = 5, $this->head);
$this->blocks[$block->getKey()] = $this->head = $block;
}
public function traverse($callback)
{
$head = $this->head;
while ($head) {
$callback($head);
$head = $head->getPrevious();
}
}
}
// Executing it...
$blockchain = new BlockChain();
$transaction1 = [
['from' => 'me', 'to' => 'you', 'amount' => 1000],
['from' => 'you', 'to' => 'me', 'amount' => 500],
];
$transaction2 = [
['from' => 'me', 'to' => 'you', 'amount' => 600],
['from' => 'you', 'to' => 'me', 'amount' => 1200],
];
$transaction3 = [
['from' => 'me', 'to' => 'you', 'amount' => 300],
['from' => 'you', 'to' => 'me', 'amount' => 1300],
];
$blockchain->createBlock($transaction1, microtime(true));
$blockchain->createBlock($transaction2, microtime(true));
$blockchain->createBlock($transaction3, microtime(true));
echo "Traversing the blocks...\n\n";
$blockchain->traverse(function ($block) {
echo "Block: {$block->getKey()}\n\n";
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment