modules: drop all circular deps.

This commit is contained in:
Christopher Jeffrey 2016-11-19 02:26:05 -08:00
parent 4c9d9d5160
commit 6f9ad90e07
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
22 changed files with 610 additions and 431 deletions

View File

@ -16,6 +16,7 @@ var constants = require('../protocol/constants');
var siphash = require('../crypto/siphash');
var AbstractBlock = require('../primitives/abstractblock');
var TX = require('../primitives/tx');
var Headers = require('../primitives/headers');
var Block = require('../primitives/block');
/**
@ -403,6 +404,13 @@ CompactBlock.prototype.destroy = function destroy() {
}
};
CompactBlock.prototype.toHeaders = function toHeaders() {
var headers = new Headers(this);
headers._hash = this._hash;
headers._valid = true;
return headers;
};
/**
* Represents a BlockTransactionsRequest (bip152): `getblocktxn` packet.
* @see https://github.com/bitcoin/bips/blob/master/bip-0152.mediawiki

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = AbstractBlock;
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');
@ -17,7 +15,6 @@ var VerifyResult = utils.VerifyResult;
var BufferWriter = require('../utils/writer');
var time = require('../net/timedata');
var InvItem = require('./invitem');
var Headers = require('./headers');
/**
* The class which all block-like objects inherit from.
@ -262,18 +259,6 @@ AbstractBlock.prototype.toInv = function toInv() {
return new InvItem(constants.inv.BLOCK, this.hash('hex'));
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
AbstractBlock.prototype.toHeaders = function toHeaders() {
var headers = new Headers(this);
headers._hash = this._hash;
headers._valid = true;
return headers;
};
/*
* Expose
*/

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Address;
var Network = require('../protocol/network');
var networks = require('../protocol/networks');
var constants = require('../protocol/constants');
@ -17,7 +15,7 @@ var crypto = require('../crypto/crypto');
var assert = require('assert');
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
var Script = require('../script/script');
var scriptTypes = constants.scriptTypes;
/**
* Represents an address.
@ -40,7 +38,7 @@ function Address(options) {
return new Address(options);
this.hash = constants.ZERO_HASH160;
this.type = Script.types.PUBKEYHASH;
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
this.network = Network.primary;
@ -141,15 +139,6 @@ Address.prototype.toBase58 = function toBase58(network) {
return utils.toBase58(this.toRaw(network));
};
/**
* Convert the address to an output script.
* @returns {Script}
*/
Address.prototype.toScript = function toScript() {
return Script.fromAddress(this);
};
/**
* Convert the Address to a string.
* @returns {Base58String}
@ -259,42 +248,42 @@ Address.fromBase58 = function fromBase58(address) {
Address.prototype.fromScript = function fromScript(script) {
if (script.isPubkey()) {
this.hash = crypto.hash160(script.get(0));
this.type = Script.types.PUBKEYHASH;
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isPubkeyhash()) {
this.hash = script.get(2);
this.type = Script.types.PUBKEYHASH;
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isScripthash()) {
this.hash = script.get(1);
this.type = Script.types.SCRIPTHASH;
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
if (script.isWitnessPubkeyhash()) {
this.hash = script.get(1);
this.type = Script.types.WITNESSPUBKEYHASH;
this.type = scriptTypes.WITNESSPUBKEYHASH;
this.version = 0;
return this;
}
if (script.isWitnessScripthash()) {
this.hash = script.get(1);
this.type = Script.types.WITNESSSCRIPTHASH;
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 0;
return this;
}
if (script.isWitnessMasthash()) {
this.hash = script.get(1);
this.type = Script.types.WITNESSSCRIPTHASH;
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 1;
return this;
}
@ -302,7 +291,7 @@ Address.prototype.fromScript = function fromScript(script) {
// Put this last: it's the slowest to check.
if (script.isMultisig()) {
this.hash = script.hash160();
this.type = Script.types.SCRIPTHASH;
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
@ -319,14 +308,14 @@ Address.prototype.fromWitness = function fromWitness(witness) {
// since we can't get the version.
if (witness.isPubkeyhashInput()) {
this.hash = crypto.hash160(witness.get(1));
this.type = Script.types.WITNESSPUBKEYHASH;
this.type = scriptTypes.WITNESSPUBKEYHASH;
this.version = 0;
return this;
}
if (witness.isScripthashInput()) {
this.hash = crypto.sha256(witness.get(witness.length - 1));
this.type = Script.types.WITNESSSCRIPTHASH;
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 0;
return this;
}
@ -341,14 +330,14 @@ Address.prototype.fromWitness = function fromWitness(witness) {
Address.prototype.fromInputScript = function fromInputScript(script) {
if (script.isPubkeyhashInput()) {
this.hash = crypto.hash160(script.get(1));
this.type = Script.types.PUBKEYHASH;
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isScripthashInput()) {
this.hash = crypto.hash160(script.get(script.length - 1));
this.type = Script.types.SCRIPTHASH;
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
@ -406,10 +395,10 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) {
hash = new Buffer(hash, 'hex');
if (typeof type === 'string')
type = Script.types[type.toUpperCase()];
type = scriptTypes[type.toUpperCase()];
if (type == null)
type = Script.types.PUBKEYHASH;
type = scriptTypes.PUBKEYHASH;
if (version == null)
version = -1;
@ -428,11 +417,11 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) {
} else {
assert(Address.isWitness(type), 'Wrong version (non-witness).');
assert(version >= 0 && version <= 16, 'Bad program version.');
if (version === 0 && type === Script.types.WITNESSPUBKEYHASH)
if (version === 0 && type === scriptTypes.WITNESSPUBKEYHASH)
assert(hash.length === 20, 'Hash is the wrong size.');
else if (version === 0 && type === Script.types.WITNESSSCRIPTHASH)
else if (version === 0 && type === scriptTypes.WITNESSSCRIPTHASH)
assert(hash.length === 32, 'Hash is the wrong size.');
else if (version === 1 && type === Script.types.WITNESSSCRIPTHASH)
else if (version === 1 && type === scriptTypes.WITNESSSCRIPTHASH)
assert(hash.length === 32, 'Hash is the wrong size.');
}
@ -469,9 +458,9 @@ Address.fromHash = function fromHash(hash, type, version, network) {
Address.prototype.fromData = function fromData(data, type, version, network) {
if (typeof type === 'string')
type = Script.types[type.toUpperCase()];
type = scriptTypes[type.toUpperCase()];
if (type === Script.types.WITNESSSCRIPTHASH) {
if (type === scriptTypes.WITNESSSCRIPTHASH) {
if (version === 0) {
assert(Buffer.isBuffer(data));
data = crypto.sha256(data);
@ -481,7 +470,7 @@ Address.prototype.fromData = function fromData(data, type, version, network) {
} else {
throw new Error('Cannot create from version=' + version);
}
} else if (type === Script.types.WITNESSPUBKEYHASH) {
} else if (type === scriptTypes.WITNESSPUBKEYHASH) {
if (version !== 0)
throw new Error('Cannot create from version=' + version);
assert(Buffer.isBuffer(data));
@ -530,7 +519,7 @@ Address.validate = function validate(address, type) {
}
if (typeof type === 'string')
type = Script.types[type.toUpperCase()];
type = scriptTypes[type.toUpperCase()];
if (type && address.type !== type)
return false;
@ -580,13 +569,13 @@ Address.getHash = function getHash(data, enc) {
Address.getPrefix = function getPrefix(type, network) {
var prefixes = network.addressPrefix;
switch (type) {
case Script.types.PUBKEYHASH:
case scriptTypes.PUBKEYHASH:
return prefixes.pubkeyhash;
case Script.types.SCRIPTHASH:
case scriptTypes.SCRIPTHASH:
return prefixes.scripthash;
case Script.types.WITNESSPUBKEYHASH:
case scriptTypes.WITNESSPUBKEYHASH:
return prefixes.witnesspubkeyhash;
case Script.types.WITNESSSCRIPTHASH:
case scriptTypes.WITNESSSCRIPTHASH:
return prefixes.witnessscripthash;
default:
return -1;
@ -604,13 +593,13 @@ Address.getType = function getType(prefix, network) {
var prefixes = network.addressPrefix;
switch (prefix) {
case prefixes.pubkeyhash:
return Script.types.PUBKEYHASH;
return scriptTypes.PUBKEYHASH;
case prefixes.scripthash:
return Script.types.SCRIPTHASH;
return scriptTypes.SCRIPTHASH;
case prefixes.witnesspubkeyhash:
return Script.types.WITNESSPUBKEYHASH;
return scriptTypes.WITNESSPUBKEYHASH;
case prefixes.witnessscripthash:
return Script.types.WITNESSSCRIPTHASH;
return scriptTypes.WITNESSSCRIPTHASH;
default:
return -1;
}
@ -624,9 +613,9 @@ Address.getType = function getType(prefix, network) {
Address.isWitness = function isWitness(type) {
switch (type) {
case Script.types.WITNESSPUBKEYHASH:
case scriptTypes.WITNESSPUBKEYHASH:
return true;
case Script.types.WITNESSSCRIPTHASH:
case scriptTypes.WITNESSSCRIPTHASH:
return true;
default:
return false;

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Block;
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');
var assert = require('assert');
@ -19,6 +17,7 @@ var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
var TX = require('./tx');
var MerkleBlock = require('./merkleblock');
var Headers = require('./headers');
var Network = require('../protocol/network');
/**
@ -790,6 +789,18 @@ Block.prototype.frameWitness = function frameWitness(writer) {
return this.frame(true, writer);
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
Block.prototype.toHeaders = function toHeaders() {
var headers = new Headers(this);
headers._hash = this._hash;
headers._valid = true;
return headers;
};
/**
* Test whether an object is a Block.
* @param {Object} obj

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Coin;
var utils = require('../utils/utils');
var constants = require('../protocol/constants');
var Network = require('../protocol/network');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Headers;
var utils = require('../utils/utils');
var AbstractBlock = require('./abstractblock');
var BufferWriter = require('../utils/writer');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Input;
var utils = require('../utils/utils');
var assert = require('assert');
var constants = require('../protocol/constants');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = InvItem;
var constants = require('../protocol/constants');
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = KeyRing;
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');

View File

@ -7,12 +7,11 @@
'use strict';
module.exports = MemBlock;
var utils = require('../utils/utils');
var AbstractBlock = require('./abstractblock');
var Block = require('./block');
var Script = require('../script/script');
var Headers = require('./headers');
var BufferReader = require('../utils/reader');
/**
@ -194,6 +193,18 @@ MemBlock.prototype.toBlock = function toBlock() {
return block;
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
MemBlock.prototype.toHeaders = function toHeaders() {
var headers = new Headers(this);
headers._hash = this._hash;
headers._valid = true;
return headers;
};
/**
* Test whether an object is a MemBlock.
* @param {Object} obj

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = MerkleBlock;
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');
var assert = require('assert');
@ -18,6 +16,7 @@ var AbstractBlock = require('./abstractblock');
var VerifyResult = utils.VerifyResult;
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
var Headers = require('./headers');
var TX = require('./tx');
/**
@ -639,6 +638,18 @@ MerkleBlock.isMerkleBlock = function isMerkleBlock(obj) {
&& typeof obj.verifyPartial === 'function';
};
/**
* Convert the block to a headers object.
* @returns {Headers}
*/
MerkleBlock.prototype.toHeaders = function toHeaders() {
var headers = new Headers(this);
headers._hash = this._hash;
headers._valid = true;
return headers;
};
/*
* Helpers
*/

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = MTX;
var utils = require('../utils/utils');
var assert = require('assert');
var constants = require('../protocol/constants');

