Browse Source

initial commit

main
ghost 3 years ago
parent
commit
72de07196d
  1. 60
      README.md
  2. 126
      chart/index.php
  3. 68
      crawler/blockchain.py
  4. BIN
      database/twister-stat.mwb
  5. 13
      dump/18.12.2021/chart.js
  6. 172
      dump/18.12.2021/index.html
  7. BIN
      media/demo.png

60
README.md

@ -1,2 +1,58 @@
# twister-stat # twister-stat ⠀
Statistic tools for twister network Statistic tools for twister network ⠀
## Blockchain crawler ⠀
Script written in python v2, that scans the blockchain and dump the data to the MySQL database. ⠀
Index contain: ⠀
- block number ⠀
- block hash ⠀
- time created ⠀
- usernames ⠀
### Configuration ⠀
line 5 - MySQL database ⠀
line 13 - blocks per step ⠀
line 31 - twister API ⠀
### Requirements ⠀
`apt install python2 php-mysql mysql-server`
`pip install mysqlclient`
### Running ⠀
`python2 crawler/blockchain.py`
## Charts ⠀
Tools for the data dumped visualization written in PHP ⠀
### Configuration ⠀
line 8 - MySQL database ⠀
### Requirements ⠀
`php-fpm`
`php-curl`
### Running ⠀
create new server instance ⠀
`cd chart`
`php -S localhost:8081`
open in browser ⠀
`http://localhost:8081/index.php`
todo: ⠀
## Static dumps directory ⠀
`dump`

126
chart/index.php

@ -0,0 +1,126 @@
<?php
// Debug level
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
// DB config
define('DB_HOSTNAME', 'localhost');
define('DB_PORT', '3306');
define('DB_DATABASE', 'twister-stat');
define('DB_USERNAME', 'root');
define('DB_PASSWORD', 'password');
// Init DB connection
try {
$db = new PDO('mysql:dbname=' . DB_DATABASE . ';host=' . DB_HOSTNAME . ';port=' . DB_PORT . ';charset=utf8', DB_USERNAME, DB_PASSWORD, [PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES utf8']);
$db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
} catch(PDOException $e) {
trigger_error($e->getMessage());
}
// Get total blocks
$query = $db->query('SELECT COUNT(*) AS `total` FROM `block`');
$blocksTotal = $query->fetch()['total'];
// Get total users
$query = $db->query('SELECT COUNT(*) AS `total` FROM `user`');
$usersTotal = $query->fetch()['total'];
// Get stats
$query = $db->query('SELECT YEAR(FROM_UNIXTIME(`time`)) AS `year`,
MONTH(FROM_UNIXTIME(`time`)) AS `month`,
COUNT(`block`.`blockId`) AS `blocks`,
SUM((SELECT COUNT(`user`.`userId`) FROM `user`
WHERE `user`.`blockId` = `block`.`blockId`
GROUP BY `user`.`blockId`)) AS `users`
FROM `block`
GROUP BY `year`, `month`
ORDER BY `year`, `month`
');
$stats = $query->fetchAll();
?>
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<meta charset="UTF-8" />
<title>twister-stat</title>
</head>
<body>
<canvas id="chart"></canvas>
<script>
const labels = [];
<?php foreach ($stats as $stat) { ?>
labels.push('<?php echo $stat['year']; ?>.<?php echo $stat['month']; ?>');
<?php } ?>
const blocks = [<?php foreach ($stats as $stat) { echo sprintf('%s,', $stat['blocks']); } ?>];
const users = [<?php foreach ($stats as $stat) { echo sprintf('%s,', $stat['users']); } ?>];
const data = {
labels: labels,
datasets: [
{
label: 'blocks',
data: blocks,
borderColor: '#555',
fill: true,
scaleOverride : false,
}, {
label: 'users',
data: users,
borderColor: '#999',
fill: true,
scaleOverride : false,
}
]
};
const config = {
type: 'line',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: "blocks: <?php echo $blocksTotal; ?> | users: <?php echo $usersTotal; ?>"
},
},
interaction: {
intersect: false,
},
scales: {
x: {
display: true,
title: {
display: true
}
},
y: {
display: true,
title: {
display: true,
text: 'Quantity'
}
}
}
},
};
const twisterStat = new Chart(document.getElementById('chart'), config);
</script>
</body>
</html>

68
crawler/blockchain.py

@ -0,0 +1,68 @@
#!/usr/bin/python
import sys, MySQLdb
db = MySQLdb.connect(host="localhost", # your host, usually localhost
user="root", # your username
passwd="password", # your password
db="twister-stat") # name of the data base
cursor = db.cursor()
blocksInStep = 1000000 # blocks processing by the one step
class MyDb:
nextBlock = 0
process = MyDb()
cursor.execute ("SELECT COUNT(*) + 1 AS nextBlock FROM block")
row = cursor.fetchone()
process.nextBlock = row[0]
try:
from bitcoinrpc.authproxy import AuthServiceProxy
except ImportError as exc:
sys.stderr.write("Error: install python-bitcoinrpc (https://github.com/jgarzik/python-bitcoinrpc)\n")
exit(-1)
serverUrl = "http://user:password@127.0.0.1:28332"
if len(sys.argv) > 1:
serverUrl = sys.argv[1]
twister = AuthServiceProxy(serverUrl)
print "blockchain reading..."
while True:
hash = twister.getblockhash(process.nextBlock)
block = twister.getblock(hash)
blocksInStep = blocksInStep - 1
if blocksInStep < 0:
break
print "add block", block["height"]
cursor.execute("INSERT INTO block SET hash = %s, time = %s", (block["hash"], block["time"]))
blockId = db.insert_id()
for userName in block["usernames"]:
print "add user", userName
cursor.execute("INSERT INTO user SET blockId = %s, username = %s", (blockId, userName))
if block.has_key("nextblockhash"):
process.nextBlock = process.nextBlock + 1
else:
print "database is up to date..."
break
db.commit()
db.close()
print "task completed."

