lib: JSON serialization for everyone

This commit is contained in:
Fedor Indutny 2014-05-06 21:33:05 +04:00
parent b98be4b388
commit a6be7bf5f7
8 changed files with 163 additions and 30 deletions

View File

@ -1,3 +1,4 @@
var assert = require('assert');
var util = require('util');
var EventEmitter = require('events').EventEmitter;
@ -27,17 +28,13 @@ function Chain(options) {
count: 0
};
this.index = {
bloom: new bcoin.bloom(28 * 1024 * 1024, 33, 0xdeadbee0),
hashes: preload.hashes.slice(),
ts: preload.ts.slice()
bloom: null,
hashes: [],
ts: []
};
this.request = new utils.RequestCache();
if (this.index.hashes.length === 0)
this.add(new bcoin.block(constants.genesis));
for (var i = 0; i < this.index.hashes.length; i++)
this.index.bloom.add(this.index.hashes[i], 'hex');
this.fromJSON(preload);
}
util.inherits(Chain, EventEmitter);
module.exports = Chain;
@ -69,7 +66,11 @@ Chain.prototype.addIndex = function addIndex(hash, ts) {
if (this.probeIndex(hash, ts))
return;
var pos = utils.binaryInsert(this.index.ts, ts, compareTs);
var pos = utils.binaryInsert(this.index.ts, ts, compareTs, true);
if (pos <= this.index.ts.length - 1000)
return;
this.index.ts.splice(pos, 0, ts);
this.index.hashes.splice(pos, 0, hash);
this.index.bloom.add(hash, 'hex');
};
@ -216,3 +217,45 @@ Chain.prototype.hashesInRange = function hashesInRange(start, end) {
Chain.prototype.getLast = function getLast() {
return this.index.hashes[this.index.hashes.length - 1];
};
Chain.prototype.toJSON = function toJSON() {
// Keep only last 1000 consequent blocks, use every 50th for older
var last = {
hashes: this.index.hashes.slice(-1000),
ts: this.index.ts.slice(-1000)
};
var first = {
hashes: [],
ts: []
};
var len = (this.index.hashes.length - 1000) - this.index.hashes.length % 50;
for (var i = 0; i < len; i += 50) {
first.hashes.push(this.index.hashes[i]);
first.ts.push(this.index.ts[i]);
}
return {
v: 1,
type: 'chain',
hashes: first.hashes.concat(last.hashes),
ts: first.ts.concat(last.ts),
};
};
Chain.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.v, 1);
assert.equal(json.type, 'chain');
this.index.hashes = json.hashes.slice();
this.index.ts = json.ts.slice();
if (this.index.bloom)
this.index.bloom.reset();
else
this.index.bloom = new bcoin.bloom(28 * 1024 * 1024, 33, 0xdeadbee0);
if (this.index.hashes.length === 0)
this.add(new bcoin.block(constants.genesis));
for (var i = 0; i < this.index.hashes.length; i++)
this.index.bloom.add(this.index.hashes[i], 'hex');
};

View File