View File

@ -6,8 +6,6 @@
'use strict';
module.exports = NetworkAddress;
var constants = require('../protocol/constants');
var Network = require('../protocol/network');
var time = require('../net/timedata');

View File

@ -6,8 +6,6 @@
'use strict';
module.exports = Outpoint;
var utils = require('../utils/utils');
var assert = require('assert');
var constants = require('../protocol/constants');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Output;
var utils = require('../utils/utils');
var constants = require('../protocol/constants');
var Network = require('../protocol/network');
@ -16,7 +14,6 @@ var Script = require('../script/script');
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
var assert = require('assert');
var TX = require('./tx');
/**
* Represents a transaction output.
@ -156,7 +153,7 @@ Output.prototype.toJSON = function toJSON(network) {
Output.prototype.getDustThreshold = function getDustThreshold(rate) {
var scale = constants.WITNESS_SCALE_FACTOR;
var size;
var size, fee;
if (rate == null)
rate = constants.tx.MIN_RELAY;
@ -173,7 +170,12 @@ Output.prototype.getDustThreshold = function getDustThreshold(rate) {
size += 32 + 4 + 1 + 107 + 4;
}
return 3 * TX.getMinFee(size, rate);
fee = Math.floor(rate * size / 1000);
if (fee === 0 && rate > 0)
fee = rate;
return 3 * fee;
};
/**

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = TX;
var utils = require('../utils/utils');
var crypto = require('../crypto/crypto');
var assert = require('assert');
@ -1466,10 +1464,7 @@ TX.prototype.hasStandardInputs = function hasStandardInputs() {
if (stack.length === 0)
return false;
redeem = stack.getRedeem();
if (!redeem)
return false;
redeem = Script.fromRaw(stack.top(-1));
if (redeem.getSigops(true) > maxSigops)
return false;

468
lib/script/encoding.js Normal file
View File

@ -0,0 +1,468 @@
/*!
* encoding.js - script-related encoding for bcoin
* Copyright (c) 2014-2015, Fedor Indutny (MIT License)
* Copyright (c) 2014-2016, Christopher Jeffrey (MIT License).
* https://github.com/bcoin-org/bcoin
*/
'use strict';
var BN = require('bn.js');
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
var assert = require('assert');
var opcodes = constants.opcodes;
var STACK_FALSE = new Buffer(0);
var ScriptError = require('../utils/errors').ScriptError;
/**
* Test whether the data element is a ripemd160 hash.
* @param {Buffer?} hash
* @returns {Boolean}
*/
exports.isHash = function isHash(hash) {
return Buffer.isBuffer(hash) && hash.length === 20;
};
/**
* Test whether the data element is a public key. Note that
* this does not verify the format of the key, only the length.
* @param {Buffer?} key
* @returns {Boolean}
*/
exports.isKey = function isKey(key) {
return Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65;
};
/**
* Test whether the data element is a signature. Note that
* this does not verify the format of the signature, only the length.
* @param {Buffer?} sig
* @returns {Boolean}
*/
exports.isSignature = function isSignature(sig) {
return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73;
};
/**
* Test whether the data element is a null dummy (a zero-length array).
* @param {Buffer?} data
* @returns {Boolean}
*/
exports.isDummy = function isDummy(data) {
return Buffer.isBuffer(data) && data.length === 0;
};
/**
* Test whether the data element is a compressed key.
* @param {Buffer} key
* @returns {Boolean}
*/
exports.isCompressedEncoding = function isCompressedEncoding(key) {
assert(Buffer.isBuffer(key));
if (key.length !== 33)
return false;
if (key[0] !== 0x02 && key[0] !== 0x03)
return false;
return true;
};
/**
* Test whether the data element is a valid key.
* @param {Buffer} key
* @returns {Boolean}
*/
exports.isKeyEncoding = function isKeyEncoding(key) {
assert(Buffer.isBuffer(key));
if (key.length < 33)
return false;
if (key[0] === 0x04) {
if (key.length !== 65)
return false;
} else if (key[0] === 0x02 || key[0] === 0x03) {
if (key.length !== 33)
return false;
} else {
return false;
}
return true;
};
/**
* Test a signature to see if it abides by BIP66.
* @see https://github.com/bitcoin/bips/blob/master/bip-0066.mediawiki
* @param {Buffer} sig
* @returns {Boolean}
*/
exports.isSignatureEncoding = function isSignatureEncoding(sig) {
var lenR, lenS;
assert(Buffer.isBuffer(sig));
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
// * total-length: 1-byte length descriptor of everything that follows,
// excluding the sighash byte.
// * R-length: 1-byte length descriptor of the R value that follows.
// * R: arbitrary-length big-endian encoded R value. It must use the shortest
// possible encoding for a positive integers (which means no null bytes at
// the start, except a single one when the next byte has its highest bit set).
// * S-length: 1-byte length descriptor of the S value that follows.
// * S: arbitrary-length big-endian encoded S value. The same rules apply.
// * sighash: 1-byte value indicating what data is hashed (not part of the DER
// signature)
// Minimum and maximum size constraints.
if (sig.length < 9)
return false;
if (sig.length > 73)
return false;
// A signature is of type 0x30 (compound).
if (sig[0] !== 0x30)
return false;
// Make sure the length covers the entire signature.
if (sig[1] !== sig.length - 3)
return false;
// Extract the length of the R element.
lenR = sig[3];
// Make sure the length of the S element is still inside the signature.
if (5 + lenR >= sig.length)
return false;
// Extract the length of the S element.
lenS = sig[5 + lenR];
// Verify that the length of the signature matches the sum of the length
// of the elements.
if (lenR + lenS + 7 !== sig.length)
return false;
// Check whether the R element is an integer.
if (sig[2] !== 0x02)
return false;
// Zero-length integers are not allowed for R.
if (lenR === 0)
return false;
// Negative numbers are not allowed for R.
if (sig[4] & 0x80)
return false;
// Null bytes at the start of R are not allowed, unless R would
// otherwise be interpreted as a negative number.
if (lenR > 1 && (sig[4] === 0x00) && !(sig[5] & 0x80))
return false;
// Check whether the S element is an integer.
if (sig[lenR + 4] !== 0x02)
return false;
// Zero-length integers are not allowed for S.
if (lenS === 0)
return false;
// Negative numbers are not allowed for S.
if (sig[lenR + 6] & 0x80)
return false;
// Null bytes at the start of S are not allowed, unless S would otherwise be
// interpreted as a negative number.
if (lenS > 1 && (sig[lenR + 6] === 0x00) && !(sig[lenR + 7] & 0x80))
return false;
return true;
};
/**
* Format script code into a human readable-string.
* @param {Array} code
* @returns {String} Human-readable string.
*/
exports.formatStack = function formatStack(items) {
var out = [];
var i;
for (i = 0; i < items.length; i++)
out.push(items[i].toString('hex'));
return out.join(' ');
};
/**
* Format script code into a human readable-string.
* @param {Array} code
* @returns {String} Human-readable string.
*/
exports.formatCode = function formatCode(code) {
var out = [];
var i, op, data, value, size;
for (i = 0; i < code.length; i++) {
op = code[i];
data = op.data;
value = op.value;
if (data) {
size = data.length.toString(16);
while (size.length % 2 !== 0)
size = '0' + size;
if (!constants.opcodesByVal[value]) {
value = value.toString(16);
if (value.length < 2)
value = '0' + value;
value = '0x' + value + ' 0x' + data.toString('hex');
out.push(value);
continue;
}
value = constants.opcodesByVal[value];
value = value + ' 0x' + size + ' 0x' + data.toString('hex');
out.push(value);
continue;
}
assert(typeof value === 'number');
if (constants.opcodesByVal[value]) {
value = constants.opcodesByVal[value];
out.push(value);
continue;
}
if (value === -1) {
out.push('OP_INVALIDOPCODE');
break;
}
value = value.toString(16);
if (value.length < 2)
value = '0' + value;
value = '0x' + value;
out.push(value);
}
return out.join(' ');
};
/**
* Format script code into bitcoind asm format.
* @param {Array} code
* @param {Boolean?} decode - Attempt to decode hash types.
* @returns {String} Human-readable string.
*/
exports.formatItem = function formatItem(data, decode) {
var symbol, type;
if (data.length <= 4) {
data = exports.num(data, constants.flags.VERIFY_NONE);
return data.toString(10);
}
if (decode) {
symbol = '';
if (exports.isSignatureEncoding(data)) {
type = data[data.length - 1];
symbol = constants.hashTypeByVal[type & 0x1f] || '';
if (symbol) {
if (type & constants.hashType.ANYONECANPAY)
symbol += '|ANYONECANPAY';
symbol = '[' + symbol + ']';
}
data = data.slice(0, -1);
}
return data.toString('hex') + symbol;
}
return data.toString('hex');
};
/**
* Format script code into bitcoind asm format.
* @param {Array} code
* @param {Boolean?} decode - Attempt to decode hash types.
* @returns {String} Human-readable string.
*/
exports.formatASM = function formatASM(code, decode) {
var out = [];
var i, op, data, value;
if (code.length > 0 && code[0].value === opcodes.OP_RETURN)
decode = false;
for (i = 0; i < code.length; i++) {
op = code[i];
data = op.data;
value = op.value;
if (value === -1) {
out.push('[error]');
break;
}
if (data) {
data = exports.formatItem(data, decode);
out.push(data);
continue;
}
value = constants.opcodesByVal[value] || 'OP_UNKNOWN';
out.push(value);
}
return out.join(' ');
};
/**
* Format script code into bitcoind asm format.
* @param {Array} code
* @param {Boolean?} decode - Attempt to decode hash types.
* @returns {String} Human-readable string.
*/
exports.formatStackASM = function formatStackASM(items, decode) {
var out = [];
var i, item, data;
for (i = 0; i < items.length; i++) {
item = items[i];
data = exports.formatItem(item, decode);
out.push(data);
}
return out.join(' ');
};
/**
* Create a CScriptNum.
* @param {Buffer} value
* @param {Number?} flags - Script standard flags.
* @param {Number?} size - Max size in bytes.
* @returns {BN}
* @throws {ScriptError}
*/
exports.num = function num(value, flags, size) {
var result;
assert(Buffer.isBuffer(value));
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (size == null)
size = 4;
if (value.length > size)
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
if ((flags & constants.flags.VERIFY_MINIMALDATA) && value.length > 0) {
// If the low bits on the last byte are unset,
// fail if the value's second to last byte does
// not have the high bit set. A number can't
// justify having the last byte's low bits unset
// unless they ran out of space for the sign bit
// in the second to last bit. We also fail on [0]
// to avoid negative zero (also avoids positive
// zero).
if (!(value[value.length - 1] & 0x7f)) {
if (value.length === 1 || !(value[value.length - 2] & 0x80)) {
throw new ScriptError(
'UNKNOWN_ERROR',
'Non-minimally encoded Script number.');
}
}
}
if (value.length === 0)
return new BN(0);
result = new BN(value, 'le');
// If the input vector's most significant byte is
// 0x80, remove it from the result's msb and return
// a negative.
// Equivalent to:
// -(result & ~(0x80 << (8 * (value.length - 1))))
if (value[value.length - 1] & 0x80)
result.setn((value.length * 8) - 1, 0).ineg();
return result;
};
/**
* Create a script array. Will convert Numbers and big
* numbers to a little-endian buffer while taking into
* account negative zero, minimaldata, etc.
* @example
* assert.deepEqual(Script.array(0), new Buffer(0));
* assert.deepEqual(Script.array(0xffee), new Buffer('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0xffee)), new Buffer('eeff00', 'hex'));
* assert.deepEqual(Script.array(new BN(0x1e).ineg()), new Buffer('9e', 'hex'));
* @param {Number|BN} value
* @returns {Buffer}
*/
exports.array = function(value) {
var neg, result;
if (utils.isNumber(value))
value = new BN(value);
assert(BN.isBN(value));
if (value.cmpn(0) === 0)
return STACK_FALSE;
// If the most significant byte is >= 0x80
// and the value is positive, push a new
// zero-byte to make the significant
// byte < 0x80 again.
// If the most significant byte is >= 0x80
// and the value is negative, push a new
// 0x80 byte that will be popped off when
// converting to an integral.
// If the most significant byte is < 0x80
// and the value is negative, add 0x80 to
// it, since it will be subtracted and
// interpreted as a negative when
// converting to an integral.
neg = value.cmpn(0) < 0;
result = value.toArray('le');
if (result[result.length - 1] & 0x80)
result.push(neg ? 0x80 : 0);
else if (neg)
result[result.length - 1] |= 0x80;
return new Buffer(result);
};

