Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
bitcoin.js
coverage
node_modules

_dev.*.js
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

hope you don't mind, but this makes it easy to keep debug files around

Copy link
Contributor

@dcousens dcousens Jul 14, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What debug files?

55 changes: 47 additions & 8 deletions src/address.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,30 +6,54 @@ var types = require('./types')

function fromBase58Check (address) {
var payload = bs58check.decode(address)
if (payload.length < 21) throw new TypeError(address + ' is too short')
if (payload.length > 21) throw new TypeError(address + ' is too long')

var version = payload[0]
var hash = payload.slice(1)
var segWitVersion
var segWitPadding

return { hash: hash, version: version }
if (hash.length === 22 || hash.length === 34) {
segWitVersion = hash.readUInt8(0)
segWitPadding = hash.readUInt8(1)
hash = hash.slice(2)

if (segWitVersion > 16 || segWitPadding !== 0) {
throw new Error(address + ' has the length of a segWit address, but is not a valid segWit address')
}
} else if (hash.length > 20) throw new Error(address + ' is too long')
else if (hash.length < 20) throw new Error(address + ' is too short')

return { hash: hash, version: version, segWitVersion: segWitVersion }
}

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

if (bscript.isPubKeyHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(3, 23), network.pubKeyHash)
if (bscript.isScriptHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(2, 22), network.scriptHash)
if (bscript.isSegWitPubKeyHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(2, 22), network.segWitPubKeyHash, 0)
if (bscript.isSegWitScriptHashOutput(scriptPubKey)) return toBase58Check(bscript.compile(scriptPubKey).slice(2, 34), network.segWitScriptHash, 0)

throw new Error(bscript.toASM(scriptPubKey) + ' has no matching Address')
}

function toBase58Check (hash, version) {
typeforce(types.tuple(types.Hash160bit, types.UInt8), arguments)
function toBase58Check (hash, version, segWitVersion) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be great if we can split out all the address based changes to another PR

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They shelved that proposal until later - pending feedback/proposals from wallet developers IIRC

var payload
var isSegWit = typeof segWitVersion !== 'undefined'

var payload = new Buffer(21)
payload.writeUInt8(version, 0)
hash.copy(payload, 1)
typeforce(types.tuple((isSegWit ? types.oneOf(types.Hash160bit, types.Hash256bit) : types.Hash160bit), types.UInt8, types.maybe(types.UInt8)), arguments)

if (isSegWit) {
payload = new Buffer(3 + hash.length) // dynamic size based on hash because of difference between P2WPKH and P2WSH
payload.writeUInt8(version, 0)
payload.writeUInt8(segWitVersion, 1)
payload.writeUInt8(0, 2) // padding byte to make pretty prefixes
hash.copy(payload, 3)
} else {
payload = new Buffer(21)
payload.writeUInt8(version, 0)
hash.copy(payload, 1)
}

return bs58check.encode(payload)
}
Expand All @@ -38,8 +62,23 @@ function toOutputScript (address, network) {
network = network || networks.bitcoin

var decode = fromBase58Check(address)

if (decode.version === network.pubKeyHash) return bscript.pubKeyHashOutput(decode.hash)
if (decode.version === network.scriptHash) return bscript.scriptHashOutput(decode.hash)
if (decode.version === network.segWitPubKeyHash) {
if (decode.segWitVersion === 0) {
if (decode.hash.length === 20) {
return bscript.segWitPubKeyHashOutput(decode.hash)
}
}
}
if (decode.version === network.segWitScriptHash) {
if (decode.segWitVersion === 0) {
if (decode.hash.length === 32) {
return bscript.segWitScriptHashOutput(decode.hash)
}
}
}

throw new Error(address + ' has no matching Script')
}
Expand Down
55 changes: 55 additions & 0 deletions src/bufferwriter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
var bufferutils = require('./bufferutils')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd rather opt for something like varstruct than go this route if possible

var typeforce = require('typeforce')
var types = require('./types')

var BufferWriter = function (length) {
typeforce(types.tuple(types.Number), arguments)
this.buffer = new Buffer(length)
this.offset = 0
}

BufferWriter.prototype.writeSlice = function (slice) {
slice.copy(this.buffer, this.offset)
this.offset += slice.length

return this
}

BufferWriter.prototype.writeSliceWithVarInt = function (script) {
this.writeVarInt(script.length)
this.writeSlice(script)

return this
}

BufferWriter.prototype.writeScript = BufferWriter.prototype.writeSliceWithVarInt

BufferWriter.prototype.writeInt = function (i) {
this.buffer.writeUInt8(i, this.offset)
this.offset += 1

return this
}

BufferWriter.prototype.writeUInt64 = function (i) {
bufferutils.writeUInt64LE(this.buffer, i, this.offset)
this.offset += 8

return this
}

BufferWriter.prototype.writeUInt32 = function (i) {
this.buffer.writeUInt32LE(i, this.offset)
this.offset += 4

return this
}

BufferWriter.prototype.writeVarInt = function (i) {
var n = bufferutils.writeVarInt(this.buffer, i, this.offset)
this.offset += n

return this
}

