From e5de8db660a960e9ae4396815695169f7eb9e84b Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Tue, 23 Feb 2016 02:27:10 -0800 Subject: [PATCH] more json improvements. --- lib/bcoin/block.js | 108 +++++++++++++++++++++++++------------------ lib/bcoin/coin.js | 68 +++++++++++++-------------- lib/bcoin/hd.js | 40 ++++++++++++---- lib/bcoin/http.js | 8 ++-- lib/bcoin/input.js | 57 ++++++++++++++++++++--- lib/bcoin/keypair.js | 22 ++++++--- lib/bcoin/output.js | 53 +++++++++++++++++++-- lib/bcoin/tx.js | 51 +++++++++++--------- lib/bcoin/wallet.js | 4 +- test/block-test.js | 6 +-- 10 files changed, 281 insertions(+), 136 deletions(-) diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 62921eac..795f79aa 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -22,8 +22,10 @@ function Block(data, subtype) { if (!(this instanceof Block)) return new Block(data, subtype); + assert(typeof data.hash !== 'string'); + this.type = 'block'; - this.subtype = subtype; + this.subtype = subtype || data.subtype; this.version = data.version; this.prevBlock = utils.toHex(data.prevBlock); this.merkleRoot = utils.toHex(data.merkleRoot); @@ -480,6 +482,51 @@ Block.prototype.inspect = function inspect() { }; Block.prototype.toJSON = function toJSON() { + var json = { + type: 'block', + subtype: this.subtype, + height: this.height, + network: this.network, + relayedBy: this.relayedBy, + hash: utils.revHex(this.hash('hex')), + version: this.version, + prevBlock: utils.revHex(this.prevBlock), + merkleRoot: utils.revHex(this.merkleRoot), + ts: this.ts, + bits: this.bits, + nonce: this.nonce, + totalTX: this.totalTX, + txs: this.txs.map(function(tx) { + return tx.toJSON(); + }) + }; + + if (this.subtype === 'headers') + return json; + + if (this.subtype === 'merkleblock') { + json.hashes = this.hashes; + json.flags = this.flags; + return json; + } + + return json; +}; + +Block._fromJSON = function _fromJSON(json) { + json.prevBlock = utils.revHex(json.prevBlock); + json.merkleRoot = utils.revHex(json.merkleRoot); + json.txs = json.txs.map(function(tx) { + return bcoin.tx._fromJSON(tx); + }); + return json; +}; + +Block.fromJSON = function fromJSON(json) { + return new Block(Block._fromJSON(json), json.subtype); +}; + +Block.prototype.toCompact = function toCompact() { return { type: 'block', subtype: this.subtype, @@ -493,8 +540,8 @@ Block.prototype.toJSON = function toJSON() { }; }; -Block.fromJSON = function fromJSON(json) { - var raw, parser, data, block; +Block._fromCompact = function _fromCompact(json) { + var raw, parser, data; assert.equal(json.type, 'block'); @@ -502,53 +549,22 @@ Block.fromJSON = function fromJSON(json) { parser = new bcoin.protocol.parser(); - if (json.subtype === 'merkleblock') + if (json.subtype === 'headers') + data = parser.parseHeaders(raw); + else if (json.subtype === 'merkleblock') data = parser.parseMerkleBlock(raw); - else if (json.subtype === 'block' || json.subtype === 'header') + else data = parser.parseBlock(raw); data.height = json.height; data.network = json.network; data.relayedBy = json.relayedBy; - block = new Block(data, json.subtype); - - block._hash = json.hash; - - return block; + return data; }; -Block.prototype.toFullJSON = function toFullJSON() { - return { - type: 'block', - subtype: this.subtype, - height: this.height, - network: this.network, - relayedBy: this.relayedBy, - hash: utils.revHex(this.hash('hex')), - version: this.version, - prevBlock: utils.revHex(this.prevBlock), - merkleRoot: utils.revHex(this.merkleRoot), - ts: this.ts, - bits: this.bits, - nonce: this.nonce, - txs: this.txs.map(function(tx) { - return tx.toFullJSON(); - }) - }; -}; - -Block.fromFullJSON = function fromFullJSON(json) { - json.prevBlock = utils.revHex(json.prevBlock); - json.merkleRoot = utils.revHex(json.merkleRoot); - json.txs = json.txs.map(function(tx) { - tx = bcoin.tx.fromFullJSON(tx); - tx.ts = block.ts; - tx.block = block.hash('hex'); - tx.height = block.height; - return tx; - }); - return new Block(json, json.subtype); +Block.fromCompact = function fromCompact(json) { + return new Block(Block._fromCompact(json), json.subtype); }; Block.prototype.toRaw = function toRaw(enc) { @@ -556,7 +572,7 @@ Block.prototype.toRaw = function toRaw(enc) { assert(this.subtype === 'block'); - data = new Buffer(this.render()); + data = this.render(); if (enc === 'hex') data = utils.toHex(data); @@ -564,13 +580,17 @@ Block.prototype.toRaw = function toRaw(enc) { return data; }; -Block.fromRaw = function fromRaw(data, enc) { +Block._fromRaw = function _fromRaw(data, enc) { var parser = new bcoin.protocol.parser(); if (enc === 'hex') data = new Buffer(data, 'hex'); - return new Block(parser.parseBlock(data), 'block'); + return parser.parseBlock(data); +}; + +Block.fromRaw = function fromRaw(data, enc) { + return new Block(Block._fromRaw(data), 'block'); }; /** diff --git a/lib/bcoin/coin.js b/lib/bcoin/coin.js index 36121a7f..d5323172 100644 --- a/lib/bcoin/coin.js +++ b/lib/bcoin/coin.js @@ -40,6 +40,7 @@ function Coin(tx, index) { this.spent = false; } else { options = tx; + assert(typeof options.script !== 'string'); this.version = options.version; this.height = options.height; this.value = options.value; @@ -119,32 +120,6 @@ Coin.prototype.__defineGetter__('age', function() { }); Coin.prototype.toJSON = function toJSON() { - return { - version: this.version, - height: this.height, - value: utils.btc(this.value), - script: utils.toHex(bcoin.script.encode(this.script)), - hash: this.hash, - index: this.index, - spent: this.spent, - address: this.getAddress() - }; -}; - -Coin.fromJSON = function fromJSON(json) { - return new Coin({ - version: json.version, - height: json.height, - value: utils.satoshi(json.value), - script: bcoin.script.decode(new Buffer(json.script, 'hex')), - hash: json.hash, - index: json.index, - spent: json.spent, - address: json.address - }); -}; - -Coin.prototype.toFullJSON = function toFullJSON() { return { version: this.version, height: this.height, @@ -152,22 +127,39 @@ Coin.prototype.toFullJSON = function toFullJSON() { script: utils.toHex(bcoin.script.encode(this.script)), hash: utils.revHex(this.hash), index: this.index, - spent: this.spent, - address: this.getAddress() + spent: this.spent }; }; -Coin.fromFullJSON = function fromFullJSON(json) { - return new Coin({ +Coin._fromJSON = function _fromJSON(json) { + return { version: json.version, height: json.height, value: utils.satoshi(json.value), script: bcoin.script.decode(new Buffer(json.script, 'hex')), hash: utils.revHex(json.hash), index: json.index, - spent: json.spent, - address: json.address - }); + spent: json.spent + }; +}; + +Coin.fromJSON = function fromJSON(json) { + return new Coin(Coin._fromJSON(json)); +}; + +Coin.prototype.toCompact = function toCompact() { + return { + type: 'coin', + coin: this.toRaw('hex') + }; +}; + +Coin._fromCompact = function _fromCompact(json) { + return Coin._fromRaw(json.coin, 'hex'); +}; + +Coin.fromCompact = function fromCompact(json) { + return new Coin(Coin._fromCompact(json)); }; Coin.prototype.toRaw = function toRaw(enc) { @@ -179,13 +171,19 @@ Coin.prototype.toRaw = function toRaw(enc) { return data; }; -Coin.fromRaw = function fromRaw(data, enc) { +Coin._fromRaw = function _fromRaw(data, enc) { + var parser = new bcoin.protocol.parser(); + if (enc === 'hex') data = new Buffer(data, 'hex'); data = parser.parseCoin(data, true); - return new Coin(data); + return data; +}; + +Coin.fromRaw = function fromRaw(data, enc) { + return new Coin(Coin._fromRaw(data, enc)); }; /** diff --git a/lib/bcoin/hd.js b/lib/bcoin/hd.js index 5057dc2e..c2b413aa 100644 --- a/lib/bcoin/hd.js +++ b/lib/bcoin/hd.js @@ -871,7 +871,7 @@ HDPrivateKey.prototype.toJSON = function toJSON(passphrase) { return json; }; -HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { +HDPrivateKey._fromJSON = function _fromJSON(json, passphrase) { assert.equal(json.v, 1); assert.equal(json.name, 'hdkey'); @@ -879,23 +879,47 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { throw new Error('Cannot decrypt address'); if (json.mnemonic) { - return new HDPrivateKey({ - seed: new HDSeed({ + return { + seed: { mnemonic: json.encrypted ? utils.decrypt(json.mnemonic, passphrase) : json.mnemonic, passphrase: json.encrypted ? utils.decrypt(json.passphrase, passphrase) : json.passphrase - }) + } + }; + } + + if (json.xprivkey) { + return { + xprivkey: json.encrypted + ? utils.decrypt(json.xprivkey, passphrase) + : json.xprivkey + }; + } + + if (json.xpubkey) { + return { + xpubkey: json.xpubkey + }; + } + + assert(false); +}; + +HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { + json = HDPrivateKey._fromJSON(json, passphrase); + + if (json.seed) { + return new HDPrivateKey({ + seed: new HDSeed(json.seed) }); } if (json.xprivkey) { return new HDPrivateKey({ - xkey: json.encrypted - ? utils.decrypt(json.xprivkey, passphrase) - : json.xprivkey + xkey: json.xprivkey }); } @@ -904,8 +928,6 @@ HDPrivateKey.fromJSON = function fromJSON(json, passphrase) { xkey: json.xpubkey }); } - - assert(false); }; /** diff --git a/lib/bcoin/http.js b/lib/bcoin/http.js index 1a2c8df1..1c6b1590 100644 --- a/lib/bcoin/http.js +++ b/lib/bcoin/http.js @@ -106,7 +106,7 @@ HTTPServer.prototype._init = function _init() { return next(err); if (!tx) return send(404); - send(200, tx.toFullJSON()); + send(200, tx.toJSON()); }); }); @@ -118,7 +118,7 @@ HTTPServer.prototype._init = function _init() { return next(err); if (!txs.length) return send(404); - send(200, txs.map(function(tx) { return tx.toFullJSON(); })); + send(200, txs.map(function(tx) { return tx.toJSON(); })); }); }); @@ -129,7 +129,7 @@ HTTPServer.prototype._init = function _init() { return next(err); if (!txs.length) return send(404); - send(200, txs.map(function(tx) { return tx.toFullJSON(); })); + send(200, txs.map(function(tx) { return tx.toJSON(); })); }); }); @@ -147,7 +147,7 @@ HTTPServer.prototype._init = function _init() { return next(err); if (!block) return send(404); - send(200, block.toFullJSON()); + send(200, block.toJSON()); }); }); diff --git a/lib/bcoin/input.js b/lib/bcoin/input.js index 1fde2ee4..0fcafaac 100644 --- a/lib/bcoin/input.js +++ b/lib/bcoin/input.js @@ -20,6 +20,8 @@ function Input(options) { if (!(this instanceof Input)) return new Input(options); + assert(typeof options.script !== 'string'); + prevout = options.prevout || options.out; this.prevout = { @@ -365,28 +367,71 @@ Input.prototype.inspect = function inspect() { }; }; -Input.prototype.toFullJSON = function toFullJSON() { +Input.prototype.toJSON = function toJSON() { return { prevout: { hash: utils.revHex(this.prevout.hash), index: this.prevout.index }, - output: this.output ? this.output.toFullJSON() : null, + output: this.output ? this.output.toJSON() : null, script: utils.toHex(bcoin.script.encode(this.script)), sequence: this.sequence }; }; -Input.fromFullJSON = function fromFullJSON(json) { - return new Input({ +Input._fromJSON = function _fromJSON(json) { + return { prevout: { hash: utils.revHex(json.prevout.hash), index: json.prevout.index }, - output: json.output ? bcoin.coin.fromFullJSON(json.output) : null, + output: json.output ? bcoin.coin._fromJSON(json.output) : null, script: bcoin.script.decode(new Buffer(json.script, 'hex')), sequence: json.sequence - }); + }; +}; + +Input.fromJSON = function fromJSON(json) { + return new Input(Input._fromJSON(json)); +}; + +Input.prototype.toCompact = function toCompact() { + return { + type: 'input', + input: this.toRaw('hex') + }; +}; + +Input._fromCompact = function _fromCompact(json) { + return Input._fromRaw(json.input, 'hex'); +}; + +Input.fromCompact = function fromCompact(json) { + return new Input(Input._fromCompact(json)); +}; + +Input.prototype.toRaw = function toRaw(enc) { + var data = bcoin.protocol.framer.input(this); + + if (enc === 'hex') + data = utils.toHex(data); + + return data; +}; + +Input._fromRaw = function _fromRaw(data, enc) { + var parser = new bcoin.protocol.parser(); + + if (enc === 'hex') + data = new Buffer(data, 'hex'); + + data = parser.parseInput(data); + + return data; +}; + +Input.fromRaw = function fromRaw(data, enc) { + return new Input(Input._fromRaw(data, enc)); }; /** diff --git a/lib/bcoin/keypair.js b/lib/bcoin/keypair.js index 49321aa8..6e33b2b4 100644 --- a/lib/bcoin/keypair.js +++ b/lib/bcoin/keypair.js @@ -161,7 +161,7 @@ KeyPair.toSecret = function toSecret(privateKey, compressed) { return utils.toBase58(buf); }; -KeyPair.fromSecret = function fromSecret(privateKey) { +KeyPair._fromSecret = function _fromSecret(privateKey) { var key, compressed; key = utils.fromBase58(privateKey); @@ -178,10 +178,14 @@ KeyPair.fromSecret = function fromSecret(privateKey) { compressed = false; } - return new KeyPair({ + return { privateKey: privateKey, compressed: compressed - }); + }; +}; + +KeyPair.fromSecret = function fromSecret(privateKey) { + return new KeyPair(KeyPair._fromSecret(privateKey)); }; KeyPair.verify = function verify(msg, sig, key) { @@ -215,7 +219,7 @@ KeyPair.prototype.toJSON = function toJSON(passphrase) { return json; }; -KeyPair.fromJSON = function fromJSON(json, passphrase) { +KeyPair._fromJSON = function _fromJSON(json, passphrase) { var privateKey, publicKey, compressed; assert.equal(json.v, 1); @@ -228,21 +232,25 @@ KeyPair.fromJSON = function fromJSON(json, passphrase) { privateKey = json.privateKey; if (json.encrypted) privateKey = utils.decrypt(privateKey, passphrase); - return KeyPair.fromSecret(privateKey); + return KeyPair._fromSecret(privateKey); } if (json.publicKey) { publicKey = utils.fromBase58(json.publicKey); compressed = publicKey[0] !== 0x04; - return new KeyPair({ + return { publicKey: publicKey, compressed: compressed - }); + }; } assert(false); }; +KeyPair.fromJSON = function fromJSON(json, passphrase) { + return new KeyPair(KeyPair._fromJSON(json, passphrase)); +}; + /** * Expose */ diff --git a/lib/bcoin/output.js b/lib/bcoin/output.js index 7d7f2a6e..32ecc086 100644 --- a/lib/bcoin/output.js +++ b/lib/bcoin/output.js @@ -20,6 +20,8 @@ function Output(options) { if (!(this instanceof Output)) return new Output(options); + assert(typeof options.script !== 'string'); + value = options.value; if (typeof value === 'number' && (value | 0) === value) @@ -259,18 +261,61 @@ Output.prototype.inspect = function inspect() { }; }; -Output.prototype.toFullJSON = function toFullJSON() { +Output.prototype.toJSON = function toJSON() { return { value: utils.btc(this.value), script: utils.toHex(bcoin.script.encode(this.script)) }; }; -Output.fromFullJSON = function fromFullJSON(json) { - return new Output({ +Output._fromJSON = function _fromJSON(json) { + return { value: utils.satoshi(json.value), script: bcoin.script.decode(new Buffer(json.script, 'hex')) - }); + }; +}; + +Output.fromJSON = function fromJSON(json) { + return new Output(Output._fromJSON(json)); +}; + +Output.prototype.toCompact = function toCompact() { + return { + type: 'output', + output: this.toRaw('hex') + }; +}; + +Output._fromCompact = function _fromCompact(json) { + return Output._fromRaw(json.output, 'hex'); +}; + +Output.fromCompact = function fromCompact(json) { + return new Output(Output._fromCompact(json)); +}; + +Output.prototype.toRaw = function toRaw(enc) { + var data = bcoin.protocol.framer.output(this); + + if (enc === 'hex') + data = utils.toHex(data); + + return data; +}; + +Output._fromRaw = function _fromRaw(data, enc) { + var parser = new bcoin.protocol.parser(); + + if (enc === 'hex') + data = new Buffer(data, 'hex'); + + data = parser.parseOutput(data); + + return data; +}; + +Output.fromRaw = function fromRaw(data, enc) { + return new Output(Output._fromRaw(data, enc)); }; /** diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 985fdbbf..720a8a6d 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -22,6 +22,8 @@ function TX(data, block) { if (!data) data = {}; + assert(typeof data.hash !== 'string'); + this.type = 'tx'; this.version = data.version || 1; this.inputs = []; @@ -1799,7 +1801,7 @@ TX.prototype.inspect = function inspect() { return copy; }; -TX.prototype.toJSON = function toJSON(coins) { +TX.prototype.toCompact = function toCompact(coins) { return { type: 'tx', block: this.block, @@ -1816,7 +1818,7 @@ TX.prototype.toJSON = function toJSON(coins) { }; }; -TX.fromJSON = function fromJSON(json) { +TX._fromCompact = function _fromCompact(json) { var raw, data, tx; assert.equal(json.type, 'tx'); @@ -1832,21 +1834,23 @@ TX.fromJSON = function fromJSON(json) { data.relayedBy = json.relayedBy; data.changeIndex = json.changeIndex; - tx = new TX(data); - if (json.coins) { json.coins.forEach(function(output, i) { if (!output) return; - tx.inputs[i].output = bcoin.coin.fromRaw(output, 'hex'); + data.inputs[i].output = bcoin.coin._fromRaw(output, 'hex'); }); } - return tx; + return data; }; -TX.prototype.toFullJSON = function toFullJSON() { +TX.fromCompact = function fromCompact(json) { + return new TX(TX._fromCompact(json)); +}; + +TX.prototype.toJSON = function toJSON() { return { type: 'tx', hash: utils.revHex(this.hash('hex')), @@ -1859,17 +1863,17 @@ TX.prototype.toFullJSON = function toFullJSON() { changeIndex: this.changeIndex, version: this.version, inputs: this.inputs.map(function(input) { - return input.toFullJSON(); + return input.toJSON(); }), outputs: this.outputs.map(function(output) { - return output.toFullJSON(); + return output.toJSON(); }), locktime: this.locktime }; }; -TX.fromFullJSON = function fromFullJSON(json) { - return new TX({ +TX._fromJSON = function fromJSON(json) { + return { block: json.block ? utils.revHex(json.block) : null, height: json.height, ts: json.ts, @@ -1879,22 +1883,21 @@ TX.fromFullJSON = function fromFullJSON(json) { changeIndex: json.changeIndex, version: json.version, inputs: json.inputs.map(function(input) { - return bcoin.input.fromFullJSON(input); + return bcoin.input._fromJSON(input); }), outputs: json.outputs.map(function(output) { - return bcoin.output.fromFullJSON(output); + return bcoin.output._fromJSON(output); }), locktime: json.locktime - }); + }; +}; + +TX.fromJSON = function fromJSON(json) { + return new TX(TX._fromJSON(json)); }; TX.prototype.toRaw = function toRaw(enc) { - var data; - - if (this.isStatic() && this._raw) - data = this._raw; - else - data = new Buffer(this.render()); + var data = this.render(); if (enc === 'hex') data = utils.toHex(data); @@ -1902,13 +1905,17 @@ TX.prototype.toRaw = function toRaw(enc) { return data; }; -TX.fromRaw = function fromRaw(data, enc) { +TX._fromRaw = function _fromRaw(data, enc) { var parser = new bcoin.protocol.parser(); if (enc === 'hex') data = new Buffer(data, 'hex'); - return new bcoin.tx(parser.parseTX(data)); + return parser.parseTX(data); +}; + +TX.fromRaw = function fromRaw(data, enc) { + return new bcoin.tx(TX._fromRaw(data, enc)); }; /** diff --git a/lib/bcoin/wallet.js b/lib/bcoin/wallet.js index b30f141b..2a55dccf 100644 --- a/lib/bcoin/wallet.js +++ b/lib/bcoin/wallet.js @@ -871,7 +871,7 @@ Wallet.prototype.toJSON = function toJSON(noPool) { }), balance: utils.btc(this.getBalance()), txs: noPool ? [] : this.tx.getAll().map(function(tx) { - return tx.toJSON(); + return tx.toCompact(); }) }; }; @@ -899,7 +899,7 @@ Wallet._fromJSON = function _fromJSON(json, passphrase) { addressMap: json.addressMap, keys: json.keys, txs: json.txs.map(function(json) { - return bcoin.tx.fromJSON(json); + return bcoin.tx.fromCompact(json); }) }; }; diff --git a/test/block-test.js b/test/block-test.js index 990efb53..0e50dff2 100644 --- a/test/block-test.js +++ b/test/block-test.js @@ -26,7 +26,7 @@ describe('Block', function() { '33825657ba32afe269819f01993bd77baba86379043168c94845d32370e53562' ], flags: [ 245, 90, 0 ] }, 'merkleblock'); - var raw = block.toJSON().block; + var raw = block.toCompact().block; it('should parse partial merkle tree', function() { assert(block.verify()); @@ -50,10 +50,10 @@ describe('Block', function() { }); it('should be jsonified and unjsonified and still verify', function() { - var json = block.toJSON(); + var json = block.toCompact(); assert.equal(json.subtype, 'merkleblock'); assert.equal(typeof json.block, 'string'); - var b = bcoin.block(bcoin.block.fromJSON(json), json.subtype); + var b = bcoin.block.fromCompact(json); assert.equal(bcoin.utils.toHex(b.render()), json.block); assert(b.verify()); });