View File

@ -7,12 +7,11 @@
'use strict';
module.exports = Opcode;
var BN = require('bn.js');
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
var Script = require('./script');
var encoding = require('./encoding');
var BufferWriter = require('../utils/writer');
var assert = require('assert');
var opcodes = constants.opcodes;
@ -40,8 +39,36 @@ function Opcode(value, data) {
* @returns {Buffer}
*/
Opcode.prototype.toRaw = function toRaw(writer) {
return Script.encode([this], writer);
Opcode.prototype.toRaw = function toRaw() {
var p = new BufferWriter();
if (this.value === -1)
throw new Error('Cannot reserialize a parse error.');
if (this.data) {
if (this.value <= 0x4b) {
p.writeU8(this.data.length);
p.writeBytes(this.data);
} else if (this.value === opcodes.OP_PUSHDATA1) {
p.writeU8(opcodes.OP_PUSHDATA1);
p.writeU8(this.data.length);
p.writeBytes(this.data);
} else if (this.value === opcodes.OP_PUSHDATA2) {
p.writeU8(opcodes.OP_PUSHDATA2);
p.writeU16(this.data.length);
p.writeBytes(this.data);
} else if (this.value === opcodes.OP_PUSHDATA4) {
p.writeU8(opcodes.OP_PUSHDATA4);
p.writeU32(this.data.length);
p.writeBytes(this.data);
} else {
throw new Error('Unknown pushdata opcode.');
}
} else {
p.writeU8(this.value);
}
return p.render();
};
/**
@ -107,7 +134,7 @@ Opcode.fromPush = function fromPush(data) {
*/
Opcode.fromNumber = function fromNumber(num) {
return Opcode.fromData(Script.array(num));
return Opcode.fromData(encoding.array(num));
};
/**

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Program;
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
var assert = require('assert');

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Script;
var BN = require('bn.js');
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
@ -26,6 +24,7 @@ var Program = require('./program');
var Opcode = require('./opcode');
var Stack = require('./stack');
var SigCache = require('./sigcache');
var encoding = require('./encoding');
var ec = require('../crypto/ec');
var Address = require('../primitives/address');
@ -1332,51 +1331,7 @@ Script.bool = function bool(value) {
*/
Script.num = function num(value, flags, size) {
var result;
assert(Buffer.isBuffer(value));
if (flags == null)
flags = constants.flags.STANDARD_VERIFY_FLAGS;
if (size == null)
size = 4;
if (value.length > size)
throw new ScriptError('UNKNOWN_ERROR', 'Script number overflow.');
if ((flags & constants.flags.VERIFY_MINIMALDATA) && value.length > 0) {
// If the low bits on the last byte are unset,
// fail if the value's second to last byte does
// not have the high bit set. A number can't
// justify having the last byte's low bits unset
// unless they ran out of space for the sign bit
// in the second to last bit. We also fail on [0]
// to avoid negative zero (also avoids positive
// zero).
if (!(value[value.length - 1] & 0x7f)) {
if (value.length === 1 || !(value[value.length - 2] & 0x80)) {
throw new ScriptError(
'UNKNOWN_ERROR',
'Non-minimally encoded Script number.');
}
}
}
if (value.length === 0)
return new BN(0);
result = new BN(value, 'le');
// If the input vector's most significant byte is
// 0x80, remove it from the result's msb and return
// a negative.
// Equivalent to:
// -(result & ~(0x80 << (8 * (value.length - 1))))
if (value[value.length - 1] & 0x80)
result.setn((value.length * 8) - 1, 0).ineg();
return result;
return encoding.num(value, flags, size);
};
/**
@ -1393,41 +1348,7 @@ Script.num = function num(value, flags, size) {
*/
Script.array = function(value) {
var neg, result;
if (utils.isNumber(value))
value = new BN(value);
assert(BN.isBN(value));
if (value.cmpn(0) === 0)
return STACK_FALSE;
// If the most significant byte is >= 0x80
// and the value is positive, push a new
// zero-byte to make the significant
// byte < 0x80 again.
// If the most significant byte is >= 0x80
// and the value is negative, push a new
// 0x80 byte that will be popped off when
// converting to an integral.
// If the most significant byte is < 0x80
// and the value is negative, add 0x80 to
// it, since it will be subtracted and
// interpreted as a negative when
// converting to an integral.
neg = value.cmpn(0) < 0;
result = value.toArray('le');
if (result[result.length - 1] & 0x80)
result.push(neg ? 0x80 : 0);
else if (neg)
result[result.length - 1] |= 0x80;
return new Buffer(result);
return encoding.array(value);
};
/**
@ -2690,7 +2611,7 @@ Script.prototype.set = function set(i, data) {
*/
Script.isHash = function isHash(hash) {
return Buffer.isBuffer(hash) && hash.length === 20;
return encoding.isHash(hash);
};
/**
@ -2701,7 +2622,7 @@ Script.isHash = function isHash(hash) {
*/
Script.isKey = function isKey(key) {
return Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65;
return encoding.isKey(key);
};
/**
@ -2712,7 +2633,7 @@ Script.isKey = function isKey(key) {
*/
Script.isSignature = function isSignature(sig) {
return Buffer.isBuffer(sig) && sig.length >= 9 && sig.length <= 73;
return encoding.isSignature(sig);
};
/**
@ -2722,7 +2643,7 @@ Script.isSignature = function isSignature(sig) {
*/
Script.isDummy = function isDummy(data) {
return Buffer.isBuffer(data) && data.length === 0;
return encoding.isDummy(data);
};
/**
@ -2761,15 +2682,7 @@ Script.validateKey = function validateKey(key, flags, version) {
*/
Script.isCompressedEncoding = function isCompressedEncoding(key) {
assert(Buffer.isBuffer(key));
if (key.length !== 33)
return false;
if (key[0] !== 0x02 && key[0] !== 0x03)
return false;
return true;
return encoding.isCompressedEncoding(key);
};
/**
@ -2779,22 +2692,7 @@ Script.isCompressedEncoding = function isCompressedEncoding(key) {
*/
Script.isKeyEncoding = function isKeyEncoding(key) {
assert(Buffer.isBuffer(key));
if (key.length < 33)
return false;
if (key[0] === 0x04) {
if (key.length !== 65)
return false;
} else if (key[0] === 0x02 || key[0] === 0x03) {
if (key.length !== 33)
return false;
} else {
return false;
}
return true;
return encoding.isKeyEncoding(key);
};
/**
@ -2847,87 +2745,7 @@ Script.validateSignature = function validateSignature(sig, flags) {
*/
Script.isSignatureEncoding = function isSignatureEncoding(sig) {
var lenR, lenS;
assert(Buffer.isBuffer(sig));
// Format: 0x30 [total-length] 0x02 [R-length] [R] 0x02 [S-length] [S] [sighash]
// * total-length: 1-byte length descriptor of everything that follows,
// excluding the sighash byte.
// * R-length: 1-byte length descriptor of the R value that follows.
// * R: arbitrary-length big-endian encoded R value. It must use the shortest
// possible encoding for a positive integers (which means no null bytes at
// the start, except a single one when the next byte has its highest bit set).
// * S-length: 1-byte length descriptor of the S value that follows.
// * S: arbitrary-length big-endian encoded S value. The same rules apply.
// * sighash: 1-byte value indicating what data is hashed (not part of the DER
// signature)
// Minimum and maximum size constraints.
if (sig.length < 9)
return false;
if (sig.length > 73)
return false;
// A signature is of type 0x30 (compound).
if (sig[0] !== 0x30)
return false;
// Make sure the length covers the entire signature.
if (sig[1] !== sig.length - 3)
return false;
// Extract the length of the R element.
lenR = sig[3];
// Make sure the length of the S element is still inside the signature.
if (5 + lenR >= sig.length)
return false;
// Extract the length of the S element.
lenS = sig[5 + lenR];
// Verify that the length of the signature matches the sum of the length
// of the elements.
if (lenR + lenS + 7 !== sig.length)
return false;
// Check whether the R element is an integer.
if (sig[2] !== 0x02)
return false;
// Zero-length integers are not allowed for R.
if (lenR === 0)
return false;
// Negative numbers are not allowed for R.
if (sig[4] & 0x80)
return false;
// Null bytes at the start of R are not allowed, unless R would
// otherwise be interpreted as a negative number.
if (lenR > 1 && (sig[4] === 0x00) && !(sig[5] & 0x80))
return false;
// Check whether the S element is an integer.
if (sig[lenR + 4] !== 0x02)
return false;
// Zero-length integers are not allowed for S.
if (lenS === 0)
return false;
// Negative numbers are not allowed for S.
if (sig[lenR + 6] & 0x80)
return false;
// Null bytes at the start of S are not allowed, unless S would otherwise be
// interpreted as a negative number.
if (lenS > 1 && (sig[lenR + 6] === 0x00) && !(sig[lenR + 7] & 0x80))
return false;
return true;
return encoding.isSignatureEncoding(sig);
};
/**
@ -2972,58 +2790,7 @@ Script.isLowDER = function isLowDER(sig) {
*/
Script.format = function format(code) {
var out = [];
var i, op, data, value, size;
for (i = 0; i < code.length; i++) {
op = code[i];
data = op.data;
value = op.value;
if (data) {
size = data.length.toString(16);
while (size.length % 2 !== 0)
size = '0' + size;
if (!constants.opcodesByVal[value]) {
value = value.toString(16);
if (value.length < 2)
value = '0' + value;
value = '0x' + value + ' 0x' + data.toString('hex');
out.push(value);
continue;
}
value = constants.opcodesByVal[value];
value = value + ' 0x' + size + ' 0x' + data.toString('hex');
out.push(value);
continue;
}
assert(typeof value === 'number');
if (constants.opcodesByVal[value]) {
value = constants.opcodesByVal[value];
out.push(value);
continue;
}
if (value === -1) {
out.push('OP_INVALIDOPCODE');
break;
}
value = value.toString(16);
if (value.length < 2)
value = '0' + value;
value = '0x' + value;
out.push(value);
}
return out.join(' ');
return encoding.formatCode(code);
};
/**
@ -3034,52 +2801,7 @@ Script.format = function format(code) {
*/
Script.formatASM = function formatASM(code, decode) {
var out = [];
var i, op, type, symbol, data, value;
for (i = 0; i < code.length; i++) {
op = code[i];
data = op.data;
value = op.value;
if (value === -1) {
out.push('[error]');
break;
}
if (data) {
if (data.length <= 4) {
data = Script.num(data, constants.flags.VERIFY_NONE);
out.push(data.toString(10));
continue;
}
if (decode && code[0] !== opcodes.OP_RETURN) {
symbol = '';
if (Script.isSignatureEncoding(data)) {
type = data[data.length - 1];
symbol = constants.hashTypeByVal[type & 0x1f] || '';
if (symbol) {
if (type & constants.hashType.ANYONECANPAY)
symbol += '|ANYONECANPAY';
symbol = '[' + symbol + ']';
}
data = data.slice(0, -1);
}
out.push(data.toString('hex') + symbol);
continue;
}
out.push(data.toString('hex'));
continue;
}
value = constants.opcodesByVal[value] || 'OP_UNKNOWN';
out.push(value);
}
return out.join(' ');
return encoding.formatASM(code, decode);
};
/**

View File

@ -7,10 +7,7 @@
'use strict';
module.exports = Stack;
var Script = require('./script');
var Witness = require('./witness');
var encoding = require('./encoding');
/**
* Represents the stack of a Script during execution.
@ -51,7 +48,7 @@ Stack.prototype.inspect = function inspect() {
*/
Stack.prototype.toString = function toString() {
return Witness.format(this.items);
return encoding.formatStack(this.items);
};
/**
@ -61,19 +58,7 @@ Stack.prototype.toString = function toString() {
*/
Stack.prototype.toASM = function toASM(decode) {
return Script.formatASM(this.items, decode);
};
/**
* Pop the redeem script off the stack and deserialize it.
* @returns {Script|null} The redeem script.
*/
Stack.prototype.getRedeem = function getRedeem() {
var redeem = this.items[this.items.length - 1];
if (!redeem)
return;
return new Script(redeem);
return encoding.formatStackASM(this.items, decode);
};
/**

View File

@ -7,8 +7,6 @@
'use strict';
module.exports = Witness;
var BN = require('bn.js');
var constants = require('../protocol/constants');
var utils = require('../utils/utils');
@ -18,6 +16,7 @@ var STACK_FALSE = new Buffer(0);
var STACK_NEGATE = new Buffer([0x81]);
var scriptTypes = constants.scriptTypes;
var Script = require('./script');
var encoding = require('./encoding');
var Opcode = require('./opcode');
var BufferWriter = require('../utils/writer');
var BufferReader = require('../utils/reader');
@ -131,7 +130,7 @@ Witness.prototype.inspect = function inspect() {
*/
Witness.prototype.toString = function toString() {
return Witness.format(this.items);
return encoding.formatStack(this.items);
};
/**
@ -141,7 +140,7 @@ Witness.prototype.toString = function toString() {
*/
Witness.prototype.toASM = function toASM(decode) {
return Script.formatASM(Script.parseArray(this.items), decode);
return encoding.formatStackASM(this.items, decode);
};
/**
@ -211,8 +210,8 @@ Witness.prototype.isPubkeyInput = function isPubkeyInput() {
Witness.prototype.isPubkeyhashInput = function isPubkeyhashInput() {
return this.items.length === 2
&& Script.isSignatureEncoding(this.items[0])
&& Script.isKeyEncoding(this.items[1]);
&& encoding.isSignatureEncoding(this.items[0])
&& encoding.isKeyEncoding(this.items[1]);
};
/**
@ -446,7 +445,7 @@ Witness.prototype.getNumber = function getNumber(i) {
var item = this.items[i];
if (!item || item.length > 5)
return;
return Script.num(item, constants.flags.VERIFY_NONE, 5);
return encoding.num(item, constants.flags.VERIFY_NONE, 5);
};
/**
@ -505,7 +504,7 @@ Witness.encodeItem = function encodeItem(data) {
}
if (BN.isBN(data))
return Script.array(data);
return encoding.array(data);
if (typeof data === 'string')
return new Buffer(data, 'utf8');
@ -583,22 +582,6 @@ Witness.fromString = function fromString(items) {
return new Witness().fromString(items);
};
/**
* Format script code into a human readable-string.
* @param {Array} code
* @returns {String} Human-readable string.
*/
Witness.format = function format(items) {
var out = [];
var i;
for (i = 0; i < items.length; i++)
out.push(items[i].toString('hex'));
return out.join(' ');
};
/**
* Test an object to see if it is a Witness.
* @param {Object} obj