module.exports = BufferWriter
17 changes: 17 additions & 0 deletions src/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ module.exports = {
},
pubKeyHash: 0x00,
scriptHash: 0x05,
segWitPubKeyHash: 0x06,
segWitScriptHash: 0x0A,
wif: 0x80,
dustThreshold: 546 // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
},
Expand All @@ -21,9 +23,24 @@ module.exports = {
},
pubKeyHash: 0x6f,
scriptHash: 0xc4,
segWitPubKeyHash: 0x03,
segWitScriptHash: 0x28,
wif: 0xef,
dustThreshold: 546
},
segnet: {
messagePrefix: '\x18Bitcoin Signed Message:\n',
bip32: {
public: 0x053587CF,
private: 0x05358394
},
pubKeyHash: 0x1e,
scriptHash: 0x32,
segWitPubKeyHash: 0x03,
segWitScriptHash: 0x28,
wif: 0x9e,
dustThreshold: 546
},
litecoin: {
messagePrefix: '\x19Litecoin Signed Message:\n',
bip32: {
Expand Down
45 changes: 44 additions & 1 deletion src/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,13 @@ function isPubKeyHashOutput (script) {
buffer[24] === OPS.OP_CHECKSIG
}

function isSegWitPubKeyHashOutput (script) {
var buffer = compile(script)

return buffer.length === 22 &&
buffer[0] === OPS.OP_0
}

function isPubKeyInput (script) {
var chunks = decompile(script)

Expand Down Expand Up @@ -194,7 +201,14 @@ function isScriptHashInput (script, allowIncomplete) {
// is redeemScript a valid script?
if (redeemScriptChunks.length === 0) return false

return classifyInput(scriptSigChunks, allowIncomplete) === classifyOutput(redeemScriptChunks)
var inputType = classifyInput(scriptSigChunks, allowIncomplete)
var outputType = classifyOutput(redeemScriptChunks)

if (outputType === 'segwitpubkeyhash') {
return inputType === 'pubkeyhash'
}

return inputType === outputType
}

function isScriptHashOutput (script) {
Expand All @@ -206,6 +220,13 @@ function isScriptHashOutput (script) {
buffer[22] === OPS.OP_EQUAL
}

function isSegWitScriptHashOutput (script) {
var buffer = compile(script)

return buffer.length === 34 &&
buffer[0] === OPS.OP_0
}

// allowIncomplete is to account for combining signatures
// See https://github.com/bitcoin/bitcoin/blob/f425050546644a36b0b8e0eb2f6934a3e0f6f80f/src/script/sign.cpp#L195-L197
function isMultisigInput (script, allowIncomplete) {
Expand Down Expand Up @@ -257,6 +278,10 @@ function classifyOutput (script) {
return 'pubkeyhash'
} else if (isScriptHashOutput(chunks)) {
return 'scripthash'
} else if (isSegWitPubKeyHashOutput(chunks)) {
return 'segwitpubkeyhash'
} else if (isSegWitScriptHashOutput(chunks)) {
return 'segwitscripthash'
} else if (isMultisigOutput(chunks)) {
return 'multisig'
} else if (isPubKeyOutput(chunks)) {
Expand Down Expand Up @@ -297,6 +322,20 @@ function pubKeyHashOutput (pubKeyHash) {
return compile([OPS.OP_DUP, OPS.OP_HASH160, pubKeyHash, OPS.OP_EQUALVERIFY, OPS.OP_CHECKSIG])
}

// OP_0 PUSH[{20-byte pubKeyHash}]
function segWitPubKeyHashOutput (pubKeyHash) {
typeforce(types.Hash160bit, pubKeyHash)

return compile([OPS.OP_0, pubKeyHash])
}

// OP_0 PUSH[{32-byte scriptHash}}]
function segWitScriptHashOutput (scriptHash) {
typeforce(types.Hash256bit, scriptHash)

return compile([OPS.OP_0, scriptHash])
}

// OP_HASH160 {scriptHash} OP_EQUAL
function scriptHashOutput (scriptHash) {
typeforce(types.Hash160bit, scriptHash)
Expand Down Expand Up @@ -379,18 +418,22 @@ module.exports = {
isDefinedHashType: isDefinedHashType,
isPubKeyHashInput: isPubKeyHashInput,
isPubKeyHashOutput: isPubKeyHashOutput,
isSegWitPubKeyHashOutput: isSegWitPubKeyHashOutput,
isPubKeyInput: isPubKeyInput,
isPubKeyOutput: isPubKeyOutput,
isScriptHashInput: isScriptHashInput,
isScriptHashOutput: isScriptHashOutput,
isSegWitScriptHashOutput: isSegWitScriptHashOutput,
isMultisigInput: isMultisigInput,
isMultisigOutput: isMultisigOutput,
isNullDataOutput: isNullDataOutput,
classifyOutput: classifyOutput,
classifyInput: classifyInput,
pubKeyOutput: pubKeyOutput,
pubKeyHashOutput: pubKeyHashOutput,
segWitPubKeyHashOutput: segWitPubKeyHashOutput,
scriptHashOutput: scriptHashOutput,
segWitScriptHashOutput: segWitScriptHashOutput,
multisigOutput: multisigOutput,
pubKeyInput: pubKeyInput,
pubKeyHashInput: pubKeyHashInput,
Expand Down
Loading