Overview

Namespaces

  • Liberty
  • None

Classes

  • Liberty\BigInteger
  • Liberty\Block
  • Liberty\Blockchain
  • Liberty\Collection
  • Liberty\CollectionFileList
  • Liberty\File
  • Liberty\Folder
  • Liberty\INI
  • Liberty\LECDSA
  • Liberty\Onecrypt
  • Liberty\SSV
  • Liberty\Text
  • Liberty\Transaction
  • Liberty\Wallet
  • Liberty\WebPeer

Functions

  • __autoload
  • Overview
  • Namespace
  • Class
  1: <?php
  2: 
  3: namespace Liberty;
  4: 
  5: use Liberty\Block;
  6: use Liberty\LECDSA;
  7: use Liberty\File;
  8: use Liberty\WebPeer;
  9: use Exception;
 10: 
 11: /**
 12:  * Liberty Blockchain Class
 13:  *
 14:  * @category  Cryptocurrency
 15:  * @package   Liberty
 16:  * @license   http://www.opensource.org/licenses/mit-license.php MIT License
 17:  * @version   1.0.0
 18:  * @since     2018-03-11
 19:  * @author    Liberty Group <cryptolibertygroup@gmail.com>
 20:  */
 21: 
 22: 
 23: 
 24: class Blockchain {
 25: 
 26: 
 27:     public $transactions = 0;
 28:     public $blockfiles = 0;
 29:     public $fee = 0;
 30: 
 31:     protected $ec;
 32:     protected $path;
 33:     protected $nclass = 0;
 34: 
 35:     const BLOCKSPERFILE = 100000;
 36: 
 37: 
 38: 
 39:     public function __construct($path)
 40:     {
 41:         $this->ec = new LECDSA();
 42: 
 43:         $this->path = $path;
 44: 
 45:         // Check nucleus address with network
 46: 
 47:         
 48:         // Detect class.
 49:         if(!$this->nclass()) {
 50:             throw new Exception("Blockchain Class Error.");;
 51:         }
 52: 
 53:         $this->update();
 54:     }
 55: 
 56: 
 57: 
 58:     public function balance($addr)
 59:     {
 60:         $balance = 0;
 61: 
 62:         for($a=1; $a < $this->blockfiles + 1; $a++) {
 63:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
 64: 
 65:             $handle = fopen($blk, "rb");
 66: 
 67:             $fl = fgets($handle);
 68:             $fl = trim($fl);
 69:             $header = explode(" ", $fl);
 70: 
 71:             while(!feof($handle)){
 72:                 $line = fgets($handle);
 73:                 $line = trim($line);
 74:                 $fields = explode(" ", $line);
 75: 
 76:                 if(!is_numeric($fields[0])) continue;
 77:                 
 78:                 for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
 79: 
 80:                 if(strpos($block["receiver"], $addr) !== false) {
 81:                     $balance += $block["amount"];
 82:                 }
 83:             }
 84:             fclose($handle);
 85:         }
 86: 
 87:         for($a=1; $a < $this->blockfiles + 1; $a++) {
 88:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
 89: 
 90:             $handle = fopen($blk, "rb");
 91: 
 92:             $fl = fgets($handle);
 93:             $fl = trim($fl);
 94:             $header = explode(" ", $fl);
 95: 
 96:             while(!feof($handle)){
 97:                 $line = fgets($handle);
 98:                 $line = trim($line);
 99:                 $fields = explode(" ", $line);
100: 
101:                 if(!is_numeric($fields[0])) continue;
102:                 
103:                 for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
104: 
105:                 if(strpos($block["sender"], $addr) !== false) {
106:                     $balance -= $block["amount"] + $block["fee"];
107:                 }
108:             }
109:             fclose($handle);
110:         }
111: 
112:         return $balance;
113:     }
114: 
115: 
116: 
117: 
118:     public function blockAddress()
119:     {
120:         $address = array();
121:         $x = 0;
122: 
123:         for($a=1; $a < $this->blockfiles + 1; $a++) {
124:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
125: 
126:             $handle = fopen($blk, "rb");
127: 
128:             $fl = fgets($handle);
129:             $fl = trim($fl);
130:             $header = explode(" ", $fl);
131: 
132:             while(!feof($handle)){
133:                 $line = fgets($handle);
134:                 $line = trim($line);
135:                 $fields = explode(" ", $line);
136: 
137:                 if(!is_numeric($fields[0])) continue;
138: 
139:                 for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
140: 
141:                 if( array_search($block["receiver"], $address) === false) {
142:                     $address[$x] = $block["receiver"];
143:                     $x++;
144:                 }
145:             }
146:             fclose($handle);
147:         }
148: 
149:         return $address;
150:     }
151: 
152: 
153: 
154:     
155:     public function blockFee()
156:     {
157:         $fee = 0;
158: 
159:         for($a=1; $a < $this->blockfiles + 1; $a++) {
160:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
161: 
162:             $handle = fopen($blk, "rb");
163: 
164:             $fl = fgets($handle);
165:             $fl = trim($fl);
166:             $header = explode(" ", $fl);
167: 
168:             while(!feof($handle)){
169:                 $line = fgets($handle);
170:                 $line = trim($line);
171:                 $fields = explode(" ", $line);
172: 
173:                 if(!is_numeric($fields[0])) continue;
174:                 
175:                 for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
176: 
177:                 $fee += $block["fee"];
178:             }
179:             fclose($handle);
180:         }
181: 
182:         return round($fee, 2);
183:     }
184: 
185: 
186: 
187: 
188: 
189:     public function blockFiles()
190:     {
191:         $a=0;
192: 
193:         do {
194:             $a++;
195:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
196:         } while(file_exists($blk));
197: 
198:         return $a-1;
199:     }
200: 
201: 
202: 
203: 
204:     // Return Block Object
205:     public function blockLast()
206:     {
207:         $item = explode(" ", $this->blockPack($this->transactions - 1)[0]);
208:         $block = new Block($item[0], $item[1], $item[2], $item[3], $item[4], $item[5], $item[6], $item[7]);
209:         return $block;
210:     }
211: 
212: 
213: 
214: 
215:     // Return array of strings
216:     public function blockPack($start=0, $limit=0)
217:     {
218:         $tr = 0;
219:         $b = 0;
220:         $pack = array();
221:         $active = false;
222: 
223:         if($limit == 0) {
224:             $limit = $this->transactions;
225:         }
226: 
227:         for($a=1; $a < $this->blockfiles + 1; $a++) {
228:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
229: 
230:             $handle = fopen($blk, "rb");
231:             while(!feof($handle)){
232:                 $line = fgets($handle);
233:                 $line = trim($line);
234:                 $fields = explode(" ", $line);
235: 
236:                 if(!is_numeric($fields[0])) continue;
237: 
238:                 // Start condition.
239:                 if($fields[0] == $start) {
240:                     $active = true;
241:                 }
242: 
243:                 // Stop condition.
244:                 if($b == ($limit - $start) + 1) {
245:                     $active = false;
246:                 }
247: 
248:                 if($active) {
249:                     $pack[$b] = $line;
250:                     $b++;
251:                 }
252: 
253:                 $tr++;
254:             }
255:             fclose($handle);
256: 
257:             if($a == 1) {
258:                 $tr = $tr - 3;
259:             } else {
260:                 $tr = $tr - 2;
261:             }
262:         }
263: 
264:         if(count($pack) < 1) return false;
265: 
266:         return $pack;
267:     }
268: 
269: 
270: 
271:     // Return array
272:     public function blockSearch($keyword, $column)
273:     {
274:         for($a=1; $a < $this->blockfiles + 1; $a++) {
275:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
276: 
277:             $handle = fopen($blk, "rb");
278: 
279:             $fl = fgets($handle);
280:             $fl = trim($fl);
281:             $header = explode(" ", $fl);
282: 
283:             while(!feof($handle)){
284:                 $line = fgets($handle);
285:                 $line = trim($line);
286:                 $fields = explode(" ", $line);
287: 
288:                 if(!is_numeric($fields[0])) continue;
289:                 
290:                 for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
291: 
292:                 if(strpos($block[$column], $keyword) !== false) {
293:                     $return["nclass"] = $this->nclass;
294:                     $return["file"] = $a;
295:                     $return["block"] = $block["index"];
296:                     return $return;
297:                 }
298:             }
299:             fclose($handle);
300:         }
301: 
302:         return false;
303:     }
304: 
305: 
306: 
307: 
308:     public function integrity()
309:     {
310:         $b = 0;
311:         $prevBlock = new Block(0,0,0,0,0,0,0,0);
312:         $prevHash = "";
313: 
314:         $balances = [];
315:         
316:         $candidates = [];
317:         $x = 0;
318: 
319:         $fee = 0;
320:         $feeStatus = false;
321:         $feeSenderBefore = "";
322: 
323:         for($a=1; $a < $this->blockfiles + 1; $a++) {
324:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
325: 
326:             $handle = fopen($blk, "rb");
327: 
328:             while(!feof($handle)){
329:                 $line = fgets($handle);
330:                 $line = trim($line);
331:                 $fields = explode(" ", $line);
332: 
333:                 if(!is_numeric($fields[0])) continue;
334:                 
335:                 $block = new Block($fields[0], $fields[1], $fields[2], $fields[3], $fields[4], $fields[5], $fields[6], $fields[7]);
336:                 $hash = $block->hash();
337: 
338: 
339:                 // Check index precedence
340:                 if($fields[0] != ($prevBlock->index + 1)) {
341:                     if($fields[0] != 0) {
342:                         throw new Exception("Fail Index at Block " . $fields[0]);
343:                         return false;
344:                     }
345:                 }
346: 
347: 
348:                 // Check block hash
349:                 if($fields[8] != $hash) {
350:                     throw new Exception("Fail Hash at Block " . $fields[0]);
351:                     return false;
352:                 }
353: 
354: 
355:                 // Check previous hash
356:                 if($fields[7] != $prevBlock->hash()) {
357:                     if($fields[0] != 0) {
358:                         throw new Exception("Fail Chain at Block " . $fields[0]);
359:                         return false;
360:                     }
361:                 }
362: 
363: 
364:                 // Check block transaction signature.
365:                 if($fields[0] != 0) {
366:                     if($fields[1] != "fee") {
367:                         if(! Transaction::verify($fields[1], $fields[2], $fields[3], $fields[4], $fields[5], $fields[6]) ) {
368:                             throw new Exception("Fail Chain in Signature at Block " . $fields[0]);
369:                             return false;
370:                         }
371:                     }
372:                 }
373: 
374: 
375:                 // Check owner balance
376: 
377:                 if(isset($balances[$fields[2]])) {
378:                     $balances[$fields[2]] += $fields[3];
379:                 } else {
380:                     $balances[$fields[2]] = $fields[3];
381:                 }
382: 
383:                 if(isset($balances[$fields[1]])) {
384:                     $balances[$fields[1]] -= ($fields[3] + $fields[4]);
385:                 } else {
386:                     $balances[$fields[1]] = "-" . ($fields[3] + $fields[4]);
387:                 }
388: 
389:                 if(self::nucleus($this->path . "/nucleus.list", $fields[1]) !== true) {
390:                     if($fields[0] != 0) {
391:                         if($balances[$fields[1]] + $fields[3] + $fields[4] < $fields[3] + $fields[4] ) {
392:                             throw new Exception("Fail Chain in Owner Balance at Block " . $fields[0]);
393:                             return false;
394:                         }
395:                     }
396:                 }
397: 
398: 
399:                 // Fee check out
400:                 $feeCheck = self::fee($fields[3]);
401:                 
402:                 if($fields[0] != 0 and $fields[1] != "fee") {
403:                     if($feeCheck != $fields[4]) {
404:                         throw new Exception("Fail Chain in Fee amount at Block " . $fields[0]);
405:                         return false;
406:                     }
407:                 }
408: 
409: 
410:                 // Check fee distribution.
411: 
412:                 foreach($balances as $key => $value) {
413:                     if($value > 99.99) {
414:                         if(array_search($key, $candidates) === false) {
415:                             $candidates[$x] = $key;
416:                             $x++;
417:                         }
418:                     }
419:                     if($value < 100) {
420:                         $index = array_search($key, $candidates);
421:                         unset($candidates[$index]);
422:                     }
423:                 }
424: 
425:                 //print_r(array_values($candidates));
426: 
427:                 $bfee = $fee;
428:                 $fee += $fields[4];
429: 
430:                 if($fields[1] == "fee") {
431:                     $feeBlock = substr($fields[0], -1);
432:                     if($feeBlock == 0) {
433:                         if($feeSenderBefore != "fee") {
434:                             $feeStatus = true;
435:                         }
436:                     } else {
437:                         if($quote != $fields[3]) {
438:                             throw new Exception("Fail Chain in Fee Quote Distribution at Block " . $fields[0]);
439:                             return false;
440:                         }
441:                     }
442:                 } else {
443:                     $feeStatus = false;
444:                 }
445: 
446:                 if($feeStatus == true) {
447:                     if( ($bfee * 100 / count($candidates)) > 0 ) {
448:                         $quote = round($bfee / count($candidates), 2, PHP_ROUND_HALF_DOWN);
449:                         
450:                         /*
451:                         echo "Block " . $fields[0] . PHP_EOL;
452:                         echo "Fee at " . $bfee . PHP_EOL;
453:                         echo "Candidates are " . count($candidates) . PHP_EOL;
454:                         echo "Quote: " . $quote . PHP_EOL;
455:                         */
456: 
457:                         if($quote != $fields[3]) {
458:                             throw new Exception("Fail Chain in Fee Quote at Block " . $fields[0]);
459:                             return false;
460:                         }
461:                     } else {
462:                         throw new Exception("Fail Chain in Fee amount at Block " . $fields[0]);
463:                         return false;
464:                     }
465: 
466:                     $feeStatus = false;
467:                 }
468: 
469:                 $feeSenderBefore = $fields[1];
470: 
471:                 $prevBlock = $block;
472:                 $b++;
473:             }
474:             fclose($handle);
475:         }
476: 
477:         return true;
478:     }
479: 
480: 
481: 
482: 
483: 
484: 
485:     public function nclass()
486:     {
487:         $stop = true;
488:         $a = 1;
489: 
490:         while($stop) {
491:             $blk = $this->path . "/blk-c" . $a . "-1.ssv";
492: 
493:             if(file_exists($blk)) {
494:                 $sectorA = self::sectorA($blk);
495:                 $msg = $sectorA[0];
496:                 $addr = $sectorA[1];
497:                 $sign = $sectorA[2];
498: 
499:                 // Check nucleus address
500:                 if( self::nucleus($this->path . "/nucleus.list", $addr) !== true ) break;
501:         
502:                 // Check class version with file.
503:                 $c = explode("-", $msg);
504:                 if($c[2] != $a) break;
505:                 
506:                 // Verify DS.
507:                 try {
508:                     if( $this->ec->checkSignatureForMessage($addr, $sign, $msg) != true ) break;            
509:                 } catch(Exception $e) {
510:                     return $e->getMessage();
511:                 }
512: 
513:                 $this->nclass = $a;
514:                 
515:             } else {
516:                 $stop = false;
517:             }
518: 
519:             $a++;
520:         }
521: 
522:         if($this->nclass == 0) return false;
523: 
524:         return $this->nclass;
525:     }
526: 
527: 
528: 
529: 
530:     public function transactions()
531:     {
532:         $tr = 0;
533: 
534:         for($a=1; $a < $this->blockfiles + 1; $a++) {
535:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
536: 
537:             $handle = fopen($blk, "rb");
538:             while(!feof($handle)){
539:                 fgets($handle);
540:                 $tr++;
541:             }
542:             fclose($handle);
543: 
544:             if($a == 1) {
545:                 $tr = $tr - 3;
546:             } else {
547:                 $tr = $tr - 1;
548:             }
549:         }
550: 
551:         return $tr;
552:     }
553: 
554: 
555: 
556: 
557:     public function tx($addr)
558:     {
559:         $tx = [];
560:         $c = 0;
561: 
562:         for($a=1; $a < $this->blockfiles + 1; $a++) {
563:             $blk = $this->path . "/blk-c" . $this->nclass . "-" . $a . ".ssv";
564: 
565:             $handle = fopen($blk, "rb");
566: 
567:             $fl = fgets($handle);
568:             $fl = trim($fl);
569:             $header = explode(" ", $fl);
570: 
571:             while(!feof($handle)){
572:                 $line = fgets($handle);
573:                 $line = trim($line);
574:                 $fields = explode(" ", $line);
575: 
576:                 if(!is_numeric($fields[0])) continue;
577:                 if($fields[1] == "fee") continue;
578:                 
579:                 //for($b=0; $b<count($header); $b++) $block[$header[$b]] = $fields[$b];
580:                 
581:                 // receiver
582:                 if(strpos($fields[2], $addr) !== false) {
583:                     $tx[$c] = $line . " in";
584:                     $c++;
585:                 }
586:                 
587:                 // sender
588:                 if(strpos($fields[1], $addr) !== false) {
589:                     $tx[$c] = $line . " out";
590:                     $c++;
591:                 }
592:             }
593:             fclose($handle);
594:         }
595: 
596:         return $tx;
597:     }
598: 
599: 
600: 
601:     
602:     public function servers()
603:     {
604:         $servers = array();
605:         $b=0;
606: 
607:         $wp = new WebPeer($this->path);
608:         $account = $wp->peerAddress();
609:         $addr = $wp->peerList();
610:         
611:         for($a=0; $a<count($addr); $a++) {
612:             if(WebPeer::curl($addr[$a] . "/server.php?ping=1")) {
613:                 if( array_search($account[$a], $servers) === false) {
614:                     $servers[$b] = $account[$a];
615:                     $b++;
616:                 }
617:             }
618:         }
619: 
620:         return $servers;
621:     }
622:     
623:     
624:     
625:     
626:     public function sync($url)
627:     {
628:         $largeSize = 0;
629: 
630:         // Local Last Block
631:         $localLast = $this->transactions - 1;
632: 
633:         $localBlocks = $localLast;
634: 
635:         if($content = WebPeer::curl($url . "/server.php?blocks=1&start=" . ($localLast+1) . "&limit=0")) {
636:             $json = json_decode($content, true);
637:             $file = new File($this->path . "/blk-c" . $this->nclass . "-" . $this->blockfiles . ".ssv");
638: 
639:             for($a=0; $a<count($json)-1; $a++) {
640:                 while($localBlocks > self::BLOCKSPERFILE) {
641:                     $this->blockfiles++;
642:                     $file = new File($this->path . "/blk-c" . $this->nclass . "-" . $this->blockfiles . ".ssv");
643:                     $localBlocks = 0;
644:                 }
645:                 
646:                 $file->append($json[$a] . "\n");
647:                 $localBlocks++;
648:             }
649:         }
650: 
651:         // Integrity and Update
652:         $this->update();
653:     }
654: 
655: 
656: 
657: 
658: 
659:     public function update()
660:     {
661:         // Count block files
662:         $this->blockfiles = $this->blockFiles();
663: 
664:         // Count transactions
665:         $this->transactions = $this->transactions();
666: 
667:         // Count fee
668:         $this->fee = $this->blockFee();
669: 
670:         // Check integrity
671:         if(!$this->integrity()) {
672:             throw new Exception("No Integrity of Blockchain.");
673:         }
674:     }
675: 
676: 
677: 
678: 
679: 
680:     public static function console($msg)
681:     {
682:         echo $msg . PHP_EOL;
683:     }
684: 
685: 
686: 
687: 
688: 
689:     public static function fee($amount)
690:     {
691:         $fee = 0;
692: 
693:         if($amount > 9) {
694:             $fee = $amount * 0.001;
695:         }
696: 
697:         if($amount < 10) {
698:             $fee = 0.01;
699:         }
700: 
701:         $fee = round($fee, 2);
702: 
703:         return $fee;
704:     }
705: 
706: 
707: 
708: 
709: 
710:     public static function nucleus($file, $addr="")
711:     {
712:         $list = file($file);
713: 
714:         for($a=0; $a<count($list); $a++) {
715:             $list[$a] = trim($list[$a]);
716:             if($addr == $list[$a]) {
717:                 return true;
718:             }
719:         }
720: 
721:         return $list;
722:     }
723: 
724: 
725: 
726: 
727: 
728:     public static function sectorA($file)
729:     {
730:         $fp = fopen($file, "rb");
731:         $header = fgets($fp);
732:         $line2 = fgets($fp);
733:         fclose($fp);
734: 
735:         $sectorA = explode(" ", trim($line2));
736:         return $sectorA;
737:     }
738:     
739: 
740: 
741:     
742:     
743: }
744: 
745: 
746: 
747: ?>
API documentation generated by ApiGen