@ -532,6 +532,22 @@ Pool.prototype.sendTX = function sendTX(tx) {
return e;
};
Pool.prototype.toJSON = function toJSON() {
return {
v: 1,
type: 'pool',
chain: this.chain.toJSON()
};
};
Pool.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.v, 1);
assert.equal(json.type, 'pool');
this.chain.fromJSON(json.chain);
return this;
};
function LoadRequest(pool, type, hash, cb) {
this.pool = pool
this.type = type;

View File

@ -270,10 +270,10 @@ Parser.prototype.parseTX = function parseTX(p) {
var inCount = readIntv(p, 4);
var off = inCount.off;
inCount = inCount.r;
if (inCount <= 0)
return this._error('Invalid tx_in count');
if (inCount < 0)
return this._error('Invalid tx_in count (negative)');
if (off + 41 * inCount + 14 > p.length)
return this._error('Invalid tx_in count');
return this._error('Invalid tx_in count (too big)');
var txIn = new Array(inCount);
for (var i = 0; i < inCount; i++) {
@ -290,10 +290,10 @@ Parser.prototype.parseTX = function parseTX(p) {
var outCount = readIntv(p, off);
var off = outCount.off;
outCount = outCount.r;
if (outCount <= 0)
return this._error('Invalid tx_out count');
if (outCount < 0)
return this._error('Invalid tx_out count (negative)');
if (off + 9 * outCount + 4 > p.length)
return this._error('Invalid tx_out count');
return this._error('Invalid tx_out count (too big)');
var txOut = new Array(outCount);
for (var i = 0; i < outCount; i++) {

View File

@ -16,6 +16,8 @@
*/
module.exports = {
"v": 1,
"type": "chain",
"hashes": [
"6fe28c0ab6f1b372c1a6a246ae63f74f931e8365e15a089c68d6190000000000",
"33aa0fa26441ead7005df4b0ad2e61405e80cb805e3c657f194df32600000000",

View File

@ -1,19 +1,22 @@
var assert = require('assert');
var bn = require('bn.js');
var bcoin = require('../bcoin');
function TXPool() {
function TXPool(wallet) {
if (!(this instanceof TXPool))
return new TXPool();
return new TXPool(wallet);
this._wallet = wallet;
this._all = {};
this._unspent = {};
this._orphans = {};
}
module.exports = TXPool;
TXPool.prototype.add = function add(tx, wallet) {
TXPool.prototype.add = function add(tx) {
var hash = tx.hash('hex');
if (!wallet.own(tx))
if (!this._wallet.own(tx))
return;
// Do not add TX two times
@ -61,17 +64,17 @@ TXPool.prototype.add = function add(tx, wallet) {
return true;
};
TXPool.prototype.unspent = function unspent(wallet) {
TXPool.prototype.unspent = function unspent() {
return Object.keys(this._unspent).map(function(key) {
return this._unspent[key];
}, this).filter(function(item) {
return wallet.own(item.tx, item.index);
});
return this._wallet.own(item.tx, item.index);
}, this);
};
TXPool.prototype.balance = function balance(wallet) {
TXPool.prototype.balance = function balance() {
var acc = new bn(0);
var unspent = this.unspent(wallet);
var unspent = this.unspent();
if (unspent.length === 0)
return acc;
@ -79,3 +82,22 @@ TXPool.prototype.balance = function balance(wallet) {
return acc.iadd(item.tx.outputs[item.index].value);
}, acc);
};
TXPool.prototype.toJSON = function toJSON() {
return {
v: 1,
type: 'tx-pool',
txs: Object.keys(this._all).map(function(hash) {
return this._all[hash].toJSON();
}, this)
};
};
TXPool.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.v, 1);
assert.equal(json.type, 'tx-pool');
json.txs.forEach(function(tx) {
this.add(bcoin.tx.fromJSON(tx));
}, this);
};

View File

@ -171,3 +171,13 @@ TX.prototype.verify = function verify() {
return stack.length > 0 && utils.isEqual(stack.pop(), [ 1 ]);
}, this);
};
TX.prototype.toJSON = function toJSON() {
// Compact representation
return utils.toBase58(this.render());
};
TX.fromJSON = function fromJSON(json) {
// Compact representation
return new TX(new bcoin.protocol.parser().parseTX(utils.fromBase58(json)));
};

View File

@ -18,7 +18,7 @@ function Wallet(options, passphrase) {
options = {};
this.compressed = true;
this.tx = new bcoin.txPool();
this.tx = new bcoin.txPool(this);
this.key = null;
if (options.passphrase) {
@ -41,7 +41,12 @@ Wallet.prototype.getPrivateKey = function getPrivateKey(enc) {
if (enc === 'base58') {
// We'll be using ncompressed public key as an address
var arr = [ 128 ].concat(priv);
var arr = [ 128 ];
// 0-pad key
while (arr.length + priv.length < 33)
arr.push(0);
arr = arr.concat(priv);
if (this.compressed)
arr.push(1);
var chk = utils.checksum(arr);
@ -149,13 +154,45 @@ Wallet.prototype.sign = function sign(tx, type) {
};
Wallet.prototype.addTX = function addTX(tx) {
return this.tx.add(tx, this);
return this.tx.add(tx);
};
Wallet.prototype.unspent = function unspent() {
return this.tx.unspent(this);
return this.tx.unspent();
};
Wallet.prototype.balance = function balance() {
return this.tx.balance(this);
return this.tx.balance();
};
Wallet.prototype.toJSON = function toJSON() {
return {
v: 1,
type: 'wallet',
key: this.getPrivateKey('base58'),
tx: this.tx.toJSON()
};
};
Wallet.prototype.fromJSON = function fromJSON(json) {
assert.equal(json.v, 1);
assert.equal(json.type, 'wallet');
var key = bcoin.utils.fromBase58(json.key);
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
assert.equal(key[0], 128);
key = key.slice(0, -4);
if (key.length === 34) {
assert.equal(key[33], 1);
this.key = bcoin.ecdsa.keyPair(key.slice(1, -1));
this.compressed = true;
} else {
this.key = bcoin.ecdsa.keyPair(key.slice(1));
this.compressed = false;
}
this.tx.fromJSON(json.tx);
return this;
};

View File

@ -71,7 +71,7 @@ describe('Wallet', function() {
assert(tx.verify());
});
it('should have TX pool', function() {
it('should have TX pool and be serializable', function() {
var w = bcoin.wallet();
// Coinbase
@ -93,5 +93,8 @@ describe('Wallet', function() {
assert.equal(w.balance().toString(10), '47000');
w.addTX(t3);
assert.equal(w.balance().toString(10), '22000');
var w2 = bcoin.wallet().fromJSON(w.toJSON());
assert.equal(w2.balance().toString(10), '22000');
});
});