commit 3fb244e4d3394cc8ace316f9f8c370f00657d573 Author: kvazar-network Date: Sat Aug 7 13:37:01 2021 +0300 initial commit diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d48824e --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 kvazar-network + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5e7a740 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# kvazar-network webapp +Web-oriented content exploring platform for Kevacoin Blockchain + +### demo +https://kvazar.ml diff --git a/config-default.php b/config-default.php new file mode 100644 index 0000000..469c419 --- /dev/null +++ b/config-default.php @@ -0,0 +1,18 @@ +_width = $width; + $this->_height = $height; + $this->_hash = $hash; + + $csh = hexdec(substr($this->_hash, 0, 1)); // corner sprite shape + $ssh = hexdec(substr($this->_hash, 1, 1)); // side sprite shape + $xsh = hexdec(substr($this->_hash, 2, 1)) & 7; // center sprite shape + + $cro = hexdec(substr($this->_hash, 3, 1)) & 3; // corner sprite rotation + $sro = hexdec(substr($this->_hash, 4, 1)) & 3; // side sprite rotation + $xbg = hexdec(substr($this->_hash, 5, 1)) % 2; // center sprite background + + $cfr = hexdec(substr($this->_hash, 6, 2)); + $cfg = hexdec(substr($this->_hash, 8, 2)); + $cfb = hexdec(substr($this->_hash, 10, 2)); + + $sfr = hexdec(substr($this->_hash, 12, 2)); + $sfg = hexdec(substr($this->_hash, 14, 2)); + $sfb = hexdec(substr($this->_hash, 16, 2)); + + $identicon = imagecreatetruecolor($this->_spriteZ * 3, $this->_spriteZ * 3); + if (function_exists('imageantialias')) { + imageantialias($identicon, TRUE); + } + + $bg = imagecolorallocate($identicon, 255, 255, 255); + imagefilledrectangle($identicon, 0, 0, $this->_spriteZ, $this->_spriteZ, $bg); + + $corner = $this->_getSprite($csh, $cfr, $cfg, $cfb, $cro); + imagecopy($identicon, $corner, 0, 0, 0, 0, $this->_spriteZ, $this->_spriteZ); + $corner = imagerotate($corner, 90, $bg); + imagecopy($identicon, $corner, 0, $this->_spriteZ * 2, 0, 0, $this->_spriteZ, $this->_spriteZ); + $corner = imagerotate($corner, 90, $bg); + imagecopy($identicon, $corner, $this->_spriteZ * 2, $this->_spriteZ * 2, 0, 0, $this->_spriteZ, $this->_spriteZ); + $corner = imagerotate($corner, 90, $bg); + imagecopy($identicon, $corner, $this->_spriteZ * 2, 0, 0, 0, $this->_spriteZ, $this->_spriteZ); + + $side = $this->_getSprite($ssh, $sfr, $sfg, $sfb, $sro); + imagecopy($identicon, $side, $this->_spriteZ, 0, 0, 0, $this->_spriteZ, $this->_spriteZ); + $side = imagerotate($side, 90, $bg); + imagecopy($identicon, $side, 0, $this->_spriteZ, 0, 0, $this->_spriteZ, $this->_spriteZ); + $side = imagerotate($side, 90, $bg); + imagecopy($identicon, $side, $this->_spriteZ, $this->_spriteZ * 2, 0, 0, $this->_spriteZ, $this->_spriteZ); + $side = imagerotate($side, 90, $bg); + imagecopy($identicon, $side, $this->_spriteZ * 2, $this->_spriteZ, 0, 0, $this->_spriteZ, $this->_spriteZ); + + $center = $this->_getCenter($xsh, $cfr, $cfg, $cfb, $sfr, $sfg, $sfb, $xbg); + imagecopy($identicon, $center, $this->_spriteZ, $this->_spriteZ, 0, 0, $this->_spriteZ, $this->_spriteZ); + + imagecolortransparent($identicon, $bg); + + $resized = imagecreatetruecolor($this->_width, $this->_height); + if (function_exists('imageantialias')) { + imageantialias($resized, TRUE); + } + + $bg = imagecolorallocate($resized, 255, 255, 255); + + imagefilledrectangle($resized, 0, 0, $this->_width, $this->_height, $bg); + + imagecopyresampled($resized, $identicon, 0, 0, (imagesx($identicon) - $this->_spriteZ * 3) / 2, (imagesx($identicon) - $this->_spriteZ * 3) / 2, $this->_width, $this->_height, $this->_spriteZ * 3, $this->_spriteZ * 3); + + imagecolortransparent($resized, $bg); + + if ($filter) { + imagefilter($resized, $filter); + } + + ob_start(); + imagejpeg($resized, null, 100); + imagedestroy($resized); + return ob_get_clean(); + } + + private function _getSprite($shape, $R, $G, $B, $rotation) { + + $sprite = imagecreatetruecolor($this->_spriteZ, $this->_spriteZ); + + if (function_exists('imageantialias')) { + imageantialias($sprite, TRUE); + } + + $fg = imagecolorallocate($sprite, $R, $G, $B); + $bg = imagecolorallocate($sprite, 255, 255, 255); + + imagefilledrectangle($sprite, 0, 0, $this->_spriteZ, $this->_spriteZ, $bg); + + switch ($shape) { + case 0: // triangle + $shape = array(0.5, 1, 1, 0, 1, 1); + break; + case 1: // parallelogram + $shape = array(0.5, 0, 1, 0, 0.5, 1, 0, 1); + break; + case 2: // mouse ears + $shape = array(0.5, 0, 1, 0, 1, 1, 0.5, 1, 1, 0.5); + break; + case 3: // ribbon + $shape = array(0, 0.5, 0.5, 0, 1, 0.5, 0.5, 1, 0.5, 0.5); + break; + case 4: // sails + $shape = array(0, 0.5, 1, 0, 1, 1, 0, 1, 1, 0.5); + break; + case 5: // fins + $shape = array(1, 0, 1, 1, 0.5, 1, 1, 0.5, 0.5, 0.5); + break; + case 6: // beak + $shape = array(0, 0, 1, 0, 1, 0.5, 0, 0, 0.5, 1, 0, 1); + break; + case 7: // chevron + $shape = array(0, 0, 0.5, 0, 1, 0.5, 0.5, 1, 0, 1, 0.5, 0.5); + break; + case 8: // fish + $shape = array(0.5, 0, 0.5, 0.5, 1, 0.5, 1, 1, 0.5, 1, 0.5, 0.5, 0, 0.5); + break; + case 9: // kite + $shape = array(0, 0, 1, 0, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1); + break; + case 10: // trough + $shape = array(0, 0.5, 0.5, 1, 1, 0.5, 0.5, 0, 1, 0, 1, 1, 0, 1); + break; + case 11: // rays + $shape = array(0.5, 0, 1, 0, 1, 1, 0.5, 1, 1, 0.75, 0.5, 0.5, 1, 0.25); + break; + case 12: // double rhombus + $shape = array(0, 0.5, 0.5, 0, 0.5, 0.5, 1, 0, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1); + break; + case 13: // crown + $shape = array(0, 0, 1, 0, 1, 1, 0, 1, 1, 0.5, 0.5, 0.25, 0.5, 0.75, 0, 0.5, 0.5, 0.25); + break; + case 14: // radioactive + $shape = array(0, 0.5, 0.5, 0.5, 0.5, 0, 1, 0, 0.5, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1); + break; + default: // tiles + $shape = array(0, 0, 1, 0, 0.5, 0.5, 0.5, 0, 0, 0.5, 1, 0.5, 0.5, 1, 0.5, 0.5, 0, 1); + break; + } + + for ($i = 0; $i < count($shape); $i++) + $shape[$i] = $shape[$i] * $this->_spriteZ; + imagefilledpolygon($sprite, $shape, count($shape) / 2, $fg); + + for ($i = 0; $i < $rotation; $i++) + $sprite = imagerotate($sprite, 90, $bg); + + return $sprite; + } + + private function _getCenter($shape, $fR, $fG, $fB, $bR, $bG, $bB, $usebg) { + $sprite = imagecreatetruecolor($this->_spriteZ, $this->_spriteZ); + if (function_exists('imageantialias')) { + imageantialias($sprite, TRUE); + } + $fg = imagecolorallocate($sprite, $fR, $fG, $fB); + + if ($usebg > 0 && (abs($fR - $bR) > 127 || abs($fG - $bG) > 127 || abs($fB - $bB) > 127)) + $bg = imagecolorallocate($sprite, $bR, $bG, $bB); else + $bg = imagecolorallocate($sprite, 255, 255, 255); + imagefilledrectangle($sprite, 0, 0, $this->_spriteZ, $this->_spriteZ, $bg); + + switch ($shape) { + case 0: // empty + $shape = array(); + break; + case 1: // fill + $shape = array(0, 0, 1, 0, 1, 1, 0, 1); + break; + case 2: // diamond + $shape = array(0.5, 0, 1, 0.5, 0.5, 1, 0, 0.5); + break; + case 3: // reverse diamond + $shape = array(0, 0, 1, 0, 1, 1, 0, 1, 0, 0.5, 0.5, 1, 1, 0.5, 0.5, 0, 0, 0.5); + break; + case 4: // cross + $shape = array(0.25, 0, 0.75, 0, 0.5, 0.5, 1, 0.25, 1, 0.75, 0.5, 0.5, 0.75, 1, 0.25, 1, 0.5, 0.5, 0, 0.75, 0, 0.25, 0.5, 0.5); + break; + case 5: // morning star + $shape = array(0, 0, 0.5, 0.25, 1, 0, 0.75, 0.5, 1, 1, 0.5, 0.75, 0, 1, 0.25, 0.5); + break; + case 6: // small square + $shape = array(0.33, 0.33, 0.67, 0.33, 0.67, 0.67, 0.33, 0.67); + break; + case 7: // checkerboard + $shape = array(0, 0, 0.33, 0, 0.33, 0.33, 0.66, 0.33, 0.67, 0, 1, 0, 1, 0.33, 0.67, 0.33, 0.67, 0.67, 1, 0.67, 1, 1, 0.67, 1, 0.67, 0.67, 0.33, 0.67, 0.33, 1, 0, 1, 0, 0.67, 0.33, 0.67, 0.33, 0.33, 0, 0.33); + break; + } + + for ($i = 0; $i < count($shape); $i++) + $shape[$i] = $shape[$i] * $this->_spriteZ; + if (count($shape) > 0) + imagefilledpolygon($sprite, $shape, count($shape) / 2, $fg); + + return $sprite; + } +} diff --git a/library/mysql.php b/library/mysql.php new file mode 100755 index 0000000..4b7bbb2 --- /dev/null +++ b/library/mysql.php @@ -0,0 +1,157 @@ +_db = new PDO('mysql:dbname=' . DB_NAME . ';host=' . DB_HOST . ';port=' . DB_PORT . ';charset=utf8', DB_USERNAME, DB_PASSWORD, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']); + $this->_db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); + $this->_db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC); + $this->_db->setAttribute(PDO::ATTR_TIMEOUT, 600); + + } catch(PDOException $e) { + trigger_error($e->getMessage()); + } + } + + public function getNamespaceName($namehash) { + + try { + + $query = $this->_db->prepare('SELECT `data`.`value` AS `value` + + FROM `data` + JOIN `namespace` ON (`namespace`.`nameSpaceId` = `data`.`nameSpaceId`) + + WHERE `namespace`.`hash` = ? + AND `data`.`ns` = "1" + AND `data`.`deleted` = "0" + + ORDER BY `data`.`blockId` DESC + + LIMIT 1'); + + $query->execute([$namehash]); + + return $query->rowCount() ? $query->fetch()['value'] : []; + + } catch(PDOException $e) { + + trigger_error($e->getMessage()); + return false; + } + } + + public function getData($namehash = false, $txid = false, $search = false, $start = 0, $limit = 10) { + + try { + + if ($txid) { + + $query = $this->_db->prepare('SELECT `block`.`blockId` AS `block`, + `namespace`.`hash` AS `namehash`, + `data`.`time` AS `time`, + `data`.`key` AS `key`, + `data`.`value` AS `value`, + `data`.`txid` AS `txid` + + FROM `data` + JOIN `block` ON (`block`.`blockId` = `data`.`blockId`) + JOIN `namespace` ON (`namespace`.`nameSpaceId` = `data`.`nameSpaceId`) + + WHERE `data`.`txid` = ? + AND `data`.`ns` = "0" + AND `data`.`deleted` = "0" + + ORDER BY `block`.`blockId` DESC + + LIMIT ' . (int) $start . ',' . (int) $limit); + + $query->execute([$txid]); + + } else if ($namehash) { + + $query = $this->_db->prepare('SELECT `block`.`blockId` AS `block`, + `namespace`.`hash` AS `namehash`, + `data`.`time` AS `time`, + `data`.`key` AS `key`, + `data`.`value` AS `value`, + `data`.`txid` AS `txid` + + FROM `data` + JOIN `block` ON (`block`.`blockId` = `data`.`blockId`) + JOIN `namespace` ON (`namespace`.`nameSpaceId` = `data`.`nameSpaceId`) + + WHERE `namespace`.`hash` = ? + AND `data`.`ns` = "0" + AND `data`.`deleted` = "0" + + ORDER BY `block`.`blockId` DESC + + LIMIT ' . (int) $start . ',' . (int) $limit); + + $query->execute([$namehash]); + + } else if ($search) { + + $query = $this->_db->prepare('SELECT `block`.`blockId` AS `block`, + `namespace`.`hash` AS `namehash`, + `data`.`time` AS `time`, + `data`.`key` AS `key`, + `data`.`value` AS `value`, + `data`.`txid` AS `txid` + + FROM `data` + JOIN `block` ON (`block`.`blockId` = `data`.`blockId`) + JOIN `namespace` ON (`namespace`.`nameSpaceId` = `data`.`nameSpaceId`) + + WHERE (`data`.`key` LIKE :search + OR `data`.`value` LIKE :search + OR `block`.`blockId` LIKE :search + OR `namespace`.`hash` LIKE :search + OR `data`.`txid` LIKE :search) + + AND `data`.`ns` = "0" + AND `data`.`deleted` = "0" + + ORDER BY `block`.`blockId` DESC'); + + $query->bindValue(':search', '%' . $search . '%', PDO::PARAM_STR); + + $query->execute(); + + } else { + + $query = $this->_db->prepare('SELECT `block`.`blockId` AS `block`, + `namespace`.`hash` AS `namehash`, + `data`.`time` AS `time`, + `data`.`key` AS `key`, + `data`.`value` AS `value`, + `data`.`txid` AS `txid` + + FROM `data` + JOIN `block` ON (`block`.`blockId` = `data`.`blockId`) + JOIN `namespace` ON (`namespace`.`nameSpaceId` = `data`.`nameSpaceId`) + + WHERE `data`.`ns` = "0" + AND `data`.`deleted` = "0" + + ORDER BY `block`.`blockId` DESC + + LIMIT ' . (int) $start . ',' . (int) $limit); + + $query->execute(); + } + + + return $query->rowCount() ? $query->fetchAll() : []; + + } catch(PDOException $e) { + + trigger_error($e->getMessage()); + return false; + } + } +} diff --git a/public/css/app.css b/public/css/app.css new file mode 100755 index 0000000..5799f99 --- /dev/null +++ b/public/css/app.css @@ -0,0 +1,199 @@ +button::-moz-focus-inner { + border: 0; + padding: 0; + margin-top:-2px; + margin-bottom: -2px; +} + +* { + padding: 0; + margin: 0; +} + +input, button { + opacity: 0.8; + color: #5A526C; +} + +body { + color: #E8E8E8; + background-color: #000; + font-family: Sans-Serif, Arial; + font-size: 14px; + font-weight: normal; + line-height: 1.8; +} + +a { + color: #BFBACC; + text-decoration: none; +} + +a { + color: #BFBACC; + text-decoration: none; + -moz-transition: all .5s ease-in; + -o-transition: all .5s ease-in; + /* issue #2 + -webkit-transition: all .5s ease-in; + transition: all .5s ease-in; */ +} + +a:hover { + color: #E8E8E8; + -webkit-transition: all .5s ease-in; /* issue #2 */ +} + +img { + opacity: 0.8; + -moz-transition: all .5s ease-in; + -o-transition: all .5s ease-in; + -webkit-transition: all .5s ease-in; + transition: all .5s ease-in; +} + +img.shine, +img:hover { + opacity: 1 +} + +.logo { + font-size: 20px; + letter-spacing: .2em; + margin-right:-.2em; +} + +.d-block { + display: block; +} + +.of-a { + overflow: auto +} + +.f-s-12 { + font-size: 12px; +} + +.f-s-16 { + font-size: 16px; +} + +.f-s-20 { + font-size: 18px; +} + +.t-center { + text-align: center; +} + +.b-g::before { + content: ""; + background-image: linear-gradient(to left, #9B94BF, #000); + height: 1px; + width: 360px; + display: inline-block; +} + +.b-g::after { + content: ""; + background-image: linear-gradient(to right, #9B94BF, #000); + height: 1px; + width: 360px; + display: inline-block; +} + +.br-50 { + border-radius: 50%; +} + +.c-0, +.c-0:active, +.c-0:visited, +.c-0:hover { + color: #FFF; +} + +.c-1, +.c-1:active, +.c-1:visited, +.c-1:hover { + color: #ABA6B7; +} + +.c-2, +.c-2:active, +.c-2:visited, +.c-2:hover { + color: #9ba5af; +} + +.cursor-pointer { + cursor: pointer; +} + +.p-8 { + padding: 8px; +} + +@-moz-document url-prefix() { + .p-8 { + padding: 4px; + } +} + +.px-16 { + padding-left: 16px; + padding-right: 16px; +} + +.py-27 { + padding-top: 27px; + padding-bottom: 27px; +} + +.py-54 { + padding-top: 54px; + padding-bottom: 54px; +} + +.pt-27 { + padding-top: 27px; +} + +.pt-54 { + padding-top: 54px; +} + +.pb-16 { + padding-bottom: 16px; +} + +.pb-27 { + padding-bottom: 27px; +} + +.pb-36 { + padding-bottom: 36px; +} + +.mx-a { + margin-left: auto; + margin-right: auto; +} + +.mb-16 { + margin-bottom: 16px +} + +.mb-20 { + margin-bottom: 20px +} + +.mb-36 { + margin-bottom: 36px +} + +.mw-560 { + max-width: 560px; +} diff --git a/public/image.php b/public/image.php new file mode 100755 index 0000000..7f97825 --- /dev/null +++ b/public/image.php @@ -0,0 +1,11 @@ +generateImageResource(md5($_GET['hash']), 60, 60, false); +} diff --git a/public/index.php b/public/index.php new file mode 100755 index 0000000..c9e2111 --- /dev/null +++ b/public/index.php @@ -0,0 +1,116 @@ + 2 ? $_GET['q'] : false; +$ns = isset($_GET['ns']) ? preg_replace('/[^a-zA-Z0-9]+/', '', $_GET['ns']) : false; +$tx = isset($_GET['tx']) ? preg_replace('/[^a-zA-Z0-9]+/', '', $_GET['tx']) : false; +$page = (int) isset($_GET['page']) ? $_GET['page'] : 0; + +if (SEF_MODE && isset($_SERVER['QUERY_STRING'])) { + + $q = explode('/', $_SERVER['QUERY_STRING']); + + if (isset($q[1])) { + if (strlen($q[1]) == 34) { + $ns = preg_replace('/[^a-zA-Z0-9]+/', '', $q[1]); + } else if (strlen($q[1]) > 34) { + $tx = preg_replace('/[^a-zA-Z0-9]+/', '', $q[1]); + } else { + $page = (int) $q[1]; + } + } + + if (isset($q[2])) { + if (strlen($q[2]) == 34) { + $ns = preg_replace('/[^a-zA-Z0-9]+/', '', $q[2]); + } else { + $page = (int) $q[2]; + } + } +} + +$limit = PAGE_LIMIT * $page; + +$db = new MySQL(); + +if ($ns) { + $namespaceValue = $db->getNamespaceName($ns); +} else { + $namespaceValue = false; +} + +$data = []; +foreach ($db->getData($ns, $tx, $query, $limit, PAGE_LIMIT) as $value) { + $data[] = [ + 'namehash' => $value['namehash'], + 'block' => $value['block'], + 'txid' => $value['txid'], + 'time' => date('d-m-Y H:i', $value['time']), + 'key' => nl2br($value['key']), + 'value' => nl2br($value['value']), + ]; +} + +if (SEF_MODE) { + + if (in_array($page, [0, 1])) { + $newer = false; + } else { + if ($page == 2) { + $newer = ($ns ? $ns : ''); + } else { + $newer = ($ns ? $ns . '/' . ($page - 1) : ($page - 1)); + } + } + + if ($data) { + if (in_array($page, [0, 1])) { + $older = ($ns ? $ns . '/2' : '/2'); + } else { + $older = ($ns ? $ns . '/' . ($page + 1) : '/' . ($page + 1)); + } + } else { + $older = false; + } + +} else { + + if (in_array($page, [0, 1])) { + $newer = false; + } else { + if ($page == 2) { + $newer = ($ns ? '?ns=' . $ns : ''); + } else { + $newer = ($ns ? '?ns=' . $ns . '&page=' . ($page - 1) : '?page=' . ($page - 1)); + } + } + + if ($data) { + if (in_array($page, [0, 1])) { + $older = ($ns ? '?ns=' . $ns . '&page=2' : '?page=2'); + } else { + $older = ($ns ? '?ns=' . $ns . '&page=' . ($page + 1) : '?page=' . ($page + 1)); + } + } else { + $older = false; + } +} + +if ($ns) { + if ($page) { + $hrefThisPage = $ns . '/' . $page; + } else { + $hrefThisPage = $ns; + } +} else { + if ($page) { + $hrefThisPage = $page; + } else { + $hrefThisPage = ''; + } +} + +require_once('index.phtml'); diff --git a/public/index.phtml b/public/index.phtml new file mode 100755 index 0000000..863f94b --- /dev/null +++ b/public/index.phtml @@ -0,0 +1,98 @@ + + + + + + + + + + KVAZAR - NS - <?php echo $namespaceValue ? $namespaceValue : $ns; ?> - PART <?php echo $page; ?> + + KVAZAR - NS - <?php echo $namespaceValue ? $namespaceValue : $ns; ?> + + + + KVAZAR - PART <?php echo $page; ?> + + KVAZAR - Observe Kevacoin Universe + + + + +
+
+
+ +
+ +
+ <?php echo $ns; ?> +
+ +
+
+
+
+
+
+ +
+
+ +
+ + + <?php echo $item['namehash']; ?> + + + + <?php echo $item['namehash']; ?> + + +
+ + + + +
+ +
+
+
+ in +
+ +
+ +
+
+
+
+
+ + +
+ + newer + + + | + + + older + +
+ +
+
+ + +
+
+
+
KVAZAR Webapp is the content exploring platform for Kevacoin Blockchain.
+
Sources distributed under the MIT License. Ownership of all content belongs to the authors.
+
+ +