Files
apps/blockchain/inc/block.php

567 lines
15 KiB
PHP

<?php
class block
{
public $hash = null;
public $height = null;
public $name = null;
public $desc = null;
function __construct($ahash, $aheight, $aname = null, $adesc = null)
{
$this->hash = $ahash;
$this->height = $aheight;
$this->name = $aname;
$this->desc = $adesc;
}
}
class blockchain
{
private static $url_info = 'https://blockchain.info/fr';
private static $offline_block = OFFLINE_PATH.'/offline_block.zip';
/*
* Liste de blocks spéciaux dans l'histoire du Bitcoin
*
* 'GENESIS' - Premier block de la blochain
* 'THE_ANSWER' - Block 42 (pour le fun)
* 'LUCIFER' - Block 666 (pour le fun)
* 'LEET' - Block 1337 (pour le fun)
* 'PIZZA' - Block 57035 : le block du pizza day, 22 05 2010
* 'HALVING_1' - First halving, block 2100000, 28 11 2012
* 'HALVING_2' - Second halving, block 420000, 09 07 2017
* 'BIP_91_LOCK' - Block 477120 : Verouillage du BIP 91, 23/07/2017
* 'BCC' - Block 478558 : Bitcoin Cash Fork 01/08/2017
* 'SEGWIT_LOCK' - Block 479808 SEGWIT est verrouillé 09 08 2017
* 'SEGWIT' - Block 481823 SEGWIT est activé 24 08 2017
*/
private static $special_blocks = null;
//
// Init special blocks array ...
//
public static function init()
{
self::$special_blocks = array ();
self::$special_blocks[] = new block(
'0000000000000000000f9f2dadfb8f312572183272802cbfcc4ff95b4ee6777d',
545911,
'WHALE201810'
);
self::$special_blocks[] = new block(
'00000000000000000021e800c1e8df51b22c1588e5a624bea17e9faa34b2dc4a',
528249,
'BLOCK21E800'
);
self::$special_blocks[] = new block(
'0000000000000000000fe6d521a187a5523d5cef6f6c178923ff82ffe5a0f372',
506734,
'HURRICANE_1'
);
self::$special_blocks[] = new block(
'000000000000000000cbeff0b533f8e1189cf09dfbebf57a8ebe349362811b80',
481823,
'SEGWIT'
);
self::$special_blocks[] = new block(
'0000000000000000012e6060980c6475a9a8e62a1bf44b76c5d51f707d54522c',
479808,
'SEGWIT_LOCK'
);
self::$special_blocks[] = new block(
'00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148',
478559,
'BCC'
);
self::$special_blocks[] = new block(
'0000000000000000015411ca4b35f7b48ecab015b14de5627b647e262ba0ec40',
477120,
'BIP_91_LOCK'
);
self::$special_blocks[] = new block(
'000000000000000002cce816c0ab2c5c269cb081896b7dcb34b8422d6b74ffa1',
420000,
'HALVING_2'
);
self::$special_blocks[] = new block(
'0000000000000001bc7156dd1183c87859b326affa3a5cdd157e809537f0b284',
270953,
'WHALE201311'
);
self::$special_blocks[] = new block(
'000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e',
210000,
'HALVING_1'
);
self::$special_blocks[] = new block(
'00000000006de085dadb3ec413ef074022fe781121b467e98960280dd246bb00',
57035,
'PIZZA'
);
self::$special_blocks[] = new block(
'000000000a73e64735a2b75c97ea674950a9018da1420d01328a918c9ff9852c',
5637,
'TOPISTO'
);
self::$special_blocks[] = new block(
'000000008bf44a528a09d203203a6a97c165cf53a92ecc27aed0b49b86a19564',
1337,
'LEET'
);
self::$special_blocks[] = new block(
'00000000fc5b3c76f27f810ee775e480ae7fd604fd196b2d8da4257fcd39f4f9',
666,
'LUCIFER'
);
self::$special_blocks[] = new block(
'00000000314e90489514c787d615cea50003af2023796ccdd085b6bcc1fa28f5',
42,
'THE_ANSWER'
);
self::$special_blocks[] = new block(
'000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
0,
'GENESIS'
);
}
// ---
// --- Retourne le JSON du tableau des blocs speciaux
// ---
public static function getSpecialBlocksJSON()
{
return json_encode(self::$special_blocks);
}
// ---
// --- Retourne le nom d'un block à partie de son hash
// --- si ce n'est pas un block special, on renvoie le hash
// ---
public static function hash2SpecialName($block_hash)
{
foreach(self::$special_blocks as $value)
if ($block_hash == $value->hash) return $value->name;
return $block_hash;
}
// ---
// --- Accès aux blocks spéciaux
// --- On en rajoute un : le LAST block
// --- En fait toute valeur ne faisant pas partie des blocks connus
// ---
public static function getSpecialBlock($nom_du_block)
{
$block_hash = '';
foreach(self::$special_blocks as $value)
{
if ($nom_du_block == $value->name)
{
$block_hash = $value->hash;
break;
}
}
if (file_exists(self::$offline_block)) $block_hash = 'offline';
if ($block_hash == '') $block_hash = self::getLastBlockHash();
return self::getBlockWithHash($block_hash);
}
// ---
// --- Accès au dernier block en cache ...
// ---
public static function getLastCacheBlockHash()
{
// on commence par le cache d'images
$myarray = glob(DATA_PATH.'/hasard/*.png');
if (isset($myarray[0]))
{
usort( $myarray, function( $a, $b ) { return filemtime($b) - filemtime($a); } );
return substr(basename($myarray[0],'.png'),0,strlen(self::$special_blocks[0]->hash));
}
// S'il n'y a rien dans le cache d'images
$filename=DATA_PATH.'/finished_block_list.txt';
if (file_exists($filename))
{
$handle = fopen($filename, "r");
if ($handle)
{
while (($buffer = fgets($handle, 4096)) !== false) {
$valeurs = explode(" ",$buffer);
if ($valeurs[0] == 'LAST') return $valeurs[1];
}
}
}
return NULL;
}
// ---
// --- Accès au dernier block
// ---
public static function getLastBlockHash()
{
if (file_exists(self::$offline_block)) return 'offline';
$filename=self::$url_info.'/latestblock';
$message = file_get_contents($filename);
if ($message === FALSE) return FALSE;
$the_block = json_decode($message);
return $the_block->hash;
}
// ---
// --- Accès à partir du hash
// ---
public static function getBlockWithHash($block_hash)
{
// Si on est offline, peut importe le hash passé
if (!file_exists(self::$offline_block))
{
if (!file_exists(DATA_PATH."/json/$block_hash.zip"))
{
$filename=self::$url_info.'/rawblock/'.$block_hash;
$message = file_get_contents($filename);
if ( $message === FALSE ) return FALSE;
$the_block = json_decode($message);
return self::saveBlockInTmpDir($the_block);
}
touch(DATA_PATH."/json/$block_hash.zip");
}
return self::zip2Block($block_hash);
}
// ---
// --- Gestion du cache
// ---
private static function saveBlockInTmpDir($the_block)
{
if (file_exists(DATA_PATH.'/json/'.$the_block->hash.'.zip')) return $the_block;
if (!isset($the_block->topisto_inputs))
{
$the_block->topisto_inputs = 0;
$the_block->topisto_outputs = 0;
$the_block->topisto_fees = 0;
$the_block->topisto_reward = 0;
foreach($the_block->tx as $transaction)
{
$tx_inputs = 0;
$tx_outputs = 0;
if (isset($transaction->inputs))
foreach($transaction->inputs as $input)
if (isset($input->prev_out->value))
$tx_inputs += $input->prev_out->value;
if (isset($transaction->out))
foreach($transaction->out as $output)
if (isset($output->value))
$tx_outputs += $output->value;
$tx_fees = abs($tx_inputs - $tx_outputs);
$the_block->topisto_inputs += $tx_inputs;
if ($tx_inputs == 0)
{
$the_block->topisto_reward += $tx_outputs;
continue;
}
$the_block->topisto_outputs += $tx_outputs;
$the_block->topisto_fees += $tx_fees;
}
// On retire les frais de la récompense
$the_block->topisto_reward -= $the_block->topisto_fees;
self::block2zip($the_block);
}
return $the_block;
}
private static function block2zip($the_block)
{
$zip = new ZipArchive();
if($zip->open(DATA_PATH.'/json/'.$the_block->hash.'.zip', ZipArchive::CREATE) === true)
{
$zip->addFromString($the_block->hash.'.json', json_encode($the_block));
$zip->close();
}
}
private static function zip2Block($the_block_hash)
{
$local_hash = $the_block_hash;
$zipBlock = DATA_PATH."/json/$the_block_hash.zip";
if (file_exists(self::$offline_block))
{
$local_hash = 'offline';
$zipBlock = self::$offline_block;
}
$zip = new ZipArchive;
if ($zip->open($zipBlock) === TRUE) {
$the_block = json_decode($zip->getFromName("$local_hash.json"));
$zip->close();
return $the_block;
}
return NULL;
}
public static function DrawBlockHeaderFooter($the_block, $vImage, $hauteur, $couleur = -1)
{
$color_tab = [
[
[19,15,64],
[255,255,255]
],
[
[106,176,76],
[0,0,0]
],
[
[106,176,76],
[255,255,255]
],
[
[240,147,43],
[0,0,0]
],
[
[34,166,179],
[0,0,0]
],
[
[240,147,43],
[255,255,255]
],
[
[48,51,107],
[255,255,255]
],
[
[246, 229, 141],
[235,77,75]
],
[
[40, 40, 40],
[158,227,253]
],
[
[255, 255, 255],
[0, 0, 0]
],
[
[0, 0, 0],
[255, 255, 255]
],
[
[255, 255, 255],
[255, 0, 0]
]
];
$color = $couleur;
if (($color == -1) || ($color > (count($color_tab)-1))) $color = rand(0,count($color_tab)-1);
// Rajout des HASHES
$white = imagecolorallocate($vImage, 254, 254, 254);
$black = imagecolorallocate($vImage, $color_tab[$color][1][0], $color_tab[$color][1][1], $color_tab[$color][1][2]);
$fond = imagecolorallocate($vImage, $color_tab[$color][0][0], $color_tab[$color][0][1], $color_tab[$color][0][2]);
$w = imagesx($vImage);
$h = imagesy($vImage);
// On rend le blanc transparent
imagecolortransparent($vImage, $white);
// On place un fond transparent
imagefilledrectangle($vImage, 0, 0, $w, $h, $white);
$len = strlen($the_block->hash);
$ratio_w = ($w - 10) / $len;
$ratio_h = $hauteur / 32; // car hexadécimal
// Récupérer les hashes dans 2 tableaux
$tableau1 = str_split($the_block->hash);
$tableau2 = str_split($the_block->prev_block);
// Convertir les tableaux en liste de points
$points1 = [];
$points2 = [];
$points1[] = 0;
$points1[] = $hauteur - 1;
$points2[] = 0;
$points2[] = $hauteur - 1;
for($i=0;$i<$len;$i++)
{
// Le HASH
$coin_x1 = $ratio_w * $i;
$coin_x2 = $ratio_w * ($i + 1);
$coin_y1 = $hauteur;
$coin_y2 = 5 + hexdec($tableau1[$i])*$ratio_h;
$points1[] = $coin_x1;
$points1[] = $coin_y2;
$points1[] = $coin_x2;
$points1[] = $coin_y2;
// Le PREV HASH
$coin_x1 = $ratio_w * $i;
$coin_x2 = $ratio_w * ($i + 1);
$coin_y1 = $hauteur;
$coin_y2 = 25 - hexdec($tableau2[$i])*$ratio_h;
$points2[] = $coin_x1;
$points2[] = $coin_y2;
$points2[] = $coin_x2;
$points2[] = $coin_y2;
}
// Rajouter une barre au HASH
$coin_x1 = $ratio_w * $i;
$coin_x2 = $ratio_w * ($i + 1);
$coin_y1 = $hauteur;
$coin_y2 = 5;
$points1[] = $coin_x1;
$points1[] = $coin_y2;
$points1[] = $coin_x2;
$points1[] = $coin_y2;
// Rajouter une barre au PREV HASH
$coin_x1 = $ratio_w * $i;
$coin_x2 = $ratio_w * ($i + 1);
$coin_y1 = $hauteur;
$coin_y2 = 25;
$points2[] = $coin_x1;
$points2[] = $coin_y2;
$points2[] = $coin_x2;
$points2[] = $coin_y2;
// Rajouter un coin
$points1[] = $w;
$points1[] = $hauteur - 1;
$points2[] = $w;
$points2[] = $hauteur - 1;
// Inverser le PREV HASH
for($i=0;$i<count($points2);$i+=2)
{
$points2[$i+1] = $h - $points2[$i+1];
if ($points2[$i+1] >= $h) $points2[$i+1] = $h - 1;
}
// Dessiner le HASH
imagefilledpolygon($vImage, $points1, (count($points1)/2), $fond);
imagepolygon($vImage, $points1, (count($points1)/2), $black);
// Dessiner le PREV HASH
imagefilledpolygon($vImage, $points2, (count($points2)/2), $fond);
imagepolygon($vImage, $points2, (count($points2)/2), $black);
// Rajout des textes
$the_name = blockchain::hash2SpecialName($the_block->hash);
if ($the_name == $the_block->hash) $the_name = date('Ymd H:i:s', $the_block->time);
putenv('GDFONTPATH='.RESS_PATH.'/fonts/');
$font = 'DS-DIGIB.TTF';
$the_texte = "Height : ".$the_block->height;
imagettftext($vImage,18, 0, 5, $hauteur-5, $black, $font, $the_texte);
$the_texte = "Inputs : ".$the_block->topisto_inputs;
if (count($the_block->tx)==1) $the_texte = "Reward : ".$the_block->topisto_reward;
imagettftext($vImage, 15, 0, 25, ($h - $hauteur)+18, $black, $font, $the_texte);
$bbox = imagettfbbox(14, 0, $font, $the_name);
imagettftext($vImage, 14, 0, ($w-3)-($bbox[2]-$bbox[0]), ($hauteur-5), $black, $font, $the_name);
return [$white, $fond, $black, $font, $color_tab[$color][1], $color_tab[$color][0]];
}
public static function getTransactionData($the_block, $type=1)
{
// Cela a déjà été calculé
if (isset($the_block->data[$type])) return $the_block->data[$type];
$data = [];
foreach($the_block->tx as $transaction)
{
$total_outputs = 0;
$total_inputs = 0;
$value = 0;
if (isset($transaction->out))
foreach($transaction->out as $output)
if (isset($output->value))
$total_outputs += $output->value;
if (isset($transaction->inputs))
foreach($transaction->inputs as $input)
if (isset($input->prev_out->value))
$total_inputs += $input->prev_out->value;
switch($type)
{
// OUTPUTS
case 1:
if ($total_inputs != 0)
$data[] = ["hash" => $transaction->hash, "value" => $total_outputs];
break;
// INPUTS
case 2:
$data[] = ["hash" => $transaction->hash, "value" => $total_inputs];
break;
// FEES
case 3:
if ($total_inputs != 0)
$data[] = ["hash" => $transaction->hash, "value" => ($total_outputs-$total_inputs)];
break;
// REWARD
case 4:
if ($total_inputs == 0)
$data[] = ["hash" => $transaction->hash, "value" => $total_outputs];
break;
}
}
if (!isset($the_block->data)) $the_block->data = [];
$the_block->data[$type] = $data;
return $data;
}
}
blockchain::init();
?>