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: 13: 14: 15: 16: 17: 18: 19: 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:
46:
47:
48:
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:
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:
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:
239: if($fields[0] == $start) {
240: $active = true;
241: }
242:
243:
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:
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:
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:
349: if($fields[8] != $hash) {
350: throw new Exception("Fail Hash at Block " . $fields[0]);
351: return false;
352: }
353:
354:
355:
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:
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:
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:
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:
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:
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: 452: 453: 454: 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:
500: if( self::nucleus($this->path . "/nucleus.list", $addr) !== true ) break;
501:
502:
503: $c = explode("-", $msg);
504: if($c[2] != $a) break;
505:
506:
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:
580:
581:
582: if(strpos($fields[2], $addr) !== false) {
583: $tx[$c] = $line . " in";
584: $c++;
585: }
586:
587:
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:
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:
652: $this->update();
653: }
654:
655:
656:
657:
658:
659: public function update()
660: {
661:
662: $this->blockfiles = $this->blockFiles();
663:
664:
665: $this->transactions = $this->transactions();
666:
667:
668: $this->fee = $this->blockFee();
669:
670:
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: ?>