coins: refactor and comments.

This commit is contained in:
Christopher Jeffrey 2016-12-10 19:42:46 -08:00
parent b437b33760
commit b77aa9240e
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
21 changed files with 690 additions and 249 deletions

View File

@ -237,8 +237,8 @@ Chain.prototype._close = function close() {
* Perform all necessary contextual verification on a block.
* @private
* @param {Block|MerkleBlock} block
* @param {ChainEntry} entry
* @returns {Promise}
* @param {ChainEntry} prev
* @returns {Promise} - Returns {@link ContextResult}.
*/
Chain.prototype.verifyContext = co(function* verifyContext(block, prev) {
@ -608,7 +608,7 @@ Chain.prototype.verifyDuplicates = co(function* verifyDuplicates(block, prev, st
* @param {Block} block
* @param {ChainEntry} prev
* @param {DeploymentState} state
* @returns {Promise}
* @returns {Promise} - Returns {@link CoinView}.
*/
Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
@ -2244,9 +2244,10 @@ Chain.prototype.verifyFinal = co(function* verifyFinal(prev, tx, flags) {
/**
* Get the necessary minimum time and height sequence locks for a transaction.
* @param {TX} tx
* @param {LockFlags} flags
* @param {ChainEntry} prev
* @param {TX} tx
* @param {CoinView} view
* @param {LockFlags} flags
* @returns {Promise}
* [Error, Number(minTime), Number(minHeight)].
*/
@ -2295,9 +2296,10 @@ Chain.prototype.getLocks = co(function* getLocks(prev, tx, view, flags) {
/**
* Verify sequence locks.
* @param {TX} tx
* @param {LockFlags} flags
* @param {ChainEntry} prev
* @param {TX} tx
* @param {CoinView} view
* @param {LockFlags} flags
* @returns {Promise} - Returns Boolean.
*/

View File

@ -937,9 +937,9 @@ ChainDB.prototype.getRawBlock = co(function* getRawBlock(block) {
});
/**
* Get a block and fill it with coins (historical).
* @param {Hash} hash
* @returns {Promise} - Returns {@link Block}.
* Get a historical block coin viewpoint.
* @param {Block} hash
* @returns {Promise} - Returns {@link CoinView}.
*/
ChainDB.prototype.getBlockView = co(function* getBlockView(block) {
@ -1789,6 +1789,7 @@ ChainDB.prototype.saveOptions = function saveOptions() {
* Index a transaction by txid and address.
* @private
* @param {TX} tx
* @param {CoinView} view
*/
ChainDB.prototype.indexTX = function indexTX(tx, view) {
@ -1838,6 +1839,7 @@ ChainDB.prototype.indexTX = function indexTX(tx, view) {
* Remove transaction from index.
* @private
* @param {TX} tx
* @param {CoinView} view
*/
ChainDB.prototype.unindexTX = function unindexTX(tx, view) {

View File

@ -19,15 +19,14 @@ var decompress = compressor.decompress;
/**
* Represents the outputs for a single transaction.
* @exports Coins
* @constructor
* @param {TX|Object} tx/options - TX or options object.
* @param {Object?} options - Options object.
* @property {Hash} hash - Transaction hash.
* @property {Number} version - Transaction version.
* @property {Number} height - Transaction height (-1 if unconfirmed).
* @property {Boolean} coinbase - Whether the containing
* transaction is a coinbase.
* @property {Coin[]} outputs - Coins.
* @property {CoinEntry[]} outputs - Coins.
*/
function Coins(options) {
@ -52,7 +51,7 @@ function Coins(options) {
Coins.prototype.fromOptions = function fromOptions(options) {
if (options.version != null) {
assert(util.isNumber(options.version));
assert(util.isUInt32(options.version));
this.version = options.version;
}
@ -74,6 +73,7 @@ Coins.prototype.fromOptions = function fromOptions(options) {
if (options.outputs) {
assert(Array.isArray(options.outputs));
this.outputs = options.outputs;
this.cleanup();
}
return this;
@ -96,6 +96,8 @@ Coins.fromOptions = function fromOptions(options) {
*/
Coins.prototype.add = function add(index, entry) {
assert(index >= 0);
while (this.outputs.length <= index)
this.outputs.push(null);
@ -139,7 +141,8 @@ Coins.prototype.has = function has(index) {
};
/**
* Test whether the collection has a coin.
* Test whether the collection
* has an unspent coin.
* @param {Number} index
* @returns {Boolean}
*/
@ -202,7 +205,7 @@ Coins.prototype.getCoin = function getCoin(index) {
};
/**
* Remove a coin entry and return it.
* Spend a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry}
*/
@ -219,7 +222,28 @@ Coins.prototype.spend = function spend(index) {
};
/**
* Cleanup spent outputs.
* Remove a coin entry and return it.
* @param {Number} index
* @returns {CoinEntry}
*/
Coins.prototype.remove = function remove(index) {
var entry;
if (index >= this.outputs.length)
return false;
entry = this.outputs[index];
this.outputs[index] = null;
this.cleanup();
return entry;
};
/**
* Calculate unspent length of coins.
* @returns {Number}
*/
Coins.prototype.length = function length() {
@ -232,7 +256,7 @@ Coins.prototype.length = function length() {
};
/**
* Cleanup spent outputs.
* Cleanup spent outputs (remove pruned).
*/
Coins.prototype.cleanup = function cleanup() {
@ -284,12 +308,12 @@ Coins.prototype.isEmpty = function isEmpty() {
*/
/**
* Serialize the coins object.
* @returns {Buffer}
* Write the coins object to writer.
* @param {BufferWriter} bw
* @returns {BufferWriter}
*/
Coins.prototype.toRaw = function toRaw() {
var bw = new BufferWriter();
Coins.prototype.toWriter = function toWriter(bw) {
var len = this.length();
var size = Math.floor((len + 5) / 8);
var first = this.isUnspent(0);
@ -344,19 +368,27 @@ Coins.prototype.toRaw = function toRaw() {
output.toWriter(bw);
}
return bw.render();
return bw;
};
/**
* Inject data from serialized coins.
* Serialize the coins object.
* @returns {Buffer}
*/
Coins.prototype.toRaw = function toRaw() {
return this.toWriter(new BufferWriter()).render();
};
/**
* Inject data from buffer reader.
* @private
* @param {Buffer} data
* @param {BufferReader} br
* @param {Hash} hash
* @returns {Coins}
*/
Coins.prototype.fromRaw = function fromRaw(data, hash) {
var br = new BufferReader(data);
Coins.prototype.fromReader = function fromReader(br, hash) {
var first = null;
var second = null;
var i, j, code, size, offset, ch;
@ -407,6 +439,18 @@ Coins.prototype.fromRaw = function fromRaw(data, hash) {
return this;
};
/**
* Inject data from serialized coins.
* @private
* @param {Buffer} data
* @param {Hash} hash
* @returns {Coins}
*/
Coins.prototype.fromRaw = function fromRaw(data, hash) {
return this.fromReader(new BufferReader(data), hash);
};
/**
* Parse a single serialized coin.
* @param {Buffer} data
@ -478,7 +522,18 @@ Coins.parseCoin = function parseCoin(data, hash, index) {
};
/**
* Instantiate coins from a serialized Buffer.
* Instantiate coins from a buffer reader.
* @param {Buffer} data
* @param {Hash} hash - Transaction hash.
* @returns {Coins}
*/
Coins.fromReader = function fromReader(br, hash) {
return new Coins().fromReader(br, hash);
};
/**
* Instantiate coins from a buffer.
* @param {Buffer} data
* @param {Hash} hash - Transaction hash.
* @returns {Coins}
@ -533,22 +588,24 @@ Coins.fromTX = function fromTX(tx, height) {
};
/**
* A compressed coin is an object which defers
* A coin entry is an object which defers
* parsing of a coin. Say there is a transaction
* with 100 outputs. When a block comes in,
* there may only be _one_ input in that entire
* block which redeems an output from that
* transaction. When parsing the Coins, there
* is no sense to get _all_ of them into their
* abstract form. A compressed coin is just a
* abstract form. A coin entry is just a
* pointer to that coin in the Coins buffer, as
* well as a size. Parsing is done only if that
* coin is being redeemed.
* well as a size. Parsing and decompression
* is done only if that coin is being redeemed.
* @constructor
* @private
* @param {Number} offset
* @param {Number} size
* @param {Buffer} raw
* @property {Number} offset
* @property {Number} size
* @property {Buffer} raw
* @property {Output|null} output
* @property {Boolean} spent
*/
function CoinEntry() {
@ -577,7 +634,7 @@ CoinEntry.prototype.reader = function reader() {
};
/**
* Parse the deferred data and return a Coin.
* Parse the deferred data and return a coin.
* @param {Coins} coins
* @param {Number} index
* @returns {Coin}
@ -601,7 +658,7 @@ CoinEntry.prototype.toCoin = function toCoin(coins, index) {
};
/**
* Parse the deferred data and return an Output.
* Parse the deferred data and return an output.
* @returns {Output}
*/
@ -639,7 +696,7 @@ CoinEntry.prototype.toWriter = function toWriter(bw) {
};
/**
* Instantiate compressed coin from reader.
* Instantiate coin entry from reader.
* @param {BufferReader} br
* @returns {CoinEntry}
*/
@ -653,7 +710,7 @@ CoinEntry.fromReader = function fromReader(br) {
};
/**
* Instantiate compressed coin from output.
* Instantiate coin entry from output.
* @param {Output} output
* @returns {CoinEntry}
*/
@ -665,7 +722,7 @@ CoinEntry.fromOutput = function fromOutput(output) {
};
/**
* Instantiate compressed coin from coin.
* Instantiate coin entry from coin.
* @param {Coin} coin
* @returns {CoinEntry}
*/

View File

@ -1,5 +1,5 @@
/*!
* coinview.js - coinview object for bcoin
* coinview.js - coin viewpoint object for bcoin
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
@ -12,20 +12,22 @@ var Coins = require('./coins');
var UndoCoins = require('./undocoins');
var BufferReader = require('../utils/reader');
var BufferWriter = require('../utils/writer');
var CoinEntry = Coins.CoinEntry;
/**
* A collections of {@link Coins} objects.
* @exports CoinView
* A collection of {@link Coins} objects.
* @constructor
* @param {Object} coins - A hash-to-coins map.
* @property {Object} coins
* @param {Object} map - A hash-to-coins map.
* @param {UndoCoins} undo - Spent coins.
* @property {Object} map
* @property {UndoCoins} undo
*/
function CoinView() {
if (!(this instanceof CoinView))
return new CoinView();
this.unspent = {};
this.map = {};
this.undo = new UndoCoins();
}
@ -36,7 +38,7 @@ function CoinView() {
*/
CoinView.prototype.get = function get(hash) {
return this.unspent[hash];
return this.map[hash];
};
/**
@ -46,7 +48,7 @@ CoinView.prototype.get = function get(hash) {
*/
CoinView.prototype.has = function has(hash) {
return this.unspent[hash] != null;
return this.map[hash] != null;
};
/**
@ -55,10 +57,25 @@ CoinView.prototype.has = function has(hash) {
*/
CoinView.prototype.add = function add(coins) {
this.unspent[coins.hash] = coins;
this.map[coins.hash] = coins;
return coins;
};
/**
* Remove coins from the collection.
* @param {Coins} coins
* @returns {Boolean}
*/
CoinView.prototype.remove = function remove(hash) {
if (!this.map[hash])
return false;
delete this.map[hash];
return true;
};
/**
* Add a tx to the collection.
* @param {TX} tx
@ -83,9 +100,8 @@ CoinView.prototype.removeTX = function removeTX(tx, height) {
};
/**
* Add a tx to the collection.
* @param {TX} tx
* @param {Number} height
* Add a coin to the collection.
* @param {Coin} coin
*/
CoinView.prototype.addCoin = function addCoin(coin) {
@ -104,9 +120,10 @@ CoinView.prototype.addCoin = function addCoin(coin) {
};
/**
* Add a tx to the collection.
* @param {TX} tx
* @param {Number} height
* Add an output to the collection.
* @param {Hash} hash
* @param {Number} index
* @param {Output} output
*/
CoinView.prototype.addOutput = function addOutput(hash, index, output) {
@ -125,10 +142,42 @@ CoinView.prototype.addOutput = function addOutput(hash, index, output) {
};
/**
* Remove a coin and return it.
* Spend an output.
* @param {Hash} hash
* @param {Number} index
* @returns {Boolean}
*/
CoinView.prototype.spendOutput = function spendOutput(hash, index) {
var coins = this.get(hash);
if (!coins)
return false;
return this.spendFrom(coins, index);
};
/**
* Remove an output.
* @param {Hash} hash
* @param {Number} index
* @returns {Boolean}
*/
CoinView.prototype.removeOutput = function removeOutput(hash, index) {
var coins = this.get(hash);
if (!coins)
return false;
return coins.remove(index);
};
/**
* Spend a coin from coins object.
* @param {Coins} coins
* @param {Number} index
* @returns {Coin}
* @returns {Boolean}
*/
CoinView.prototype.spendFrom = function spendFrom(coins, index) {
@ -152,9 +201,8 @@ CoinView.prototype.spendFrom = function spendFrom(coins, index) {
};
/**
* Get a single coin.
* @param {Hash} hash
* @param {Number} index
* Get a single coin by input.
* @param {Input} input
* @returns {Coin}
*/
@ -168,26 +216,9 @@ CoinView.prototype.getCoin = function getCoin(input) {
};
/**
* Get a single coin.
* @param {Hash} hash
* @param {Number} index
* @returns {Coin}
*/
CoinView.prototype.getHeight = function getHeight(input) {
var coins = this.get(input.prevout.hash);
if (!coins)
return -1;
return coins.height;
};
/**
* Get a single coin.
* @param {Hash} hash
* @param {Number} index
* @returns {Coin}
* Get a single output by input.
* @param {Input} input
* @returns {Output}
*/
CoinView.prototype.getOutput = function getOutput(input) {
@ -200,13 +231,74 @@ CoinView.prototype.getOutput = function getOutput(input) {
};
/**
* Retrieve coins from database.
* @param {TX} tx
* @returns {Promise}
* Get a single entry by input.
* @param {Input} input
* @returns {CoinEntry}
*/
CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
var coins = this.unspent[hash];
CoinView.prototype.getEntry = function getEntry(input) {
var coins = this.get(input.prevout.hash);
if (!coins)
return;
return coins.get(input.prevout.index);
};
/**
* Test whether the view has an entry by input.
* @param {Input} input
* @returns {Boolean}
*/
CoinView.prototype.hasEntry = function hasEntry(input) {
var coins = this.get(input.prevout.hash);
if (!coins)
return false;
return coins.has(input.prevout.index);
};
/**
* Get coins height by input.
* @param {Input} input
* @returns {Number}
*/
CoinView.prototype.getHeight = function getHeight(input) {
var coins = this.get(input.prevout.hash);
if (!coins)
return -1;
return coins.height;
};
/**
* Get coins coinbase flag by input.
* @param {Input} input
* @returns {Boolean}
*/
CoinView.prototype.isCoinbase = function isCoinbase(input) {
var coins = this.get(input.prevout.hash);
if (!coins)
return false;
return coins.coinbase;
};
/**
* Retrieve coins from database.
* @param {ChainDB} db
* @param {TX} tx
* @returns {Promise} - Returns {@link Coins}.
*/
CoinView.prototype.readCoins = co(function* readCoins(db, hash) {
var coins = this.map[hash];
if (!coins) {
coins = yield db.getCoins(hash);
@ -214,7 +306,7 @@ CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
if (!coins)
return;
this.unspent[hash] = coins;
this.map[hash] = coins;
}
return coins;
@ -224,21 +316,27 @@ CoinView.prototype.getCoins = co(function* getCoins(db, hash) {
* Read all input coins into unspent map.
* @param {ChainDB} db
* @param {TX} tx
* @returns {Promise} - Returns {Boolean}.
*/
CoinView.prototype.ensureInputs = co(function* ensureInputs(db, tx) {
var found = true;
var i, input;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
yield this.getCoins(db, input.prevout.hash);
if (!(yield this.readCoins(db, input.prevout.hash)))
found = false;
}
return found;
});
/**
* Spend coins for transaction.
* @param {ChainDB} db
* @param {TX} tx
* @returns {Boolean}
* @returns {Promise} - Returns {Boolean}.
*/
CoinView.prototype.spendInputs = co(function* spendInputs(db, tx) {
@ -247,7 +345,7 @@ CoinView.prototype.spendInputs = co(function* spendInputs(db, tx) {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
coins = yield this.getCoins(db, prevout.hash);
coins = yield this.readCoins(db, prevout.hash);
if (!coins)
return false;
@ -265,24 +363,26 @@ CoinView.prototype.spendInputs = co(function* spendInputs(db, tx) {
*/
CoinView.prototype.toArray = function toArray() {
var keys = Object.keys(this.unspent);
var keys = Object.keys(this.map);
var out = [];
var i, hash;
for (i = 0; i < keys.length; i++) {
hash = keys[i];
out.push(this.unspent[hash]);
out.push(this.map[hash]);
}
return out;
};
/**
* Convert collection to an array.
* @returns {Coins[]}
* Write coin data to buffer writer
* as it pertains to a transaction.
* @param {BufferWriter} bw
* @param {TX} tx
*/
CoinView.prototype.toPrevWriter = function toPrevWriter(bw, tx) {
CoinView.prototype.toFast = function toFast(bw, tx) {
var i, input, coins, entry;
for (i = 0; i < tx.inputs.length; i++) {
@ -309,58 +409,157 @@ CoinView.prototype.toPrevWriter = function toPrevWriter(bw, tx) {
};
/**
* Convert collection to an array.
* @returns {Coins[]}
* Read serialized view data from a buffer
* reader as it pertains to a transaction.
* @private
* @param {BufferReader} br
* @param {TX} tx
*/
CoinView.prototype.toPrev = function toPrev(tx) {
return this.toPrevWriter(new BufferWriter()).render();
};
/**
* Convert collection to an array.
* @returns {Coins[]}
*/
CoinView.prototype.fromPrevReader = function fromPrevReader(br, tx) {
var i, input, coins, entry;
CoinView.prototype.fromFast = function fromFast(br, tx) {
var i, input, prevout, coins, entry;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
coins = this.get(input.prevout.hash);
prevout = input.prevout.hash;
if (br.readU8() === 0)
continue;
coins = this.get(prevout.hash);
if (!coins) {
coins = new Coins();
coins.hash = input.prevout.hash;
coins.hash = prevout.hash;
coins.coinbase = false;
this.add(coins);
}
if (br.readU8() === 1) {
entry = Coins.CoinEntry.fromReader(br);
coins.add(input.prevout.index, entry);
}
entry = CoinEntry.fromReader(br);
coins.add(prevout.index, entry);
}
return this;
};
/**
* Convert collection to an array.
* @returns {Coins[]}
* Write coin data to buffer writer
* as it pertains to a transaction.
* @param {BufferWriter} bw
* @param {TX} tx
*/
CoinView.fromPrevReader = function fromPrevReader(br, tx) {
return new CoinView().fromPrevReader(br, tx);
CoinView.prototype.toWriter = function toWriter(bw, tx) {
var map = {};
var i, input, prevout, coins, entry, height;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
coins = this.get(prevout.hash);
if (!coins) {
bw.writeU8(0);
continue;
}
entry = coins.get(prevout.index);
if (!entry) {
bw.writeU8(0);
continue;
}
bw.writeU8(1);
if (!map[prevout.hash]) {
height = coins.height;
if (height === -1)
height = 0;
bw.writeVarint(height * 2 + (coins.coinbase ? 1 : 0));
bw.writeVarint(coins.version);
map[prevout.hash] = true;
}
entry.toWriter(bw);
}
return bw;
};
/**
* Convert collection to an array.
* @returns {Coins[]}
* Serialize coin data to as it
* pertains to a transaction.
* @param {TX} tx
* @returns {Buffer}
*/
CoinView.fromPrev = function fromPrev(data, tx) {
return new CoinView().fromPrevReader(new BufferReader(data), tx);
CoinView.prototype.toRaw = function toRaw(tx) {
return this.toWriter(new BufferWriter()).render();
};
/**
* Read serialized view data from a buffer
* reader as it pertains to a transaction.
* @private
* @param {BufferReader} br
* @param {TX} tx
*/
CoinView.prototype.fromReader = function fromReader(br, tx) {
var i, input, prevout, coins, entry, height;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout.hash;
if (br.readU8() === 0)
continue;
coins = this.get(prevout.hash);
if (!coins) {
coins = new Coins();
coins.hash = prevout.hash;
height = br.readVarint();
coins.coinbase = (height & 1) !== 0;
height = height / 2 | 0;
if (height === 0)
height = -1;
coins.height = height;
coins.version = br.readVarint();
this.add(coins);
}
entry = CoinEntry.fromReader(br);
coins.add(prevout.index, entry);
}
return this;
};
/**
* Read serialized view data from a buffer
* reader as it pertains to a transaction.
* @param {BufferReader} br
* @param {TX} tx
* @returns {CoinView}
*/
CoinView.fromReader = function fromReader(br, tx) {
return new CoinView().fromReader(br, tx);
};
/**
* Read serialized view data from a buffer
* as it pertains to a transaction.
* @param {Buffer} data
* @param {TX} tx
* @returns {CoinView}
*/
CoinView.fromRaw = function fromRaw(data, tx) {
return new CoinView().fromReader(new BufferReader(data), tx);
};
/*

View File

@ -123,7 +123,6 @@ UndoCoins.prototype.top = function top() {
* Re-apply undo coins to a view, effectively unspending them.
* @param {CoinView} view
* @param {Outpoint} outpoint
* @returns {Coin}
*/
UndoCoins.prototype.apply = function apply(view, outpoint) {
@ -137,15 +136,15 @@ UndoCoins.prototype.apply = function apply(view, outpoint) {
if (undo.height !== -1) {
coins = new Coins();
assert(!view.unspent[hash]);
view.unspent[hash] = coins;
assert(!view.map[hash]);
view.map[hash] = coins;
coins.hash = hash;
coins.coinbase = undo.coinbase;
coins.height = undo.height;
coins.version = undo.version;
} else {
coins = view.unspent[hash];
coins = view.map[hash];
assert(coins);
}

View File

@ -617,7 +617,7 @@ HTTPServer.prototype._init = function _init() {
coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON(this.network);
return coin.getJSON(this.network);
}, this));
}));
@ -633,7 +633,7 @@ HTTPServer.prototype._init = function _init() {
if (!coin)
return send(404);
send(200, coin.toJSON(this.network));
send(200, coin.getJSON(this.network));
}));
// Bulk read UTXOs
@ -645,7 +645,7 @@ HTTPServer.prototype._init = function _init() {
coins = yield this.node.getCoinsByAddress(req.options.address);
send(200, coins.map(function(coin) {
return coin.toJSON(this.network);
return coin.getJSON(this.network);
}, this));
}));
@ -660,7 +660,7 @@ HTTPServer.prototype._init = function _init() {
if (!tx)
return send(404);
send(200, tx.toJSON(this.network));
send(200, tx.getJSON(this.network));
}));
// TX by address
@ -672,7 +672,7 @@ HTTPServer.prototype._init = function _init() {
txs = yield this.node.getTXByAddress(req.options.address);
send(200, txs.map(function(tx) {
return tx.toJSON(this.network);
return tx.getJSON(this.network);
}, this));
}));
@ -685,7 +685,7 @@ HTTPServer.prototype._init = function _init() {
txs = yield this.node.getTXByAddress(req.options.address);
send(200, txs.map(function(tx) {
return tx.toJSON(this.network);
return tx.getJSON(this.network);
}, this));
}));
@ -706,7 +706,7 @@ HTTPServer.prototype._init = function _init() {
if (!view)
return send(404);
send(200, block.toJSON(this.network, view));
send(200, block.getJSON(this.network, view));
}));
// Mempool snapshot
@ -719,7 +719,7 @@ HTTPServer.prototype._init = function _init() {
txs = this.mempool.getHistory();
send(200, txs.map(function(tx) {
return tx.toJSON(this.network);
return tx.getJSON(this.network);
}, this));
}));
@ -910,7 +910,7 @@ HTTPServer.prototype._init = function _init() {
var passphrase = options.passphrase;
var tx = yield req.wallet.createTX(options);
yield req.wallet.sign(tx, passphrase);
send(200, tx.toJSON(this.network));
send(200, tx.getJSON(this.network));
}));
// Sign TX
@ -920,7 +920,7 @@ HTTPServer.prototype._init = function _init() {
var tx = req.options.tx;
enforce(tx, 'TX is required.');
yield req.wallet.sign(tx, passphrase);
send(200, tx.toJSON(this.network));
send(200, tx.getJSON(this.network));
}));
// Zap Wallet TXs
@ -1060,7 +1060,7 @@ HTTPServer.prototype._init = function _init() {
sortCoins(coins);
send(200, coins.map(function(coin) {
return coin.toJSON(this.network);
return coin.getJSON(this.network);
}, this));
}));
@ -1112,7 +1112,7 @@ HTTPServer.prototype._init = function _init() {
if (!coin)
return send(404);
send(200, coin.toJSON(this.network));
send(200, coin.getJSON(this.network));
}));
// Wallet TXs
@ -1898,7 +1898,7 @@ ClientSocket.prototype.frameEntry = function frameEntry(entry) {
ClientSocket.prototype.frameTX = function frameTX(tx) {
if (this.raw)
return tx.toRaw();
return tx.toJSON(this.network);
return tx.getJSON(this.network);
};
ClientSocket.prototype.join = function join(id) {

View File

@ -420,7 +420,8 @@ Mempool.prototype.getCoin = function getCoin(hash, index) {
*/
Mempool.prototype.isSpent = function isSpent(hash, index) {
return this.spents[hash + index] != null;
var key = Outpoint.toKey(hash, index);
return this.spents[key] != null;
};
/**
@ -431,7 +432,8 @@ Mempool.prototype.isSpent = function isSpent(hash, index) {
*/
Mempool.prototype.getSpent = function getSpent(hash, index) {
return this.spents[hash + index];
var key = Outpoint.toKey(hash, index);
return this.spents[key];
};
/**
@ -442,7 +444,8 @@ Mempool.prototype.getSpent = function getSpent(hash, index) {
*/
Mempool.prototype.getSpentTX = function getSpentTX(hash, index) {
var entry = this.spents[hash + index];
var key = Outpoint.toKey(hash, index);
var entry = this.spents[key];
if (!entry)
return;
@ -732,6 +735,7 @@ Mempool.prototype._addTX = co(function* _addTX(tx) {
* This function will also resolve orphans if possible (the
* resolved orphans _will_ be validated).
* @param {MempoolEntry} entry
* @param {CoinView} view
* @returns {Promise}
*/
@ -809,6 +813,7 @@ Mempool.prototype.removeEntry = function removeEntry(entry, limit) {
/**
* Verify a transaction with mempool standards.
* @param {TX} tx
* @param {CoinView} view
* @returns {Promise}
*/
@ -947,6 +952,7 @@ Mempool.prototype.verify = co(function* verify(entry, view) {
* Verify inputs, return a boolean
* instead of an error based on success.
* @param {TX} tx
* @param {CoinView} view
* @param {VerifyFlags} flags
* @returns {Promise}
*/
@ -966,6 +972,7 @@ Mempool.prototype.verifyResult = co(function* verifyResult(tx, view, flags) {
* Verify inputs for standard
* _and_ mandatory flags on failure.
* @param {TX} tx
* @param {CoinView} view
* @param {VerifyFlags} flags
* @returns {Promise}
*/
@ -1418,21 +1425,13 @@ Mempool.prototype.removeOrphan = function removeOrphan(hash) {
*/
Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx) {
var spent = {};
var i, input, prevout, key;
var i, input, prevout;
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
prevout = input.prevout;
key = prevout.hash + prevout.index;
if (this.isSpent(prevout.hash, prevout.index))
return true;
if (spent[key])
return true;
spent[key] = true;
}
return false;
@ -1441,6 +1440,7 @@ Mempool.prototype.isDoubleSpend = function isDoubleSpend(tx) {
/**
* Get coin viewpoint.
* @param {TX} tx
* @param {CoinView} view
* @returns {Promise} - Returns {@link CoinView}.
*/
@ -1476,6 +1476,7 @@ Mempool.prototype.getCoinView = co(function* getCoinView(tx) {
/**
* Spend coins for transaction.
* @param {TX} tx
* @param {CoinView} view
* @returns {Boolean}
*/
@ -1511,6 +1512,7 @@ Mempool.prototype.getSnapshot = function getSnapshot() {
/**
* Check sequence locks on a transaction against the current tip.
* @param {TX} tx
* @param {CoinView} view
* @param {LockFlags} flags
* @returns {Promise} - Returns Boolean.
*/
@ -1534,6 +1536,7 @@ Mempool.prototype.verifyFinal = function verifyFinal(tx, flags) {
* Map a transaction to the mempool.
* @private
* @param {MempoolEntry} entry
* @param {CoinView} view
*/
Mempool.prototype.trackEntry = function trackEntry(entry, view) {
@ -1550,7 +1553,7 @@ Mempool.prototype.trackEntry = function trackEntry(entry, view) {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
key = input.prevout.hash + input.prevout.index;
key = input.prevout.toKey();
if (this.options.indexAddress)
this.coinIndex.removeCoin(input.prevout);
@ -1595,7 +1598,7 @@ Mempool.prototype.untrackEntry = function untrackEntry(entry) {
for (i = 0; i < tx.inputs.length; i++) {
input = tx.inputs[i];
key = input.prevout.hash + input.prevout.index;
key = input.prevout.toKey();
if (this.options.indexAddress)
this.coinIndex.removeCoin(input.prevout);
@ -1821,7 +1824,7 @@ AddressIndex.prototype.getCoins = function getCoins(address) {
};
AddressIndex.prototype.addCoin = function addCoin(coin) {
var key = coin.hash + coin.index;
var key = coin.toKey();
var hash = coin.getHash('hex');
var outpoint, items;
@ -1842,7 +1845,7 @@ AddressIndex.prototype.addCoin = function addCoin(coin) {
};
AddressIndex.prototype.removeCoin = function removeCoin(coin) {
var key = coin.hash + coin.index;
var key = coin.toKey();
var hash = this.map[key];
var outpoint, items;

View File

@ -617,6 +617,7 @@ Block.prototype.inspect = function inspect() {
/**
* Inspect the block and return a more
* user-friendly representation of the data.
* @param {CoinView?} view
* @returns {Object}
*/
@ -645,13 +646,25 @@ Block.prototype.format = function format(view) {
/**
* Convert the block to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* for JSON serialization.
* @returns {Object}
*/
Block.prototype.toJSON = function toJSON(network, view) {
Block.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the block to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {CoinView} view
* @returns {Object}
*/
Block.prototype.getJSON = function getJSON(network, view) {
network = Network.get(network);
return {
hash: this.rhash(),
@ -664,7 +677,7 @@ Block.prototype.toJSON = function toJSON(network, view) {
nonce: this.nonce,
totalTX: this.totalTX,
txs: this.txs.map(function(tx) {
return tx.toJSON(network, view);
return tx.getJSON(network, view);
})
};
};

View File

@ -110,6 +110,40 @@ Coin.prototype.getConfirmations = function getConfirmations(height) {
return height - this.height + 1;
};
/**
* Serialize coin to a key
* suitable for a hash table.
* @returns {String}
*/
Coin.prototype.toKey = function toKey() {
return this.hash + this.index;
};
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Coin}
*/
Coin.prototype.fromKey = function fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = +key.slice(64);
return this;
};
/**
* Instantiate coin from hash table key.
* @param {String} key
* @returns {Coin}
*/
Coin.fromKey = function fromKey(key) {
return new Coin().fromKey(key);
};
/**
* Convert the coin to a more user-friendly object.
* @returns {Object}
@ -131,13 +165,25 @@ Coin.prototype.inspect = function inspect() {
/**
* Convert the coin to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* for JSON serialization.
* @returns {Object}
*/
Coin.prototype.toJSON = function toJSON(network, minimal) {
Coin.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the coin to an object suitable
* for JSON serialization. Note that the hash
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Boolean} minimal
* @returns {Object}
*/
Coin.prototype.getJSON = function getJSON(network, minimal) {
var address = this.getAddress();
network = Network.get(network);

View File

@ -81,6 +81,7 @@ Input.fromOptions = function fromOptions(options) {
* Get the previous output script type as a string.
* Will "guess" based on the input script and/or
* witness if coin is not available.
* @param {Coin?} coin
* @returns {ScriptType} type
*/
@ -104,6 +105,7 @@ Input.prototype.getType = function getType(coin) {
/**
* Get the redeem script. Will attempt to resolve nested
* redeem scripts if witnessscripthash is behind a scripthash.
* @param {Coin?} coin
* @returns {Script?} Redeem script.
*/
@ -140,6 +142,7 @@ Input.prototype.getRedeem = function getRedeem(coin) {
/**
* Get the redeem script type.
* @param {Coin?} coin
* @returns {String} subtype
*/
@ -163,6 +166,7 @@ Input.prototype.getSubtype = function getSubtype(coin) {
* Get the previous output script's address. Will "guess"
* based on the input script and/or witness if coin
* is not available.
* @param {Coin?} coin
* @returns {Address?} address
*/
@ -230,6 +234,7 @@ Input.prototype.inspect = function inspect() {
/**
* Convert the input to a more user-friendly object.
* @param {Coin?} coin
* @returns {Object}
*/
@ -249,13 +254,25 @@ Input.prototype.format = function format(coin) {
/**
* Convert the input to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* for JSON serialization.
* @returns {Object}
*/
Input.prototype.toJSON = function toJSON(network, coin) {
return this.getJSON();
};
/**
* Convert the input to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {Coin} coin
* @returns {Object}
*/
Input.prototype.getJSON = function getJSON(network, coin) {
var address;
network = Network.get(network);
@ -272,7 +289,7 @@ Input.prototype.toJSON = function toJSON(network, coin) {
witness: this.witness.toJSON(),
sequence: this.sequence,
address: address,
coin: coin ? coin.toJSON(network, true) : null
coin: coin ? coin.getJSON(network, true) : null
};
};

View File

@ -20,7 +20,7 @@ var Input = require('./input');
var Output = require('./output');
var Coin = require('./coin');
var Outpoint = require('./outpoint');
var CoinMap = require('../blockchain/coinview');
var CoinView = require('../blockchain/coinview');
var KeyRing = require('./keyring');
var Address = require('./address');
var workerPool = require('../workers/workerpool').pool;
@ -55,13 +55,7 @@ var encoding = require('../utils/encoding');
* @property {Number} changeIndex - Index of the change output (-1 if unknown).
* @property {Number} height - Height of the block the
* transaction was included in (-1 if unconfirmed).
* @property {ReversedHash|null} rblock - Reversed block hash (uint256le).
* @property {ReversedHash} rhash - Reversed transaction hash (uint256le).
* @property {ReversedHash} rwhash - Reversed witness
* transaction hash (uint256le).
* @property {String} txid - Transaction ID.
* @property {String} wtxid - Witness transaction ID (Same as txid if no
* witness is present. All zeroes if coinbase).
* @property {CoinView} view
*/
function MTX(options) {
@ -72,7 +66,7 @@ function MTX(options) {
this.mutable = true;
this.changeIndex = -1;
this.view = new CoinMap();
this.view = new CoinView();
if (options)
this.fromOptions(options);
@ -1341,14 +1335,23 @@ MTX.prototype.format = function format() {
return TX.prototype.inspect.call(this, this.view);
};
/**
* Convert transaction to JSON.
* @returns {Object}
*/
MTX.prototype.toJSON = function toJSON() {
return TX.prototype.toJSON.call(this, null, this.view);
};
/**
* Convert transaction to JSON.
* @param {Network} network
* @returns {Object}
*/
MTX.prototype.toJSON = function toJSON(network) {
return TX.prototype.toJSON.call(this, network, this.view);
MTX.prototype.getJSON = function getJSON(network) {
return TX.prototype.getJSON.call(this, network, this.view);
};
/**

View File

@ -64,6 +64,40 @@ Outpoint.prototype.isNull = function isNull() {
return this.index === 0xffffffff && this.hash === constants.NULL_HASH;
};
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @returns {String}
*/
Outpoint.prototype.toKey = function toKey() {
return Outpoint.toKey(this.hash, this.index);
};
/**
* Inject properties from hash table key.
* @private
* @param {String} key
* @returns {Outpoint}
*/
Outpoint.prototype.fromKey = function fromKey(key) {
assert(key.length > 64);
this.hash = key.slice(0, 64);
this.index = +key.slice(64);
return this;
};
/**
* Instantiate outpoint from hash table key.
* @param {String} key
* @returns {Outpoint}
*/
Outpoint.fromKey = function fromKey(key) {
return new Outpoint().fromKey(key);
};
/**
* Write outpoint to a buffer writer.
* @param {BufferWriter} bw
@ -190,6 +224,21 @@ Outpoint.fromTX = function fromTX(tx, index) {
return new Outpoint().fromTX(tx, index);
};
/**
* Serialize outpoint to a key
* suitable for a hash table.
* @param {Hash} hash
* @param {Number} index
* @returns {String}
*/
Outpoint.toKey = function toKey(hash, index) {
assert(typeof hash === 'string');
assert(hash.length === 64);
assert(index >= 0);
return hash + index;
};
/**
* Convert the outpoint to a user-friendly string.
* @returns {String}

View File

@ -131,7 +131,18 @@ Output.prototype.inspect = function inspect() {
* @returns {Object}
*/
Output.prototype.toJSON = function toJSON(network) {
Output.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the output to an object suitable
* for JSON serialization.
* @param {Network} network
* @returns {Object}
*/
Output.prototype.getJSON = function getJSON(network) {
var address = this.getAddress();
network = Network.get(network);

View File

@ -58,13 +58,6 @@ var BAD_NONSTD_P2WSH = 3;
* was first seen. Only non-zero on unconfirmed transactions.
* @property {Number} height - Height of the block the
* transaction was included in (-1 if unconfirmed).
* @property {ReversedHash|null} rblock - Reversed block hash (uint256le).
* @property {ReversedHash} rhash - Reversed transaction hash (uint256le).
* @property {ReversedHash} rwhash - Reversed witness
* transaction hash (uint256le).
* @property {String} txid - Transaction ID.
* @property {String} wtxid - Witness transaction ID (Same as txid if no
* witness is present. All zeroes if coinbase).
*/
function TX(options) {
@ -689,7 +682,8 @@ TX.prototype.signatureHashV1 = function signatureHashV1(index, prev, value, type
/**
* Verify all transaction inputs.
* @param {VerifyFlags} [flags=STANDARD_VERIFY_FLAGS]
* @param {CoinView} view
* @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
* @returns {Boolean} Whether the inputs are valid.
*/
@ -753,6 +747,7 @@ TX.prototype.verifyInput = function verifyInput(index, coin, flags) {
/**
* Verify the transaction inputs on the worker pool
* (if workers are enabled).
* @param {CoinView} view
* @param {VerifyFlags?} [flags=STANDARD_VERIFY_FLAGS]
* @returns {Promise}
* @returns {Boolean} Whether the inputs are valid.
@ -817,6 +812,7 @@ TX.prototype.isRBF = function isRBF() {
/**
* Calculate the fee for the transaction.
* @param {CoinView} view
* @returns {Amount} fee (zero if not all coins are available).
*/
@ -829,6 +825,7 @@ TX.prototype.getFee = function getFee(view) {
/**
* Calculate the total input value.
* @param {CoinView} view
* @returns {Amount} value
*/
@ -881,6 +878,7 @@ TX.prototype.getOutputValue = function getOutputValue() {
/**
* Get all input addresses.
* @private
* @param {CoinView} view
* @returns {Array}
*/
@ -943,6 +941,7 @@ TX.prototype._getOutputAddresses = function getOutputAddresses() {
/**
* Get all addresses.
* @private
* @param {CoinView} view
* @returns {Array}
*/
@ -968,6 +967,7 @@ TX.prototype._getAddresses = function getAddresses(view) {
/**
* Get all input addresses.
* @private
* @param {CoinView} view
* @returns {Address[]} addresses
*/
@ -986,6 +986,7 @@ TX.prototype.getOutputAddresses = function getOutputAddresses() {
/**
* Get all addresses.
* @param {CoinView} view
* @returns {Address[]} addresses
*/
@ -995,6 +996,7 @@ TX.prototype.getAddresses = function getAddresses(view) {
/**
* Get all input address hashes.
* @param {CoinView} view
* @returns {Hash[]} hashes
*/
@ -1037,6 +1039,7 @@ TX.prototype.getOutputHashes = function getOutputHashes(enc) {
/**
* Get all address hashes.
* @param {CoinView} view
* @returns {Hash[]} hashes
*/
@ -1059,6 +1062,7 @@ TX.prototype.getHashes = function getHashes(view, enc) {
/**
* Test whether the transaction has
* all coins available/filled.
* @param {CoinView} view
* @returns {Boolean}
*/
@ -1135,6 +1139,7 @@ TX.prototype.getLegacySigops = function getLegacySigops() {
/**
* Calculate accurate sigop count, taking into account redeem scripts.
* @param {CoinView} view
* @returns {Number} sigop count
*/
@ -1161,6 +1166,7 @@ TX.prototype.getScripthashSigops = function getScripthashSigops(view) {
/**
* Calculate sigops weight, taking into account witness programs.
* @param {CoinView} view
* @param {VerifyFlags?} flags
* @returns {Number} sigop weight
*/
@ -1197,6 +1203,7 @@ TX.prototype.getSigopsWeight = function getSigopsWeight(view, flags) {
/**
* Calculate virtual sigop count.
* @param {CoinView} view
* @param {VerifyFlags?} flags
* @returns {Number} sigop count
*/
@ -1271,7 +1278,7 @@ TX.prototype.isSane = function isSane(ret) {
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
key = input.prevout.hash + input.prevout.index;
key = input.prevout.toKey();
if (prevout[key]) {
ret.reason = 'bad-txns-inputs-duplicate';
ret.score = 100;
@ -1388,6 +1395,7 @@ TX.prototype.isStandard = function isStandard(ret) {
* Perform contextual checks to verify coin and input
* script standardness (including the redeem script).
* @see AreInputsStandard()
* @param {CoinView} view
* @param {VerifyFlags?} flags
* @returns {Boolean}
*/
@ -1431,6 +1439,7 @@ TX.prototype.hasStandardInputs = function hasStandardInputs(view) {
/**
* Perform contextual checks to verify coin and witness standardness.
* @see IsBadWitness()
* @param {CoinView} view
* @returns {Boolean}
*/
@ -1464,6 +1473,7 @@ TX.prototype.hasStandardWitness = function hasStandardWitness(view, ret) {
* Perform contextual checks to verify coin and witness standardness.
* @private
* @see IsBadWitness()
* @param {CoinView} view
* @returns {Boolean}
*/
@ -1595,6 +1605,7 @@ TX.prototype.getWitnessStandard = function getWitnessStandard(view) {
* (coinbases can only be spent 100 blocks or more
* after they're created). Note that this function is
* consensus critical.
* @param {CoinView} view
* @param {Number} spendHeight - Height at which the
* transaction is being spent. In the mempool this is
* the chain height plus one at the time it entered the pool.
@ -1698,6 +1709,7 @@ TX.prototype.getModifiedSize = function getModifiedSize(size) {
/**
* Calculate the transaction priority.
* @param {CoinView} view
* @param {Number?} height - If not present, tx height
* or network height will be used.
* @param {Number?} size - Size to calculate priority
@ -1740,6 +1752,7 @@ TX.prototype.getPriority = function getPriority(view, height, size) {
/**
* Calculate the transaction's on-chain value.
* @param {CoinView} view
* @param {Number?} height
* @returns {Number}
*/
@ -1778,6 +1791,7 @@ TX.prototype.getChainValue = function getChainValue(view, height) {
* free threshold in priority. A transaction which
* passed this test is most likely relayable
* without a fee.
* @param {CoinView} view
* @param {Number?} height - If not present, tx
* height or network height will be used.
* @param {Number?} size - If not present, modified
@ -1826,6 +1840,7 @@ TX.prototype.getRoundFee = function getRoundFee(size, rate) {
/**
* Calculate the transaction's rate based on size
* and fees. Size will be calculated if not present.
* @param {CoinView} view
* @param {Number?} size
* @returns {Rate}
*/
@ -2009,6 +2024,7 @@ TX.prototype.inspect = function inspect() {
/**
* Inspect the transaction and return a more
* user-friendly representation of the data.
* @param {CoinView} view
* @returns {Object}
*/
@ -2026,8 +2042,8 @@ TX.prototype.format = function format(view) {
}
return {
hash: this.rhash(),
witnessHash: this.rwhash(),
hash: this.txid(),
witnessHash: this.wtxid(),
size: this.getSize(),
virtualSize: this.getVirtualSize(),
height: this.height,
@ -2053,13 +2069,25 @@ TX.prototype.format = function format(view) {
/**
* Convert the transaction to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* for JSON serialization.
* @returns {Object}
*/
TX.prototype.toJSON = function toJSON(network, view) {
TX.prototype.toJSON = function toJSON() {
return this.getJSON();
};
/**
* Convert the transaction to an object suitable
* for JSON serialization. Note that the hashes
* will be reversed to abide by bitcoind's legacy
* of little-endian uint256s.
* @param {Network} network
* @param {CoinView} view
* @returns {Object}
*/
TX.prototype.getJSON = function getJSON(network, view) {
var rate = 0;
var fee = 0;
@ -2075,10 +2103,10 @@ TX.prototype.toJSON = function toJSON(network, view) {
network = Network.get(network);
return {
hash: util.revHex(this.hash('hex')),
witnessHash: util.revHex(this.witnessHash('hex')),
hash: this.txid(),
witnessHash: this.wtxid(),
height: this.height,
block: this.block ? util.revHex(this.block) : null,
block: this.rblock(),
ts: this.ts,
ps: this.ps,
date: util.date(this.ts || this.ps),
@ -2089,10 +2117,10 @@ TX.prototype.toJSON = function toJSON(network, view) {
flag: this.flag,
inputs: this.inputs.map(function(input) {
var coin = view ? view.getCoin(input) : null;
return input.toJSON(network, coin);
return input.getJSON(network, coin);
}),
outputs: this.outputs.map(function(output) {
return output.toJSON(network);
return output.getJSON(network);
}),
locktime: this.locktime
};
@ -2398,8 +2426,10 @@ TX.prototype.frameWitnessWriter = function frameWitnessWriter(bw) {
start = bw.written;
for (i = 0; i < this.inputs.length; i++)
this.inputs[i].witness.toWriter(bw);
for (i = 0; i < this.inputs.length; i++) {
input = this.inputs[i];
input.witness.toWriter(bw);
}
witnessSize += bw.written - start;

View File

@ -390,7 +390,7 @@ TXDB.prototype.verifyInputs = co(function* verifyInputs(tx, block) {
if (!orphans[i])
continue;
key = prevout.hash + prevout.index;
key = prevout.toKey();
// In theory, someone could try to DoS
// us by creating tons of fake transactions
@ -452,7 +452,7 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved)
for (i = 0; i < tx.outputs.length; i++) {
output = tx.outputs[i];
key = hash + i;
key = Outpoint.toKey(hash, i);
orphans = this.orphans[key];
if (!orphans)
@ -505,7 +505,7 @@ TXDB.prototype.resolveOutputs = co(function* resolveOutputs(tx, block, resolved)
TXDB.prototype.saveCredit = co(function* saveCredit(credit, path) {
var coin = credit.coin;
var key = coin.hash + coin.index;
var key = coin.toKey();
var raw = credit.toRaw();
yield this.addOutpointMap(coin.hash, coin.index);
@ -524,7 +524,7 @@ TXDB.prototype.saveCredit = co(function* saveCredit(credit, path) {
TXDB.prototype.removeCredit = co(function* removeCredit(credit, path) {
var coin = credit.coin;
var key = coin.hash + coin.index;
var key = coin.toKey();
yield this.removeOutpointMap(coin.hash, coin.index);
@ -1789,7 +1789,7 @@ TXDB.prototype.unlockTX = function unlockTX(tx) {
*/
TXDB.prototype.lockCoin = function lockCoin(coin) {
var key = coin.hash + coin.index;
var key = coin.toKey();
this.locked[key] = true;
};
@ -1799,7 +1799,7 @@ TXDB.prototype.lockCoin = function lockCoin(coin) {
*/
TXDB.prototype.unlockCoin = function unlockCoin(coin) {
var key = coin.hash + coin.index;
var key = coin.toKey();
delete this.locked[key];
};
@ -1809,7 +1809,7 @@ TXDB.prototype.unlockCoin = function unlockCoin(coin) {
*/
TXDB.prototype.isLocked = function isLocked(coin) {
var key = coin.hash + coin.index;
var key = coin.toKey();
return this.locked[key] === true;
};
@ -2237,7 +2237,7 @@ TXDB.prototype.getCredits = function getCredits(account) {
var hash = parts[0];
var index = parts[1];
var credit = Credit.fromRaw(value);
var ckey = hash + index;
var ckey = Outpoint.toKey(hash, index);
credit.coin.hash = hash;
credit.coin.index = index;
self.coinCache.set(ckey, value);
@ -2514,7 +2514,7 @@ TXDB.prototype.getCoin = co(function* getCoin(hash, index) {
TXDB.prototype.getCredit = co(function* getCredit(hash, index) {
var state = this.state;
var key = hash + index;
var key = Outpoint.toKey(hash, index);
var data = this.coinCache.get(key);
var credit;
@ -2603,7 +2603,7 @@ TXDB.prototype.updateSpentCoin = co(function* updateSpentCoin(tx, index) {
*/
TXDB.prototype.hasCoin = function hasCoin(hash, index) {
var key = hash + index;
var key = Outpoint.toKey(hash, index);
if (this.coinCache.has(key))
return Promise.resolve(true);

View File

@ -1583,7 +1583,7 @@ Wallet.prototype.createTX = co(function* createTX(options, force) {
if (!tx.isSane())
throw new Error('CheckTransaction failed.');
if (!tx.checkInputs(this.db.state.height))
if (!tx.checkInputs(this.db.state.height + 1))
throw new Error('CheckInputs failed.');
total = yield this.template(tx);

View File

@ -70,6 +70,7 @@ jobs._execute = function execute(p) {
* Execute tx.verify() on worker.
* @see TX#verify
* @param {TX} tx
* @param {CoinView} view
* @param {VerifyFlags} flags
* @returns {Boolean}
*/

View File

@ -183,7 +183,7 @@ VerifyPacket.prototype.cmd = packetTypes.VERIFY;
VerifyPacket.prototype.toWriter = function(bw) {
this.tx.toWriter(bw);
this.view.toPrevWriter(bw, this.tx);
this.view.toWriter(bw, this.tx);
bw.write32(this.flags != null ? this.flags : -1);
};
@ -192,7 +192,7 @@ VerifyPacket.fromRaw = function fromRaw(TX, CoinView, data) {
var packet = new VerifyPacket();
packet.tx = TX.fromReader(br);
packet.view = CoinView.fromPrevReader(br, packet.tx);
packet.view = CoinView.fromReader(br, packet.tx);
packet.flags = br.read32();
@ -247,7 +247,7 @@ SignPacket.prototype.toWriter = function toWriter(bw) {
var i, ring;
this.tx.toWriter(bw);
this.tx.view.toPrevWriter(bw, this.tx);
this.tx.view.toWriter(bw, this.tx);
bw.writeVarint(this.rings.length);
@ -265,7 +265,7 @@ SignPacket.fromRaw = function fromRaw(MTX, KeyRing, data) {
var i, count, ring;
packet.tx = MTX.fromReader(br);
packet.tx.view.fromPrevReader(br, packet.tx);
packet.tx.view.fromReader(br, packet.tx);
count = br.readVarint();

View File

@ -260,6 +260,7 @@ WorkerPool.prototype.execute = function execute(packet, timeout) {
/**
* Execute the tx verification job (default timeout).
* @param {TX} tx
* @param {CoinView} view
* @param {VerifyFlags} flags
* @returns {Promise} - Returns Boolean.
*/

View File

@ -1,32 +1,36 @@
'use strict';
var fs = require('fs');
var heapdump = require('heapdump');
var MempoolEntry = require('../lib/mempool/mempoolentry');
var Coins = require('../lib/blockchain/coins');
var TX = require('../lib/primitives/tx');
var CoinView = require('../lib/blockchain/coinview');
var SNAPSHOT = __dirname + '/../dump.heapsnapshot';
var tx = parseTX('../test/data/tx4.hex');
var raw, coins, entry;
function parseTX(file) {
var filename = __dirname + '/' + file;
var data = fs.readFileSync(filename, 'utf8');
var data = fs.readFileSync(__dirname + '/' + file, 'utf8');
var parts = data.trim().split(/\n+/);
var hex = parts[0].trim();
var tx = TX.fromRaw(hex, 'hex');
var i, tx, coin;
var raw = parts[0];
var tx = TX.fromRaw(raw.trim(), 'hex');
var view = new CoinView();
var i, prev;
for (i = 1; i < parts.length; i++) {
hex = parts[i].trim();
coin = TX.fromRaw(hex, 'hex');
tx.fillCoins(coin);
raw = parts[i];
prev = TX.fromRaw(raw.trim(), 'hex');
view.addTX(prev, -1);
}
return tx;
return { tx: tx, view: view };
}
var coins = Coins.fromRaw(Coins.fromTX(tx).toRaw(), tx.hash('hex'));
var entry = MempoolEntry.fromTX(tx, 1000000);
raw = Coins.fromTX(tx.tx, 0).toRaw();
coins = Coins.fromRaw(raw, tx.hash('hex'));
entry = MempoolEntry.fromTX(tx, tx.view, 1000000);
setInterval(function() {
console.log(tx.hash('hex'));

View File

@ -1,10 +1,14 @@
'use strict';
var BN = require('bn.js');
var bcoin = require('bcoin');
var constants = bcoin.constants;
var util = require('../lib/utils/util');
var constants = require('../lib/protocol/constants');
var TX = require('../lib/primitives/tx');
var Block = require('../lib/primitives/block');
var Script = require('../lib/script/script');
var Opcode = require('../lib/script/opcode');
var opcodes = constants.opcodes;
var util = bcoin.util;
var main, testnet, regtest, segnet3, segnet4, btcd;
function createGenesisBlock(options) {
var flags = options.flags;
@ -19,7 +23,7 @@ function createGenesisBlock(options) {
}
if (!script) {
script = bcoin.script.fromArray([
script = Script.fromArray([
new Buffer('04678afdb0fe5548271967f1a67130b7105cd6a828e039'
+ '09a67962e0ea1f61deb649f6bc3f4cef38c4f35504e51ec112de5c3'
+ '84df7ba0b8d578a4c702b6bf11d5f', 'hex'),
@ -30,7 +34,7 @@ function createGenesisBlock(options) {
if (!reward)
reward = 50 * constants.COIN;
tx = new bcoin.tx({
tx = new TX({
version: 1,
flag: 1,
inputs: [{
@ -39,9 +43,9 @@ function createGenesisBlock(options) {
index: 0xffffffff
},
script: [
bcoin.opcode.fromNumber(new BN(486604799)),
bcoin.opcode.fromPush(new Buffer([4])),
bcoin.opcode.fromData(flags)
Opcode.fromNumber(new BN(486604799)),
Opcode.fromPush(new Buffer([4])),
Opcode.fromData(flags)
],
sequence: 0xffffffff
}],
@ -52,7 +56,7 @@ function createGenesisBlock(options) {
locktime: 0
});
block = new bcoin.block({
block = new Block({
version: options.version,
prevBlock: constants.NULL_HASH,
merkleRoot: tx.hash('hex'),
@ -67,42 +71,42 @@ function createGenesisBlock(options) {
return block;
}
var main = createGenesisBlock({
main = createGenesisBlock({
version: 1,
ts: 1231006505,
bits: 486604799,
nonce: 2083236893
});
var testnet = createGenesisBlock({
testnet = createGenesisBlock({
version: 1,
ts: 1296688602,
bits: 486604799,
nonce: 414098458
});
var regtest = createGenesisBlock({
regtest = createGenesisBlock({
version: 1,
ts: 1296688602,
bits: 545259519,
nonce: 2
});
var segnet3 = createGenesisBlock({
segnet3 = createGenesisBlock({
version: 1,
ts: 1452831101,
bits: 486604799,
nonce: 0
});
var segnet4 = createGenesisBlock({
segnet4 = createGenesisBlock({
version: 1,
ts: 1452831101,
bits: 503447551,
nonce: 0
});
var btcd = createGenesisBlock({
btcd = createGenesisBlock({
version: 1,
ts: 1401292357,
bits: 545259519,
@ -120,20 +124,20 @@ util.log('');
util.log(segnet4);
util.log('');
util.log('');
util.log('main hash: %s', main.rhash);
util.log('main hash: %s', main.rhash());
util.log('main raw: %s', main.toRaw().toString('hex'));
util.log('');
util.log('testnet hash: %s', testnet.rhash);
util.log('testnet hash: %s', testnet.rhash());
util.log('testnet raw: %s', testnet.toRaw().toString('hex'));
util.log('');
util.log('regtest hash: %s', regtest.rhash);
util.log('regtest hash: %s', regtest.rhash());
util.log('regtest raw: %s', regtest.toRaw().toString('hex'));
util.log('');
util.log('segnet3 hash: %s', segnet3.rhash);
util.log('segnet3 hash: %s', segnet3.rhash());
util.log('segnet3 raw: %s', segnet3.toRaw().toString('hex'));
util.log('');
util.log('segnet4 hash: %s', segnet4.rhash);
util.log('segnet4 hash: %s', segnet4.rhash());
util.log('segnet4 raw: %s', segnet4.toRaw().toString('hex'));
util.log('');
util.log('btcd simnet hash: %s', btcd.rhash);
util.log('btcd simnet hash: %s', btcd.rhash());
util.log('btcd simnet raw: %s', btcd.toRaw().toString('hex'));