Merge pull request #46 from cmgustavo/feature/get_async_transactions

Feature/get async transactions
This commit is contained in:
Matias Alejo Garcia 2014-01-15 05:42:45 -08:00
commit fad7cb73aa
10 changed files with 157 additions and 76 deletions

View File

@ -103,22 +103,30 @@ $ npm install -g bower
A REST API is provided at /api. The entry points are:
### Blocks
### Block
```
/api/block/[:hash]
/api/block/00000000a967199a2fad0877433c93df785a8d8ce062e5f9b451cd1397bdbf62
```
### Transactions
### Transaction
```
/api/tx/[:txid]
/api/tx/525de308971eabd941b139f46c7198b5af9479325c2395db7f2fb5ae8562556c
```
### Addresses
### Addresse
```
/api/addr/[:addr]
/api/addr/mmvP3mTe53qxHdPqXEvdu8WdC7GfQ2vmx5
```
### Transactions by Block
```
/api/txs/?block=HASH
/api/txs/?block=00000000fa6cf7367e50ad14eb0ca4737131f256fc4c5841fd3c3f140140e6b6
```
### Transactions by Address
```
/api/txs/?address=ADDR
/api/txs/?address=mmhmMNfBiZZ37g1tgg2t8DDbNoEdqKVxAL
## Web Socket API
The web socket API is served using [socket.io](http://socket.io) at:

View File

@ -2,6 +2,9 @@
var Transaction = require('../models/Transaction');
var Block = require('../models/Block');
var Address = require('../models/Address');
var async = require('async');
//, _ = require('lodash');
@ -38,3 +41,50 @@ exports.show = function(req, res) {
}
};
var getTransaction = function(txid, cb) {
Transaction.fromIdWithInfo(txid, function(err, tx) {
if (err) {
console.log(err);
return cb(err);
}
return cb(null, tx.info);
});
};
exports.transactions = function(req, res, next) {
var bId = req.query.block;
var aId = req.query.address;
if (bId) {
Block.fromHashWithInfo(bId, function(err, block) {
if (err && !block) {
console.log(err);
res.status(404).send('Not found');
return next();
}
async.mapSeries(block.info.tx, getTransaction,
function(err, results) {
res.jsonp(results);
});
});
}
else {
var a = Address.new(aId);
a.update(function(err) {
if (err && !a.totalReceivedSat) {
console.log(err);
res.status(404).send('Invalid address');
return next();
}
async.mapSeries(a.transactions, getTransaction,
function(err, results) {
res.jsonp(results);
});
});
}
};

View File

@ -14,11 +14,14 @@ module.exports = function(app) {
app.get('/api/block/:blockHash', blocks.show);
app.param('blockHash', blocks.block);
// Transaction routes
var transactions = require('../app/controllers/transactions');
app.get('/api/tx/:txid', transactions.show);
app.param('txid', transactions.transaction);
app.get('/api/txs', transactions.transactions);
// Address routes
var addresses = require('../app/controllers/addresses');
app.get('/api/addr/:addr', addresses.show);
app.param('addr', addresses.address);

View File

@ -1,22 +1,6 @@
'use strict';
angular.module('mystery.address').controller('AddressController', ['$scope', '$routeParams', '$location', 'Global', 'Address', function ($scope, $routeParams, $location, Global, Address) {
/*
$scope.address = '1JmTTDcksW7A6GN7JnxuXkMAXsVN9zmgm1';
$scope.hash160 = '77ad7d08aaa9cf489ea4e468eaeb892b85f71e27';
$scope.transactions = [
{
hash: '49a1d01759690476dbeec4a8efd969c09c6d4269ea2d88f4d9d4f098f021413c',
time: 1234123445,
amount: 0.3
},
{
hash: 'cce948b422a4d485900fb82e64458720eb89f545af3f07ddf7d18660f9f881e9',
time: 1234123445,
amount: 0.1
}
];
*/
$scope.global = Global;
$scope.findOne = function() {
@ -27,4 +11,5 @@ angular.module('mystery.address').controller('AddressController', ['$scope', '$r
});
};
$scope.params = $routeParams;
}]);

View File

@ -20,4 +20,5 @@ angular.module('mystery.blocks').controller('BlocksController', ['$scope', '$rou
});
};
$scope.params = $routeParams;
}]);

View File

@ -1,6 +1,6 @@
'use strict';
angular.module('mystery.transactions').controller('transactionsController', ['$scope', '$routeParams', '$location', 'Global', 'Transaction', function ($scope, $routeParams, $location, Global, Transaction) {
angular.module('mystery.transactions').controller('transactionsController', ['$scope', '$routeParams', '$location', 'Global', 'Transaction', 'TransactionsByBlock', 'TransactionsByAddress', function ($scope, $routeParams, $location, Global, Transaction, TransactionsByBlock, TransactionsByAddress) {
$scope.global = Global;
$scope.findOne = function() {
@ -10,5 +10,23 @@ angular.module('mystery.transactions').controller('transactionsController', ['$s
$scope.tx = tx;
});
};
$scope.byBlock = function(bId) {
TransactionsByBlock.query({
block: bId
}, function(txs) {
$scope.txs = txs;
});
};
$scope.byAddress = function(aId) {
TransactionsByAddress.query({
address: aId
}, function(txs) {
$scope.txs = txs;
});
};
}]);

View File

@ -20,3 +20,15 @@ angular.module('mystery.transactions').factory('Transaction', ['$resource', func
});
}]);
angular.module('mystery.transactions').factory('TransactionsByBlock', ['$resource', function($resource) {
return $resource('/api/txs', {
block: '@block'
});
}]);
angular.module('mystery.transactions').factory('TransactionsByAddress', ['$resource', function($resource) {
return $resource('/api/txs', {
address: '@address'
});
}]);

View File

@ -5,12 +5,13 @@
<small>Addresses are identifiers which you use to send bitcoins to another person.</small>
</h1>
</div>
<div class="col-lg-9">
<table class="table table-striped">
<tbody>
<div class="row">
<div class="col-lg-9">
<table class="table table-striped">
<tbody>
<tr>
<td>Address</td>
<td><a href="/#!/address/{{address}}">{{address.addrStr}}</a></td>
<td><a href="/#!/address/{{address.addrStr}}">{{address.addrStr}}</a></td>
</tr>
<tr>
<td>Total Received</td>
@ -29,32 +30,35 @@
<td>{{address.txApperances}}</td>
</tr>
</tbody>
</table>
</tbody>
</table>
</div>
<div class="col-lg-3">
<qrcode size="200" data="{{address.addrStr}}"></qrcode>
</div>
</div>
<div class="col-lg-3">
<qrcode size="200" data="{{address.addrStr}}"></qrcode>
</div>
<div class="col-lg-12">
<h3>
Transactions
<small>transactions this address relates to</small>
</h3>
<div data-ng-controller="transactionsController" data-ng-init="byAddress(params.addrStr)">
<h2>Transactions <small>Transactions contained within this block</small></h2>
<table class="table table-striped">
<thead>
<tr>
<th>Transaction Hash</th>
<th>Datetime</th>
<th>Transacted amount</th>
<th>Fee</th>
<th>Transacted Amount</th>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="transaction in address.transactions track by $index">
<td><a href="/#!/tx/{{transaction}}">{{transaction}}</a></td>
<td>--</td>
<td>--</td>
</tr>
<tr data-ng-show="!txs.length">
<td colspan="4" class="text-center">Loading...</td>
</tr>
<tr data-ng-repeat="tx in txs">
<td><a href="/#!/tx/{{tx.txid}}">{{tx.txid}}</a></td>
<td>{{tx.time * 1000 | date:'medium'}}</td>
<td>{{tx.feeds}}</td>
<td>{{tx.valueOut}}</td>
</tr>
</tbody>
</table>
</div>

View File

@ -16,18 +16,6 @@
<td>Number Of Transactions</td>
<td>{{block.tx.length}}<td>
</tr>
<tr>
<td>Output Total</td>
<td>--</td>
</tr>
<tr>
<td>Estimated Transaction Volume</td>
<td>--</td>
</tr>
<tr>
<td>Transaction Fees</td>
<td>--</td>
</tr>
<tr>
<td>Height</td>
<td>{{block.height}}</td>
@ -36,10 +24,6 @@
<td>Timestamp</td>
<td>{{block.time * 1000 | date:'medium'}}</td>
</tr>
<tr>
<td>Relayed By</td>
<td>--</td>
</tr>
<tr>
<td>Difficulty</td>
<td>{{block.difficulty}}</td>
@ -96,17 +80,28 @@
</div>
</div>
<h2>Transactions <small>Transactions contained within this block</small></h2>
<table class="table table-striped">
<thead>
<tr>
<th>Hash</th>
<div data-ng-controller="transactionsController" data-ng-init="byBlock(params.blockHash)">
<h2>Transactions <small>Transactions contained within this block</small></h2>
<table class="table table-striped">
<thead>
<tr>
<th>Transaction Hash</th>
<th>Datetime</th>
<th>Fee</th>
<th>Transacted Amount</th>
</tr>
</thead>
<tbody>
<tr data-ng-show="!txs.length">
<td colspan="4" class="text-center">Loading...</td>
</tr>
</thead>
<tbody>
<tr data-ng-repeat="tx in block.tx track by $index">
<td><a href="/#!/tx/{{tx}}">{{tx}}</a></td>
</tr>
</tbody>
</table>
<tr data-ng-repeat="tx in txs">
<td><a href="/#!/tx/{{tx.txid}}">{{tx.txid}}</a></td>
<td>{{tx.time * 1000 | date:'medium'}}</td>
<td>{{tx.feeds}}</td>
<td>{{tx.valueOut}}</td>
</tr>
</tbody>
</table>
</div>
</section>

View File

@ -23,13 +23,18 @@
<td width="45%">
<ul class="list-unstyled" data-ng-repeat="vin in tx.vin" data-ng-show="!tx.isCoinBase">
<li>
<span data-ng-show="!vin.addr">No parse address</span>
<a data-ng-show="vin.addr" href="/#!/address/{{vin.addr}}">{{vin.addr}}</a>
<span class="pull-right badge">{{vin.value}} BTC</span>
<span data-ng-show="!vin.addr"><a href="/#!/tx/{{vin.txid}}">Address could not be parsed, {{vin.vout}}</a></span>
<a data-ng-show="vin.addr" href="/#!/address/{{vin.addr}}">{{vin.addr}}</a>
<span class="pull-right badge">{{vin.value}} BTC</span>
</li>
</ul>
<div data-ng-show="tx.isCoinBase">
No Inputs (Newly Generated isCoinBasens)
<ul class="list-unstyled" data-ng-repeat="vinn in tx.vin">
<li>
No Inputs (Newly Generated isCoinBasens)
<span class="pull-right badge">{{vinn.reward}} BTC</span>
</li>
</ul>
</div>
</td>
<td width="10%" style="text-align: center;"><span class="glyphicon glyphicon-chevron-right">&nbsp;</span></td>
@ -82,8 +87,8 @@
<td>{{tx.time * 1000|date:'medium'}}</td>
</tr>
<tr>
<td>Reward From Block</td>
<td><a href="/#!/block/{{tx.blockhash}}">Need height to show (it links to block page)</a></td>
<td>Block</td>
<td><a href="/#!/block/{{tx.blockhash}}">Block</a></td>
</tr>
</tbody>
</table>