Skip to content

Commit 8f9fdc1

Browse files
author
Mario Colque
committed
Merge pull request #57 from matiu/feature/04sync
awesome!
2 parents f2aa5ec + 213a8e5 commit 8f9fdc1

File tree

6 files changed

+201
-167
lines changed

6 files changed

+201
-167
lines changed

app/models/Block.js

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,13 @@ var mongoose = require('mongoose'),
88
RpcClient = require('bitcore/RpcClient').class(),
99
util = require('bitcore/util/util'),
1010
BitcoreBlock= require('bitcore/Block').class(),
11+
Transaction = require('./Transaction'),
12+
async = require('async'),
1113
config = require('../../config/config')
1214
;
1315

16+
var CONCURRENCY = 5;
17+
1418
/**
1519
* Block Schema
1620
*/
@@ -25,7 +29,6 @@ var BlockSchema = new Schema({
2529
unique: true,
2630
},
2731
time: Number,
28-
fromP2P: Boolean,
2932
});
3033

3134
/**
@@ -43,9 +46,18 @@ BlockSchema.path('title').validate(function(title) {
4346
*/
4447

4548
BlockSchema.statics.createTimestamped = function(block, cb) {
49+
50+
var that = this;
4651
var now = Math.round(new Date().getTime() / 1000);
47-
block.time = now;
48-
this.create(block, cb);
52+
53+
var BlockSchema = mongoose.model('Block', BlockSchema);
54+
var newBlock = new that();
55+
newBlock.time = now;
56+
57+
Transaction.createFromArray(block.tx, function(err, inserted_txs) {
58+
if (err) return cb(err);
59+
newBlock.save(cb);
60+
});
4961
};
5062

5163
BlockSchema.statics.load = function(id, cb) {

app/models/Transaction.js

Lines changed: 72 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,12 @@ var TransactionSchema = new Schema({
3030
index: true,
3131
unique: true,
3232
},
33-
processed: {
34-
type: Boolean,
35-
default: false,
36-
index: true,
37-
},
33+
/* TODO?
3834
orphaned: {
3935
type: Boolean,
4036
default: false,
4137
},
38+
*/
4239
time: Number,
4340
});
4441

