coins: refactor.
This commit is contained in:
parent
63c42bf390
commit
cdee4ce251
44
bench/coin-old.js
Normal file
44
bench/coin-old.js
Normal file
@ -0,0 +1,44 @@
|
||||
'use strict';
|
||||
|
||||
var BN = require('bn.js');
|
||||
var constants = require('../lib/protocol/constants');
|
||||
var util = require('../lib/utils/util');
|
||||
var assert = require('assert');
|
||||
var scriptTypes = constants.scriptTypes;
|
||||
var bench = require('./bench');
|
||||
var fs = require('fs');
|
||||
var Coins = require('../lib/blockchain/coins-old');
|
||||
var TX = require('../lib/primitives/tx');
|
||||
|
||||
var wtx = fs.readFileSync(__dirname + '/../test/data/wtx.hex', 'utf8');
|
||||
wtx = TX.fromRaw(wtx.trim(), 'hex');
|
||||
|
||||
var coins = Coins.fromTX(wtx);
|
||||
var raw;
|
||||
//raw = coins.toRaw2();
|
||||
//console.log(Coins.fromRaw2(raw));
|
||||
|
||||
var end = bench('serialize');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
raw = coins.toRaw();
|
||||
end(i);
|
||||
|
||||
var end = bench('parse');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
Coins.fromRaw(raw);
|
||||
end(i);
|
||||
|
||||
var end = bench('parse-single');
|
||||
var hash = wtx.hash('hex');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
Coins.parseCoin(raw, hash, 5);
|
||||
end(i);
|
||||
|
||||
var coins = Coins.fromRaw2(raw);
|
||||
var end = bench('get');
|
||||
var j;
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
for (var j = 0; j < coins.outputs.length; j++)
|
||||
coins.get(j);
|
||||
end(i * coins.outputs.length);
|
||||
@ -20,18 +20,18 @@ var raw;
|
||||
|
||||
var end = bench('serialize');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
raw = coins.toRaw2();
|
||||
raw = coins.toRaw();
|
||||
end(i);
|
||||
|
||||
var end = bench('parse');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
Coins.fromRaw2(raw);
|
||||
Coins.fromRaw(raw);
|
||||
end(i);
|
||||
|
||||
var end = bench('parse-single');
|
||||
var hash = wtx.hash('hex');
|
||||
for (var i = 0; i < 10000; i++)
|
||||
Coins.parseCoin2(raw, hash, 5);
|
||||
Coins.parseCoin(raw, hash, 5);
|
||||
end(i);
|
||||
|
||||
var coins = Coins.fromRaw2(raw);
|
||||
@ -40,5 +40,5 @@ var j;
|
||||
|
||||
for (var i = 0; i < 10000; i++)
|
||||
for (var j = 0; j < coins.outputs.length; j++)
|
||||
coins.get2(j);
|
||||
coins.get(j);
|
||||
end(i * coins.outputs.length);
|
||||
|
||||
@ -639,7 +639,7 @@ Chain.prototype.verifyInputs = co(function* verifyInputs(block, prev, state) {
|
||||
|
||||
// Ensure tx is not double spending an output.
|
||||
if (!tx.isCoinbase()) {
|
||||
if (!view.fillCoins2(tx)) {
|
||||
if (!view.fillCoins(tx)) {
|
||||
assert(!historical, 'BUG: Spent inputs in historical data!');
|
||||
throw new VerifyError(block,
|
||||
'invalid',
|
||||
|
||||
@ -19,6 +19,8 @@ var co = require('../utils/co');
|
||||
var Network = require('../protocol/network');
|
||||
var CoinView = require('./coinview');
|
||||
var Coins = require('./coins');
|
||||
var CoinViewOld = require('./coinview-old');
|
||||
var CoinsOld = require('./coins-old');
|
||||
var LDB = require('../db/ldb');
|
||||
var layout = require('./layout');
|
||||
var LRU = require('../utils/lru');
|
||||
@ -633,7 +635,7 @@ ChainDB.prototype.getCoin = co(function* getCoin(hash, index) {
|
||||
coins = this.coinCache.get(hash);
|
||||
|
||||
if (coins)
|
||||
return Coins.parseCoin2(coins, hash, index);
|
||||
return Coins.parseCoin(coins, hash, index);
|
||||
|
||||
coins = yield this.db.get(layout.c(hash));
|
||||
|
||||
@ -643,7 +645,7 @@ ChainDB.prototype.getCoin = co(function* getCoin(hash, index) {
|
||||
if (state === this.state)
|
||||
this.coinCache.set(hash, coins);
|
||||
|
||||
return Coins.parseCoin2(coins, hash, index);
|
||||
return Coins.parseCoin(coins, hash, index);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -662,7 +664,7 @@ ChainDB.prototype.getCoins = co(function* getCoins(hash) {
|
||||
coins = this.coinCache.get(hash);
|
||||
|
||||
if (coins)
|
||||
return Coins.fromRaw2(coins, hash);
|
||||
return Coins.fromRaw(coins, hash);
|
||||
|
||||
coins = yield this.db.get(layout.c(hash));
|
||||
|
||||
@ -672,7 +674,7 @@ ChainDB.prototype.getCoins = co(function* getCoins(hash) {
|
||||
if (state === this.state)
|
||||
this.coinCache.set(hash, coins);
|
||||
|
||||
return Coins.fromRaw2(coins, hash);
|
||||
return Coins.fromRaw(coins, hash);
|
||||
});
|
||||
|
||||
/**
|
||||
@ -1672,7 +1674,7 @@ ChainDB.prototype.connectBlock = co(function* connectBlock(block, view) {
|
||||
|
||||
for (i = 0; i < view.length; i++) {
|
||||
coins = view[i];
|
||||
raw = coins.toRaw2();
|
||||
raw = coins.toRaw();
|
||||
if (!raw) {
|
||||
this.del(layout.c(coins.hash));
|
||||
this.coinCache.unpush(coins.hash);
|
||||
@ -1768,7 +1770,7 @@ ChainDB.prototype.disconnectBlock = co(function* disconnectBlock(block) {
|
||||
|
||||
for (i = 0; i < view.length; i++) {
|
||||
coins = view[i];
|
||||
raw = coins.toRaw2();
|
||||
raw = coins.toRaw();
|
||||
if (!raw) {
|
||||
this.del(layout.c(coins.hash));
|
||||
this.coinCache.unpush(coins.hash);
|
||||
|
||||
614
lib/blockchain/coins-old.js
Normal file
614
lib/blockchain/coins-old.js
Normal file
@ -0,0 +1,614 @@
|
||||
/*!
|
||||
* coins.js - coins object for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var util = require('../utils/util');
|
||||
var assert = require('assert');
|
||||
var constants = require('../protocol/constants');
|
||||
var Coin = require('../primitives/coin');
|
||||
var Output = require('../primitives/output');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var compressor = require('./compress-old');
|
||||
var compress = compressor.compress;
|
||||
var decompress = compressor.decompress;
|
||||
|
||||
/**
|
||||
* Represents the outputs for a single transaction.
|
||||
* @exports Coins
|
||||
* @constructor
|
||||
* @param {TX|Object} tx/options - TX or 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.
|
||||
*/
|
||||
|
||||
function Coins(options) {
|
||||
if (!(this instanceof Coins))
|
||||
return new Coins(options);
|
||||
|
||||
this.version = 1;
|
||||
this.hash = constants.NULL_HASH;
|
||||
this.height = -1;
|
||||
this.coinbase = true;
|
||||
this.outputs = [];
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inject properties from options object.
|
||||
* @private
|
||||
* @param {Object} options
|
||||
*/
|
||||
|
||||
Coins.prototype.fromOptions = function fromOptions(options) {
|
||||
if (options.version != null) {
|
||||
assert(util.isNumber(options.version));
|
||||
this.version = options.version;
|
||||
}
|
||||
|
||||
if (options.hash) {
|
||||
assert(typeof options.hash === 'string');
|
||||
this.hash = options.hash;
|
||||
}
|
||||
|
||||
if (options.height != null) {
|
||||
assert(util.isNumber(options.height));
|
||||
this.height = options.height;
|
||||
}
|
||||
|
||||
if (options.coinbase != null) {
|
||||
assert(typeof options.coinbase === 'boolean');
|
||||
this.coinbase = options.coinbase;
|
||||
}
|
||||
|
||||
if (options.outputs) {
|
||||
assert(Array.isArray(options.outputs));
|
||||
this.outputs = options.outputs;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate coins from options object.
|
||||
* @param {Object} options
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromOptions = function fromOptions(options) {
|
||||
return new Coins().fromOptions(options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a single coin to the collection.
|
||||
* @param {Coin} coin
|
||||
*/
|
||||
|
||||
Coins.prototype.add = function add(coin) {
|
||||
if (this.outputs.length === 0) {
|
||||
this.version = coin.version;
|
||||
this.hash = coin.hash;
|
||||
this.height = coin.height;
|
||||
this.coinbase = coin.coinbase;
|
||||
}
|
||||
|
||||
while (this.outputs.length <= coin.index)
|
||||
this.outputs.push(null);
|
||||
|
||||
if (coin.script.isUnspendable()) {
|
||||
this.outputs[coin.index] = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.outputs[coin.index] = CoinEntry.fromCoin(coin);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the collection has a coin.
|
||||
* @param {Number} index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
Coins.prototype.has = function has(index) {
|
||||
if (index >= this.outputs.length)
|
||||
return false;
|
||||
|
||||
return this.outputs[index] != null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.get = function get(index) {
|
||||
var coin;
|
||||
|
||||
if (index >= this.outputs.length)
|
||||
return;
|
||||
|
||||
coin = this.outputs[index];
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
return coin.toCoin(this, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.spend = function spend(index) {
|
||||
var coin = this.get(index);
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
this.outputs[index] = null;
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Count up to the last available index.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Coins.prototype.size = function size() {
|
||||
var index = -1;
|
||||
var i, output;
|
||||
|
||||
for (i = this.outputs.length - 1; i >= 0; i--) {
|
||||
output = this.outputs[i];
|
||||
if (output) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index + 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Coins serialization:
|
||||
* version: varint
|
||||
* bits: uint32 (31-bit height | 1-bit coinbase-flag)
|
||||
* spent-field: varint size | bitfield (0=unspent, 1=spent)
|
||||
* outputs (repeated):
|
||||
* compressed-script:
|
||||
* prefix: 0x00 = varint size | raw script
|
||||
* 0x01 = 20 byte pubkey hash
|
||||
* 0x02 = 20 byte script hash
|
||||
* 0x03 = 33 byte compressed key
|
||||
* data: script data, dictated by the prefix
|
||||
* value: varint
|
||||
*
|
||||
* The compression below sacrifices some cpu in exchange
|
||||
* for reduced size, but in some cases the use of varints
|
||||
* actually increases speed (varint versions and values
|
||||
* for example). We do as much compression as possible
|
||||
* without sacrificing too much cpu. Value compression
|
||||
* is intentionally excluded for now as it seems to be
|
||||
* too much of a perf hit. Maybe when v8 optimizes
|
||||
* non-smi arithmetic better we can enable it.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Serialize the coins object.
|
||||
* @param {TX|Coins} tx
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Coins.prototype.toRaw = function toRaw() {
|
||||
var bw = new BufferWriter();
|
||||
var length = this.size();
|
||||
var len = Math.ceil(length / 8);
|
||||
var i, output, bits, start, bit, oct, data;
|
||||
|
||||
// Return nothing if we're fully spent.
|
||||
if (length === 0)
|
||||
return;
|
||||
|
||||
// Varint version: hopefully we
|
||||
// never run into `-1` versions.
|
||||
bw.writeVarint(this.version);
|
||||
|
||||
// Create the `bits` value:
|
||||
// (height | coinbase-flag).
|
||||
bits = this.height << 1;
|
||||
|
||||
// Append the coinbase bit.
|
||||
if (this.coinbase)
|
||||
bits |= 1;
|
||||
|
||||
if (bits < 0)
|
||||
bits += 0x100000000;
|
||||
|
||||
// Making this a varint would actually
|
||||
// make 99% of coins bigger. Varints
|
||||
// are really only useful up until
|
||||
// 0x10000, but since we're also
|
||||
// storing the coinbase flag on the
|
||||
// lo bit, varints are useless (and
|
||||
// actually harmful) after height
|
||||
// 32767 (0x7fff).
|
||||
bw.writeU32(bits);
|
||||
|
||||
// Fill the spent field with zeroes to avoid
|
||||
// allocating a buffer. We mark the spents
|
||||
// after rendering the final buffer.
|
||||
bw.writeVarint(len);
|
||||
start = bw.written;
|
||||
bw.fill(0, len);
|
||||
|
||||
// Write the compressed outputs.
|
||||
for (i = 0; i < length; i++) {
|
||||
output = this.outputs[i];
|
||||
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
output.toWriter(bw);
|
||||
}
|
||||
|
||||
// Render the buffer with all
|
||||
// zeroes in the spent field.
|
||||
data = bw.render();
|
||||
|
||||
// Mark the spents in the spent field.
|
||||
// This is essentially a NOP for new coins.
|
||||
for (i = 0; i < length; i++) {
|
||||
output = this.outputs[i];
|
||||
|
||||
if (output)
|
||||
continue;
|
||||
|
||||
bit = i % 8;
|
||||
oct = (i - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
data[oct] |= 1 << (7 - bit);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse serialized coins.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @returns {Object} A "naked" coins object.
|
||||
*/
|
||||
|
||||
Coins.prototype.fromRaw = function fromRaw(data, hash, index) {
|
||||
var br = new BufferReader(data);
|
||||
var pos = 0;
|
||||
var bits, len, start, bit, oct, spent, coin;
|
||||
|
||||
this.version = br.readVarint();
|
||||
|
||||
bits = br.readU32();
|
||||
|
||||
this.height = bits >>> 1;
|
||||
this.hash = hash;
|
||||
this.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
len = br.readVarint();
|
||||
start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
this.outputs.push(null);
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store the offset and size
|
||||
// in the compressed coin object.
|
||||
coin = CoinEntry.fromReader(br);
|
||||
|
||||
this.outputs.push(coin);
|
||||
pos++;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single serialized coin.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
var br = new BufferReader(data);
|
||||
var coin = new Coin();
|
||||
var pos = 0;
|
||||
var bits, len, start, bit, oct, spent;
|
||||
|
||||
coin.version = br.readVarint();
|
||||
|
||||
bits = br.readU32();
|
||||
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
coin.height = bits >>> 1;
|
||||
coin.hash = hash;
|
||||
coin.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
len = br.readVarint();
|
||||
start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// We found our coin.
|
||||
if (pos === index) {
|
||||
if (spent)
|
||||
return;
|
||||
decompress.script(coin.script, br);
|
||||
coin.value = br.readVarint();
|
||||
return coin;
|
||||
}
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past the compressed coin.
|
||||
skipCoin(br);
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate coins from a serialized Buffer.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash - Transaction hash.
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromRaw = function fromRaw(data, hash) {
|
||||
return new Coins().fromRaw(data, hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from tx.
|
||||
* @private
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
Coins.prototype.fromTX = function fromTX(tx) {
|
||||
var i, output;
|
||||
|
||||
this.version = tx.version;
|
||||
this.hash = tx.hash('hex');
|
||||
this.height = tx.height;
|
||||
this.coinbase = tx.isCoinbase();
|
||||
|
||||
for (i = 0; i < tx.outputs.length; i++) {
|
||||
output = tx.outputs[i];
|
||||
|
||||
if (output.script.isUnspendable()) {
|
||||
this.outputs.push(null);
|
||||
continue;
|
||||
}
|
||||
|
||||
this.outputs.push(CoinEntry.fromTX(tx, i));
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate a coins object from a transaction.
|
||||
* @param {TX} tx
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromTX = function fromTX(tx) {
|
||||
return new Coins().fromTX(tx);
|
||||
};
|
||||
|
||||
/**
|
||||
* A compressed coin 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
|
||||
* pointer to that coin in the Coins buffer, as
|
||||
* well as a size. Parsing is done only if that
|
||||
* coin is being redeemed.
|
||||
* @constructor
|
||||
* @private
|
||||
* @param {Number} offset
|
||||
* @param {Number} size
|
||||
* @param {Buffer} raw
|
||||
*/
|
||||
|
||||
function CoinEntry() {
|
||||
this.offset = 0;
|
||||
this.size = 0;
|
||||
this.raw = null;
|
||||
this.output = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the deferred data and return a Coin.
|
||||
* @param {Coins} coins
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toCoin = function toCoin(coins, index) {
|
||||
var coin = new Coin();
|
||||
var br;
|
||||
|
||||
// Load in all necessary properties
|
||||
// from the parent Coins object.
|
||||
coin.version = coins.version;
|
||||
coin.coinbase = coins.coinbase;
|
||||
coin.height = coins.height;
|
||||
coin.hash = coins.hash;
|
||||
coin.index = index;
|
||||
|
||||
if (this.output) {
|
||||
coin.script = this.output.script;
|
||||
coin.value = this.output.value;
|
||||
return coin;
|
||||
}
|
||||
|
||||
br = new BufferReader(this.raw);
|
||||
|
||||
// Seek to the coin's offset.
|
||||
br.seek(this.offset);
|
||||
|
||||
decompress.script(coin.script, br);
|
||||
|
||||
coin.value = br.readVarint();
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice off the part of the buffer
|
||||
* relevant to this particular coin.
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toWriter = function toWriter(bw) {
|
||||
var raw;
|
||||
|
||||
if (this.output) {
|
||||
compress.script(this.output.script, bw);
|
||||
bw.writeVarint(this.output.value);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(this.raw);
|
||||
|
||||
// If we read this coin from the db and
|
||||
// didn't use it, it's still in its
|
||||
// compressed form. Just write it back
|
||||
// as a buffer for speed.
|
||||
raw = this.raw.slice(this.offset, this.offset + this.size);
|
||||
|
||||
bw.writeBytes(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from reader.
|
||||
* @param {BufferReader} br
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromReader = function fromReader(br) {
|
||||
var entry = new CoinEntry();
|
||||
entry.offset = br.offset;
|
||||
entry.size = skipCoin(br);
|
||||
entry.raw = br.data;
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from tx.
|
||||
* @param {TX} tx
|
||||
* @param {Number} index
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromTX = function fromTX(tx, index) {
|
||||
var entry = new CoinEntry();
|
||||
entry.output = tx.outputs[index];
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from coin.
|
||||
* @param {Coin} coin
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromCoin = function fromCoin(coin) {
|
||||
var entry = new CoinEntry();
|
||||
entry.output = new Output();
|
||||
entry.output.script = coin.script;
|
||||
entry.output.value = coin.value;
|
||||
return entry;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helpers
|
||||
*/
|
||||
|
||||
function skipCoin(br) {
|
||||
var start = br.offset;
|
||||
|
||||
// Skip past the compressed scripts.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
br.seek(br.readVarint());
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
br.seek(20);
|
||||
break;
|
||||
case 3:
|
||||
br.seek(33);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
// Skip past the value.
|
||||
br.skipVarint();
|
||||
|
||||
return br.offset - start;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = Coins;
|
||||
@ -162,44 +162,6 @@ Coins.prototype.spend = function spend(index) {
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.get2 = function get2(index) {
|
||||
var coin;
|
||||
|
||||
if (index >= this.outputs.length)
|
||||
return;
|
||||
|
||||
coin = this.outputs[index];
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
return coin.toCoin2(this, index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.prototype.spend2 = function spend2(index) {
|
||||
var coin = this.get2(index);
|
||||
|
||||
if (!coin)
|
||||
return;
|
||||
|
||||
this.outputs[index] = null;
|
||||
this.cleanup();
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Cleanup spent outputs.
|
||||
*/
|
||||
@ -213,39 +175,21 @@ Coins.prototype.cleanup = function cleanup() {
|
||||
this.outputs.length = len;
|
||||
};
|
||||
|
||||
/**
|
||||
* Count up to the last available index.
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
Coins.prototype.size = function size() {
|
||||
var index = -1;
|
||||
var i, output;
|
||||
|
||||
for (i = this.outputs.length - 1; i >= 0; i--) {
|
||||
output = this.outputs[i];
|
||||
if (output) {
|
||||
index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return index + 1;
|
||||
};
|
||||
|
||||
/*
|
||||
* Coins serialization:
|
||||
* version: varint
|
||||
* bits: uint32 (31-bit height | 1-bit coinbase-flag)
|
||||
* spent-field: varint size | bitfield (0=unspent, 1=spent)
|
||||
* height: uint32
|
||||
* header-code: varint (31-bit fields | 1-bit coinbase-flag)
|
||||
* spent-field: bitfield (0=spent, 1=unspent)
|
||||
* outputs (repeated):
|
||||
* compressed-script:
|
||||
* prefix: 0x00 = varint size | raw script
|
||||
* 0x01 = 20 byte pubkey hash
|
||||
* 0x02 = 20 byte script hash
|
||||
* 0x03 = 33 byte compressed key
|
||||
* data: script data, dictated by the prefix
|
||||
* value: varint
|
||||
* compressed-script:
|
||||
* prefix:
|
||||
* 0x00 = 20 byte pubkey hash
|
||||
* 0x01 = 20 byte script hash
|
||||
* 0x02-0x05 = 32 byte ec-key x-value
|
||||
* >=0x06 = varint-size + 6 | raw script
|
||||
* data: script data, dictated by the prefix
|
||||
*
|
||||
* The compression below sacrifices some cpu in exchange
|
||||
* for reduced size, but in some cases the use of varints
|
||||
@ -259,215 +203,10 @@ Coins.prototype.size = function size() {
|
||||
|
||||
/**
|
||||
* Serialize the coins object.
|
||||
* @param {TX|Coins} tx
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Coins.prototype.toRaw = function toRaw() {
|
||||
var bw = new BufferWriter();
|
||||
var length = this.size();
|
||||
var len = Math.ceil(length / 8);
|
||||
var i, output, bits, start, bit, oct, data;
|
||||
|
||||
// Return nothing if we're fully spent.
|
||||
if (length === 0)
|
||||
return;
|
||||
|
||||
// Varint version: hopefully we
|
||||
// never run into `-1` versions.
|
||||
bw.writeVarint(this.version);
|
||||
|
||||
// Create the `bits` value:
|
||||
// (height | coinbase-flag).
|
||||
bits = this.height << 1;
|
||||
|
||||
// Append the coinbase bit.
|
||||
if (this.coinbase)
|
||||
bits |= 1;
|
||||
|
||||
if (bits < 0)
|
||||
bits += 0x100000000;
|
||||
|
||||
// Making this a varint would actually
|
||||
// make 99% of coins bigger. Varints
|
||||
// are really only useful up until
|
||||
// 0x10000, but since we're also
|
||||
// storing the coinbase flag on the
|
||||
// lo bit, varints are useless (and
|
||||
// actually harmful) after height
|
||||
// 32767 (0x7fff).
|
||||
bw.writeU32(bits);
|
||||
|
||||
// Fill the spent field with zeroes to avoid
|
||||
// allocating a buffer. We mark the spents
|
||||
// after rendering the final buffer.
|
||||
bw.writeVarint(len);
|
||||
start = bw.written;
|
||||
bw.fill(0, len);
|
||||
|
||||
// Write the compressed outputs.
|
||||
for (i = 0; i < length; i++) {
|
||||
output = this.outputs[i];
|
||||
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
output.toWriter(bw);
|
||||
}
|
||||
|
||||
// Render the buffer with all
|
||||
// zeroes in the spent field.
|
||||
data = bw.render();
|
||||
|
||||
// Mark the spents in the spent field.
|
||||
// This is essentially a NOP for new coins.
|
||||
for (i = 0; i < length; i++) {
|
||||
output = this.outputs[i];
|
||||
|
||||
if (output)
|
||||
continue;
|
||||
|
||||
bit = i % 8;
|
||||
oct = (i - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
data[oct] |= 1 << (7 - bit);
|
||||
}
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse serialized coins.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @returns {Object} A "naked" coins object.
|
||||
*/
|
||||
|
||||
Coins.prototype.fromRaw = function fromRaw(data, hash) {
|
||||
var br = new BufferReader(data);
|
||||
var pos = 0;
|
||||
var bits, len, start, bit, oct, spent, coin;
|
||||
|
||||
this.version = br.readVarint();
|
||||
|
||||
bits = br.readU32();
|
||||
|
||||
this.height = bits >>> 1;
|
||||
this.hash = hash;
|
||||
this.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
len = br.readVarint();
|
||||
start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
this.outputs.push(null);
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Store the offset and size
|
||||
// in the compressed coin object.
|
||||
coin = CoinEntry.fromReader(br);
|
||||
|
||||
this.outputs.push(coin);
|
||||
pos++;
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse a single serialized coin.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
var br = new BufferReader(data);
|
||||
var coin = new Coin();
|
||||
var pos = 0;
|
||||
var bits, len, start, bit, oct, spent;
|
||||
|
||||
coin.version = br.readVarint();
|
||||
|
||||
bits = br.readU32();
|
||||
|
||||
coin.hash = hash;
|
||||
coin.index = index;
|
||||
coin.height = bits >>> 1;
|
||||
coin.hash = hash;
|
||||
coin.coinbase = (bits & 1) !== 0;
|
||||
|
||||
// Mark the start of the spent field and
|
||||
// seek past it to avoid reading a buffer.
|
||||
len = br.readVarint();
|
||||
start = br.offset;
|
||||
br.seek(len);
|
||||
|
||||
while (br.left()) {
|
||||
bit = pos % 8;
|
||||
oct = (pos - bit) / 8;
|
||||
oct += start;
|
||||
|
||||
// Read a single bit out of the spent field.
|
||||
spent = data[oct] >>> (7 - bit);
|
||||
spent &= 1;
|
||||
|
||||
// We found our coin.
|
||||
if (pos === index) {
|
||||
if (spent)
|
||||
return;
|
||||
decompress.script(coin.script, br);
|
||||
coin.value = br.readVarint();
|
||||
return coin;
|
||||
}
|
||||
|
||||
// Already spent.
|
||||
if (spent) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Skip past the compressed coin.
|
||||
decompress.skip(br);
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate coins from a serialized Buffer.
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash - Transaction hash.
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromRaw = function fromRaw(data, hash) {
|
||||
return new Coins().fromRaw(data, hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the coins object.
|
||||
* @param {TX|Coins} tx
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
Coins.prototype.toRaw2 = function toRaw2() {
|
||||
var bw = new BufferWriter();
|
||||
var len = this.outputs.length;
|
||||
var first = len > 0 && this.outputs[0];
|
||||
@ -530,20 +269,21 @@ Coins.prototype.toRaw2 = function toRaw2() {
|
||||
if (!output)
|
||||
continue;
|
||||
|
||||
output.toWriter2(bw);
|
||||
output.toWriter(bw);
|
||||
}
|
||||
|
||||
return bw.render();
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse serialized coins.
|
||||
* Inject data from serialized coins.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
* @param {Hash} hash
|
||||
* @returns {Object} A "naked" coins object.
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.prototype.fromRaw2 = function fromRaw2(data, hash) {
|
||||
Coins.prototype.fromRaw = function fromRaw(data, hash) {
|
||||
var br = new BufferReader(data);
|
||||
var i, code, field, nonzero, ch, unspent, coin;
|
||||
|
||||
@ -587,7 +327,7 @@ Coins.prototype.fromRaw2 = function fromRaw2(data, hash) {
|
||||
|
||||
// Store the offset and size
|
||||
// in the compressed coin object.
|
||||
coin = CoinEntry.fromReader2(br);
|
||||
coin = CoinEntry.fromReader(br);
|
||||
|
||||
this.outputs.push(coin);
|
||||
}
|
||||
@ -605,7 +345,7 @@ Coins.prototype.fromRaw2 = function fromRaw2(data, hash) {
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coins.parseCoin2 = function parseCoin2(data, hash, index) {
|
||||
Coins.parseCoin = function parseCoin(data, hash, index) {
|
||||
var br = new BufferReader(data);
|
||||
var coin = new Coin();
|
||||
var i, code, field, nonzero, ch, unspent;
|
||||
@ -657,7 +397,7 @@ Coins.parseCoin2 = function parseCoin2(data, hash, index) {
|
||||
return;
|
||||
|
||||
// Read compressed output.
|
||||
decompress.output2(coin, br);
|
||||
decompress.output(coin, br);
|
||||
|
||||
break;
|
||||
}
|
||||
@ -665,7 +405,7 @@ Coins.parseCoin2 = function parseCoin2(data, hash, index) {
|
||||
if (!field[i])
|
||||
continue;
|
||||
|
||||
decompress.skip2(br);
|
||||
decompress.skip(br);
|
||||
}
|
||||
|
||||
return coin;
|
||||
@ -678,8 +418,8 @@ Coins.parseCoin2 = function parseCoin2(data, hash, index) {
|
||||
* @returns {Coins}
|
||||
*/
|
||||
|
||||
Coins.fromRaw2 = function fromRaw2(data, hash) {
|
||||
return new Coins().fromRaw2(data, hash);
|
||||
Coins.fromRaw = function fromRaw(data, hash) {
|
||||
return new Coins().fromRaw(data, hash);
|
||||
};
|
||||
|
||||
/**
|
||||
@ -783,41 +523,6 @@ CoinEntry.prototype.toCoin = function toCoin(coins, index) {
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the deferred data and return a Coin.
|
||||
* @param {Coins} coins
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toCoin2 = function toCoin2(coins, index) {
|
||||
var coin = new Coin();
|
||||
var br;
|
||||
|
||||
// Load in all necessary properties
|
||||
// from the parent Coins object.
|
||||
coin.version = coins.version;
|
||||
coin.coinbase = coins.coinbase;
|
||||
coin.height = coins.height;
|
||||
coin.hash = coins.hash;
|
||||
coin.index = index;
|
||||
|
||||
if (this.output) {
|
||||
coin.script = this.output.script;
|
||||
coin.value = this.output.value;
|
||||
return coin;
|
||||
}
|
||||
|
||||
br = new BufferReader(this.raw);
|
||||
|
||||
// Seek to the coin's offset.
|
||||
br.seek(this.offset);
|
||||
|
||||
decompress.output2(coin, br);
|
||||
|
||||
return coin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice off the part of the buffer
|
||||
* relevant to this particular coin.
|
||||
@ -842,30 +547,6 @@ CoinEntry.prototype.toWriter = function toWriter(bw) {
|
||||
bw.writeBytes(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Slice off the part of the buffer
|
||||
* relevant to this particular coin.
|
||||
*/
|
||||
|
||||
CoinEntry.prototype.toWriter2 = function toWriter2(bw) {
|
||||
var raw;
|
||||
|
||||
if (this.output) {
|
||||
compress.output2(this.output, bw);
|
||||
return;
|
||||
}
|
||||
|
||||
assert(this.raw);
|
||||
|
||||
// If we read this coin from the db and
|
||||
// didn't use it, it's still in its
|
||||
// compressed form. Just write it back
|
||||
// as a buffer for speed.
|
||||
raw = this.raw.slice(this.offset, this.offset + this.size);
|
||||
|
||||
bw.writeBytes(raw);
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from reader.
|
||||
* @param {BufferReader} br
|
||||
@ -880,20 +561,6 @@ CoinEntry.fromReader = function fromReader(br) {
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from reader.
|
||||
* @param {BufferReader} br
|
||||
* @returns {CoinEntry}
|
||||
*/
|
||||
|
||||
CoinEntry.fromReader2 = function fromReader2(br) {
|
||||
var entry = new CoinEntry();
|
||||
entry.offset = br.offset;
|
||||
entry.size = decompress.skip2(br);
|
||||
entry.raw = br.data;
|
||||
return entry;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate compressed coin from tx.
|
||||
* @param {TX} tx
|
||||
|
||||
147
lib/blockchain/coinview-old.js
Normal file
147
lib/blockchain/coinview-old.js
Normal file
@ -0,0 +1,147 @@
|
||||
/*!
|
||||
* coinview.js - coinview object for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var Coins = require('./coins-old');
|
||||
|
||||
/**
|
||||
* A collections of {@link Coins} objects.
|
||||
* @exports CoinView
|
||||
* @constructor
|
||||
* @param {Object} coins - A hash-to-coins map.
|
||||
* @property {Object} coins
|
||||
*/
|
||||
|
||||
function CoinView(coins) {
|
||||
if (!(this instanceof CoinView))
|
||||
return new CoinView(coins);
|
||||
|
||||
this.coins = coins || {};
|
||||
}
|
||||
|
||||
/**
|
||||
* Add coins to the collection.
|
||||
* @param {Coins} coins
|
||||
*/
|
||||
|
||||
CoinView.prototype.add = function add(coins) {
|
||||
this.coins[coins.hash] = coins;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a coin to the collection.
|
||||
* @param {Coin} coin
|
||||
*/
|
||||
|
||||
CoinView.prototype.addCoin = function addCoin(coin) {
|
||||
assert(typeof coin.hash === 'string');
|
||||
if (!this.coins[coin.hash])
|
||||
this.coins[coin.hash] = new Coins();
|
||||
this.coins[coin.hash].add(coin);
|
||||
};
|
||||
|
||||
/**
|
||||
* Add a tx to the collection.
|
||||
* @param {TX} tx
|
||||
*/
|
||||
|
||||
CoinView.prototype.addTX = function addTX(tx) {
|
||||
this.add(Coins.fromTX(tx));
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.get = function get(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.get(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the collection has a coin.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
|
||||
CoinView.prototype.has = function has(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return false;
|
||||
|
||||
return coins.has(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.spend = function spend(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.spend(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill transaction(s) with coins.
|
||||
* @param {TX} tx
|
||||
* @returns {Boolean} True if all inputs were filled.
|
||||
*/
|
||||
|
||||
CoinView.prototype.fillCoins = function fillCoins(tx) {
|
||||
var i, input, prevout;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
input.coin = this.spend(prevout.hash, prevout.index);
|
||||
if (!input.coin)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert collection to an array.
|
||||
* @returns {Coins[]}
|
||||
*/
|
||||
|
||||
CoinView.prototype.toArray = function toArray() {
|
||||
var keys = Object.keys(this.coins);
|
||||
var out = [];
|
||||
var i, hash;
|
||||
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
hash = keys[i];
|
||||
out.push(this.coins[hash]);
|
||||
}
|
||||
|
||||
return out;
|
||||
};
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
module.exports = CoinView;
|
||||
@ -70,22 +70,6 @@ CoinView.prototype.get = function get(hash, index) {
|
||||
return coins.get(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a coin.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.get2 = function get2(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.get2(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Test whether the collection has a coin.
|
||||
* @param {Hash} hash
|
||||
@ -118,22 +102,6 @@ CoinView.prototype.spend = function spend(hash, index) {
|
||||
return coins.spend(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a coin and return it.
|
||||
* @param {Hash} hash
|
||||
* @param {Number} index
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
CoinView.prototype.spend2 = function spend2(hash, index) {
|
||||
var coins = this.coins[hash];
|
||||
|
||||
if (!coins)
|
||||
return;
|
||||
|
||||
return coins.spend2(index);
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill transaction(s) with coins.
|
||||
* @param {TX} tx
|
||||
@ -154,26 +122,6 @@ CoinView.prototype.fillCoins = function fillCoins(tx) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fill transaction(s) with coins.
|
||||
* @param {TX} tx
|
||||
* @returns {Boolean} True if all inputs were filled.
|
||||
*/
|
||||
|
||||
CoinView.prototype.fillCoins2 = function fillCoins2(tx) {
|
||||
var i, input, prevout;
|
||||
|
||||
for (i = 0; i < tx.inputs.length; i++) {
|
||||
input = tx.inputs[i];
|
||||
prevout = input.prevout;
|
||||
input.coin = this.spend2(prevout.hash, prevout.index);
|
||||
if (!input.coin)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert collection to an array.
|
||||
* @returns {Coins[]}
|
||||
|
||||
253
lib/blockchain/compress-old.js
Normal file
253
lib/blockchain/compress-old.js
Normal file
@ -0,0 +1,253 @@
|
||||
/*!
|
||||
* compress.js - coin compressor for bcoin
|
||||
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
|
||||
* https://github.com/bcoin-org/bcoin
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
var assert = require('assert');
|
||||
var ec = require('../crypto/ec');
|
||||
|
||||
/*
|
||||
* Compression
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compress a script, write directly to the buffer.
|
||||
* @param {Script} script
|
||||
* @param {BufferWriter} bw
|
||||
*/
|
||||
|
||||
function compressScript(script, bw) {
|
||||
var data;
|
||||
|
||||
// Attempt to compress the output scripts.
|
||||
// We can _only_ ever compress them if
|
||||
// they are serialized as minimaldata, as
|
||||
// we need to recreate them when we read
|
||||
// them.
|
||||
|
||||
// P2PKH -> 1 | key-hash
|
||||
// Saves 5 bytes.
|
||||
if (script.isPubkeyhash(true)) {
|
||||
data = script.code[2].data;
|
||||
bw.writeU8(1);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2SH -> 2 | script-hash
|
||||
// Saves 3 bytes.
|
||||
if (script.isScripthash()) {
|
||||
data = script.code[1].data;
|
||||
bw.writeU8(2);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2PK -> 3 | compressed-key
|
||||
// Only works if the key is valid.
|
||||
// Saves up to 34 bytes.
|
||||
if (script.isPubkey(true)) {
|
||||
data = script.code[0].data;
|
||||
if (ec.publicKeyVerify(data)) {
|
||||
data = compressKey(data);
|
||||
bw.writeU8(3);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
}
|
||||
|
||||
// Raw -> 0 | varlen | script
|
||||
bw.writeU8(0);
|
||||
bw.writeVarBytes(script.toRaw());
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a script from buffer reader.
|
||||
* @param {Script} script
|
||||
* @param {BufferReader} br
|
||||
*/
|
||||
|
||||
function decompressScript(script, br) {
|
||||
var data;
|
||||
|
||||
// Decompress the script.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
data = br.readVarBytes();
|
||||
script.fromRaw(data);
|
||||
break;
|
||||
case 1:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromPubkeyhash(data);
|
||||
break;
|
||||
case 2:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromScripthash(data);
|
||||
break;
|
||||
case 3:
|
||||
data = br.readBytes(33, true);
|
||||
// Decompress the key. If this fails,
|
||||
// we have database corruption!
|
||||
data = decompressKey(data);
|
||||
script.fromPubkey(data);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress value using an exponent. Takes advantage of
|
||||
* the fact that many bitcoin values are divisible by 10.
|
||||
* @see https://github.com/btcsuite/btcd/blob/master/blockblockchain/compress.go
|
||||
* @param {Amount} value
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
function compressValue(value) {
|
||||
var exp, last;
|
||||
|
||||
if (value === 0)
|
||||
return 0;
|
||||
|
||||
exp = 0;
|
||||
while (value % 10 === 0 && exp < 9) {
|
||||
value /= 10;
|
||||
exp++;
|
||||
}
|
||||
|
||||
if (exp < 9) {
|
||||
last = value % 10;
|
||||
value = (value - last) / 10;
|
||||
return 1 + 10 * (9 * value + last - 1) + exp;
|
||||
}
|
||||
|
||||
return 10 + 10 * (value - 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress value.
|
||||
* @param {Number} value - Compressed value.
|
||||
* @returns {Amount} value
|
||||
*/
|
||||
|
||||
function decompressValue(value) {
|
||||
var exp, n, last;
|
||||
|
||||
if (value === 0)
|
||||
return 0;
|
||||
|
||||
value--;
|
||||
|
||||
exp = value % 10;
|
||||
value = (value - exp) / 10;
|
||||
|
||||
if (exp < 9) {
|
||||
last = value % 9;
|
||||
value = (value - last) / 9;
|
||||
n = value * 10 + last + 1;
|
||||
} else {
|
||||
n = value + 1;
|
||||
}
|
||||
|
||||
while (exp > 0) {
|
||||
n *= 10;
|
||||
exp--;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a public key to coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function compressKey(key) {
|
||||
var out;
|
||||
|
||||
switch (key[0]) {
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
// Key is already compressed.
|
||||
out = key;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
// Compress the key normally.
|
||||
out = ec.publicKeyConvert(key, true);
|
||||
// Store the original format (which
|
||||
// may be a hybrid byte) in the hi
|
||||
// 3 bits so we can restore it later.
|
||||
// The hi bits being set also lets us
|
||||
// know that this key was originally
|
||||
// decompressed.
|
||||
out[0] |= key[0] << 2;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad point format.');
|
||||
}
|
||||
|
||||
assert(out.length === 33);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a public key from the coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function decompressKey(key) {
|
||||
var format = key[0] >>> 2;
|
||||
var out;
|
||||
|
||||
assert(key.length === 33);
|
||||
|
||||
// Hi bits are not set. This key
|
||||
// is not meant to be decompressed.
|
||||
if (format === 0)
|
||||
return key;
|
||||
|
||||
// Decompress the key, and off the
|
||||
// low bits so publicKeyConvert
|
||||
// actually understands it.
|
||||
key[0] &= 0x03;
|
||||
out = ec.publicKeyConvert(key, false);
|
||||
|
||||
// Reset the hi bits so as not to
|
||||
// mutate the original buffer.
|
||||
key[0] |= format << 2;
|
||||
|
||||
// Set the original format, which
|
||||
// may have been a hybrid prefix byte.
|
||||
out[0] = format;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/*
|
||||
* Expose
|
||||
*/
|
||||
|
||||
exports.compress = {
|
||||
script: compressScript,
|
||||
value: compressValue,
|
||||
key: compressKey
|
||||
};
|
||||
|
||||
exports.decompress = {
|
||||
script: decompressScript,
|
||||
value: decompressValue,
|
||||
key: decompressKey
|
||||
};
|
||||
@ -10,9 +10,11 @@ var assert = require('assert');
|
||||
var ec = require('../crypto/ec');
|
||||
|
||||
/*
|
||||
* Compression
|
||||
* Constants
|
||||
*/
|
||||
|
||||
var COMPRESS_TYPES = 6;
|
||||
|
||||
/**
|
||||
* Compress a script, write directly to the buffer.
|
||||
* @param {Script} script
|
||||
@ -28,96 +30,6 @@ function compressScript(script, bw) {
|
||||
// we need to recreate them when we read
|
||||
// them.
|
||||
|
||||
// P2PKH -> 1 | key-hash
|
||||
// Saves 5 bytes.
|
||||
if (script.isPubkeyhash(true)) {
|
||||
data = script.code[2].data;
|
||||
bw.writeU8(1);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2SH -> 2 | script-hash
|
||||
// Saves 3 bytes.
|
||||
if (script.isScripthash()) {
|
||||
data = script.code[1].data;
|
||||
bw.writeU8(2);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
|
||||
// P2PK -> 3 | compressed-key
|
||||
// Only works if the key is valid.
|
||||
// Saves up to 34 bytes.
|
||||
if (script.isPubkey(true)) {
|
||||
data = script.code[0].data;
|
||||
if (ec.publicKeyVerify(data)) {
|
||||
data = compressKey(data);
|
||||
bw.writeU8(3);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
}
|
||||
|
||||
// Raw -> 0 | varlen | script
|
||||
bw.writeU8(0);
|
||||
bw.writeVarBytes(script.toRaw());
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a script from buffer reader.
|
||||
* @param {Script} script
|
||||
* @param {BufferReader} br
|
||||
*/
|
||||
|
||||
function decompressScript(script, br) {
|
||||
var data;
|
||||
|
||||
// Decompress the script.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
data = br.readVarBytes();
|
||||
script.fromRaw(data);
|
||||
break;
|
||||
case 1:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromPubkeyhash(data);
|
||||
break;
|
||||
case 2:
|
||||
data = br.readBytes(20, true);
|
||||
script.fromScripthash(data);
|
||||
break;
|
||||
case 3:
|
||||
data = br.readBytes(33, true);
|
||||
// Decompress the key. If this fails,
|
||||
// we have database corruption!
|
||||
data = decompressKey(data);
|
||||
script.fromPubkey(data);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
return script;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a script, write directly to the buffer.
|
||||
* @param {Script} script
|
||||
* @param {BufferWriter} bw
|
||||
*/
|
||||
|
||||
function compressScript2(script, bw) {
|
||||
var data;
|
||||
|
||||
// Attempt to compress the output scripts.
|
||||
// We can _only_ ever compress them if
|
||||
// they are serialized as minimaldata, as
|
||||
// we need to recreate them when we read
|
||||
// them.
|
||||
|
||||
// P2PKH -> 0 | key-hash
|
||||
// Saves 5 bytes.
|
||||
if (script.isPubkeyhash(true)) {
|
||||
@ -142,14 +54,14 @@ function compressScript2(script, bw) {
|
||||
if (script.isPubkey(true)) {
|
||||
data = script.code[0].data;
|
||||
if (publicKeyVerify(data)) {
|
||||
data = compressKey2(data);
|
||||
data = compressKey(data);
|
||||
bw.writeBytes(data);
|
||||
return bw;
|
||||
}
|
||||
}
|
||||
|
||||
// Raw -> varlen + 6 | script
|
||||
bw.writeVarint(script.raw.length + 6);
|
||||
bw.writeVarint(script.raw.length + COMPRESS_TYPES);
|
||||
bw.writeBytes(script.raw);
|
||||
|
||||
return bw;
|
||||
@ -161,7 +73,7 @@ function compressScript2(script, bw) {
|
||||
* @param {BufferReader} br
|
||||
*/
|
||||
|
||||
function decompressScript2(script, br) {
|
||||
function decompressScript(script, br) {
|
||||
var size, data;
|
||||
|
||||
// Decompress the script.
|
||||
@ -182,16 +94,16 @@ function decompressScript2(script, br) {
|
||||
data = br.readBytes(33, true);
|
||||
// Decompress the key. If this fails,
|
||||
// we have database corruption!
|
||||
data = decompressKey2(data);
|
||||
data = decompressKey(data);
|
||||
script.fromPubkey(data);
|
||||
break;
|
||||
default:
|
||||
br.offset -= 1;
|
||||
size = br.readVarint() - 6;
|
||||
size = br.readVarint() - COMPRESS_TYPES;
|
||||
if (size > 10000) {
|
||||
// This violates consensus rules.
|
||||
// We don't need to read it.
|
||||
script.fromUnspendable();
|
||||
script.fromNulldata(new Buffer(0));
|
||||
p.seek(size);
|
||||
} else {
|
||||
data = br.readBytes(size);
|
||||
@ -210,8 +122,8 @@ function decompressScript2(script, br) {
|
||||
*/
|
||||
|
||||
function compressOutput(output, bw) {
|
||||
compressScript(output.script, bw);
|
||||
bw.writeVarint(output.value);
|
||||
compressScript(output.script, bw);
|
||||
return bw;
|
||||
}
|
||||
|
||||
@ -222,8 +134,8 @@ function compressOutput(output, bw) {
|
||||
*/
|
||||
|
||||
function decompressOutput(output, br) {
|
||||
decompressScript(output.script, br);
|
||||
output.value = br.readVarint();
|
||||
decompressScript(output.script, br);
|
||||
return output;
|
||||
}
|
||||
|
||||
@ -236,61 +148,9 @@ function decompressOutput(output, br) {
|
||||
function skipOutput(br) {
|
||||
var start = br.offset;
|
||||
|
||||
// Skip past the compressed scripts.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
br.seek(br.readVarint());
|
||||
break;
|
||||
case 1:
|
||||
case 2:
|
||||
br.seek(20);
|
||||
break;
|
||||
case 3:
|
||||
br.seek(33);
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad prefix.');
|
||||
}
|
||||
|
||||
// Skip past the value.
|
||||
br.skipVarint();
|
||||
|
||||
return br.offset - start;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress an output.
|
||||
* @param {Output|Coin} output
|
||||
* @param {BufferWriter} bw
|
||||
*/
|
||||
|
||||
function compressOutput2(output, bw) {
|
||||
compressScript2(output.script, bw);
|
||||
bw.writeVarint(output.value);
|
||||
return bw;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a script from buffer reader.
|
||||
* @param {Output|Coin} output
|
||||
* @param {BufferReader} br
|
||||
*/
|
||||
|
||||
function decompressOutput2(output, br) {
|
||||
decompressScript2(output.script, br);
|
||||
output.value = br.readVarint();
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip past a compressed output.
|
||||
* @param {BufferWriter} bw
|
||||
* @returns {Number}
|
||||
*/
|
||||
|
||||
function skipOutput2(br) {
|
||||
var start = br.offset;
|
||||
|
||||
// Skip past the compressed scripts.
|
||||
switch (br.readU8()) {
|
||||
case 0:
|
||||
@ -305,13 +165,10 @@ function skipOutput2(br) {
|
||||
break;
|
||||
default:
|
||||
br.offset -= 1;
|
||||
br.seek(br.readVarint() - 6);
|
||||
br.seek(br.readVarint() - COMPRESS_TYPES);
|
||||
break;
|
||||
}
|
||||
|
||||
// Skip past the value.
|
||||
br.skipVarint();
|
||||
|
||||
return br.offset - start;
|
||||
}
|
||||
|
||||
@ -377,77 +234,6 @@ function decompressValue(value) {
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compress a public key to coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function compressKey(key) {
|
||||
var out;
|
||||
|
||||
switch (key[0]) {
|
||||
case 0x02:
|
||||
case 0x03:
|
||||
// Key is already compressed.
|
||||
out = key;
|
||||
break;
|
||||
case 0x04:
|
||||
case 0x06:
|
||||
case 0x07:
|
||||
// Compress the key normally.
|
||||
out = ec.publicKeyConvert(key, true);
|
||||
// Store the original format (which
|
||||
// may be a hybrid byte) in the hi
|
||||
// 3 bits so we can restore it later.
|
||||
// The hi bits being set also lets us
|
||||
// know that this key was originally
|
||||
// decompressed.
|
||||
out[0] |= key[0] << 2;
|
||||
break;
|
||||
default:
|
||||
throw new Error('Bad point format.');
|
||||
}
|
||||
|
||||
assert(out.length === 33);
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decompress a public key from the coins compression format.
|
||||
* @param {Buffer} key
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function decompressKey(key) {
|
||||
var format = key[0] >>> 2;
|
||||
var out;
|
||||
|
||||
assert(key.length === 33);
|
||||
|
||||
// Hi bits are not set. This key
|
||||
// is not meant to be decompressed.
|
||||
if (format === 0)
|
||||
return key;
|
||||
|
||||
// Decompress the key, and off the
|
||||
// low bits so publicKeyConvert
|
||||
// actually understands it.
|
||||
key[0] &= 0x03;
|
||||
out = ec.publicKeyConvert(key, false);
|
||||
|
||||
// Reset the hi bits so as not to
|
||||
// mutate the original buffer.
|
||||
key[0] |= format << 2;
|
||||
|
||||
// Set the original format, which
|
||||
// may have been a hybrid prefix byte.
|
||||
out[0] = format;
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify a public key (no hybrid keys allowed).
|
||||
* @param {Buffer} key
|
||||
@ -478,7 +264,7 @@ function publicKeyVerify(key) {
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function compressKey2(key) {
|
||||
function compressKey(key) {
|
||||
var out;
|
||||
|
||||
switch (key[0]) {
|
||||
@ -491,6 +277,7 @@ function compressKey2(key) {
|
||||
// Compress the key normally.
|
||||
out = ec.publicKeyConvert(key, true);
|
||||
// Store the oddness.
|
||||
// Pseudo-hybrid format.
|
||||
out[0] = 0x04 | (key[64] & 0x01);
|
||||
break;
|
||||
default:
|
||||
@ -508,7 +295,7 @@ function compressKey2(key) {
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
function decompressKey2(key) {
|
||||
function decompressKey(key) {
|
||||
var format = key[0];
|
||||
var out;
|
||||
|
||||
@ -528,9 +315,7 @@ function decompressKey2(key) {
|
||||
throw new Error('Bad point format.');
|
||||
}
|
||||
|
||||
// Decompress the key, and off the
|
||||
// low bits so publicKeyConvert
|
||||
// actually understands it.
|
||||
// Decompress the key.
|
||||
out = ec.publicKeyConvert(key, false);
|
||||
|
||||
// Reset the first byte so as not to
|
||||
@ -546,22 +331,15 @@ function decompressKey2(key) {
|
||||
|
||||
exports.compress = {
|
||||
output: compressOutput,
|
||||
output2: compressOutput2,
|
||||
script: compressScript,
|
||||
script2: compressScript2,
|
||||
value: compressValue,
|
||||
key: compressKey,
|
||||
key2: compressKey2
|
||||
key: compressKey
|
||||
};
|
||||
|
||||
exports.decompress = {
|
||||
output: decompressOutput,
|
||||
output2: decompressOutput2,
|
||||
skip: skipOutput,
|
||||
skip2: skipOutput2,
|
||||
script: decompressScript,
|
||||
script2: decompressScript2,
|
||||
value: decompressValue,
|
||||
key: decompressKey,
|
||||
key2: decompressKey
|
||||
key: decompressKey
|
||||
};
|
||||
|
||||
@ -17,9 +17,6 @@ var Script = require('../script/script');
|
||||
var Network = require('../protocol/network');
|
||||
var BufferWriter = require('../utils/writer');
|
||||
var BufferReader = require('../utils/reader');
|
||||
var compressor = require('../blockchain/compress');
|
||||
var compress = compressor.compress;
|
||||
var decompress = compressor.decompress;
|
||||
|
||||
/**
|
||||
* Represents an unspent output.
|
||||
@ -255,75 +252,6 @@ Coin.fromRaw = function fromRaw(data, enc) {
|
||||
return new Coin().fromRaw(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Serialize the coin to its compressed form.
|
||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||
* @returns {Buffer|String}
|
||||
*/
|
||||
|
||||
Coin.prototype.toCompressed = function toCompressed(writer) {
|
||||
var bw = BufferWriter(writer);
|
||||
var height = this.height;
|
||||
var bits;
|
||||
|
||||
if (height === -1)
|
||||
height = 0x7fffffff;
|
||||
|
||||
bits = height << 1;
|
||||
|
||||
if (this.coinbase)
|
||||
bits |= 1;
|
||||
|
||||
if (bits < 0)
|
||||
bits += 0x100000000;
|
||||
|
||||
bw.writeVarint(this.version);
|
||||
bw.writeU32(bits);
|
||||
bw.writeVarint(this.value);
|
||||
compress.script(this.script, bw);
|
||||
|
||||
if (!writer)
|
||||
bw = bw.render();
|
||||
|
||||
return bw;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from compressed serialized data.
|
||||
* @private
|
||||
* @param {Buffer} data
|
||||
*/
|
||||
|
||||
Coin.prototype.fromCompressed = function fromCompressed(data) {
|
||||
var br = BufferReader(data);
|
||||
var bits;
|
||||
|
||||
this.version = br.readVarint();
|
||||
bits = br.readU32();
|
||||
this.height = bits >>> 1;
|
||||
this.coinbase = (bits & 1) !== 0;
|
||||
this.value = br.readVarint();
|
||||
decompress.script(this.script, br);
|
||||
|
||||
if (this.height === 0x7fffffff)
|
||||
this.height = -1;
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Instantiate an coin from a serialized Buffer.
|
||||
* @param {Buffer} data
|
||||
* @param {String?} enc - Encoding, can be `'hex'` or null.
|
||||
* @returns {Coin}
|
||||
*/
|
||||
|
||||
Coin.fromCompressed = function fromCompressed(data, enc) {
|
||||
if (typeof data === 'string')
|
||||
data = new Buffer(data, enc);
|
||||
return new Coin().fromCompressed(data);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from TX.
|
||||
* @param {TX} tx
|
||||
|
||||
@ -1500,28 +1500,6 @@ Script.isCode = function isCode(raw) {
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from a unspendable script.
|
||||
* @private
|
||||
* @param {Buffer} key
|
||||
*/
|
||||
|
||||
Script.prototype.fromUnspendable = function fromUnspendable() {
|
||||
this.raw = new Buffer(1);
|
||||
this.raw[0] = opcodes.OP_RETURN;
|
||||
this.code.push(new Opcode(opcodes.OP_RETURN));
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Create an unspendable script.
|
||||
* @returns {Script}
|
||||
*/
|
||||
|
||||
Script.fromUnspendable = function fromUnspendable() {
|
||||
return new Script().fromUnspendable();
|
||||
};
|
||||
|
||||
/**
|
||||
* Inject properties from a pay-to-pubkey script.
|
||||
* @private
|
||||
|
||||
Loading…
Reference in New Issue
Block a user