Skip to content

Instantly share code, notes, and snippets.

@TomckySan
Created January 9, 2018 13:37
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save TomckySan/eb1b62bd1b9e9518c7b32829b501c5bf to your computer and use it in GitHub Desktop.
Save TomckySan/eb1b62bd1b9e9518c7b32829b501c5bf to your computer and use it in GitHub Desktop.
<?php
class Block
{
private $index;
private $previousHash;
private $timestamp;
private $data;
private $hash;
function __construct($index, $previousHash, $timestamp, $data, $hash)
{
$this->index = $index;
$this->previousHash = $previousHash;
$this->timestamp = $timestamp;
$this->data = $data;
$this->hash = $hash;
}
function getIndex()
{
return $this->index;
}
function getPreviousHash()
{
return $this->previousHash;
}
function getTimestamp()
{
return $this->timestamp;
}
function getData()
{
return $this->data;
}
function getHash()
{
return $this->hash;
}
}
class Blockchain
{
private $blockchain = [];
function __construct()
{
$this->blockchain[] = $this->getGenesisBlock();
}
function getBlockchain()
{
return $this->blockchain;
}
/**
* ジェネシスブロック生成・取得
*/
function getGenesisBlock(): Block
{
return new Block(
0,
'0',
1465154705,
'my genesis block!!',
'816534932c2b7154836da6afc367695e6337db8a921823784c14378abed4f7d7'
);
}
/**
* ハッシュ生成
*/
function calculateHash($index, $previousHash, $timestamp, $data)
{
return hash('sha256', $index + $previousHash + $timestamp + $data);
}
/**
* ブロックからハッシュを生成
*/
function calculateHashForBlock(Block $block): string
{
return $this->calculateHash(
$block->getIndex(),
$block->getPreviousHash(),
$block->getTimestamp(),
$block->getData()
);
}
/**
* ブロックチェーンの最後のブロックを取得
*/
function getLatestBlock(): Block
{
return $this->blockchain[count($this->blockchain) - 1];
}
/**
* 次のブロックを生成
*/
function generateNextBlock($blockData): Block
{
$previousBlock = $this->getLatestBlock();
$nextIndex = $previousBlock->getIndex() + 1;
$nextTimestamp = (new DateTime())->getTimestamp() / 1000;
$nextHash = $this->calculateHash($nextIndex, $previousBlock->getHash(), $nextTimestamp, $blockData);
return new Block($nextIndex, $previousBlock->getHash(), $nextTimestamp, $blockData, $nextHash);
}
/**
* 新しく作成するブロックの安全性チェック
*/
function isValidNewBlock($newBlock, $previousBlock)
{
if ($previousBlock->getIndex() + 1 !== $newBlock->getIndex()) {
echo "Warning: Invalid index.\n";
return false;
} else if ($previousBlock->getHash() !== $newBlock->getPreviousHash()) {
echo "Warning: Invalid previous hash.\n";
return false;
} else if ($this->calculateHashForBlock($newBlock) !== $newBlock->getHash()) {
echo 'Warning: Invalid hash: ' . $this->calculateHashForBlock($newBlock) . ' ' . $newBlock->getHash() . "\n";
return false;
}
return true;
}
/**
* 最長チェーンを選択
*/
function replaceChain(Blockchain $newBlockchain)
{
$newBlocks = $newBlockchain->getBlockchain();
if ($this->isValidChain($newBlockchain) && count($newBlocks) > count($this->blockchain)) {
echo "Received blockchain is valid. Replacing current blockchain with received blockchain\n";
$this->blockchain = $newBlocks;
} else {
echo "Received blockchain invalid\n";
}
}
/**
* ブロックチェーンの妥当性チェック
*/
function isValidChain(Blockchain $blockchain): bool
{
$blockchainToValidate = $blockchain->getBlockchain();
// ジェネシスブロックが一致するかをチェック
if ($blockchainToValidate[0]->getHash() !== $this->getGenesisBlock()->getHash()) {
return false;
}
// 全てのブロックの妥当性をチェック
foreach ($blockchainToValidate as $index => $blockToVaridate) {
if (0 === $index || $this->isValidNewBlock($blockToVaridate, $blockchainToValidate[$index - 1])) {
continue;
}
return false;
}
return true;
}
/**
* ブロックチェーンにブロックを追加
*/
function addBlock(Block $newBlock)
{
if ($this->isValidNewBlock($newBlock, $this->getLatestBlock())) {
$this->blockchain[] = $newBlock;
}
}
function broadcast(Blockchain $newBlockchain, string $name)
{
echo "$name broadcast.\n";
$size = count($newBlockchain->getBlockchain());
$this->replaceChain($newBlockchain);
$latestBlockHash = $this->getLatestBlock()->getHash();
echo "$name new blockchain. SIZE: $size, LATEST_BLOCK: $latestBlockHash\n";
}
}
<?php
require_once 'blockchain.php';
require_once 'vendor/autoload.php';
use Amp\Loop;
// ブロックチェーン初期化
$masterBlockchain = new Blockchain();
function createMiner(Blockchain &$masterBlockchain, string $name)
{
$msInterval = rand(800, 5000);
Loop::repeat($msInterval, function () use (&$masterBlockchain, $name) {
$myBlockchain = clone $masterBlockchain;
$blockCount = rand(1, 5);
for ($i = 0; $i < $blockCount; $i++) {
// ブロックを追加
$newBlock = $myBlockchain->generateNextBlock('dummy_block_data');
$newBlockHash = $newBlock->getHash();
$myBlockchain->addBlock($newBlock);
echo "$name add block. BLOCK: $newBlockHash\n";
}
// 新しいブロックを生成したら、ブロードキャストする
$masterBlockchain->broadcast($myBlockchain, $name);
});
}
echo "=== START SIMULATE ===\n";
Loop::run(function () use (&$masterBlockchain) {
createMiner($masterBlockchain, 'Miner1');
createMiner($masterBlockchain, 'Miner2');
createMiner($masterBlockchain, 'Miner3');
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment