diff --git a/DevelopmentNotes.md b/DevelopmentNotes.md index 3c0ef67..6049511 100644 --- a/DevelopmentNotes.md +++ b/DevelopmentNotes.md @@ -30,3 +30,6 @@ other - mantain connection with wallet using websockets - num address evolution throught time + +- fix error in exploreBlockchain.go, when getting the tx.Vin +tx 8f04960da36beaa928b9693f7dca4afae5a6122bb6874d409a1156e4c6c55024 has 4 vin, but exploreBlockchain is only getting the first diff --git a/goBlockchainDataAnalysis b/goBlockchainDataAnalysis index ddaf02e..2cf7d51 100755 Binary files a/goBlockchainDataAnalysis and b/goBlockchainDataAnalysis differ diff --git a/serverRoutes.go b/serverRoutes.go index 03d6063..44cd036 100644 --- a/serverRoutes.go +++ b/serverRoutes.go @@ -70,6 +70,18 @@ var routes = Routes{ "/address/network/{address}", AddressNetwork, }, + Route{ + "BlockSankey", + "GET", + "/block/{height}/sankey", + BlockSankey, + }, + Route{ + "TxSankey", + "GET", + "/tx/{txid}/sankey", + TxSankey, + }, Route{ "AddressSankey", "GET", @@ -275,6 +287,139 @@ func AddressNetwork(w http.ResponseWriter, r *http.Request) { fmt.Fprintln(w, string(jNetwork)) } } +func BlockSankey(w http.ResponseWriter, r *http.Request) { + ipFilter(w, r) + vars := mux.Vars(r) + var heightString string + heightString = vars["height"] + height, err := strconv.ParseInt(heightString, 10, 64) + if err != nil { + fmt.Fprintln(w, "not valid height") + } else { + block := BlockModel{} + err := blockCollection.Find(bson.M{"height": height}).One(&block) + + txs := []TxModel{} + err = txCollection.Find(bson.M{"blockheight": heightString}).All(&txs) + block.Txs = txs + + var nodesCount int + mapNodeK := make(map[string]int) + var sankey SankeyModel + for _, tx := range block.Txs { + var sankeyNodeA SankeyNodeModel + sankeyNodeA.Node = nodesCount + mapNodeK["tx"] = nodesCount + nodesCount++ + sankeyNodeA.Name = "tx" + sankey.Nodes = append(sankey.Nodes, sankeyNodeA) + + for _, vin := range tx.Vin { + var sankeyNode SankeyNodeModel + sankeyNode.Node = nodesCount + mapNodeK[vin.Address] = nodesCount + nodesCount++ + sankeyNode.Name = vin.Address + sankey.Nodes = append(sankey.Nodes, sankeyNode) + + var sankeyLink SankeyLinkModel + sankeyLink.Source = mapNodeK[vin.Address] + sankeyLink.Target = mapNodeK["tx"] + sankeyLink.Value = vin.Amount + fmt.Println(sankeyLink) + sankey.Links = append(sankey.Links, sankeyLink) + fmt.Println(sankey.Links) + } + + for _, vout := range tx.Vout { + var sankeyNode SankeyNodeModel + sankeyNode.Node = nodesCount + mapNodeK[vout.Address] = nodesCount + nodesCount++ + sankeyNode.Name = vout.Address + sankey.Nodes = append(sankey.Nodes, sankeyNode) + + var sankeyLink SankeyLinkModel + sankeyLink.Source = mapNodeK["tx"] + sankeyLink.Target = mapNodeK[vout.Address] + sankeyLink.Value = vout.Value + fmt.Println(sankeyLink) + sankey.Links = append(sankey.Links, sankeyLink) + } + + } + + fmt.Println("Sankey generated") + fmt.Println(sankey) + + //convert []resp struct to json + jsonSankey, err := json.Marshal(sankey) + check(err) + + fmt.Fprintln(w, string(jsonSankey)) + } +} +func TxSankey(w http.ResponseWriter, r *http.Request) { + ipFilter(w, r) + vars := mux.Vars(r) + txid := vars["txid"] + if txid == "undefined" { + fmt.Fprintln(w, "not valid height") + } else { + + tx := TxModel{} + err := txCollection.Find(bson.M{"txid": txid}).One(&tx) + + var nodesCount int + mapNodeK := make(map[string]int) + var sankey SankeyModel + var sankeyNodeA SankeyNodeModel + sankeyNodeA.Node = nodesCount + mapNodeK["tx"] = nodesCount + nodesCount++ + sankeyNodeA.Name = "tx" + sankey.Nodes = append(sankey.Nodes, sankeyNodeA) + + fmt.Println(tx.Vin) + for _, vin := range tx.Vin { + var sankeyNode SankeyNodeModel + sankeyNode.Node = nodesCount + mapNodeK[vin.Address] = nodesCount + nodesCount++ + sankeyNode.Name = vin.Address + sankey.Nodes = append(sankey.Nodes, sankeyNode) + + var sankeyLink SankeyLinkModel + sankeyLink.Source = mapNodeK[vin.Address] + sankeyLink.Target = mapNodeK["tx"] + sankeyLink.Value = vin.Amount + sankey.Links = append(sankey.Links, sankeyLink) + } + + for _, vout := range tx.Vout { + var sankeyNode SankeyNodeModel + sankeyNode.Node = nodesCount + mapNodeK[vout.Address] = nodesCount + nodesCount++ + sankeyNode.Name = vout.Address + sankey.Nodes = append(sankey.Nodes, sankeyNode) + + var sankeyLink SankeyLinkModel + sankeyLink.Source = mapNodeK["tx"] + sankeyLink.Target = mapNodeK[vout.Address] + sankeyLink.Value = vout.Value + sankey.Links = append(sankey.Links, sankeyLink) + } + + fmt.Println("Sankey generated") + + //convert []resp struct to json + jsonSankey, err := json.Marshal(sankey) + check(err) + + fmt.Fprintln(w, string(jsonSankey)) + } +} func AddressSankey(w http.ResponseWriter, r *http.Request) { ipFilter(w, r) vars := mux.Vars(r) diff --git a/web/views/block/block.html b/web/views/block/block.html index fea5f41..2d4e408 100644 --- a/web/views/block/block.html +++ b/web/views/block/block.html @@ -15,10 +15,13 @@
-

Block evolution

+

Block transactions flow

-
- +
+
+ + +
diff --git a/web/views/block/block.js b/web/views/block/block.js index ccec46b..020648c 100644 --- a/web/views/block/block.js +++ b/web/views/block/block.js @@ -1,6 +1,6 @@ 'use strict'; -angular.module('app.block', ['ngRoute']) +angular.module('app.block', ['ngRoute', 'ngSankey']) .config(['$routeProvider', function($routeProvider) { $routeProvider.when('/block/:height', { @@ -18,4 +18,34 @@ angular.module('app.block', ['ngRoute']) }, function(data, status, headers, config) { console.log('data error'); }); + + + //Sankey + $scope.options = { + chart: '#sankeyChart', + width: 700, + height: 280, + margin: {top: 1, right: 1, bottom: 6, left: 1}, + node: {width: 15, padding :10, showValue: false}, + value: {format: ',.0f', unit : ''}, + dynamicLinkColor: true, + trafficInLinks: true + }; + $scope.data={ + nodes: [], + links: [] + }; + $http.get(urlapi + 'block/' + $routeParams.height + '/sankey') + .then(function(data, status, headers, config) { + console.log('data success'); + console.log(data); + $scope.data.nodes = data.data.nodes; + $scope.data.links = data.data.links; + console.log($scope.data); + d3.selectAll("svg > *").remove(); + let chart = new d3.sankeyChart(data.data, $scope.options); + //$scope.data = data.data; + }, function(data, status, headers, config) { + console.log('data error'); + }); }); diff --git a/web/views/tx/tx.html b/web/views/tx/tx.html index 76fa3a4..15d3859 100644 --- a/web/views/tx/tx.html +++ b/web/views/tx/tx.html @@ -45,3 +45,18 @@
+
+
+
+
+

Transaction flow

+
+
+
+ + +
+
+
+
+
diff --git a/web/views/tx/tx.js b/web/views/tx/tx.js index c66e791..5336e24 100644 --- a/web/views/tx/tx.js +++ b/web/views/tx/tx.js @@ -18,4 +18,32 @@ angular.module('app.tx', ['ngRoute']) }, function(data, status, headers, config) { console.log('data error'); }); + //Sankey + $scope.options = { + chart: '#sankeyChart', + width: 1000, + height: 280, + margin: {top: 1, right: 1, bottom: 6, left: 1}, + node: {width: 15, padding :10, showValue: false}, + value: {format: ',.0f', unit : ''}, + dynamicLinkColor: true, + trafficInLinks: true + }; + $scope.data={ + nodes: [], + links: [] + }; + $http.get(urlapi + 'tx/' + $routeParams.txid + '/sankey') + .then(function(data, status, headers, config) { + console.log('data success'); + console.log(data); + $scope.data.nodes = data.data.nodes; + $scope.data.links = data.data.links; + console.log($scope.data); + d3.selectAll("svg > *").remove(); + let chart = new d3.sankeyChart(data.data, $scope.options); + //$scope.data = data.data; + }, function(data, status, headers, config) { + console.log('data error'); + }); });