@@ -71,18 +68,18 @@ TransactionSchema.statics.fromIdWithInfo = function(txid, cb) {
7168
tx = new That();
7269

7370
tx.txid = txid;
74-
tx.queryInfo(function(err, txInfo) {
71+
tx.fillInfo(function(err, txInfo) {
7572

7673
if (!txInfo)
77-
return cb(new Error('TX not found1'));
74+
return cb(new Error('TX not found'));
7875

7976
tx.save(function(err) {
8077
return cb(err,tx);
8178
});
8279
});
8380
}
8481
else {
85-
tx.queryInfo(function(err) {
82+
tx.fillInfo(function(err) {
8683
return cb(err,tx);
8784
});
8885
}
@@ -94,75 +91,79 @@ TransactionSchema.statics.createFromArray = function(txs, next) {
9491
var that = this;
9592
if (!txs) return next();
9693
var mongo_txs = [];
97-
async.forEach(txs,
98-
function(tx, cb) {
99-
var now = Math.round(new Date().getTime() / 1000);
100-
that.create({ txid: tx, time: now }, function(err, new_tx) {
101-
if (err) {
102-
if (err.toString().match(/E11000/)) {
103-
return cb();
104-
}
105-
return cb(err);
106-
}
107-
mongo_txs.push(new_tx);
94+
var now = Math.round(new Date().getTime() / 1000);
95+
96+
async.forEachLimit(txs, CONCURRENCY, function(txid, cb) {
97+
98+
that.explodeTransactionItems( txid, function(err) {
99+
100+
that.create({txid: txid, time: now}, function(err, new_tx) {
101+
102+
//console.log("created:", err, new_tx);
103+
104+
if (err && ! err.toString().match(/E11000/)) return cb(err);
105+
106+
if (new_tx) mongo_txs.push(new_tx);
108107
return cb();
109108
});
110-
},
111-
function(err) {
112-
return next(err, mongo_txs);
113-
}
114-
);
109+
})
110+
},
111+
function(err) {
112+
return next(err, mongo_txs);
113+
});
115114
};
116115

117116

118117
TransactionSchema.statics.explodeTransactionItems = function(txid, cb) {
119118

120-
this.fromIdWithInfo(txid, function(err, t) {
121-
if (err || !t) return cb(err);
119+
this.queryInfo(txid, function(err, info) {
120+
121+
//console.log("INFO",info);
122+
if (err || !info) return cb(err);
122123

123124
var index = 0;
124-
t.info.vin.forEach( function(i){
125+
info.vin.forEach( function(i){
125126
i.n = index++;
126127
});
127128

128-
async.forEachLimit(t.info.vin, CONCURRENCY, function(i, next_in) {
129+
async.forEachLimit(info.vin, CONCURRENCY, function(i, next_in) {
129130
if (i.addr && i.value) {
130131

131132
//console.log("Creating IN %s %d", i.addr, i.valueSat);
132133
TransactionItem.create({
133-
txid : t.txid,
134+
txid : txid,
134135
value_sat : -1 * i.valueSat,
135136
addr : i.addr,
136137
index : i.n,
137-
ts : t.info.time,
138+
ts : info.time,
138139
}, next_in);
139140
}
140141
else {
141142
if ( !i.coinbase ) {
142-
console.log ('TX: %s,%d could not parse INPUT', t.txid, i.n);
143+
console.log ('TX: %s,%d could not parse INPUT', txid, i.n);
143144
}
144145
return next_in();
145146
}
146147
},
147148
function (err) {
148149
if (err) console.log (err);
149-
async.forEachLimit(t.info.vout, CONCURRENCY, function(o, next_out) {
150+
async.forEachLimit(info.vout, CONCURRENCY, function(o, next_out) {
150151

151152
/*
152153
* TODO Support multisigs
153154
*/
154155
if (o.value && o.scriptPubKey && o.scriptPubKey.addresses && o.scriptPubKey.addresses[0]) {
155156
//console.log("Creating OUT %s %d", o.scriptPubKey.addresses[0], o.valueSat);
156157
TransactionItem.create({
157-
txid : t.txid,
158+
txid : txid,
158159
value_sat : o.valueSat,
159160
addr : o.scriptPubKey.addresses[0],
160161
index : o.n,
161-
ts : t.info.time,
162+
ts : info.time,
162163
}, next_out);
163164
}
164165
else {
165-
console.log ('TX: %s,%d could not parse OUTPUT', t.txid, o.n);
166+
console.log ('TX: %s,%d could not parse OUTPUT', txid, o.n);
166167
return next_out();
167168
}
168169
},
@@ -175,22 +176,21 @@ TransactionSchema.statics.explodeTransactionItems = function(txid, cb) {
175176

176177

177178

178-
TransactionSchema.methods.fillInputValues = function (tx, next) {
179+
TransactionSchema.statics.getOutpoints = function (tx, next) {
179180

180181
if (tx.isCoinBase()) return next();
181182

182-
if (! this.rpc) this.rpc = new RpcClient(config.bitcoind);
183+
var rpc = new RpcClient(config.bitcoind);
183184
var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ;
184185

185-
var that = this;
186186
async.forEachLimit(tx.ins, CONCURRENCY, function(i, cb) {
187187

188188
var outHash = i.getOutpointHash();
189189
var outIndex = i.getOutpointIndex();
190190
var outHashBase64 = outHash.reverse().toString('hex');
191191

192192
var c=0;
193-
that.rpc.getRawTransaction(outHashBase64, function(err, txdata) {
193+
rpc.getRawTransaction(outHashBase64, function(err, txdata) {
194194
var txin = new Transaction();
195195
if (err || ! txdata.result) return cb( new Error('Input TX '+outHashBase64+' not found'));
196196

@@ -229,42 +229,40 @@ TransactionSchema.methods.fillInputValues = function (tx, next) {
229229
);
230230
};
231231

232-
TransactionSchema.methods.queryInfo = function (next) {
233232

234-
var that = this;
233+
TransactionSchema.statics.queryInfo = function(txid, cb) {
234+
var that = this;
235235
var network = ( config.network === 'testnet') ? networks.testnet : networks.livenet ;
236-
this.rpc = new RpcClient(config.bitcoind);
237-
236+
var rpc = new RpcClient(config.bitcoind);
238237

239-
this.rpc.getRawTransaction(this.txid, 1, function(err, txInfo) {
240-
if (err) return next(err);
238+
rpc.getRawTransaction(txid, 1, function(err, txInfo) {
239+
if (err) return cb(err);
241240

242-
that.info = txInfo.result;
241+
var info = txInfo.result;
243242

244243
// Transaction parsing
245244
var b = new Buffer(txInfo.result.hex,'hex');
246245
var tx = new Transaction();
247246
tx.parse(b);
248247

249-
that.fillInputValues(tx, function(err) {
248+
that.getOutpoints(tx, function(err) {
249+
if (err) return cb(err);
250250

251251
// Copy TX relevant values to .info
252252

253253
var c = 0;
254-
255-
256254
var valueIn = bignum(0);
257255
var valueOut = bignum(0);
258256

259257
if ( tx.isCoinBase() ) {
260-
that.info.isCoinBase = true;
258+
info.isCoinBase = true;
261259
}
262260
else {
263261
tx.ins.forEach(function(i) {
264262
if (i.value) {
265-
that.info.vin[c].value = util.formatValue(i.value);
263+
info.vin[c].value = util.formatValue(i.value);
266264
var n = util.valueToBigInt(i.value).toNumber();
267-
that.info.vin[c].valueSat = n;
265+
info.vin[c].valueSat = n;
268266
valueIn = valueIn.add( n );
269267

270268
var scriptSig = i.getScript();
@@ -275,11 +273,11 @@ TransactionSchema.methods.queryInfo = function (next) {
275273
var pubKeyHash = util.sha256ripe160(pubKey);
276274
var addr = new Address(network.addressPubkey, pubKeyHash);
277275
var addrStr = addr.toString();
278-
that.info.vin[c].addr = addrStr;
276+
info.vin[c].addr = addrStr;
279277
}
280278
else {
281279
if (i.addrFromOutput)
282-
that.info.vin[c].addr = i.addrFromOutput;
280+
info.vin[c].addr = i.addrFromOutput;
283281
}
284282
}
285283
else {
@@ -294,33 +292,42 @@ TransactionSchema.methods.queryInfo = function (next) {
294292
var n = util.valueToBigInt(i.v).toNumber();
295293
valueOut = valueOut.add(n);
296294

297-
that.info.vout[c].valueSat = n;
295+
info.vout[c].valueSat = n;
298296
c++;
299297
});
300298

301-
that.info.valueOut = valueOut / util.COIN;
299+
info.valueOut = valueOut / util.COIN;
302300

303301
if ( !tx.isCoinBase() ) {
304-
that.info.valueIn = valueIn / util.COIN;
305-
that.info.feeds = (valueIn - valueOut) / util.COIN;
302+
info.valueIn = valueIn / util.COIN;
303+
info.feeds = (valueIn - valueOut) / util.COIN;
306304
}
307305
else {
308-
var reward = BitcoreBlock.getBlockValue(that.info.height) / util.COIN;
309-
that.info.vin[0].reward = reward;
310-
that.info.valueIn = reward;
306+
var reward = BitcoreBlock.getBlockValue(info.height) / util.COIN;
307+
info.vin[0].reward = reward;
308+
info.valueIn = reward;
311309
}
312310

311+
info.size = b.length;
313312

314-
that.info.size = b.length;
313+
return cb(null, info);
314+
});
315+
});
316+
};
315317

316318

317319

318-
return next(err, that.info);
319-
});
320+
TransactionSchema.methods.fillInfo = function(next) {
321+
var that = this;
322+
323+
mongoose.model('Transaction', TransactionSchema).queryInfo(that.txid, function(err, info) {
324+
if (err) return next(err);
325+
326+
that.info = info;
327+
return next();
320328
});
321329
};
322330

323331

324332

325-
326333
module.exports = mongoose.model('Transaction', TransactionSchema);

config/env/test.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ module.exports = {
1212
pass: process.env.BITCOIND_PASS || 'real_mystery',
1313
host: process.env.BITCOIND_HOST || '127.0.0.1',
1414
port: process.env.BITCOIND_PORT || '18332',
15+
keepConnectionAlive: false,
1516
},
1617
network: 'testnet',
1718
}

lib/PeerSync.js

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,21 @@ function spec() {
6969
var block = info.message.block;
7070
var blockHash = coinUtil.formatHashFull(block.calcHash());
7171
console.log('[p2p_sync] Handle block: ' + blockHash);
72+
73+
74+
var tx_hashes = block.txs.map(function(tx) {
75+
return coinUtil.formatHashFull(tx.hash);
76+
});
77+
7278
this.sync.storeBlock({
7379
'hash': blockHash,
74-
'fromP2P': true,
80+
'tx': tx_hashes,
7581
},
7682
function(err) {
7783
if (err) {
7884
console.log('[p2p_sync] Error in handle Block: ' + err);
79-
} else {
80-
// if no errors importing block, import the transactions
81-
var hashes = block.txs.map(function(tx) {
82-
return coinUtil.formatHashFull(tx.hash);
83-
});
84-
self.sync.storeTxs(hashes, function() {});
8585
}
8686
});
87-
8887
};
8988

9089
PeerSync.prototype.handle_connected = function(data) {

0 commit comments

Comments
 (0)