BIN
database/twister-stat.mwb

Binary file not shown.

13
dump/18.12.2021/chart.js

File diff suppressed because one or more lines are too long

172
dump/18.12.2021/index.html

@ -0,0 +1,172 @@
<!DOCTYPE html>
<html>
<head>
<script src="chart.js"></script>
<meta charset="UTF-8" />
<title>twister-stat</title>
</head>
<body>
<canvas id="chart"></canvas>
<script>
const labels = [];
labels.push('2013.11');
labels.push('2013.12');
labels.push('2014.1');
labels.push('2014.2');
labels.push('2014.3');
labels.push('2014.4');
labels.push('2014.5');
labels.push('2014.6');
labels.push('2014.7');
labels.push('2014.8');
labels.push('2014.9');
labels.push('2014.10');
labels.push('2014.11');
labels.push('2014.12');
labels.push('2015.1');
labels.push('2015.2');
labels.push('2015.3');
labels.push('2015.4');
labels.push('2015.5');
labels.push('2015.6');
labels.push('2015.7');
labels.push('2015.8');
labels.push('2015.9');
labels.push('2015.10');
labels.push('2015.11');
labels.push('2015.12');
labels.push('2016.1');
labels.push('2016.2');
labels.push('2016.3');
labels.push('2016.4');
labels.push('2016.5');
labels.push('2016.6');
labels.push('2016.7');
labels.push('2016.8');
labels.push('2016.9');
labels.push('2016.10');
labels.push('2016.11');
labels.push('2016.12');
labels.push('2017.1');
labels.push('2017.2');
labels.push('2017.3');
labels.push('2017.4');
labels.push('2017.5');
labels.push('2017.6');
labels.push('2017.7');
labels.push('2017.8');
labels.push('2017.9');
labels.push('2017.10');
labels.push('2017.11');
labels.push('2017.12');
labels.push('2018.1');
labels.push('2018.2');
labels.push('2018.3');
labels.push('2018.4');
labels.push('2018.5');
labels.push('2018.6');
labels.push('2018.7');
labels.push('2018.8');
labels.push('2018.9');
labels.push('2018.10');
labels.push('2018.11');
labels.push('2018.12');
labels.push('2019.1');
labels.push('2019.2');
labels.push('2019.3');
labels.push('2019.4');
labels.push('2019.5');
labels.push('2019.6');
labels.push('2019.7');
labels.push('2019.8');
labels.push('2019.9');
labels.push('2019.10');
labels.push('2019.11');
labels.push('2019.12');
labels.push('2020.1');
labels.push('2020.2');
labels.push('2020.3');
labels.push('2020.4');
labels.push('2020.5');
labels.push('2020.6');
labels.push('2020.7');
labels.push('2020.8');
labels.push('2020.9');
labels.push('2020.10');
labels.push('2020.11');
labels.push('2020.12');
labels.push('2021.1');
labels.push('2021.2');
labels.push('2021.3');
labels.push('2021.4');
labels.push('2021.5');
labels.push('2021.6');
labels.push('2021.7');
labels.push('2021.8');
labels.push('2021.9');
labels.push('2021.10');
labels.push('2021.11');
labels.push('2021.12');
const blocks = [9244,4322,8940,4368,4928,2160,5117,2920,5087,4548,4236,3921,4187,5088,2977,4062,4339,4470,4088,4243,4544,3569,4207,3632,4977,4320,3978,4280,4713,4402,3885,1412,4762,4966,3331,4370,4657,5023,4319,4238,4579,2806,3573,4315,4182,4366,4300,4369,4642,4284,4289,3719,4386,4544,4505,3994,4516,4583,4407,4400,4526,4491,3975,4664,4381,3460,4338,3917,4627,4400,2985,4041,4534,3763,4726,3378,4654,4289,4295,4811,4562,4393,4193,3937,4257,5273,4450,3880,3877,4854,4137,3902,4069,2787,4278,4523,4327,2846,];
const users = [12,537,34286,10633,2060,2019,1236,438,1482,5034,1859,967,2569,1218,5162,7424,1255,391,615,340,240,353,296,173,175,5573,240,196,166,154,94,895,530,69,72,81,63,79,67,60,51,71,50,70,101,45,57,124,62,42,50,38,86,39,55,34,38,40,517,243,31,23,22,23,13,14,10,28,15,19,7,45,14,17,12,14,10,11,10,4,15,3,1,1,8,30,29,8,6,5,5,8,1,,4,,2,3,];
const data = {
labels: labels,
datasets: [
{
label: 'blocks',
data: blocks,
borderColor: '#555',
fill: true,
scaleOverride : false,
}, {
label: 'users',
data: users,
borderColor: '#999',
fill: true,
scaleOverride : false,
}
]
};
const config = {
type: 'line',
data: data,
options: {
responsive: true,
plugins: {
title: {
display: true,
text: "blocks: 420389 | users: 91397"
},
},
interaction: {
intersect: false,
},
scales: {
x: {
display: true,
title: {
display: true
}
},
y: {
display: true,
title: {
display: true,
text: 'Quantity'
}
}
}
},
};
const twisterStat = new Chart(document.getElementById('chart'), config);
</script>
</body>
</html>

BIN
media/demo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Loading…
Cancel
Save