mirror of
https://github.com/arnaucube/goBlockchainDataAnalysis.git
synced 2026-02-07 03:36:44 +01:00
fixed generation of tx and address relations structure. Improved webapp layout. Not finished addressTree generation algorithm
This commit is contained in:
31
README.md
31
README.md
@@ -2,21 +2,10 @@
|
||||
blockchain data analysis, written in Go
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
### Install
|
||||
1. Nodejs & NPM https://nodejs.org/
|
||||
1. Nodejs & NPM https://nodejs.org/ --> to serve the web, not necessary if the web files are in a webserver
|
||||
2. MongoDB https://www.mongodb.com/
|
||||
3. Faircoin wallet https://download.faircoin.world/
|
||||
3. Faircoin wallet https://download.faircoin.world/, or the Cryptocurrency desired wallet
|
||||
4. goBlockchainDataAnalysis https://github.com/arnaucode/goBlockchainDataAnalysis
|
||||
|
||||
### Configure
|
||||
@@ -54,6 +43,7 @@ sudo service mongod start
|
||||
```
|
||||
./faircoind -txindex -reindex-chainstate
|
||||
```
|
||||
Wait until the entire blockchain is downloaded.
|
||||
|
||||
3. Run explorer, to fill the database
|
||||
```
|
||||
@@ -80,3 +70,18 @@ Webapp will run on 127.0.0.1:8080
|
||||
- Frontend
|
||||
- AngularJS https://angularjs.org/
|
||||
- Angular-Bootstrap-Material https://tilwinjoy.github.io/angular-bootstrap-material
|
||||
|
||||
|
||||
### Some screenshots
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/mgo.v2/bson"
|
||||
)
|
||||
|
||||
@@ -19,7 +21,6 @@ func upTree(address string, network NetworkModel) NetworkModel {
|
||||
edges := []EdgeModel{}
|
||||
err = edgeCollection.Find(bson.M{"to": address}).All(&edges)
|
||||
check(err)
|
||||
|
||||
for _, e := range edges {
|
||||
if edgeInEdges(network.Edges, e) == false {
|
||||
network.Edges = append(network.Edges, e)
|
||||
@@ -31,6 +32,7 @@ func upTree(address string, network NetworkModel) NetworkModel {
|
||||
check(err)
|
||||
*/
|
||||
|
||||
fmt.Println(e.From + " - " + e.To)
|
||||
if e.From != e.To {
|
||||
upNetwork = upTree(e.From, network)
|
||||
for _, upN := range upNetwork.Nodes {
|
||||
|
||||
@@ -16,13 +16,13 @@ func timeToDate(blockTime int64) string {
|
||||
panic(err)
|
||||
}
|
||||
tm := time.Unix(i, 0)
|
||||
fmt.Println(tm)
|
||||
//fmt.Println(tm)
|
||||
stringDate := tm.String()
|
||||
fmt.Println(stringDate)
|
||||
//fmt.Println(stringDate)
|
||||
return stringDate
|
||||
}
|
||||
func hourAnalysis(e EdgeModel, blockTime int64) {
|
||||
fmt.Println(blockTime)
|
||||
//fmt.Println(blockTime)
|
||||
date := timeToDate(blockTime)
|
||||
date_hour := strings.Split(date, " ")[1]
|
||||
hour := strings.Split(date_hour, ":")[0]
|
||||
|
||||
@@ -26,7 +26,7 @@ func explore(client *btcrpcclient.Client, blockHash string) {
|
||||
block, err := client.GetBlockVerbose(bh)
|
||||
check(err)
|
||||
|
||||
if block.Height > 160 {
|
||||
if block.Height > 0 {
|
||||
for k, txHash := range block.Tx {
|
||||
if k > 0 {
|
||||
realBlocks++
|
||||
@@ -42,12 +42,25 @@ func explore(client *btcrpcclient.Client, blockHash string) {
|
||||
tx, err := client.GetRawTransactionVerbose(th)
|
||||
check(err)
|
||||
|
||||
//save Tx
|
||||
var nTx NodeModel
|
||||
nTx.Id = txHash
|
||||
nTx.Label = txHash
|
||||
nTx.Title = txHash
|
||||
nTx.Group = strconv.FormatInt(block.Height, 10)
|
||||
nTx.Value = 1
|
||||
nTx.Shape = "square"
|
||||
nTx.Type = "tx"
|
||||
saveNode(nodeCollection, nTx)
|
||||
|
||||
var originAddresses []string
|
||||
var outputAddresses []string
|
||||
var outputAmount []float64
|
||||
for _, Vo := range tx.Vout {
|
||||
//if Vo.Value > 0 {
|
||||
for _, outputAddr := range Vo.ScriptPubKey.Addresses {
|
||||
outputAddresses = append(outputAddresses, outputAddr)
|
||||
outputAmount = append(outputAmount, Vo.Value)
|
||||
var n2 NodeModel
|
||||
n2.Id = outputAddr
|
||||
n2.Label = outputAddr
|
||||
@@ -55,6 +68,7 @@ func explore(client *btcrpcclient.Client, blockHash string) {
|
||||
n2.Group = strconv.FormatInt(block.Height, 10)
|
||||
n2.Value = 1
|
||||
n2.Shape = "dot"
|
||||
n2.Type = "address"
|
||||
saveNode(nodeCollection, n2)
|
||||
}
|
||||
//}
|
||||
@@ -71,25 +85,35 @@ func explore(client *btcrpcclient.Client, blockHash string) {
|
||||
n1.Id = originAddr
|
||||
n1.Label = originAddr
|
||||
n1.Title = originAddr
|
||||
n1.Group = string(block.Height)
|
||||
n1.Group = strconv.FormatInt(block.Height, 10)
|
||||
n1.Value = 1
|
||||
n1.Shape = "dot"
|
||||
n1.Type = "address"
|
||||
saveNode(nodeCollection, n1)
|
||||
|
||||
for _, outputAddr := range outputAddresses {
|
||||
var e EdgeModel
|
||||
e.From = originAddr
|
||||
e.To = outputAddr
|
||||
e.Label = txVi.Vout[Vi.Vout].Value
|
||||
e.Txid = tx.Txid
|
||||
e.Arrows = "to"
|
||||
e.BlockHeight = block.Height
|
||||
saveEdge(edgeCollection, e)
|
||||
for k, outputAddr := range outputAddresses {
|
||||
var eIn EdgeModel
|
||||
eIn.From = originAddr
|
||||
eIn.To = txHash
|
||||
eIn.Label = txVi.Vout[Vi.Vout].Value
|
||||
eIn.Txid = tx.Txid
|
||||
eIn.Arrows = "to"
|
||||
eIn.BlockHeight = block.Height
|
||||
saveEdge(edgeCollection, eIn)
|
||||
|
||||
var eOut EdgeModel
|
||||
eOut.From = txHash
|
||||
eOut.To = outputAddr
|
||||
eOut.Label = outputAmount[k]
|
||||
eOut.Txid = tx.Txid
|
||||
eOut.Arrows = "to"
|
||||
eOut.BlockHeight = block.Height
|
||||
saveEdge(edgeCollection, eOut)
|
||||
|
||||
//date analysis
|
||||
//dateAnalysis(e, tx.Time)
|
||||
//hour analysis
|
||||
hourAnalysis(e, tx.Time)
|
||||
hourAnalysis(eIn, tx.Time)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -22,6 +22,7 @@ type NodeModel struct {
|
||||
Group string `json:"group"`
|
||||
Value int `json:"value"`
|
||||
Shape string `json:"shape"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
||||
type EdgeModel struct {
|
||||
|
||||
@@ -26,6 +26,12 @@ var routes = Routes{
|
||||
"/alladdresses",
|
||||
AllAddresses,
|
||||
},
|
||||
Route{
|
||||
"GetLastAddr",
|
||||
"Get",
|
||||
"/lastaddr",
|
||||
GetLastAddr,
|
||||
},
|
||||
Route{
|
||||
"GetLastTx",
|
||||
"Get",
|
||||
@@ -93,7 +99,7 @@ func AllAddresses(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fmt.Fprintln(w, string(jsonNodes))
|
||||
}
|
||||
func GetLastTx(w http.ResponseWriter, r *http.Request) {
|
||||
func GetLastAddr(w http.ResponseWriter, r *http.Request) {
|
||||
ipFilter(w, r)
|
||||
|
||||
nodes := []NodeModel{}
|
||||
@@ -106,6 +112,19 @@ func GetLastTx(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
fmt.Fprintln(w, string(jNodes))
|
||||
}
|
||||
func GetLastTx(w http.ResponseWriter, r *http.Request) {
|
||||
ipFilter(w, r)
|
||||
|
||||
edges := []EdgeModel{}
|
||||
err := edgeCollection.Find(bson.M{}).Limit(10).Sort("-$natural").All(&edges)
|
||||
check(err)
|
||||
|
||||
//convert []resp struct to json
|
||||
jsonData, err := json.Marshal(edges)
|
||||
check(err)
|
||||
|
||||
fmt.Fprintln(w, string(jsonData))
|
||||
}
|
||||
func AddressNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
ipFilter(w, r)
|
||||
|
||||
@@ -125,7 +144,6 @@ func AddressNetwork(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
func AddressSankey(w http.ResponseWriter, r *http.Request) {
|
||||
ipFilter(w, r)
|
||||
|
||||
vars := mux.Vars(r)
|
||||
address := vars["address"]
|
||||
if address == "undefined" {
|
||||
|
||||
39
web/css/style.css
Normal file
39
web/css/style.css
Normal file
@@ -0,0 +1,39 @@
|
||||
#mynetwork {
|
||||
/*background: black;*/
|
||||
}
|
||||
|
||||
.o-height600 {
|
||||
max-height: 600px!important;
|
||||
}
|
||||
|
||||
.o-height500 {
|
||||
max-height: 500px!important;
|
||||
}
|
||||
|
||||
.o-height300 {
|
||||
max-height: 300px!important;
|
||||
}
|
||||
|
||||
.o-overflowScroll {
|
||||
/*max-height: 600px;*/
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.o-mouseoverGrey {
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.o-mouseoverGrey:hover {
|
||||
background: #cccccc;
|
||||
}
|
||||
|
||||
|
||||
/* sidebar */
|
||||
.o_sidebar a{
|
||||
color: white;
|
||||
}
|
||||
.o_sidebar a:hover{
|
||||
background: #9E9E9E!important;
|
||||
color: white;
|
||||
}
|
||||
@@ -14,48 +14,25 @@
|
||||
<link rel="stylesheet" href="bower_components/components-font-awesome/css/font-awesome.min.css">
|
||||
|
||||
|
||||
<link href="css/style.css" rel="stylesheet">
|
||||
<link href="css/colors.css" rel="stylesheet">
|
||||
|
||||
<!-- IF using Sass (run gulp sass first), then uncomment below and remove the CSS includes above
|
||||
<link href="css/ionic.app.css" rel="stylesheet">
|
||||
-->
|
||||
<style type="text/css">
|
||||
#mynetwork {
|
||||
/*background: black;*/
|
||||
}
|
||||
|
||||
.o-height600 {
|
||||
max-height: 600px!important;
|
||||
}
|
||||
|
||||
.o-height500 {
|
||||
max-height: 500px!important;
|
||||
}
|
||||
|
||||
.o-height300 {
|
||||
max-height: 300px!important;
|
||||
}
|
||||
|
||||
.o-overflowScroll {
|
||||
/*max-height: 600px;*/
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.o-mouseoverGrey {
|
||||
background: white;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.o-mouseoverGrey:hover {
|
||||
background: #cccccc;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body ng-app="webApp">
|
||||
<body ng-app="webApp" style="overflow-x:scroll;">
|
||||
<div ng-include="'views/navbar.html'"></div>
|
||||
|
||||
<div class="row" style="padding-right:10px;">
|
||||
<div class="col-md-2">
|
||||
<div ng-include="'views/sidebar.html'"></div>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
<div ng-view></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">All addresses</h3>
|
||||
@@ -25,5 +24,4 @@
|
||||
<div id="mynetwork" style="height:800px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-sm-12">
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Tx for each Hour</h3>
|
||||
</div>
|
||||
@@ -9,5 +8,4 @@
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="panel">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Last addresses</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="max-height: 500px;overflow-y: scroll;">
|
||||
<div class="panel-body" style="max-height: 300px;overflow-y: scroll;">
|
||||
<div class="list-group-item" ng-repeat="node in addresses">
|
||||
<div class="row-content">
|
||||
<p class="list-group-item-text">{{node.id}}</p>
|
||||
@@ -13,36 +12,15 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="panel">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Last transactions</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="max-height: 500px;overflow-y: scroll;">
|
||||
<table class="table table-striped table-hover ">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Hash</th>
|
||||
<th>From</th>
|
||||
<th>Date</th>
|
||||
<th>nºSubtx</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tx in txs">
|
||||
<td>
|
||||
txhash
|
||||
</td>
|
||||
<td>{{tx.f}}</td>
|
||||
<td>{{tx.date}}</td>
|
||||
<td>{{tx.subtx.length}}</td>
|
||||
<td><a ng-href="#!/tx/{{tx.id}}">View</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<h3 class="panel-title">Tx/Hour</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<canvas id="line" class="chart chart-line" chart-data="data" chart-labels="labels">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
@@ -51,7 +29,47 @@
|
||||
<h3 class="panel-title">Other</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
</div>
|
||||
<canvas id="doughnut" class="chart chart-doughnut" chart-data="data" chart-labels="labels">
|
||||
</canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12">
|
||||
<div class="panel">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Last Tx with amount</h3>
|
||||
</div>
|
||||
<div class="panel-body" style="max-height: 350px;overflow-y: scroll;">
|
||||
<table class="table table-striped table-hover">
|
||||
<colgroup>
|
||||
<col class="col-md-2">
|
||||
<col class="col-md-2">
|
||||
<col class="col-md-2">
|
||||
<col class="col-md-2">
|
||||
<col class="col-md-2">
|
||||
<col class="col-md-2">
|
||||
</colgroup>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>BlockHeight</th>
|
||||
<th>From</th>
|
||||
<th>To</th>
|
||||
<th>Amount</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="tx in txs">
|
||||
<td>{{tx.blockheight}}</td>
|
||||
<td style="max-width:100px;overflow-x:hidden;">{{tx.from}}</td>
|
||||
<td style="max-width:100px;overflow-x:hidden;">{{tx.to}}</td>
|
||||
<td>{{tx.label}}</td>
|
||||
<td><a ng-href="#!/tx/{{tx.id}}">View</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,16 +2,17 @@
|
||||
|
||||
angular.module('app.main', ['ngRoute'])
|
||||
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
.config(['$routeProvider', function($routeProvider) {
|
||||
$routeProvider.when('/main', {
|
||||
templateUrl: 'views/main/main.html',
|
||||
controller: 'MainCtrl'
|
||||
});
|
||||
}])
|
||||
}])
|
||||
|
||||
.controller('MainCtrl', function($scope, $http) {
|
||||
.controller('MainCtrl', function($scope, $http) {
|
||||
//last addr
|
||||
$scope.addresses = [];
|
||||
$http.get(urlapi + 'lasttx')
|
||||
$http.get(urlapi + 'lastaddr')
|
||||
.then(function(data, status, headers, config) {
|
||||
console.log('data success');
|
||||
console.log(data);
|
||||
@@ -20,4 +21,30 @@ angular.module('app.main', ['ngRoute'])
|
||||
}, function(data, status, headers, config) {
|
||||
console.log('data error');
|
||||
});
|
||||
});
|
||||
|
||||
//last tx
|
||||
$scope.txs = [];
|
||||
$http.get(urlapi + 'lasttx')
|
||||
.then(function(data, status, headers, config) {
|
||||
console.log('data success');
|
||||
console.log(data);
|
||||
|
||||
$scope.txs = data.data;
|
||||
}, function(data, status, headers, config) {
|
||||
console.log('data error');
|
||||
});
|
||||
|
||||
//date analysis
|
||||
$scope.data = [];
|
||||
$scope.labels = [];
|
||||
$http.get(urlapi + 'houranalysis')
|
||||
.then(function(data, status, headers, config) {
|
||||
console.log('data success');
|
||||
console.log(data);
|
||||
|
||||
$scope.data = data.data.data;
|
||||
$scope.labels = data.data.labels;
|
||||
}, function(data, status, headers, config) {
|
||||
console.log('data error');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<li><a href="#!/network">Network</a></li>
|
||||
<li><a href="#!/addressNetwork">Address Network</a></li>
|
||||
<li><a href="#!/sankey">Sankey diagram</a></li>
|
||||
<li class="active"><a href="#!/dateAnalysis">Date Analysis</a></li>
|
||||
<li><a href="#!/dateAnalysis">Date Analysis</a></li>
|
||||
<li><a href="javascript:void(0)">Timeline</a></li>
|
||||
</ul>
|
||||
<form class="navbar-form navbar-left">
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-sm-3">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Nodes</h3>
|
||||
@@ -28,5 +27,4 @@
|
||||
<div id="mynetwork" style="height:500px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">All addresses</h3>
|
||||
@@ -17,7 +16,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<div class="col-sm-8">
|
||||
<div class="panel-heading c_blueGrey300">
|
||||
<h3 class="panel-title">Sankey - address {{selectedAddress}}</h3>
|
||||
</div>
|
||||
@@ -30,5 +29,4 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
34
web/views/sidebar.html
Normal file
34
web/views/sidebar.html
Normal file
@@ -0,0 +1,34 @@
|
||||
<div class="c_grey600" style="height:800px;margin-top:-20px;">
|
||||
<ul class="nav o_sidebar">
|
||||
<li>
|
||||
<a href="#!/main">
|
||||
<i class="fa fa-home" aria-hidden="true"></i> Main
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#!/network">
|
||||
<i class="fa fa-chain" aria-hidden="true"></i> Network
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#!/addressNetwork">
|
||||
<i class="fa fa-sitemap" aria-hidden="true"></i> Address Network
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#!/sankey">
|
||||
<i class="fa fa-code-fork" aria-hidden="true"></i> Sankey diagram
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#!/dateAnalysis">
|
||||
<i class="fa fa-bar-chart" aria-hidden="true"></i> Date Analysis
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:void(0)">
|
||||
<i class="fa fa-area-chart" aria-hidden="true"></i> Timeline
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
Reference in New Issue
Block a user