Skip to content

Commit

Permalink
Address: consistent namespacing and decode/encode functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
dcousens committed Nov 25, 2014
1 parent 49a8296 commit 39f67b2
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 49 deletions.
71 changes: 44 additions & 27 deletions src/address.js
Expand Up @@ -3,52 +3,69 @@ var base58check = require('bs58check')
var networks = require('./networks')
var scripts = require('./scripts')

function fromOutputScript(script, network) {
network = network || networks.bitcoin

var type = scripts.classifyOutput(script)
var version
var hash

if (type === 'pubkeyhash') {
version = network.pubKeyHash
hash = script.chunks[2]

} else if (type === 'scripthash') {
version = network.scriptHash
hash = script.chunks[1]

} else {
assert(false, type + ' has no matching Address')
}

function encode(version, hash) {
var payload = new Buffer(21)
payload.writeUInt8(version, 0)
hash.copy(payload, 1)

return base58check.encode(payload)
}

function toOutputScript(address) {
function decode(address) {
var payload = base58check.decode(address)
var version = payload.readUInt8(0)
var hash = payload.slice(1)
var network

for (var networkName in networks) {
var network = networks[networkName]
var network2 = networks[networkName]

if (version === network.pubKeyHash) {
return scripts.pubKeyHashOutput(hash)

} else if (version === network.scriptHash) {
return scripts.scriptHashOutput(hash)
if (version === network2.pubKeyHash || version === network2.scriptHash) {
network = network2
break
}
}

return {
hash: payload.slice(1),
network: network,
version: version
}
}

function fromOutputScript(script, network) {
network = network || networks.bitcoin

var type = scripts.classifyOutput(script)

switch (type) {
case 'pubkeyhash':
return encode(network.pubKeyHash, script.chunks[2])

case 'scripthash':
return encode(network.scriptHash, script.chunks[1])
}

assert(false, type + ' has no matching Address')
}

function toOutputScript(address) {
var decoded = decode(address)
var network = decoded.network || {}

switch (decoded.version) {
case network.pubKeyHash:
return scripts.pubKeyHashOutput(decoded.hash)

case network.scriptHash:
return scripts.scriptHashOutput(decoded.hash)
}

assert(false, address + ' has no matching Script')
}

module.exports = {
decode: decode,
encode: encode,
fromOutputScript: fromOutputScript,
toOutputScript: toOutputScript
}
7 changes: 2 additions & 5 deletions src/ecpair.js
@@ -1,3 +1,4 @@
var address = require('./address')
var assert = require('assert')
var base58check = require('bs58check')
var bcrypto = require('./crypto')
Expand Down Expand Up @@ -122,11 +123,7 @@ ECPair.prototype.getAddress = function() {
var hash = bcrypto.hash160(pubKey)
var version = this.network.pubKeyHash

var payload = new Buffer(21)
payload.writeUInt8(version, 0)
hash.copy(payload, 1)

return base58check.encode(payload)
return address.encode(version, hash)
}

ECPair.prototype.getPublicKeyBuffer = function() {
Expand Down
6 changes: 3 additions & 3 deletions src/transaction.js
@@ -1,7 +1,7 @@
var address = require('./address')
var assert = require('assert')
var scripts = require('./scripts')

var Address = require('./address')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var RawTransaction = require('./raw_transaction')
Expand Down Expand Up @@ -152,7 +152,7 @@ Transaction.prototype.addOutput = function(scriptPubKey, value) {

// Attempt to get a valid script if it's a base58 address
if (typeof scriptPubKey === 'string') {
scriptPubKey = Address.toOutputScript(scriptPubKey)
scriptPubKey = address.toOutputScript(scriptPubKey)
}

return this.tx.addOutput(scriptPubKey, value)
Expand Down Expand Up @@ -243,7 +243,7 @@ Transaction.prototype.sign = function(index, keyPair, redeemScript, hashType) {
hash = this.tx.hashForSignature(index, redeemScript, hashType)

} else {
prevOutScript = prevOutScript || Address.toOutputScript(keyPair.getAddress())
prevOutScript = prevOutScript || address.toOutputScript(keyPair.getAddress())
prevOutType = prevOutType || 'pubkeyhash'

assert.notEqual(prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')
Expand Down
38 changes: 30 additions & 8 deletions test/address.js
@@ -1,17 +1,39 @@
var assert = require('assert')
var networks = require('../src/networks')

var Address = require('../src/address')
var address = require('../src/address')
var Script = require('../src/script')

var fixtures = require('./fixtures/address.json')

describe('Address', function() {
describe('address', function() {
describe('decode', function() {
fixtures.valid.forEach(function(f) {
it('decodes ' + f.address + ' correctly', function() {
var decoded = address.decode(f.address)

assert.equal(decoded.version, f.version)
assert.equal(decoded.hash.toString('hex'), f.hash)
assert.equal(decoded.network, networks[f.network])
})
})
})

describe('encode', function() {
fixtures.valid.forEach(function(f) {
it('encoded ' + f.address + ' correctly', function() {
var result = address.encode(f.version, new Buffer(f.hash, 'hex'))

assert.equal(result, f.address)
})
})
})

describe('fromOutputScript', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
it('transforms ' + f.script + ' (' + f.network + ') to ' + f.address, function() {
var script = Script.fromHex(f.script)
var addr = Address.fromOutputScript(script, networks[f.network])
var addr = address.fromOutputScript(script, networks[f.network])

assert.equal(addr, f.address)
})
Expand All @@ -22,16 +44,16 @@ describe('Address', function() {
var script = Script.fromHex(f.hex)

assert.throws(function() {
Address.fromOutputScript(script)
address.fromOutputScript(script)
}, new RegExp(f.description))
})
})
})

describe('toOutputScript', function() {
fixtures.valid.forEach(function(f) {
it('imports ' + f.description + '(' + f.network + ') correctly', function() {
var script = Address.toOutputScript(f.address)
it('transforms ' + f.address + ' to ' + f.script, function() {
var script = address.toOutputScript(f.address)

assert.equal(script.toHex(), f.script)
})
Expand All @@ -40,7 +62,7 @@ describe('Address', function() {
fixtures.invalid.toOutputScript.forEach(function(f) {
it('throws when ' + f.description, function() {
assert.throws(function() {
Address.toOutputScript(f.address)
address.toOutputScript(f.address)
}, new RegExp(f.description))
})
})
Expand Down
18 changes: 13 additions & 5 deletions test/fixtures/address.json
Expand Up @@ -4,25 +4,33 @@
"description": "pubKeyHash",
"network": "bitcoin",
"address": "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH",
"script": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac"
"script": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac",
"version": 0,
"hash": "751e76e8199196d454941c45d1b3a323f1433bd6"
},
{
"description": "scriptHash",
"network": "bitcoin",
"address": "3LRW7jeCvQCRdPF8S3yUCfRAx4eqXFmdcr",
"script": "a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687"
"script": "a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687",
"version": 5,
"hash": "cd7b44d0b03f2d026d1e586d7ae18903b0d385f6"
},
{
"description": "pubKeyHash",
"network": "testnet",
"address": "mrCDrCybB6J1vRfbwM5hemdJz73FwDBC8r",
"script": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac"
"script": "76a914751e76e8199196d454941c45d1b3a323f1433bd688ac",
"version": 111,
"hash": "751e76e8199196d454941c45d1b3a323f1433bd6"
},
{
"description": "scriptHash",
"network": "testnet",
"address": "2NByiBUaEXrhmqAsg7BbLpcQSAQs1EDwt5w",
"script": "a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687"
"script": "a914cd7b44d0b03f2d026d1e586d7ae18903b0d385f687",
"version": 196,
"hash": "cd7b44d0b03f2d026d1e586d7ae18903b0d385f6"
}
],
"invalid": {
Expand All @@ -47,4 +55,4 @@
}
]
}
}
}
2 changes: 1 addition & 1 deletion test/fixtures/crypto.json
Expand Up @@ -33,4 +33,4 @@
"sha256": "a7fb8276035057ed6479c5f2305a96da100ac43f0ac10f277e5ab8c5457429da"
}
]
}
}

0 comments on commit 39f67b2

Please sign in to comment.