Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 

446 lignes
13 KiB

<?php
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 = array (
    'GENESIS'     => '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
    'THE_ANSWER'  => '00000000314e90489514c787d615cea50003af2023796ccdd085b6bcc1fa28f5',
    'LUCIFER'     => '00000000fc5b3c76f27f810ee775e480ae7fd604fd196b2d8da4257fcd39f4f9',
    'LEET'        => '000000008bf44a528a09d203203a6a97c165cf53a92ecc27aed0b49b86a19564',
    'TOPISTO'     => '000000000a73e64735a2b75c97ea674950a9018da1420d01328a918c9ff9852c',
    'PIZZA'       => '00000000006de085dadb3ec413ef074022fe781121b467e98960280dd246bb00',
    'HALVING_1'   => '000000000000048b95347e83192f69cf0366076336c639f9b7228e9ba171342e',
    'WHALE201311' => '0000000000000001bc7156dd1183c87859b326affa3a5cdd157e809537f0b284',
    'HALVING_2'   => '000000000000000002cce816c0ab2c5c269cb081896b7dcb34b8422d6b74ffa1',
    'BIP_91_LOCK' => '0000000000000000015411ca4b35f7b48ecab015b14de5627b647e262ba0ec40',
    'BCC'         => '00000000000000000019f112ec0a9982926f1258cdcc558dd7c3b7e5dc7fa148',
    'SEGWIT_LOCK' => '0000000000000000012e6060980c6475a9a8e62a1bf44b76c5d51f707d54522c',
    'SEGWIT'      => '000000000000000000cbeff0b533f8e1189cf09dfbebf57a8ebe349362811b80',
    'HURRICANE_1' => '0000000000000000000fe6d521a187a5523d5cef6f6c178923ff82ffe5a0f372',
    'BLOCK21E800' => '00000000000000000021e800c1e8df51b22c1588e5a624bea17e9faa34b2dc4a',
    'WHALE201810' => '0000000000000000000f9f2dadfb8f312572183272802cbfcc4ff95b4ee6777d'
  );
  // ---
  // --- 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 $key => $value)
      if ($block_hash == $value) return $key;
    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 = '';
    if (isset(self::$special_blocks[$nom_du_block])) $block_hash = self::$special_blocks[$nom_du_block];
    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['GENESIS']));
    }
    // 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;
  }  
}
?>