diff --git a/lib/bcoin/address.js b/lib/bcoin/address.js index 529bbab8..0502f627 100644 --- a/lib/bcoin/address.js +++ b/lib/bcoin/address.js @@ -614,41 +614,7 @@ Address.sha256 = function sha256(key) { */ Address.compileHash = function compileHash(hash, type, version, network) { - var p, prefix; - - if (!Buffer.isBuffer(hash)) - hash = new Buffer(hash, 'hex'); - - if (!type) - type = 'pubkeyhash'; - - network = bcoin.network.get(network); - - prefix = network.address.prefixes[type]; - - if (version == null) - version = network.address.versions[type]; - - assert(prefix != null, 'Not a valid address prefix.'); - - if (version == null) - assert(hash.length === 20, 'Hash is the wrong size.'); - else if (version === 0 && type === 'witnesspubkeyhash') - assert(hash.length === 20, 'Hash is the wrong size.'); - else if (version === 0 && type === 'witnessscripthash') - assert(hash.length === 32, 'Hash is the wrong size.'); - - p = new BufferWriter(); - - p.writeU8(prefix); - if (version != null) { - p.writeU8(version); - p.writeU8(0) - } - p.writeBytes(hash); - p.writeChecksum(); - - return utils.toBase58(p.render()); + return bcoin.script.Address.toBase58(hash, type, version, network); }; /** @@ -676,44 +642,7 @@ Address.compileData = function compileData(data, type, version, network) { */ Address.parse = function parse(address) { - var i, prefix, type, version, hash, network; - - if (!Buffer.isBuffer(address)) - address = utils.fromBase58(address); - - p = new BufferReader(address, true); - prefix = p.readU8(); - - for (i = 0; i < networks.types.length; i++) { - network = networks[networks.types[i]]; - type = network.address.prefixesByVal[prefix]; - if (type != null) - break; - } - - assert(type != null, 'Unknown address prefix.'); - - version = network.address.versions[type]; - - if (version != null) { - version = p.readU8(); - assert(version >= 0 && version <= 16, 'Bad program version.'); - assert(p.readU8() === 0, 'Address version padding is non-zero.'); - } - - if (type === 'witnessscripthash') - hash = p.readBytes(32); - else - hash = p.readBytes(20); - - p.verifyChecksum(); - - return { - network: network.type, - type: type, - hash: hash, - version: version == null ? -1 : version - }; + return bcoin.script.Address.parseBase58(address); }; /** diff --git a/lib/bcoin/script.js b/lib/bcoin/script.js index 6f0cd2e4..f558c1e9 100644 --- a/lib/bcoin/script.js +++ b/lib/bcoin/script.js @@ -8,6 +8,7 @@ var bcoin = require('./env'); var bn = require('bn.js'); var constants = bcoin.protocol.constants; +var networks = bcoin.protocol.network; var utils = require('./utils'); var assert = utils.assert; var BufferWriter = require('./writer'); @@ -117,11 +118,11 @@ Witness.prototype.getInputType = function getInputType() { */ Witness.prototype.getInputAddress = function getInputAddress(network) { - return Script.getInputAddress(this.items, network, true); -}; + var addr = Address.fromWitness(this); + if (!addr) + return; -Witness.prototype.getInputHash = function getInputHash() { - return Script.getInputHash(this.items, true); + return addr.toBase58(network); }; /** @@ -2359,37 +2360,11 @@ Script.prototype.getSize = function getSize() { */ Script.prototype.getInputAddress = function getInputAddress(network) { - return Script.getInputAddress(this.code, network, false); -}; - -Script.getInputAddress = function getInputAddress(code, network, isWitness) { - if (Script.isPubkeyInput(code)) + var addr = Address.fromInputScript(this); + if (!addr) return; - if (Script.isPubkeyhashInput(code)) { - if (isWitness) { - return bcoin.address.compileData( - code[1], - 'witnesspubkeyhash', - 0, - network); - } - return bcoin.address.compileData(code[1], 'pubkeyhash', null, network); - } - - if (Script.isMultisigInput(code, isWitness)) - return; - - if (Script.isScripthashInput(code)) { - if (isWitness) { - return bcoin.address.compileData( - code[code.length - 1], - 'witnessscripthash', - 0, - network); - } - return bcoin.address.compileData(code[code.length - 1], 'scripthash', null, network); - } + return addr.toBase58(network); }; /** @@ -2400,89 +2375,11 @@ Script.getInputAddress = function getInputAddress(code, network, isWitness) { */ Script.prototype.getAddress = function getAddress(network) { - var program; - - if (this.isWitnessProgram()) { - program = this.getWitnessProgram(); - if (!program.type || program.type === 'unknown') - return; - return bcoin.address.compileHash( - program.data, - program.type, - program.version, - network); - } - - // Convert p2pk to p2pkh addresses - if (this.isPubkey()) - return bcoin.address.compileData(this.code[0], 'pubkeyhash', null, network); - - if (this.isPubkeyhash()) - return bcoin.address.compileHash(this.code[2], 'pubkeyhash', null, network); - - // Convert bare multisig to scripthash address - if (this.isMultisig()) - return bcoin.address.compileData(this.encode(), 'scripthash', null, network); - - if (this.isScripthash()) - return bcoin.address.compileHash(this.code[1], 'scripthash', null, network); -}; - -/** - * "Guess" the address hash of the input script. - * This method is not 100% reliable. - * @returns {Hash|null} - */ - -Script.prototype.getInputHash = function getInputHash() { - return Script.getInputHash(this.code, false); -}; - -Script.getInputHash = function getInputHash(isWitness) { - if (Script.isPubkeyInput(code)) + var addr = Address.fromScript(this); + if (!addr) return; - if (Script.isPubkeyhashInput(code)) - return utils.ripesha(code[1]).toString('hex'); - - if (Script.isMultisigInput(code, isWitness)) - return; - - if (Script.isScripthashInput(code)) { - return isWitness - ? utils.sha256(code[code.length - 1]).toString('hex') - : utils.ripesha(code[code.length - 1]).toString('hex') - } -}; - -/** - * Get the address hash of the script if present. Note that - * pubkey and multisig scripts will be treated as though - * they are pubkeyhash and scripthashes respectively. - * @returns {Hash|null} - */ - -Script.prototype.getHash = function getHash() { - var program; - - if (this.isWitnessProgram()) { - program = this.getWitnessProgram(); - if (!program.type || program.type === 'unknown') - return; - return program.data.toString('hex'); - } - - if (this.isPubkey()) - return utils.ripesha(this.code[0]).toString('hex'); - - if (this.isPubkeyhash()) - return this.code[2].toString('hex'); - - if (this.isMultisig()) - return utils.ripesha(this.encode()).toString('hex'); - - if (this.isScripthash()) - return this.code[1].toString('hex'); + return addr.toBase58(network); }; /** @@ -4329,3 +4226,242 @@ Script.Witness = Witness; Script.Stack = Stack; module.exports = Script; + +function Address(options) { + if (!(this instanceof Address)) + return new Address(options); + + this.hash = options.hash; + this.type = options.type || 'pubkeyhash'; + this.version = options.version == null ? -1 : options.version; + this.network = options.network; + + if (!Buffer.isBuffer(this.hash)) + this.hash = new Buffer(this.hash, 'hex'); +} + +Address.prototype.getHash = function getHash(enc) { + if (enc === 'hex') + return this.hash.toString(enc); + return this.hash; +}; + +Address.prototype.toBase58 = function toBase58(network) { + if (!network) + network = this.network; + + return Address.toBase58(this.hash, this.type, this.version, network); +}; + +Address.toBase58 = function toBase58(hash, type, version, network) { + var p, prefix; + + if (!Buffer.isBuffer(hash)) + hash = new Buffer(hash, 'hex'); + + if (!type) + type = 'pubkeyhash'; + + network = bcoin.network.get(network); + + prefix = network.address.prefixes[type]; + + if (!(version >= 0)) + version = network.address.versions[type]; + + assert(prefix != null, 'Not a valid address prefix.'); + + if (!(version >= 0)) + assert(hash.length === 20, 'Hash is the wrong size.'); + else if (version === 0 && type === 'witnesspubkeyhash') + assert(hash.length === 20, 'Hash is the wrong size.'); + else if (version === 0 && type === 'witnessscripthash') + assert(hash.length === 32, 'Hash is the wrong size.'); + + p = new BufferWriter(); + + p.writeU8(prefix); + if (version != null) { + p.writeU8(version); + p.writeU8(0) + } + p.writeBytes(hash); + p.writeChecksum(); + + return utils.toBase58(p.render()); +}; + +Address.parseBase58 = function parseBase58(address) { + var i, prefix, type, version, hash, network; + + if (!Buffer.isBuffer(address)) + address = utils.fromBase58(address); + + p = new BufferReader(address, true); + prefix = p.readU8(); + + for (i = 0; i < networks.types.length; i++) { + network = networks[networks.types[i]]; + type = network.address.prefixesByVal[prefix]; + if (type != null) + break; + } + + assert(type != null, 'Unknown address prefix.'); + + version = network.address.versions[type]; + + if (version != null) { + version = p.readU8(); + assert(version >= 0 && version <= 16, 'Bad program version.'); + assert(p.readU8() === 0, 'Address version padding is non-zero.'); + } + + if (type === 'witnessscripthash') + hash = p.readBytes(32); + else + hash = p.readBytes(20); + + p.verifyChecksum(); + + return { + network: network.type, + type: type, + hash: hash, + version: version == null ? -1 : version + }; +}; + +Address.fromBase58 = function fromBase58(addr) { + return new Address(Address.parseBase58(addr)); +}; + +Address.parseScript = function parseScript(script) { + var program; + + if (script.isWitnessProgram()) { + program = script.getWitnessProgram(); + if (!program.type || program.type === 'unknown') + return; + return { + hash: program.data, + type: program.type, + version: program.version + }; + } + + if (script.isPubkey()) { + hash = utils.ripesha(script.code[0]); + return { hash: hash, type: 'pubkeyhash', version: -1 }; + } + + if (script.isPubkeyhash()) { + hash = script.code[2]; + return { hash: hash, type: 'pubkeyhash', version: -1 }; + } + + if (script.isMultisig()) { + hash = utils.ripesha(script.encode()); + return { hash: hash, type: 'scripthash', version: -1 }; + } + + if (script.isScripthash()) { + hash = script.code[1]; + return { hash: hash, type: 'scripthash', version: -1 }; + } +}; + +Address.parseInput = function parseInput(code, witness) { + var hash; + + if (Script.isPubkeyInput(code)) + return; + + if (Script.isPubkeyhashInput(code)) { + hash = utils.ripesha(code[1]); + if (witness) + return { hash: hash, type: 'witnesspubkeyhash', version: 0 }; + return { hash: hash, type: 'pubkeyhash', version: -1 }; + } + + if (Script.isMultisigInput(code, witness)) + return; + + if (Script.isScripthashInput(code)) { + if (witness) { + hash = utils.sha256(code[code.length - 1]); + return { hash: hash, type: 'witnessscripthash', version: 0 }; + } + hash = utils.ripesha(code[code.length - 1]); + return { hash: hash, type: 'scripthash', version: -1 }; + } +}; + +Address.parseWitness = function parseWitness(witness) { + return Address.parseInput(witness.items, true); +}; + +Address.parseInputScript = function parseInputScript(script) { + return Address.parseInput(script.code, false); +}; + +Address.fromWitness = function fromWitness(witness) { + var data = Address.parseWitness(witness); + + if (!data) + return; + + return new Address(data); +}; + +Address.fromInputScript = function fromInputScript(script) { + var data = Address.parseInputScript(script); + + if (!data) + return; + + return new Address(data); +}; + +Address.fromScript = function fromScript(script) { + var data = Address.parseScript(script); + + if (!data) + return; + + return new Address(data); +}; + +Address.parseHash = function parseHash(hash, type, version) { + return { + hash: hash, + type: type || 'pubkeyhash', + version: version == null ? -1 : version + }; +}; + +Address.fromHash = function fromHash(hash, type, version) { + return new Address(Address.parseHash(hash, type, version)); +}; + +Address.parseData = function parseData(data, type, version) { + if (type === 'witnessscripthash') + data = utils.sha256(data); + else + data = utils.ripesha(data); + return Address.parseHash(data, type, version); +}; + +Address.fromData = function fromData(data, type, version) { + return new Address(Address.parseData(data, type, version)); +}; + +Address.toScript = function toScript() { + if (this.type === 'pubkeyhash') + return Script.createPubkeyhash(this.hash); + if (this.type === 'scripthash') + return Script.createScripthash(this.hash); + assert(false, 'Bad type.'); +}; + +Script.Address = Address;