address: types and MAST.

This commit is contained in:
Christopher Jeffrey 2016-07-02 04:53:42 -07:00
parent afa7862e55
commit 503cbfc886
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
12 changed files with 395 additions and 267 deletions

View File

@ -15,6 +15,7 @@ var assert = utils.assert;
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
var Script = bcoin.script;
var scriptTypes = constants.scriptTypes;
/**
* Represents an address.
@ -37,7 +38,7 @@ function Address(options) {
return new Address(options);
this.hash = constants.ZERO_HASH160;
this.type = 'pubkeyhash';
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
this.network = bcoin.network.get().type;
@ -82,6 +83,15 @@ Address.prototype.getHash = function getHash(enc) {
return this.hash;
};
/**
* Get the address type as a string.
* @returns {AddressType}
*/
Address.prototype.getType = function getType() {
return constants.scriptTypesByVal[this.type].toLowerCase();
};
/**
* Compile the address object to its raw serialization.
* @param {{NetworkType|Network)?} network
@ -97,9 +107,9 @@ Address.prototype.toRaw = function toRaw(network) {
network = this.network;
network = bcoin.network.get(network);
prefix = network.address.prefixes[this.type];
prefix = Address.getPrefix(this.type, network);
assert(prefix != null, 'Not a valid address prefix.');
assert(prefix !== -1, 'Not a valid address prefix.');
p.writeU8(prefix);
if (this.version !== -1) {
@ -140,8 +150,10 @@ Address.prototype.toScript = function toScript() {
Address.prototype.toString = function toString(enc) {
if (enc === 'hex')
return this.getHash('hex');
if (enc === 'base58')
enc = null;
return this.toBase58(enc);
};
@ -152,7 +164,7 @@ Address.prototype.toString = function toString(enc) {
Address.prototype.inspect = function inspect() {
return '<Address:'
+ ' type=' + this.type
+ ' type=' + this.getType()
+ ' version=' + this.version
+ ' base58=' + this.toBase58()
+ '>';
@ -175,12 +187,12 @@ Address.prototype.fromRaw = function fromRaw(data) {
for (i = 0; i < networks.types.length; i++) {
network = networks[networks.types[i]];
type = network.address.prefixesByVal[prefix];
if (type != null)
type = Address.getType(prefix, network);
if (type !== -1)
break;
}
assert(type != null, 'Unknown address prefix.');
assert(i < networks.types.length, 'Unknown address prefix.');
if (data.length > 25) {
version = p.readU8();
@ -236,59 +248,52 @@ Address.fromBase58 = function fromBase58(address) {
*/
Address.prototype.fromScript = function fromScript(script) {
var program;
if (script.isProgram()) {
program = script.toProgram();
// TODO: MAST support
if (program.isUnknown())
return;
this.hash = program.data;
this.type = program.type;
this.version = program.version;
return this;
}
// Fast case
if (script.isPubkey(true)) {
this.hash = utils.hash160(script.raw.slice(1, script.raw[0] + 1));
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isPubkeyhash(true)) {
this.hash = script.raw.slice(3, 23);
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isScripthash()) {
this.hash = script.raw.slice(2, 22);
this.type = 'scripthash';
this.version = -1;
return this;
}
// Slow case (allow non-minimal data and parse script)
if (script.isPubkey()) {
this.hash = utils.hash160(script.code[0].data);
this.type = 'pubkeyhash';
this.hash = utils.hash160(script.get(0));
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isPubkeyhash()) {
this.hash = script.code[2].data;
this.type = 'pubkeyhash';
this.hash = script.get(2);
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isScripthash()) {
this.hash = script.get(1);
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
if (script.isWitnessPubkeyhash()) {
this.hash = script.get(1);
this.type = scriptTypes.WITNESSPUBKEYHASH;
this.version = 0;
return this;
}
if (script.isWitnessScripthash()) {
this.hash = script.get(1);
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 0;
return this;
}
if (script.isWitnessMasthash()) {
this.hash = script.get(1);
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 1;
return this;
}
// Put this last: it's the slowest to check.
if (script.isMultisig()) {
this.hash = utils.hash160(script.raw);
this.type = 'scripthash';
this.hash = utils.hash160(script.toRaw());
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
@ -301,16 +306,18 @@ Address.prototype.fromScript = function fromScript(script) {
*/
Address.prototype.fromWitness = function fromWitness(witness) {
// We're pretty much screwed here
// since we can't get the version.
if (witness.isPubkeyhashInput()) {
this.hash = utils.hash160(witness.items[1]);
this.type = 'witnesspubkeyhash';
this.hash = utils.hash160(witness.get(1));
this.type = scriptTypes.WITNESSPUBKEYHASH;
this.version = 0;
return this;
}
if (witness.isScripthashInput()) {
this.hash = utils.sha256(witness.items[witness.items.length - 1]);
this.type = 'witnessscripthash';
this.hash = utils.sha256(witness.get(witness.length - 1));
this.type = scriptTypes.WITNESSSCRIPTHASH;
this.version = 0;
return this;
}
@ -324,15 +331,15 @@ Address.prototype.fromWitness = function fromWitness(witness) {
Address.prototype.fromInputScript = function fromInputScript(script) {
if (script.isPubkeyhashInput()) {
this.hash = utils.hash160(script.code[1].data);
this.type = 'pubkeyhash';
this.hash = utils.hash160(script.get(1));
this.type = scriptTypes.PUBKEYHASH;
this.version = -1;
return this;
}
if (script.isScripthashInput()) {
this.hash = utils.hash160(script.code[script.code.length - 1].data);
this.type = 'scripthash';
this.hash = utils.hash160(script.get(script.length - 1));
this.type = scriptTypes.SCRIPTHASH;
this.version = -1;
return this;
}
@ -389,8 +396,11 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) {
if (typeof hash === 'string')
hash = new Buffer(hash, 'hex');
if (!type)
type = 'pubkeyhash';
if (typeof type === 'string')
type = scriptTypes[type.toUpperCase()];
if (type == null)
type = scriptTypes.PUBKEYHASH;
if (version == null)
version = -1;
@ -398,21 +408,22 @@ Address.prototype.fromHash = function fromHash(hash, type, version, network) {
network = bcoin.network.get(network);
assert(Buffer.isBuffer(hash));
assert(typeof type === 'string');
assert(utils.isNumber(type));
assert(utils.isNumber(version));
assert(network.address.prefixes[type] != null, 'Not a valid address prefix.');
assert(Address.getPrefix(type, network) !== -1, 'Not a valid address type.');
if (version === -1) {
assert(!network.address.witness[type], 'Wrong version (witness)');
assert(!Address.isWitness(type), 'Wrong version (witness)');
assert(hash.length === 20, 'Hash is the wrong size.');
} else {
assert(network.address.witness[type], 'Wrong version (non-witness).');
assert(Address.isWitness(type), 'Wrong version (non-witness).');
assert(version >= 0 && version <= 16, 'Bad program version.');
if (version === 0 && type === 'witnesspubkeyhash')
if (version === 0 && type === scriptTypes.WITNESSPUBKEYHASH)
assert(hash.length === 20, 'Hash is the wrong size.');
else if (version === 0 && type === 'witnessscripthash')
else if (version === 0 && type === scriptTypes.WITNESSSCRIPTHASH)
assert(hash.length === 32, 'Hash is the wrong size.');
else if (version === 1 && type === 'witnessscripthash')
else if (version === 1 && type === scriptTypes.WITNESSSCRIPTHASH)
assert(hash.length === 32, 'Hash is the wrong size.');
}
@ -439,30 +450,45 @@ Address.fromHash = function fromHash(hash, type, version, network) {
};
/**
* Inject properties from hash.
* @param {Hash|Buffer} hash
* @param {AddressType?} type
* @param {Number?} version - Witness program version.
* Inject properties from data.
* @private
* @param {Buffer|Buffer[]} data
* @param {AddressType} type
* @param {Number} [version=-1]
* @param {(Network|NetworkType)?} network
* @throws on bad hash size
*/
Address.prototype.fromData = function fromData(data, type, version, network) {
if (type === 'witnessscripthash') {
assert(version === 0);
data = utils.sha256(data);
} else if (type === 'witnesspubkeyhash') {
assert(version === 0);
if (typeof type === 'string')
type = scriptTypes[type.toUpperCase()];
if (type === scriptTypes.WITNESSSCRIPTHASH) {
if (version === 0) {
assert(Buffer.isBuffer(data));
data = utils.sha256(data);
} else if (version === 1) {
assert(Array.isArray(data));
data = utils.getMerkleRoot(data);
} else {
throw new Error('Cannot create from version=' + version);
}
} else if (type === scriptTypes.WITNESSPUBKEYHASH) {
if (version !== 0)
throw new Error('Cannot create from version=' + version);
assert(Buffer.isBuffer(data));
data = utils.hash160(data);
} else {
data = utils.hash160(data);
}
return this.fromHash(data, type, version, network);
};
/**
* Create an Address from data/type/version.
* @param {Buffer} data - Data to be hashed.
* @param {Buffer|Buffer[]} data - Data to be hashed.
* Normally a buffer, but can also be an array of
* buffers for MAST.
* @param {AddressType} type
* @param {Number} [version=-1]
* @param {(Network|NetworkType)?} network
@ -494,6 +520,9 @@ Address.validate = function validate(address, type) {
return false;
}
if (typeof type === 'string')
type = scriptTypes[type.toUpperCase()];
if (type && address.type !== type)
return false;
@ -530,6 +559,69 @@ Address.getHash = function getHash(data, enc) {
: hash;
};
/**
* Get a network address prefix for a specified address type.
* @param {AddressType} type
* @param {Network} network
* @returns {Number}
*/
Address.getPrefix = function getPrefix(type, network) {
var prefixes = network.addressPrefix;
switch (type) {
case scriptTypes.PUBKEYHASH:
return prefixes.pubkeyhash;
case scriptTypes.SCRIPTHASH:
return prefixes.scripthash;
case scriptTypes.WITNESSPUBKEYHASH:
return prefixes.witnesspubkeyhash;
case scriptTypes.WITNESSSCRIPTHASH:
return prefixes.witnessscripthash;
default:
return -1;
}
};
/**
* Get an address type for a specified network address prefix.
* @param {Number} prefix
* @param {Network} network
* @returns {AddressType}
*/
Address.getType = function getType(prefix, network) {
var prefixes = network.addressPrefix;
switch (prefix) {
case prefixes.pubkeyhash:
return scriptTypes.PUBKEYHASH;
case prefixes.scripthash:
return scriptTypes.SCRIPTHASH;
case prefixes.witnesspubkeyhash:
return scriptTypes.WITNESSPUBKEYHASH;
case prefixes.witnessscripthash:
return scriptTypes.WITNESSSCRIPTHASH;
default:
return -1;
}
};
/**
* Test whether an address type is a witness program.
* @param {AddressType} type
* @returns {Boolean}
*/
Address.isWitness = function isWitness(type) {
switch (type) {
case scriptTypes.WITNESSPUBKEYHASH:
return true;
case scriptTypes.WITNESSSCRIPTHASH:
return true;
default:
return false;
}
};
/*
* Expose
*/

View File

@ -1083,7 +1083,7 @@ HDPrivateKey.isExtended = function isExtended(data) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xprivkey58;
prefix = networks[type].keyPrefix.xprivkey58;
if (data.indexOf(prefix) === 0)
return true;
}
@ -1107,7 +1107,7 @@ HDPrivateKey.hasPrefix = function hasPrefix(data) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xprivkey;
prefix = networks[type].keyPrefix.xprivkey;
if (version === prefix)
return type;
}
@ -1326,7 +1326,7 @@ HDPrivateKey.prototype.fromRaw = function fromRaw(raw) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xprivkey;
prefix = networks[type].keyPrefix.xprivkey;
if (version === prefix)
break;
}
@ -1363,7 +1363,7 @@ HDPrivateKey.prototype.toRaw = function toRaw(network, writer) {
network = bcoin.network.get(network);
p.writeU32BE(network.prefixes.xprivkey);
p.writeU32BE(network.keyPrefix.xprivkey);
p.writeU8(this.depth);
p.writeBytes(this.parentFingerPrint);
p.writeU32BE(this.childIndex);
@ -1806,7 +1806,7 @@ HDPublicKey.isExtended = function isExtended(data) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xpubkey58;
prefix = networks[type].keyPrefix.xpubkey58;
if (data.indexOf(prefix) === 0)
return true;
}
@ -1830,7 +1830,7 @@ HDPublicKey.hasPrefix = function hasPrefix(data) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xpubkey;
prefix = networks[type].keyPrefix.xpubkey;
if (version === prefix)
return type;
}
@ -1870,7 +1870,7 @@ HDPublicKey.prototype.fromRaw = function fromRaw(raw) {
for (i = 0; i < networks.types.length; i++) {
type = networks.types[i];
prefix = networks[type].prefixes.xpubkey;
prefix = networks[type].keyPrefix.xpubkey;
if (version === prefix)
break;
}
@ -1906,7 +1906,7 @@ HDPublicKey.prototype.toRaw = function toRaw(network, writer) {
network = bcoin.network.get(network);
p.writeU32BE(network.prefixes.xpubkey);
p.writeU32BE(network.keyPrefix.xpubkey);
p.writeU8(this.depth);
p.writeBytes(this.parentFingerPrint);
p.writeU32BE(this.childIndex);

View File

@ -247,10 +247,10 @@ Input.fromOptions = function fromOptions(options) {
};
/**
* Get the previous output script type. Will "guess"
* based on the input script and/or witness if coin
* is not available.
* @returns {String} type
* Get the previous output script type as a string.
* Will "guess" based on the input script and/or
* witness if coin is not available.
* @returns {ScriptType} type
*/
Input.prototype.getType = function getType() {
@ -267,7 +267,7 @@ Input.prototype.getType = function getType() {
else
type = this.script.getInputType();
return type;
return constants.scriptTypesByVal[type].toLowerCase();
};
/**

View File

@ -161,7 +161,7 @@ KeyPair.prototype.toRaw = function toRaw(network, writer) {
network = bcoin.network.get(network);
p.writeU8(network.prefixes.privkey);
p.writeU8(network.keyPrefix.privkey);
p.writeBytes(this.getPrivateKey());
if (this.compressed)
@ -199,7 +199,7 @@ KeyPair.prototype.fromRaw = function fromRaw(data) {
for (i = 0; i < network.types.length; i++) {
type = network.types[i];
prefix = network[type].prefixes.privkey;
prefix = network[type].keyPrefix.privkey;
if (version === prefix)
break;
}

View File

@ -8,10 +8,12 @@
'use strict';
var bcoin = require('./env');
var constants = bcoin.protocol.constants;
var utils = bcoin.utils;
var assert = utils.assert;
var BufferReader = require('./reader');
var BufferWriter = require('./writer');
var scriptTypes = constants.scriptTypes;
/**
* Represents a key ring which amounts to an address. Used for {@link Wallet}.
@ -259,7 +261,7 @@ KeyRing.prototype.getProgramAddress = function getProgramAddress(enc) {
if (!this._programAddress) {
hash = this.getProgramHash();
address = this.compile(hash, 'scripthash');
address = this.compile(hash, scriptTypes.SCRIPTHASH);
this._programAddress = address;
}
@ -332,10 +334,10 @@ KeyRing.prototype.getScriptAddress = function getScriptAddress(enc) {
if (!this._scriptAddress) {
if (this.witness) {
hash = this.getScriptHash256();
address = this.compile(hash, 'witnessscripthash', 0);
address = this.compile(hash, scriptTypes.WITNESSSCRIPTHASH, 0);
} else {
hash = this.getScriptHash160();
address = this.compile(hash, 'scripthash');
address = this.compile(hash, scriptTypes.SCRIPTHASH);
}
this._scriptAddress = address;
}
@ -373,9 +375,9 @@ KeyRing.prototype.getKeyAddress = function getKeyAddress(enc) {
if (!this._keyAddress) {
hash = this.getKeyHash();
if (this.witness)
address = this.compile(hash, 'witnesspubkeyhash', 0);
address = this.compile(hash, scriptTypes.WITNESSPUBKEYHASH, 0);
else
address = this.compile(hash, 'pubkeyhash');
address = this.compile(hash, scriptTypes.PUBKEYHASH);
this._keyAddress = address;
}

View File

@ -46,8 +46,8 @@ function Network(options) {
this.activationThreshold = options.activationThreshold;
this.minerWindow = options.minerWindow;
this.deployments = options.deployments;
this.prefixes = options.prefixes;
this.address = options.address;
this.keyPrefix = options.keyPrefix;
this.addressPrefix = options.addressPrefix;
this.requireStandard = options.requireStandard;
this.rpcPort = options.rpcPort;
this.minRelay = options.minRelay;

View File

@ -68,12 +68,12 @@ Output.fromOptions = function fromOptions(options) {
};
/**
* Get the script type.
* @returns {String} type
* Get the script type as a string.
* @returns {ScriptType} type
*/
Output.prototype.getType = function getType() {
return this.script.getType();
return constants.scriptTypesByVal[this.script.getType()].toLowerCase();
};
/**

View File

@ -674,11 +674,19 @@ exports.scriptTypes = {
SCRIPTHASH: 3,
MULTISIG: 4,
NULLDATA: 5,
WITNESSSCRIPTHASH: 6,
WITNESSKEYHASH: 7,
WITNESSMAST: 8
WITNESSMALFORMED: 0x80 | 0,
WITNESSSCRIPTHASH: 0x80 | 1,
WITNESSPUBKEYHASH: 0x80 | 2,
WITNESSMASTHASH: 0x80 | 3
};
/**
* Output script types by value.
* @const {RevMap}
*/
exports.scriptTypesByVal = utils.revMap(exports.scriptTypes);
/**
* Script and locktime flags. See {@link VerifyFlags}.
* @enum {Number}

View File

@ -358,7 +358,7 @@ main.deployments = {
* @default
*/
main.prefixes = {
main.keyPrefix = {
privkey: 128,
xpubkey: 0x0488b21e,
xprivkey: 0x0488ade4,
@ -367,42 +367,17 @@ main.prefixes = {
};
/**
* {@link Base58Address} constants.
* {@link Address} prefixes.
* @enum {Object}
*/
main.address = {
/**
* {@link Base58Address} prefixes.
* @enum {Number}
*/
prefixes: {
pubkeyhash: 0,
scripthash: 5,
witnesspubkeyhash: 6,
witnessscripthash: 10
},
/**
* {@link Base58Address} witness types.
* @enum {Number}
*/
witness: {
witnesspubkeyhash: true,
witnessscripthash: true
}
main.addressPrefix = {
pubkeyhash: 0,
scripthash: 5,
witnesspubkeyhash: 6,
witnessscripthash: 10
};
/**
* {@link Base58Address} prefixes by value.
* @type {RevMap}
* @default
*/
main.address.prefixesByVal = utils.revMap(main.address.prefixes);
/**
* Default value for whether the mempool
* accepts non-standard transactions.
@ -585,7 +560,7 @@ testnet.deployments = {
}
};
testnet.prefixes = {
testnet.keyPrefix = {
privkey: 239,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
@ -593,21 +568,13 @@ testnet.prefixes = {
xpubkey58: 'tpub'
};
testnet.address = {
prefixes: {
pubkeyhash: 111,
scripthash: 196,
witnesspubkeyhash: 3,
witnessscripthash: 40
},
witness: {
witnesspubkeyhash: true,
witnessscripthash: true
}
testnet.addressPrefix = {
pubkeyhash: 111,
scripthash: 196,
witnesspubkeyhash: 3,
witnessscripthash: 40
};
testnet.address.prefixesByVal = utils.revMap(testnet.address.prefixes);
testnet.requireStandard = false;
testnet.rpcPort = 18332;
@ -736,7 +703,7 @@ regtest.deployments = {
}
};
regtest.prefixes = {
regtest.keyPrefix = {
privkey: 239,
xpubkey: 0x043587cf,
xprivkey: 0x04358394,
@ -744,21 +711,13 @@ regtest.prefixes = {
xpubkey58: 'tpub'
};
regtest.address = {
prefixes: {
pubkeyhash: 111,
scripthash: 196,
witnesspubkeyhash: 3,
witnessscripthash: 40
},
witness: {
witnesspubkeyhash: true,
witnessscripthash: true
}
regtest.addressPrefix = {
pubkeyhash: 111,
scripthash: 196,
witnesspubkeyhash: 3,
witnessscripthash: 40
};
regtest.address.prefixesByVal = utils.revMap(regtest.address.prefixes);
regtest.requireStandard = false;
regtest.rpcPort = 18332;
@ -866,7 +825,7 @@ segnet3.minerWindow = 144;
segnet3.deployments = {};
segnet3.prefixes = {
segnet3.keyPrefix = {
privkey: 158,
xpubkey: 0x053587cf,
xprivkey: 0x05358394,
@ -874,21 +833,13 @@ segnet3.prefixes = {
xpubkey58: '2793'
};
segnet3.address = {
prefixes: {
pubkeyhash: 30,
scripthash: 50,
witnesspubkeyhash: 3,
witnessscripthash: 40
},
witness: {
witnesspubkeyhash: true,
witnessscripthash: true
}
segnet3.addressPrefix = {
pubkeyhash: 30,
scripthash: 50,
witnesspubkeyhash: 3,
witnessscripthash: 40
};
segnet3.address.prefixesByVal = utils.revMap(segnet3.address.prefixes);
segnet3.requireStandard = false;
segnet3.rpcPort = 28332;
@ -1011,7 +962,7 @@ segnet4.deployments = {
}
};
segnet4.prefixes = {
segnet4.keyPrefix = {
privkey: 158,
xpubkey: 0x053587cf,
xprivkey: 0x05358394,
@ -1019,21 +970,13 @@ segnet4.prefixes = {
xpubkey58: '2793'
};
segnet4.address = {
prefixes: {
pubkeyhash: 30,
scripthash: 50,
witnesspubkeyhash: 3,
witnessscripthash: 40
},
witness: {
witnesspubkeyhash: true,
witnessscripthash: true
}
segnet4.addressPrefix = {
pubkeyhash: 30,
scripthash: 50,
witnesspubkeyhash: 3,
witnessscripthash: 40
};
segnet4.address.prefixesByVal = utils.revMap(segnet4.address.prefixes);
segnet4.requireStandard = false;
segnet4.rpcPort = 28902;

View File

@ -19,6 +19,7 @@ var STACK_TRUE = new Buffer([1]);
var STACK_FALSE = new Buffer([]);
var STACK_NEGATE = new Buffer([0x81]);
var ScriptError = bcoin.errors.ScriptError;
var scriptTypes = constants.scriptTypes;
/**
* Refers to the witness field of segregated witness transactions.
@ -166,13 +167,17 @@ Witness.prototype.toStack = function toStack() {
/**
* "Guess" the type of the witness.
* This method is not 100% reliable.
* @returns {String}
* @returns {ScriptType}
*/
Witness.prototype.getInputType = function getInputType() {
return (this.isPubkeyhashInput() && 'witnesspubkeyhash')
|| (this.isScripthashInput() && 'witnessscripthash')
|| 'unknown';
if (this.isPubkeyhashInput())
return scriptTypes.WITNESSPUBKEYHASH;
if (this.isScripthashInput())
return scriptTypes.WITNESSSCRIPTHASH;
return scriptTypes.NONSTANDARD;
};
/**
@ -234,7 +239,7 @@ Witness.prototype.isScripthashInput = function isScripthashInput() {
*/
Witness.prototype.isUnknownInput = function isUnknownInput() {
return this.getInputType() === 'unknown';
return this.getInputType() === scriptTypes.NONSTANDARD;
};
/**
@ -2663,10 +2668,10 @@ Script.prototype.fromAddress = function fromAddress(address) {
if (!address)
throw new Error('Unknown address type.');
if (address.type === 'pubkeyhash')
if (address.type === scriptTypes.PUBKEYHASH)
return this.fromPubkeyhash(address.hash);
if (address.type === 'scripthash')
if (address.type === scriptTypes.SCRIPTHASH)
return this.fromScripthash(address.hash);
if (address.version !== -1)
@ -2740,27 +2745,35 @@ Script.prototype.getRedeem = function getRedeem() {
/**
* Get the standard script type.
* @returns {String} Script script (can be
* any of 'witnesspubkeyhash', 'witnessscripthash',
* 'pubkey', 'multisig', 'scripthash', 'nulldata',
* or 'unknown').
* @returns {ScriptType}
*/
Script.prototype.getType = function getType() {
if (this.isProgram()) {
if (this.isWitnessPubkeyhash())
return 'witnesspubkeyhash';
if (this.isWitnessScripthash())
return 'witnessscripthash';
return 'unknown';
}
if (this.isPubkey())
return scriptTypes.PUBKEY;
return (this.isPubkey() && 'pubkey')
|| (this.isPubkeyhash() && 'pubkeyhash')
|| (this.isMultisig() && 'multisig')
|| (this.isScripthash() && 'scripthash')
|| (this.isNulldata() && 'nulldata')
|| 'unknown';
if (this.isPubkeyhash())
return scriptTypes.PUBKEYHASH;
if (this.isScripthash())
return scriptTypes.SCRIPTHASH;
if (this.isWitnessPubkeyhash())
return scriptTypes.WITNESSPUBKEYHASH;
if (this.isWitnessScripthash())
return scriptTypes.WITNESSSCRIPTHASH;
if (this.isWitnessMasthash())
return scriptTypes.WITNESSMASTHASH;
if (this.isMultisig())
return scriptTypes.MULTISIG;
if (this.isNulldata())
return scriptTypes.NULLDATA;
return scriptTypes.NONSTANDARD;
};
/**
@ -2769,7 +2782,7 @@ Script.prototype.getType = function getType() {
*/
Script.prototype.isUnknown = function isUnknown() {
return this.getType() === 'unknown';
return this.getType() === scriptTypes.NONSTANDARD;
};
/**
@ -2781,7 +2794,7 @@ Script.prototype.isStandard = function isStandard() {
var type = this.getType();
var m, n;
if (type === 'multisig') {
if (type === scriptTypes.MULTISIG) {
m = Script.getSmall(this.raw[0]);
n = Script.getSmall(this.raw[this.raw.length - 2]);
@ -2790,9 +2803,17 @@ Script.prototype.isStandard = function isStandard() {
if (m < 1 || m > n)
return false;
return true;
}
return type !== 'unknown';
if (type === scriptTypes.NULLDATA) {
if (this.raw.length > constants.script.MAX_OP_RETURN_BYTES)
return false;
return true;
}
return type !== scriptTypes.NONSTANDARD;
};
/**
@ -2935,9 +2956,6 @@ Script.prototype.isScripthash = function isScripthash() {
Script.prototype.isNulldata = function isNulldata(minimal) {
var i, op;
if (this.raw.length > constants.script.MAX_OP_RETURN_BYTES)
return false;
if (this.raw.length === 0)
return false;
@ -2948,6 +2966,9 @@ Script.prototype.isNulldata = function isNulldata(minimal) {
return true;
if (minimal) {
if (this.raw.length > constants.script.MAX_OP_RETURN_BYTES)
return false;
if (this.raw.length === 2)
return Script.getSmall(this.raw[1]) !== -1;
@ -2962,10 +2983,13 @@ Script.prototype.isNulldata = function isNulldata(minimal) {
for (i = 1; i < this.code.length; i++) {
op = this.code[i];
if (op.data)
continue;
if (op.value === -1)
return false;
if (op.value > opcodes.OP_16)
return false;
}
@ -3070,7 +3094,7 @@ Script.prototype.isWitnessScripthash = function isWitnessScripthash() {
* @returns {Boolean}
*/
Script.prototype.isMAST = function isMAST() {
Script.prototype.isWitnessMasthash = function isWitnessMasthash() {
return this.raw.length === 34
&& this.raw[0] === opcodes.OP_1
&& this.raw[1] === 0x20;
@ -3088,15 +3112,23 @@ Script.prototype.isUnspendable = function isUnspendable() {
/**
* "Guess" the type of the input script.
* This method is not 100% reliable.
* @returns {String}
* @returns {ScriptType}
*/
Script.prototype.getInputType = function getInputType() {
return (this.isPubkeyInput() && 'pubkey')
|| (this.isPubkeyhashInput() && 'pubkeyhash')
|| (this.isMultisigInput() && 'multisig')
|| (this.isScripthashInput() && 'scripthash')
|| 'unknown';
if (this.isPubkeyInput())
return scriptTypes.PUBKEY;
if (this.isPubkeyhashInput())
return scriptTypes.PUBKEYHASH;
if (this.isScripthashInput())
return scriptTypes.SCRIPTHASH;
if (this.isMultisigInput())
return scriptTypes.MULTISIG;
return scriptTypes.NONSTANDARD;
};
/**
@ -3106,7 +3138,7 @@ Script.prototype.getInputType = function getInputType() {
*/
Script.prototype.isUnknownInput = function isUnknownInput() {
return this.getInputType() === 'unknown';
return this.getInputType() === scriptTypes.NONSTANDARD;
};
/**
@ -4157,10 +4189,10 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
if (flags & constants.flags.VERIFY_P2SH)
copy = stack.clone();
// Execute the previous output script
// Execute the previous output script.
output.execute(stack, flags, tx, i, 0);
// Verify the script did not fail as well as the stack values
// Verify the stack values.
if (stack.length === 0 || !Script.bool(stack.pop()))
throw new ScriptError('EVAL_FALSE');
@ -4171,7 +4203,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
if (input.raw.length !== 0)
throw new ScriptError('WITNESS_MALLEATED');
// Verify the program in the output script
// Verify the program in the output script.
Script.verifyProgram(witness, output, flags, tx, i);
// Force a cleanstack
@ -4195,10 +4227,10 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
raw = stack.pop();
redeem = new Script(raw);
// Execute the redeem script
// Execute the redeem script.
redeem.execute(stack, flags, tx, i, 0);
// Verify the script did not fail as well as the stack values
// Verify the the stack values.
if (stack.length === 0 || !Script.bool(stack.pop()))
throw new ScriptError('EVAL_FALSE');
@ -4209,15 +4241,15 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
if (!utils.equal(input.raw, Opcode.fromPush(raw).toRaw()))
throw new ScriptError('WITNESS_MALLEATED_P2SH');
// Verify the program in the redeem script
// Verify the program in the redeem script.
Script.verifyProgram(witness, redeem, flags, tx, i);
// Force a cleanstack
// Force a cleanstack.
stack.length = 0;
}
}
// Ensure there is nothing left on the stack
// Ensure there is nothing left on the stack.
if (flags & constants.flags.VERIFY_CLEANSTACK) {
assert((flags & constants.flags.VERIFY_P2SH) !== 0);
// assert((flags & constants.flags.VERIFY_WITNESS) !== 0);
@ -4273,7 +4305,7 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
redeem = Script.fromPubkeyhash(program.data);
} else {
// Failure on version=0 (bad program data length)
// Failure on version=0 (bad program data length).
throw new ScriptError('WITNESS_PROGRAM_WRONG_LENGTH');
}
} else if ((flags & constants.flags.VERIFY_MAST) && program.version === 1) {
@ -4341,14 +4373,16 @@ Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
return true;
}
// Witnesses still have push limits.
for (j = 0; j < stack.length; j++) {
if (stack.get(j).length > constants.script.MAX_PUSH)
throw new ScriptError('PUSH_SIZE');
}
// Verify the redeem script.
redeem.execute(stack, flags, tx, i, 1);
// Verify the script did not fail as well as the stack values
// Verify the stack values.
if (stack.length !== 1 || !Script.bool(stack.pop()))
throw new ScriptError('EVAL_FALSE');
@ -4799,24 +4833,44 @@ function Program(version, data) {
if (!(this instanceof Program))
return new Program(version, data);
assert(utils.isNumber(version));
assert(Buffer.isBuffer(data));
assert(version >= 0 && version <= 16);
assert(data.length >= 2 && data.length <= 40);
this.version = version;
this.data = data;
this.type = null;
// TODO: MAST support
if (version > 0) {
// No interpretation of script (anyone can spend)
this.type = 'unknown';
} else if (version === 0 && data.length === 20) {
this.type = 'witnesspubkeyhash';
} else if (version === 0 && data.length === 32) {
this.type = 'witnessscripthash';
} else {
// Fail on bad version=0
this.type = null;
}
}
/**
* Get the witness program type.
* @returns {ScriptType}
*/
Program.prototype.getType = function getType() {
if (this.version === 0) {
if (this.data.length === 20)
return scriptTypes.WITNESSPUBKEYHASH;
if (this.data.length === 32)
return scriptTypes.WITNESSSCRIPTHASH;
// Fail on bad version=0
return scriptTypes.WITNESSMALFORMED;
}
if (this.version === 1) {
if (this.data.length === 32)
return scriptTypes.WITNESSMASTHASH;
// Fail on bad version=1
return scriptTypes.WITNESSMALFORMED;
}
// No interpretation of script (anyone can spend)
return scriptTypes.NONSTANDARD;
};
/**
* Test whether the program is either
* an unknown version or malformed.
@ -4824,7 +4878,18 @@ function Program(version, data) {
*/
Program.prototype.isUnknown = function isUnknown() {
return !this.type || this.type === 'unknown';
var type = this.getType();
return type === scriptTypes.WITNESSMALFORMED
|| type === scriptTypes.NONSTANDARD;
};
/**
* Test whether the program is malformed.
* @returns {Boolean}
*/
Program.prototype.isMalformed = function isMalformed() {
return this.getType() === scriptTypes.WITNESSMALFORMED;
};
/**
@ -4836,7 +4901,7 @@ Program.prototype.inspect = function inspect() {
return '<Program:'
+ ' version=' + this.version
+ ' data=' + this.data.toString('hex')
+ ' type=' + this.type
+ ' type=' + constants.scriptTypesByVal[this.getType()].toLowerCase()
+ '>';
};
@ -4848,7 +4913,8 @@ exports = Script;
exports.opcodes = constants.opcodes;
exports.opcodesByVal = constants.opcodesByVal;
exports.types = constants.scriptTypes;
exports.types = scriptTypes;
exports.typesByVal = constants.scriptTypesByVal;
exports.flags = constants.flags;
exports.Script = Script;

View File

@ -27,10 +27,22 @@
*/
/**
* Can be `pubkeyhash`, `scripthash`, `witnesspubkeyhash`,
* or `witnessscripthash`, or an address prefix
* (see {@link network.address}).
* @typedef {String|Number} AddressType
* An output script type.
* @see {module:constants.scriptTypes}
* May sometimes be a string if specified.
* @typedef {Number|String} ScriptType
* @global
*/
/**
* A subset of {@link ScriptType}, including
* pubkeyhash, scripthash, witnesspubkeyhash,
* and witnessscripthash. This value
* specifically refers to the address prefix.
* It is a network-agnostic way of representing
* prefixes. May sometimes be a string if
* specified.
* @typedef {Number|String} AddressType
* @global
*/

View File

@ -6,6 +6,7 @@ var constants = bcoin.protocol.constants;
var network = bcoin.protocol.network;
var utils = bcoin.utils;
var assert = require('assert');
var scriptTypes = constants.scriptTypes;
var FAKE_SIG = new Buffer([0,0,0,0,0,0,0,0,0]);
@ -104,10 +105,12 @@ describe('Wallet', function() {
walletdb.create({ witness: witness }, function(err, w) {
assert.ifError(err);
var ad = bcoin.address.fromBase58(w.getAddress('base58'));
if (witness)
assert(bcoin.address.fromBase58(w.getAddress('base58')).type === 'witnesspubkeyhash');
assert(ad.type === scriptTypes.WITNESSPUBKEYHASH);
else
assert(bcoin.address.fromBase58(w.getAddress('base58')).type === 'pubkeyhash');
assert(ad.type === scriptTypes.PUBKEYHASH);
var src = bcoin.mtx({
outputs: [{
@ -232,7 +235,7 @@ describe('Wallet', function() {
w.scriptInputs(fake, function(err) {
assert.ifError(err);
// Fake signature
fake.inputs[0].script.code[0] = bcoin.opcode.fromData(FAKE_SIG);
fake.inputs[0].script.set(0, FAKE_SIG);
fake.inputs[0].script.compile();
// balance: 11000
@ -609,10 +612,12 @@ describe('Wallet', function() {
// Our p2sh address
var addr = w1.getAddress('base58');
var ad = bcoin.address.fromBase58(addr);
if (witness)
assert(bcoin.address.fromBase58(addr).type === 'witnessscripthash');
assert(ad.type === scriptTypes.WITNESSSCRIPTHASH);
else
assert(bcoin.address.fromBase58(addr).type === 'scripthash');
assert(ad.type === scriptTypes.SCRIPTHASH);
assert.equal(w1.getAddress('base58'), addr);
assert.equal(w2.getAddress('base58'), addr);