From cf9a62a75baa360c18fb7f79792af1833c960342 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Wed, 23 Mar 2016 16:15:36 -0700 Subject: [PATCH] coins and coinview. --- lib/bcoin.js | 2 + lib/bcoin/block.js | 15 ++++ lib/bcoin/coins.js | 197 ++++++++++++++++++++++++++++++++++++++++++ lib/bcoin/coinview.js | 104 ++++++++++++++++++++++ lib/bcoin/tx.js | 17 ++++ 5 files changed, 335 insertions(+) create mode 100644 lib/bcoin/coins.js create mode 100644 lib/bcoin/coinview.js diff --git a/lib/bcoin.js b/lib/bcoin.js index 548ed0de..60751c93 100644 --- a/lib/bcoin.js +++ b/lib/bcoin.js @@ -95,6 +95,8 @@ bcoin.script = require('./bcoin/script'); bcoin.input = require('./bcoin/input'); bcoin.output = require('./bcoin/output'); bcoin.coin = require('./bcoin/coin'); +bcoin.coins = require('./bcoin/coins'); +bcoin.coinview = require('./bcoin/coinview'); bcoin.tx = require('./bcoin/tx'); bcoin.mtx = require('./bcoin/mtx'); bcoin.ldb = require('./bcoin/ldb'); diff --git a/lib/bcoin/block.js b/lib/bcoin/block.js index 3339d207..24ce2570 100644 --- a/lib/bcoin/block.js +++ b/lib/bcoin/block.js @@ -271,6 +271,21 @@ Block.reward = function reward(height) { return reward; }; +Block.prototype.getPrevout = function getPrevout() { + var prevout = {}; + + this.txs.forEach(function(tx) { + if (tx.isCoinbase()) + return; + + tx.inputs.forEach(function(input) { + prevout[input.prevout.hash] = true; + }); + }); + + return Object.keys(prevout); +}; + Block.prototype.inspect = function inspect() { return { type: this.type, diff --git a/lib/bcoin/coins.js b/lib/bcoin/coins.js new file mode 100644 index 00000000..f9d26cc7 --- /dev/null +++ b/lib/bcoin/coins.js @@ -0,0 +1,197 @@ +/** + * coins.js - coins object for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * https://github.com/indutny/bcoin + */ + +var bcoin = require('../bcoin'); +var utils = bcoin.utils; +var assert = utils.assert; +var constants = bcoin.protocol.constants; +var BufferReader = require('./reader'); +var BufferWriter = require('./writer'); + +/** + * Coins + */ + +function Coins(options, hash) { + if (!(this instanceof Coins)) + return new Coins(options, hash); + + if (Buffer.isBuffer(hash)) + hash = utils.toHex(hash); + + this.version = options.version; + this.height = options.height; + + this.coinbase = options.isCoinbase + ? options.isCoinbase() + : options.coinbase; + + this.hash = hash; + + this.outputs = options.outputs.map(function(coin, i) { + if (!coin) + return null; + + if (coin instanceof bcoin.coin) + return coin; + + coin = utils.merge({}, coin); + coin.version = options.version; + coin.height = options.height; + coin.hash = hash; + coin.index = i; + coin.coinbase = this.coinbase; + + return new bcoin.coin(coin); + }, this); +} + +Coins.prototype.add = function add(tx, i) { + var coin; + + if (i == null) { + coin = tx; + this.outputs[coin.index] = coin; + return; + } + + this.outputs[i] = new bcoin.coin(tx, i); +}; + +Coins.prototype.has = function has(index) { + return this.outputs[index] != null; +}; + +Coins.prototype.get = function get(index) { + return this.outputs[index]; +}; + +Coins.prototype.remove = function remove(index) { + if (index < this.outputs.length) + this.outputs[index] = null; +}; + +Coins.prototype.spend = function spend(hash, index) { + var coin = this.get(hash, index); + this.remove(hash, index); + return coin; +}; + +Coins.prototype.fill = function fill(tx, spend) { + var res = true; + var i, input; + + if (tx.txs) + tx = tx.txs; + + if (Array.isArray(tx)) { + for (i = 0; i < tx.length; i++) { + if (!this.fill(tx[i])) + res = false; + } + return res; + } + + for (i = 0; i < tx.inputs.length; i++) { + input = tx.inputs[i]; + + if (input.prevout.hash !== this.hash) + continue; + + if (!input.output) { + if (spend) + input.output = this.spend(input.prevout.index); + else + input.output = this.get(input.prevout.index); + + if (!input.output) + res = false; + } + } + + return res; +}; + +Coins.prototype.count = function count(index) { + return this.outputs.reduce(function(total, output) { + if (!output) + return total; + return total + 1; + }, 0); +}; + +Coins.prototype.forEach = function forEach(callback, context) { + this.outputs.forEach(function(output, i) { + callback.call(context || this, output, i); + }, this); +}; + +Coins.prototype.toRaw = function toRaw() { + return Coins.toRaw(this); +}; + +Coins.fromTX = function fromTX(tx) { + return new Coins(tx, tx.hash('hex')); +}; + +Coins.toRaw = function toRaw(tx) { + var p = new BufferWriter(); + var height = tx.height; + + if (height === -1) + height = 0x7fffffff; + + p.writeU32(tx.version); + p.writeU32(height); + p.writeU8(tx.coinbase ? 1 : 0); + p.writeUIntv(tx.outputs.length); + + tx.outputs.forEach(function(output) { + if (!output) { + p.writeUIntv(0); + return; + } + p.writeVarBytes(bcoin.protocol.framer.output(output)); + }); + + return p.render(); +}; + +Coins._fromRaw = function _fromRaw(buf) { + var tx = { outputs: [] }; + var p = new BufferReader(buf); + var coinCount, i, coin; + + tx.version = p.readU32(); + tx.height = p.readU32(); + tx.coinbase = p.readU8() === 1; + + if (tx.height === 0x7fffffff) + tx.height = -1; + + coinCount = p.readUIntv(); + for (i = 0; i < coinCount; i++) { + coin = p.readVarBytes(); + if (coin.length === 0) { + tx.outputs.push(null); + continue; + } + coin = bcoin.protocol.parser.parseOutput(coin); + tx.outputs.push(coin); + } + + return tx; +}; + +Coins.fromRaw = function fromRaw(buf, hash) { + return new Coins(Coins._fromRaw(buf), hash); +}; + +/** + * Expose + */ + +module.exports = Coins; diff --git a/lib/bcoin/coinview.js b/lib/bcoin/coinview.js new file mode 100644 index 00000000..848645cd --- /dev/null +++ b/lib/bcoin/coinview.js @@ -0,0 +1,104 @@ +/** + * coinview.js - coinview object for bcoin + * Copyright (c) 2014-2015, Fedor Indutny (MIT License) + * https://github.com/indutny/bcoin + */ + +var bcoin = require('../bcoin'); +var utils = bcoin.utils; +var assert = utils.assert; +var constants = bcoin.protocol.constants; + +/** + * CoinView + */ + +function CoinView(coins) { + if (!(this instanceof CoinView)) + return new CoinView(coins); + + this.coins = coins || {}; +} + +CoinView.prototype.add = function add(tx, i) { + var coin, hash; + + if (i == null) { + coin = tx; + this.coins[coin.hash] = coin; + return; + } + + hash = tx.hash('hex'); + + if (!this.coins[hash]) { + this.coins[hash] = Object.create(bcoin.coins.prototype); + this.coins[hash].version = tx.version; + this.coins[hash].height = tx.height; + this.coins[hash].coinbase = tx.isCoinbase(); + this.coins[hash].hash = hash; + this.coins[hash].outputs = new Array(tx.outputs.length); + } + + this.coins[hash].add(tx, i); +}; + +CoinView.prototype.get = function get(hash, index) { + if (!this.coins[hash]) + return; + + return this.coins[hash].get(index); +}; + +CoinView.prototype.count = function count(hash) { + if (!this.coins[hash]) + return 0; + + return this.coins[hash].count(); +}; + +CoinView.prototype.has = function has(hash, index) { + if (!this.coins[hash]) + return; + + return this.coins[hash].has(index); +}; + +CoinView.prototype.remove = function remove(hash, index) { + if (!this.coins[hash]) + return; + + return this.coins[hash].remove(index); +}; + +CoinView.prototype.spend = function spend(hash, index) { + if (!this.coins[hash]) + return; + + return this.coins[hash].spend(index); +}; + +CoinView.prototype.fill = function fill(obj, spend) { + var keys = Object.keys(this.coins); + var res = true; + var i; + + for (i = 0; i < keys.length; i++) { + if (!this.coins[keys[i]].fill(obj, spend)) + res = false; + } + + return res; +}; + +CoinView.prototype.toArray = function toArray() { + return Object.keys(this.coins).map(function(hash) { + return this.coins[hash]; + }, this); +}; + +/** + * Expose + */ + +module.exports = CoinView; diff --git a/lib/bcoin/tx.js b/lib/bcoin/tx.js index 4cd6600c..cfc4a640 100644 --- a/lib/bcoin/tx.js +++ b/lib/bcoin/tx.js @@ -1065,6 +1065,19 @@ TX.prototype.__defineGetter__('priority', function() { return this.getPriority(); }); +TX.prototype.getPrevout = function getPrevout() { + var prevout = {}; + + if (this.isCoinbase()) + return []; + + this.inputs.forEach(function(input) { + prevout[input.prevout.hash] = true; + }); + + return Object.keys(prevout); +}; + TX.prototype.inspect = function inspect() { return { type: this.type, @@ -1242,6 +1255,10 @@ TX.fromExtended = function fromExtended(buf, saveCoins) { return new TX(TX._fromExtended(buf, saveCoins)); }; +TX.prototype.toCoins = function toCoins() { + return bcoin.coins.fromTX(this); +}; + /** * Expose */