From 9a72c7437b16731260ae539eb35ad487ea0550e6 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 12:48:04 +1000 Subject: [PATCH 01/18] Script: adds Script.EMPTY constant --- src/script.js | 3 +++ src/transaction.js | 4 ++-- test/transaction.js | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/script.js b/src/script.js index cce5189..4b086b9 100644 --- a/src/script.js +++ b/src/script.js @@ -414,4 +414,7 @@ Script.prototype.without = function(needle) { })) } +// Constants +Script.EMPTY = new Script() + module.exports = Script diff --git a/src/transaction.js b/src/transaction.js index be0d0bb..e9590d8 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -76,7 +76,7 @@ Transaction.prototype.addInput = function (tx, outIndex) { hash: hash, index: outIndex }, - script: new Script() + script: Script.EMPTY })) } @@ -206,7 +206,7 @@ Transaction.prototype.hashForSignature = function(scriptPubKey, inIndex, hashTyp // Blank out other inputs' signatures txTmp.ins.forEach(function(txin) { - txin.script = new Script() + txin.script = Script.EMPTY }) txTmp.ins[inIndex].script = hashScript diff --git a/test/transaction.js b/test/transaction.js index e22f7f8..69f6b16 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -131,7 +131,7 @@ describe('Transaction', function() { verifyTransactionIn() }) - function verifyTransactionIn(){ + function verifyTransactionIn() { assert.equal(tx.ins.length, 1) var input = tx.ins[0] @@ -140,7 +140,7 @@ describe('Transaction', function() { assert.equal(input.outpoint.index, 0) assert.equal(input.outpoint.hash, "0cb859105100ebc3344f749c835c7af7d7103ec0d8cbc3d8ccbd5d28c3c36b57") - assert.deepEqual(input.script.buffer, []) + assert.equal(input.script, Script.EMPTY) } }) From 4e04eea436aab72e223a6a893fd11ece7c42da32 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 12:54:13 +1000 Subject: [PATCH 02/18] Script: use fromChunks internally for all Script templates --- src/script.js | 227 +++++++++++++++++++------------------------------ test/script.js | 13 +-- 2 files changed, 95 insertions(+), 145 deletions(-) diff --git a/src/script.js b/src/script.js index 4b086b9..7f581c5 100644 --- a/src/script.js +++ b/src/script.js @@ -3,19 +3,20 @@ var bufferutils = require('./bufferutils') var crypto = require('./crypto') var opcodes = require('./opcodes') -function Script(data) { - data = data || [] - assert(Array.isArray(data), 'Expected Array, got ' + data) +function Script(buffer, chunks) { + assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer) + assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) - this.buffer = data - this.parse() + this.buffer = buffer + this.chunks = chunks } // Import operations Script.fromBuffer = function(buffer) { - assert(Buffer.isBuffer(buffer)) // FIXME: transitionary + assert(Buffer.isBuffer(buffer)) - return new Script(Array.prototype.slice.call(buffer)) + var chunks = Script.parseChunks(buffer) + return new Script(buffer, chunks) } Script.fromHex = function(hex) { @@ -24,7 +25,7 @@ Script.fromHex = function(hex) { // Export operations Script.prototype.toBuffer = function() { - return new Buffer(this.buffer) + return this.buffer } Script.prototype.toHex = function() { @@ -32,35 +33,27 @@ Script.prototype.toHex = function() { } /** - * Update the parsed script representation. - * - * Each Script object stores the script in two formats. First as a raw byte - * array and second as an array of 'chunks', such as opcodes and pieces of - * data. - * - * This method updates the chunks cache. Normally this is called by the - * constructor and you don't need to worry about it. However, if you change - * the script buffer manually, you should update the chunks using this method. + * Parse a Script from a Buffer to its chunked representation. */ -Script.prototype.parse = function() { - var self = this +Script.parseChunks = function(buffer) { + assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer) - this.chunks = [] + var chunks = [] // Cursor var i = 0 // Read n bytes and store result as a chunk function readChunk(n) { - self.chunks.push(self.buffer.slice(i, i + n)) + chunks.push(buffer.slice(i, i + n)) i += n } - while (i < this.buffer.length) { - var opcode = this.buffer[i++] + while (i < buffer.length) { + var opcode = buffer[i++] if (opcode >= 0xF0) { // Two byte opcode - opcode = (opcode << 8) | this.buffer[i++] + opcode = (opcode << 8) | buffer[i++] } var len @@ -68,21 +61,23 @@ Script.prototype.parse = function() { // Read some bytes of data, opcode value is the length of data readChunk(opcode) } else if (opcode == opcodes.OP_PUSHDATA1) { - len = this.buffer[i++] + len = buffer[i++] readChunk(len) } else if (opcode == opcodes.OP_PUSHDATA2) { - len = (this.buffer[i++] << 8) | this.buffer[i++] + len = (buffer[i++] << 8) | buffer[i++] readChunk(len) } else if (opcode == opcodes.OP_PUSHDATA4) { - len = (this.buffer[i++] << 24) | - (this.buffer[i++] << 16) | - (this.buffer[i++] << 8) | - this.buffer[i++] + len = (buffer[i++] << 24) | + (buffer[i++] << 16) | + (buffer[i++] << 8) | + buffer[i++] readChunk(len) } else { - this.chunks.push(opcode) + chunks.push(opcode) } } + + return chunks } @@ -139,7 +134,7 @@ function isPubkeyhash() { return this.chunks.length == 5 && this.chunks[0] == opcodes.OP_DUP && this.chunks[1] == opcodes.OP_HASH160 && - Array.isArray(this.chunks[2]) && + Buffer.isBuffer(this.chunks[2]) && this.chunks[2].length === 20 && this.chunks[3] == opcodes.OP_EQUALVERIFY && this.chunks[4] == opcodes.OP_CHECKSIG @@ -147,14 +142,14 @@ function isPubkeyhash() { function isPubkey() { return this.chunks.length === 2 && - Array.isArray(this.chunks[0]) && + Buffer.isBuffer(this.chunks[0]) && this.chunks[1] === opcodes.OP_CHECKSIG } function isScripthash() { return this.chunks[this.chunks.length - 1] == opcodes.OP_EQUAL && this.chunks[0] == opcodes.OP_HASH160 && - Array.isArray(this.chunks[1]) && + Buffer.isBuffer(this.chunks[1]) && this.chunks[1].length === 20 && this.chunks.length == 3 } @@ -185,7 +180,7 @@ function isSmallIntOp(opcode) { } Script.prototype.getHash = function() { - return crypto.hash160(new Buffer(this.buffer)) + return crypto.hash160(this.buffer) } /** @@ -215,17 +210,17 @@ Script.prototype.getHash = function() { */ Script.prototype.getInType = function() { if (this.chunks.length == 1 && - Array.isArray(this.chunks[0])) { + Buffer.isBuffer(this.chunks[0])) { // Direct IP to IP transactions only have the signature in their scriptSig. // TODO: We could also check that the length of the data is correct. return 'pubkey' } else if (this.chunks.length == 2 && - Array.isArray(this.chunks[0]) && - Array.isArray(this.chunks[1])) { + Buffer.isBuffer(this.chunks[0]) && + Buffer.isBuffer(this.chunks[1])) { return 'pubkeyhash' } else if (this.chunks[0] == opcodes.OP_0 && this.chunks.slice(1).reduce(function(t, chunk, i) { - return t && Array.isArray(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1) + return t && Buffer.isBuffer(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1) }, true)) { return 'multisig' } else { @@ -233,115 +228,76 @@ Script.prototype.getInType = function() { } } -/** - * Add an op code to the script. - */ -Script.prototype.writeOp = function(opcode) { - this.buffer.push(opcode) - this.chunks.push(opcode) -} - -/** - * Add a data chunk to the script. - */ -Script.prototype.writeBytes = function(data) { - // FIXME: Script module doesn't support buffers yet - if (Buffer.isBuffer(data)) data = Array.prototype.slice.call(data); - assert(Array.isArray(data), 'Expected a byte array, got ' + data) - - if (data.length < opcodes.OP_PUSHDATA1) { - this.buffer.push(data.length) - } else if (data.length <= 0xff) { - this.buffer.push(opcodes.OP_PUSHDATA1) - this.buffer.push(data.length) - } else if (data.length <= 0xffff) { - this.buffer.push(opcodes.OP_PUSHDATA2) - this.buffer.push(data.length & 0xff) - this.buffer.push((data.length >>> 8) & 0xff) - } else { - this.buffer.push(opcodes.OP_PUSHDATA4) - this.buffer.push(data.length & 0xff) - this.buffer.push((data.length >>> 8) & 0xff) - this.buffer.push((data.length >>> 16) & 0xff) - this.buffer.push((data.length >>> 24) & 0xff) - } - this.buffer = this.buffer.concat(data) - this.chunks.push(data) -} - // {pubKey} OP_CHECKSIG Script.createPubKeyScriptPubKey = function(pubKey) { - var script = new Script() - - script.writeBytes(pubKey.toBuffer()) - script.writeOp(opcodes.OP_CHECKSIG) - - return script + return Script.fromChunks([ + pubKey.toBuffer(), + opcodes.OP_CHECKSIG + ]) } // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG Script.createPubKeyHashScriptPubKey = function(hash) { - var script = new Script() + assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) - script.writeOp(opcodes.OP_DUP) - script.writeOp(opcodes.OP_HASH160) - script.writeBytes(hash) - script.writeOp(opcodes.OP_EQUALVERIFY) - script.writeOp(opcodes.OP_CHECKSIG) - - return script + return Script.fromChunks([ + opcodes.OP_DUP, + opcodes.OP_HASH160, + hash, + opcodes.OP_EQUALVERIFY, + opcodes.OP_CHECKSIG + ]) } // OP_HASH160 {scriptHash} OP_EQUAL Script.createP2SHScriptPubKey = function(hash) { - var script = new Script() + assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) - script.writeOp(opcodes.OP_HASH160) - script.writeBytes(hash) - script.writeOp(opcodes.OP_EQUAL) - - return script + return Script.fromChunks([ + opcodes.OP_HASH160, + hash, + opcodes.OP_EQUAL + ]) } // m [pubKeys ...] n OP_CHECKMULTISIG Script.createMultisigScriptPubKey = function(m, pubKeys) { assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys) assert(pubKeys.length >= m, 'Not enough pubKeys provided') - var script = new Script() + + var pubKeyBuffers = pubKeys.map(function(pubKey) { + return pubKey.toBuffer() + }) var n = pubKeys.length - script.writeOp((opcodes.OP_1 - 1) + m) - - pubKeys.forEach(function(pubKey) { - script.writeBytes(pubKey.toBuffer()) - }) - - script.writeOp((opcodes.OP_1 - 1) + n) - script.writeOp(opcodes.OP_CHECKMULTISIG) - - return script + return Script.fromChunks([].concat( + (opcodes.OP_1 - 1) + m, + pubKeyBuffers, + (opcodes.OP_1 - 1) + n, + opcodes.OP_CHECKMULTISIG + )) } // {signature} Script.createPubKeyScriptSig = function(signature) { - var script = new Script() - script.writeBytes(signature) - return script + assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) + + return Script.fromChunks(signature) } // {signature} {pubKey} Script.createPubKeyHashScriptSig = function(signature, pubKey) { - var script = new Script() - script.writeBytes(signature) - script.writeBytes(pubKey.toBuffer()) - return script + assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) + + return Script.fromChunks([signature, pubKey.toBuffer()]) } // {serialized scriptPubKey script} Script.createP2SHScriptSig = function(scriptSig, scriptPubKey) { - var inScript = new Script(scriptSig.buffer) - inScript.writeBytes(scriptPubKey.buffer) - return inScript + return Script.fromChunks([].concat( + scriptSig.chunks, + scriptPubKey.toBuffer() + )) } // OP_0 [signatures ...] @@ -354,29 +310,25 @@ Script.createMultisigScriptSig = function(signatures, scriptPubKey) { assert(k <= signatures.length, 'Not enough signatures provided') } - var inScript = new Script() - - inScript.writeOp(opcodes.OP_0) - signatures.map(function(sig) { - inScript.writeBytes(sig) - }) - - return inScript + return Script.fromChunks([].concat(opcodes.OP_0, signatures)) } Script.prototype.clone = function() { - return new Script(this.buffer) + return new Script(this.buffer, this.chunks) } -Script.fromChunks = function(chunks) { +Script.fromChunks = function fromChunks(chunks) { assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) var bufferSize = chunks.reduce(function(accum, chunk) { - var chunkSize = 1 + var chunkSize - // FIXME: transitionary - if (Array.isArray(chunk) || Buffer.isBuffer(chunk)) { + if (Buffer.isBuffer(chunk)) { chunkSize = bufferutils.pushDataSize(chunk.length) + chunk.length + + } else { + chunkSize = 1 + } return accum + chunkSize @@ -386,25 +338,22 @@ Script.fromChunks = function(chunks) { var offset = 0 chunks.forEach(function(chunk) { - // FIXME: transitionary - if (Array.isArray(chunk) || Buffer.isBuffer(chunk)) { + if (Buffer.isBuffer(chunk)) { offset += bufferutils.writePushDataInt(buffer, chunk.length, offset) - // FIXME: transitionary -// chunk.copy(buffer, offset) - for (var i = 0; i < chunk.length; ++i) { - buffer[offset + i] = chunk[i] - } - + chunk.copy(buffer, offset) offset += chunk.length } else { + assert(typeof chunk == 'number') + buffer.writeUInt8(chunk, offset) offset += 1 } }) - return Script.fromBuffer(buffer) + assert.equal(offset, buffer.length, 'Could not decode chunks') + return new Script(buffer, chunks) } // FIXME: doesn't work for data chunks, maybe time to use buffertools.compare... @@ -415,6 +364,6 @@ Script.prototype.without = function(needle) { } // Constants -Script.EMPTY = new Script() +Script.EMPTY = Script.fromChunks([]) module.exports = Script diff --git a/test/script.js b/test/script.js index a1ff8cc..a5b0ce7 100644 --- a/test/script.js +++ b/test/script.js @@ -14,16 +14,17 @@ function h2b(h) { return new Buffer(h, 'hex') } describe('Script', function() { describe('constructor', function() { - it('works for a byte array', function() { - assert.ok(new Script([])) - }) + it('accepts valid parameters', function() { + var buffer = new Buffer([1]) + var chunks = [1] + var script = new Script(buffer, chunks) - it('works when nothing is passed in', function() { - assert.ok(new Script()) + assert.equal(script.buffer, buffer) + assert.equal(script.chunks, chunks) }) it('throws an error when input is not an array', function() { - assert.throws(function(){ new Script({}) }, /Expected Array, got/) + assert.throws(function(){ new Script({}) }, /Expected Buffer, got/) }) }) From 323f0612d0d76e4dff12d106ebe1f7badbb30444 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 16:44:09 +1000 Subject: [PATCH 03/18] Transaction: remove unnecessary Buffer copy --- src/transaction.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/transaction.js b/src/transaction.js index e9590d8..d2d7a16 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -133,7 +133,6 @@ Transaction.prototype.toBuffer = function () { var offset = 0 function writeSlice(slice) { - if (Array.isArray(slice)) slice = new Buffer(slice) // FIXME: Performance: transitionary only slice.copy(buffer, offset) offset += slice.length } From 005ca31fb3398064f0df5f1d24cb9f9073f50735 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 16:45:53 +1000 Subject: [PATCH 04/18] Transaction: remove unnecessary clone --- src/transaction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/transaction.js b/src/transaction.js index d2d7a16..c427bd5 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -409,7 +409,7 @@ TransactionIn.prototype.clone = function () { hash: this.outpoint.hash, index: this.outpoint.index }, - script: this.script.clone(), + script: this.script, sequence: this.sequence }) } @@ -424,7 +424,7 @@ function TransactionOut(data) { TransactionOut.prototype.clone = function() { return new TransactionOut({ - script: this.script.clone(), + script: this.script, value: this.value, address: this.address }) From 3521584b3a9af6faca15da20af0c3ebe42f93696 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 17:21:43 +1000 Subject: [PATCH 05/18] Script: parseChunks now uses bufferutils --- src/script.js | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/src/script.js b/src/script.js index 7f581c5..236cf8d 100644 --- a/src/script.js +++ b/src/script.js @@ -40,47 +40,30 @@ Script.parseChunks = function(buffer) { var chunks = [] - // Cursor var i = 0 - // Read n bytes and store result as a chunk - function readChunk(n) { - chunks.push(buffer.slice(i, i + n)) - i += n - } - while (i < buffer.length) { - var opcode = buffer[i++] - if (opcode >= 0xF0) { - // Two byte opcode - opcode = (opcode << 8) | buffer[i++] - } + var opcode = buffer.readUInt8(i) + + if ((opcode > opcodes.OP_0) && (opcode <= opcodes.OP_PUSHDATA4)) { + var d = bufferutils.readPushDataInt(buffer, i) + i += d.size + + var data = buffer.slice(i, i + d.number) + i += d.number + + chunks.push(data) - var len - if (opcode > 0 && opcode < opcodes.OP_PUSHDATA1) { - // Read some bytes of data, opcode value is the length of data - readChunk(opcode) - } else if (opcode == opcodes.OP_PUSHDATA1) { - len = buffer[i++] - readChunk(len) - } else if (opcode == opcodes.OP_PUSHDATA2) { - len = (buffer[i++] << 8) | buffer[i++] - readChunk(len) - } else if (opcode == opcodes.OP_PUSHDATA4) { - len = (buffer[i++] << 24) | - (buffer[i++] << 16) | - (buffer[i++] << 8) | - buffer[i++] - readChunk(len) } else { chunks.push(opcode) + + ++i } } return chunks } - /** * Compare the script to known templates of scriptPubKey. * From 91bb25c00ccec94c5724f734dc8606d8eff696d7 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 17:27:27 +1000 Subject: [PATCH 06/18] Script: re-order functions to project order Removes Script.parseChunks and merges it into Script.fromBuffer --- src/script.js | 155 +++++++++++++++++++++++--------------------------- 1 file changed, 72 insertions(+), 83 deletions(-) diff --git a/src/script.js b/src/script.js index 236cf8d..79e01cc 100644 --- a/src/script.js +++ b/src/script.js @@ -13,31 +13,6 @@ function Script(buffer, chunks) { // Import operations Script.fromBuffer = function(buffer) { - assert(Buffer.isBuffer(buffer)) - - var chunks = Script.parseChunks(buffer) - return new Script(buffer, chunks) -} - -Script.fromHex = function(hex) { - return Script.fromBuffer(new Buffer(hex, 'hex')) -} - -// Export operations -Script.prototype.toBuffer = function() { - return this.buffer -} - -Script.prototype.toHex = function() { - return this.toBuffer().toString('hex') -} - -/** - * Parse a Script from a Buffer to its chunked representation. - */ -Script.parseChunks = function(buffer) { - assert(Buffer.isBuffer(buffer), 'Expected Buffer, got ' + buffer) - var chunks = [] var i = 0 @@ -61,7 +36,78 @@ Script.parseChunks = function(buffer) { } } - return chunks + return new Script(buffer, chunks) +} + +Script.fromChunks = function(chunks) { + assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) + + var bufferSize = chunks.reduce(function(accum, chunk) { + var chunkSize + + if (Buffer.isBuffer(chunk)) { + chunkSize = bufferutils.pushDataSize(chunk.length) + chunk.length + + } else { + chunkSize = 1 + + } + + return accum + chunkSize + }, 0.0) + + var buffer = new Buffer(bufferSize) + var offset = 0 + + chunks.forEach(function(chunk) { + if (Buffer.isBuffer(chunk)) { + offset += bufferutils.writePushDataInt(buffer, chunk.length, offset) + + chunk.copy(buffer, offset) + offset += chunk.length + + } else { + assert(typeof chunk == 'number') + + buffer.writeUInt8(chunk, offset) + offset += 1 + } + }) + + assert.equal(offset, buffer.length, 'Could not decode chunks') + return new Script(buffer, chunks) +} + +Script.fromHex = function(hex) { + return Script.fromBuffer(new Buffer(hex, 'hex')) +} + +// Constants +Script.EMPTY = Script.fromChunks([]) + +// Operations +Script.prototype.clone = function() { + return new Script(this.buffer, this.chunks) +} + +Script.prototype.getHash = function() { + return crypto.hash160(this.buffer) +} + +// FIXME: doesn't work for data chunks, maybe time to use buffertools.compare... +Script.prototype.without = function(needle) { + return Script.fromChunks(this.chunks.filter(function(op) { + return op !== needle + })) +} + +// Export operations +Script.prototype.toBuffer = function() { + return this.buffer +} + +Script.prototype.toHex = function() { + return this.toBuffer().toString('hex') } /** @@ -162,10 +208,6 @@ function isSmallIntOp(opcode) { ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) } -Script.prototype.getHash = function() { - return crypto.hash160(this.buffer) -} - /** * Compare the script to known templates of scriptSig. * @@ -296,57 +338,4 @@ Script.createMultisigScriptSig = function(signatures, scriptPubKey) { return Script.fromChunks([].concat(opcodes.OP_0, signatures)) } -Script.prototype.clone = function() { - return new Script(this.buffer, this.chunks) -} - -Script.fromChunks = function fromChunks(chunks) { - assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) - - var bufferSize = chunks.reduce(function(accum, chunk) { - var chunkSize - - if (Buffer.isBuffer(chunk)) { - chunkSize = bufferutils.pushDataSize(chunk.length) + chunk.length - - } else { - chunkSize = 1 - - } - - return accum + chunkSize - }, 0.0) - - var buffer = new Buffer(bufferSize) - var offset = 0 - - chunks.forEach(function(chunk) { - if (Buffer.isBuffer(chunk)) { - offset += bufferutils.writePushDataInt(buffer, chunk.length, offset) - - chunk.copy(buffer, offset) - offset += chunk.length - - } else { - assert(typeof chunk == 'number') - - buffer.writeUInt8(chunk, offset) - offset += 1 - } - }) - - assert.equal(offset, buffer.length, 'Could not decode chunks') - return new Script(buffer, chunks) -} - -// FIXME: doesn't work for data chunks, maybe time to use buffertools.compare... -Script.prototype.without = function(needle) { - return Script.fromChunks(this.chunks.filter(function(op) { - return op !== needle - })) -} - -// Constants -Script.EMPTY = Script.fromChunks([]) - module.exports = Script From aded938ab69fe2af6a9af69e550bcb9cf873295d Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 20:58:56 +1000 Subject: [PATCH 07/18] Script: check hex not template result --- test/script.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/script.js b/test/script.js index a5b0ce7..52e12c0 100644 --- a/test/script.js +++ b/test/script.js @@ -162,13 +162,15 @@ describe('Script', function() { describe('fromChunks', function() { it('should match expected behaviour', function() { var hash = new Buffer(32) + hash.fill(0) + var script = Script.fromChunks([ opcodes.OP_HASH160, hash, opcodes.OP_EQUAL ]) - assert.deepEqual(script, Script.createP2SHScriptPubKey(hash)) + assert.equal(script.toHex(), 'a920000000000000000000000000000000000000000000000000000000000000000087') }) }) From 00cec9ce649ba5939fe3ee47c2f8ef44bf227e14 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 21:14:22 +1000 Subject: [PATCH 08/18] Script: move all templates to templates.js --- src/address.js | 18 ++-- src/script.js | 228 -------------------------------------------- src/templates.js | 180 ++++++++++++++++++++++++++++++++++ src/transaction.js | 5 +- test/address.js | 3 +- test/script.js | 120 ----------------------- test/templates.js | 132 +++++++++++++++++++++++++ test/transaction.js | 7 +- test/wallet.js | 4 +- 9 files changed, 331 insertions(+), 366 deletions(-) create mode 100644 src/templates.js create mode 100644 test/templates.js diff --git a/src/address.js b/src/address.js index 5e15845..c54b94e 100644 --- a/src/address.js +++ b/src/address.js @@ -1,7 +1,7 @@ var assert = require('assert') var base58check = require('./base58check') var networks = require('./networks') -var Script = require('./script') +var templates = require('./templates') function findScriptTypeByVersion(queryVersion) { for (var networkName in networks) { @@ -38,14 +38,13 @@ Address.fromBase58Check = function(string) { Address.fromScriptPubKey = function(script, network) { network = network || networks.bitcoin - var type = script.getOutType() + var type = templates.classifyScriptPubKey(script) if (type === 'pubkeyhash') { - return new Address(new Buffer(script.chunks[2]), network.pubKeyHash) - } + return new Address(script.chunks[2], network.pubKeyHash) - else if (type === 'scripthash') { - return new Address(new Buffer(script.chunks[1]), network.scriptHash) + } else if (type === 'scripthash') { + return new Address(script.chunks[1], network.scriptHash) } assert(false, type + ' has no matching Address') @@ -64,11 +63,10 @@ Address.prototype.toScriptPubKey = function() { var scriptType = findScriptTypeByVersion(this.version) if (scriptType === 'pubKeyHash') { - return Script.createPubKeyHashScriptPubKey(this.hash) - } + return templates.createPubKeyHashScriptPubKey(this.hash) - else if (scriptType === 'scriptHash') { - return Script.createP2SHScriptPubKey(this.hash) + } else if (scriptType === 'scriptHash') { + return templates.createP2SHScriptPubKey(this.hash) } assert(false, this.toString() + ' has no matching script') diff --git a/src/script.js b/src/script.js index 79e01cc..57e2ebe 100644 --- a/src/script.js +++ b/src/script.js @@ -110,232 +110,4 @@ Script.prototype.toHex = function() { return this.toBuffer().toString('hex') } -/** - * Compare the script to known templates of scriptPubKey. - * - * This method will compare the script to a small number of standard script - * templates and return a string naming the detected type. - * - * Currently supported are: - * Pubkeyhash (address) - * Paying to a Bitcoin address which is the hash of a pubkey. - * OP_DUP OP_HASH160 [pubKeyHash] OP_EQUALVERIFY OP_CHECKSIG - * - * Pubkey - * Paying to a public key directly. - * [pubKey] OP_CHECKSIG - * - * Scripthash (P2SH) - * Paying to an address which is the hash of a script - * OP_HASH160 [Scripthash] OP_EQUAL - * - * Multisig - * Paying to multiple pubkeys and require a number of the signatures - * m [pubkey] [pubkey] [pubkey] n OP_CHECKMULTISIG - * - * Nulldata - * Provably prune-able outputs - * OP_RETURN [data] - * - * Nonstandard: - * Any other script (no template matched). - * - * https://github.com/bitcoin/bitcoin/blob/19e5b9d2dfcac4efadba636745485d9660fb1abe/src/script.cpp#L75 - */ - -Script.prototype.getOutType = function() { - if (isPubkeyhash.call(this)) { - return 'pubkeyhash' - } else if (isPubkey.call(this)) { - return 'pubkey' - } else if (isScripthash.call(this)) { - return 'scripthash' - } else if (isMultisig.call(this)) { - return 'multisig' - } else if (isNulldata.call(this)) { - return 'nulldata' - } else { - return 'nonstandard' - } -} - -function isPubkeyhash() { - return this.chunks.length == 5 && - this.chunks[0] == opcodes.OP_DUP && - this.chunks[1] == opcodes.OP_HASH160 && - Buffer.isBuffer(this.chunks[2]) && - this.chunks[2].length === 20 && - this.chunks[3] == opcodes.OP_EQUALVERIFY && - this.chunks[4] == opcodes.OP_CHECKSIG -} - -function isPubkey() { - return this.chunks.length === 2 && - Buffer.isBuffer(this.chunks[0]) && - this.chunks[1] === opcodes.OP_CHECKSIG -} - -function isScripthash() { - return this.chunks[this.chunks.length - 1] == opcodes.OP_EQUAL && - this.chunks[0] == opcodes.OP_HASH160 && - Buffer.isBuffer(this.chunks[1]) && - this.chunks[1].length === 20 && - this.chunks.length == 3 -} - -function isMultisig() { - return this.chunks.length > 3 && - // m is a smallint - isSmallIntOp(this.chunks[0]) && - // n is a smallint - isSmallIntOp(this.chunks[this.chunks.length - 2]) && - // n greater or equal to m - this.chunks[0] <= this.chunks[this.chunks.length - 2] && - // n cannot be 0 - this.chunks[this.chunks.length - 2] !== opcodes.OP_0 && - // n is the size of chunk length minus 3 (m, n, OP_CHECKMULTISIG) - this.chunks.length - 3 === this.chunks[this.chunks.length - 2] - opcodes.OP_RESERVED && - // last chunk is OP_CHECKMULTISIG - this.chunks[this.chunks.length - 1] == opcodes.OP_CHECKMULTISIG -} - -function isNulldata() { - return this.chunks[0] === opcodes.OP_RETURN -} - -function isSmallIntOp(opcode) { - return ((opcode == opcodes.OP_0) || - ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) -} - -/** - * Compare the script to known templates of scriptSig. - * - * This method will compare the script to a small number of standard script - * templates and return a string naming the detected type. - * - * WARNING: Use this method with caution. It merely represents a heuristic - * based on common transaction formats. A non-standard transaction could - * very easily match one of these templates by accident. - * - * Currently supported are: - * Address: - * Paying to a Bitcoin address which is the hash of a pubkey. - * [sig] [pubKey] - * - * Pubkey: - * Paying to a public key directly. - * [sig] - * - * Multisig: - * Paying to M-of-N public keys. - * - * Nonstandard: - * Any other script (no template matched). - */ -Script.prototype.getInType = function() { - if (this.chunks.length == 1 && - Buffer.isBuffer(this.chunks[0])) { - // Direct IP to IP transactions only have the signature in their scriptSig. - // TODO: We could also check that the length of the data is correct. - return 'pubkey' - } else if (this.chunks.length == 2 && - Buffer.isBuffer(this.chunks[0]) && - Buffer.isBuffer(this.chunks[1])) { - return 'pubkeyhash' - } else if (this.chunks[0] == opcodes.OP_0 && - this.chunks.slice(1).reduce(function(t, chunk, i) { - return t && Buffer.isBuffer(chunk) && (chunk[0] == 48 || i == this.chunks.length - 1) - }, true)) { - return 'multisig' - } else { - return 'nonstandard' - } -} - -// {pubKey} OP_CHECKSIG -Script.createPubKeyScriptPubKey = function(pubKey) { - return Script.fromChunks([ - pubKey.toBuffer(), - opcodes.OP_CHECKSIG - ]) -} - -// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -Script.createPubKeyHashScriptPubKey = function(hash) { - assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) - - return Script.fromChunks([ - opcodes.OP_DUP, - opcodes.OP_HASH160, - hash, - opcodes.OP_EQUALVERIFY, - opcodes.OP_CHECKSIG - ]) -} - -// OP_HASH160 {scriptHash} OP_EQUAL -Script.createP2SHScriptPubKey = function(hash) { - assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) - - return Script.fromChunks([ - opcodes.OP_HASH160, - hash, - opcodes.OP_EQUAL - ]) -} - -// m [pubKeys ...] n OP_CHECKMULTISIG -Script.createMultisigScriptPubKey = function(m, pubKeys) { - assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys) - assert(pubKeys.length >= m, 'Not enough pubKeys provided') - - var pubKeyBuffers = pubKeys.map(function(pubKey) { - return pubKey.toBuffer() - }) - var n = pubKeys.length - - return Script.fromChunks([].concat( - (opcodes.OP_1 - 1) + m, - pubKeyBuffers, - (opcodes.OP_1 - 1) + n, - opcodes.OP_CHECKMULTISIG - )) -} - -// {signature} -Script.createPubKeyScriptSig = function(signature) { - assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) - - return Script.fromChunks(signature) -} - -// {signature} {pubKey} -Script.createPubKeyHashScriptSig = function(signature, pubKey) { - assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) - - return Script.fromChunks([signature, pubKey.toBuffer()]) -} - -// {serialized scriptPubKey script} -Script.createP2SHScriptSig = function(scriptSig, scriptPubKey) { - return Script.fromChunks([].concat( - scriptSig.chunks, - scriptPubKey.toBuffer() - )) -} - -// OP_0 [signatures ...] -Script.createMultisigScriptSig = function(signatures, scriptPubKey) { - if (scriptPubKey) { - assert(isMultisig.call(scriptPubKey)) - - var m = scriptPubKey.chunks[0] - var k = m - (opcodes.OP_1 - 1) - assert(k <= signatures.length, 'Not enough signatures provided') - } - - return Script.fromChunks([].concat(opcodes.OP_0, signatures)) -} - module.exports = Script diff --git a/src/templates.js b/src/templates.js new file mode 100644 index 0000000..6eecc19 --- /dev/null +++ b/src/templates.js @@ -0,0 +1,180 @@ +var assert = require('assert') +var opcodes = require('./opcodes') +var Script = require('./script') + +function classifyScriptPubKey(script) { + if (isPubkeyhash.call(script)) { + return 'pubkeyhash' + } else if (isPubkey.call(script)) { + return 'pubkey' + } else if (isScripthash.call(script)) { + return 'scripthash' + } else if (isMultisig.call(script)) { + return 'multisig' + } else if (isNulldata.call(script)) { + return 'nulldata' + } else { + return 'nonstandard' + } +} + +function classifyScriptSig(script) { + if (script.chunks.length == 1 && Buffer.isBuffer(script.chunks[0])) { + return 'pubkey' + } else if (script.chunks.length == 2 && Buffer.isBuffer(script.chunks[0]) && Buffer.isBuffer(script.chunks[1])) { + return 'pubkeyhash' + } else if (script.chunks[0] == opcodes.OP_0 && script.chunks.slice(1).reduce(function(t, chunk, i) { + return t && Buffer.isBuffer(chunk) && (chunk[0] == 48 || i == script.chunks.length - 1) + }, true)) { + return 'multisig' + } else { + return 'nonstandard' + } +} + +function isPubkeyhash() { + return this.chunks.length == 5 && + this.chunks[0] == opcodes.OP_DUP && + this.chunks[1] == opcodes.OP_HASH160 && + Buffer.isBuffer(this.chunks[2]) && + this.chunks[2].length === 20 && + this.chunks[3] == opcodes.OP_EQUALVERIFY && + this.chunks[4] == opcodes.OP_CHECKSIG +} + +function isPubkey() { + return this.chunks.length === 2 && + Buffer.isBuffer(this.chunks[0]) && + this.chunks[1] === opcodes.OP_CHECKSIG +} + +function isScripthash() { + return this.chunks[this.chunks.length - 1] == opcodes.OP_EQUAL && + this.chunks[0] == opcodes.OP_HASH160 && + Buffer.isBuffer(this.chunks[1]) && + this.chunks[1].length === 20 && + this.chunks.length == 3 +} + +function isMultisig() { + return this.chunks.length > 3 && + // m is a smallint + isSmallIntOp(this.chunks[0]) && + // n is a smallint + isSmallIntOp(this.chunks[this.chunks.length - 2]) && + // n greater or equal to m + this.chunks[0] <= this.chunks[this.chunks.length - 2] && + // n cannot be 0 + this.chunks[this.chunks.length - 2] !== opcodes.OP_0 && + // n is the size of chunk length minus 3 (m, n, OP_CHECKMULTISIG) + this.chunks.length - 3 === this.chunks[this.chunks.length - 2] - opcodes.OP_RESERVED && + // last chunk is OP_CHECKMULTISIG + this.chunks[this.chunks.length - 1] == opcodes.OP_CHECKMULTISIG +} + +function isNulldata() { + return this.chunks[0] === opcodes.OP_RETURN +} + +function isSmallIntOp(opcode) { + return ((opcode == opcodes.OP_0) || ((opcode >= opcodes.OP_1) && (opcode <= opcodes.OP_16))) +} + +// Standard Script Templates +// {pubKey} OP_CHECKSIG +function createPubKeyScriptPubKey(pubKey) { + return Script.fromChunks([ + pubKey.toBuffer(), + opcodes.OP_CHECKSIG + ]) +} + +// OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG +function createPubKeyHashScriptPubKey(hash) { + assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) + + return Script.fromChunks([ + opcodes.OP_DUP, + opcodes.OP_HASH160, + hash, + opcodes.OP_EQUALVERIFY, + opcodes.OP_CHECKSIG + ]) +} + +// OP_HASH160 {scriptHash} OP_EQUAL +function createP2SHScriptPubKey(hash) { + assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) + + return Script.fromChunks([ + opcodes.OP_HASH160, + hash, + opcodes.OP_EQUAL + ]) +} + +// m [pubKeys ...] n OP_CHECKMULTISIG +function createMultisigScriptPubKey(m, pubKeys) { + assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys) + assert(pubKeys.length >= m, 'Not enough pubKeys provided') + + var pubKeyBuffers = pubKeys.map(function(pubKey) { + return pubKey.toBuffer() + }) + var n = pubKeys.length + + return Script.fromChunks([].concat( + (opcodes.OP_1 - 1) + m, + pubKeyBuffers, + (opcodes.OP_1 - 1) + n, + opcodes.OP_CHECKMULTISIG + )) +} + +// {signature} +function createPubKeyScriptSig(signature) { + assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) + + return Script.fromChunks(signature) +} + +// {signature} {pubKey} +function createPubKeyHashScriptSig(signature, pubKey) { + assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) + + return Script.fromChunks([signature, pubKey.toBuffer()]) +} + +// {serialized scriptPubKey script} +function createP2SHScriptSig(scriptSig, scriptPubKey) { + return Script.fromChunks([].concat( + scriptSig.chunks, + scriptPubKey.toBuffer() + )) +} + +// OP_0 [signatures ...] +function createMultisigScriptSig(signatures, scriptPubKey) { + if (scriptPubKey) { + assert(isMultisig.call(scriptPubKey)) + + var m = scriptPubKey.chunks[0] + var k = m - (opcodes.OP_1 - 1) + assert(k <= signatures.length, 'Not enough signatures provided') + } + + return Script.fromChunks([].concat(opcodes.OP_0, signatures)) +} + +module.exports = { + classifyScriptPubKey: classifyScriptPubKey, + classifyScriptSig: classifyScriptSig, + createMultisigScriptPubKey: createMultisigScriptPubKey, + createMultisigScriptSig: createMultisigScriptSig, + createP2SHScriptPubKey: createP2SHScriptPubKey, + createP2SHScriptSig: createP2SHScriptSig, + createPubKeyHashScriptPubKey: createPubKeyHashScriptPubKey, + createPubKeyHashScriptSig: createPubKeyHashScriptSig, + createPubKeyScriptPubKey: createPubKeyScriptPubKey, + createPubKeyScriptSig: createPubKeyScriptSig +} diff --git a/src/transaction.js b/src/transaction.js index c427bd5..d0de5c5 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -5,10 +5,11 @@ var bufferutils = require('./bufferutils') var crypto = require('./crypto') var ecdsa = require('./ecdsa') var opcodes = require('./opcodes') +var templates = require('./templates') var Address = require('./address') -var Script = require('./script') var ECKey = require('./eckey') +var Script = require('./script') var DEFAULT_SEQUENCE = 0xffffffff @@ -345,7 +346,7 @@ Transaction.prototype.sign = function(index, key, type) { var signature = this.signScriptSig(index, script, key, type) // FIXME: Assumed prior TX was pay-to-pubkey-hash - var scriptSig = Script.createPubKeyHashScriptSig(signature, key.pub) + var scriptSig = templates.createPubKeyHashScriptSig(signature, key.pub) this.setScriptSig(index, scriptSig) } diff --git a/test/address.js b/test/address.js index 4e09322..fb066fe 100644 --- a/test/address.js +++ b/test/address.js @@ -1,6 +1,7 @@ var assert = require('assert') -var Address = require('../src/address') var networks = require('../src/networks') + +var Address = require('../src/address') var Script = require('../src/script') var fixtures = require('./fixtures/address.json') diff --git a/test/script.js b/test/script.js index 52e12c0..bb9b9b9 100644 --- a/test/script.js +++ b/test/script.js @@ -1,17 +1,10 @@ var assert = require('assert') -var crypto = require('../src/crypto') -var networks = require('../src/networks') var opcodes = require('../src/opcodes') -var Address = require('../src/address') -var ECPubKey = require('../src/ecpubkey') var Script = require('../src/script') var fixtures = require('./fixtures/script.json') -function b2h(b) { return new Buffer(b).toString('hex') } -function h2b(h) { return new Buffer(h, 'hex') } - describe('Script', function() { describe('constructor', function() { it('accepts valid parameters', function() { @@ -46,119 +39,6 @@ describe('Script', function() { }) }) - describe('getInType', function() { - fixtures.valid.forEach(function(f) { - if (!f.scriptPubKey) { - it('supports ' + f.description, function() { - var script = Script.fromHex(f.hex) - - assert.equal(script.getInType(), f.type) - }) - } - }) - }) - - describe('getOutType', function() { - fixtures.valid.forEach(function(f) { - if (f.scriptPubKey) { - it('supports ' + f.description, function() { - var script = Script.fromHex(f.hex) - - assert.equal(script.getOutType(), f.type) - }) - } - }) - }) - - describe('pay-to-pubKeyHash', function() { - it('matches the test data', function() { - // FIXME: bad - var f = fixtures.valid[2] - var address = Address.fromBase58Check('19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu') - var script = Script.createPubKeyHashScriptPubKey(address.hash) - - assert.equal(script.toHex(), f.hex) - }) - }) - - describe('pay-to-pubkey', function() { - it('matches the test data', function() { - // FIXME: bad - var f = fixtures.valid[0] - var pubKey = ECPubKey.fromHex(f.pubKey) - var script = Script.createPubKeyScriptPubKey(pubKey) - - assert.equal(script.toHex(), f.hex) - }) - }) - - describe('pay-to-scriptHash', function() { - it('matches the test data', function() { - // FIXME: bad - var f = fixtures.valid[1] - var address = Address.fromBase58Check('3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8') - var script = Script.createP2SHScriptPubKey(address.hash) - - assert.equal(script.toHex(), f.hex) - }) - }) - - describe('2-of-3 Multi-Signature scriptPubKey', function() { - var pubKeys - - beforeEach(function() { - pubKeys = [ - '02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f', - '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f', - '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19' - ].map(ECPubKey.fromHex) - }) - - it('should create valid redeemScript', function() { - var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys) - - var hash160 = crypto.hash160(new Buffer(redeemScript.buffer)) - var multisigAddress = new Address(hash160, networks.bitcoin.scriptHash) - - assert.equal(multisigAddress.toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v') - }) - - it('should throw on not enough pubKeys provided', function() { - assert.throws(function() { - Script.createMultisigScriptPubKey(4, pubKeys) - }, /Not enough pubKeys provided/) - }) - }) - - describe('2-of-2 Multisig scriptSig', function() { - var pubKeys = [ - '02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1', - '0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a' - ].map(ECPubKey.fromHex) - var signatures = [ - '304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801', - '3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501' - ].map(h2b) - var expected = '0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae' - - it('should create a valid P2SH multisig scriptSig', function() { - var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys) - var redeemScriptSig = Script.createMultisigScriptSig(signatures) - - var scriptSig = Script.createP2SHScriptSig(redeemScriptSig, redeemScript) - - assert.equal(b2h(scriptSig.buffer), expected) - }) - - it('should throw on not enough signatures', function() { - var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys) - - assert.throws(function() { - Script.createMultisigScriptSig(signatures.slice(1), redeemScript) - }, /Not enough signatures provided/) - }) - }) - describe('fromChunks', function() { it('should match expected behaviour', function() { var hash = new Buffer(32) diff --git a/test/templates.js b/test/templates.js new file mode 100644 index 0000000..b969e53 --- /dev/null +++ b/test/templates.js @@ -0,0 +1,132 @@ +var assert = require('assert') +var crypto = require('../src/crypto') +var networks = require('../src/networks') +var templates = require('../src/templates') + +var Address = require('../src/address') +var ECPubKey = require('../src/ecpubkey') +var Script = require('../src/script') + +var fixtures = require('./fixtures/script.json') + +function b2h(b) { return new Buffer(b).toString('hex') } +function h2b(h) { return new Buffer(h, 'hex') } + +describe('Templates', function() { + describe('classifyScriptSig', function() { + fixtures.valid.forEach(function(f) { + if (f.scriptPubKey) return + + it('supports ' + f.type, function() { + var script = Script.fromHex(f.hex) + var type = templates.classifyScriptSig(script) + + assert.equal(type, f.type) + }) + }) + }) + + describe('classifyScriptPubKey', function() { + fixtures.valid.forEach(function(f) { + if (!f.scriptPubKey) return + + it('supports ' + f.type, function() { + var script = Script.fromHex(f.hex) + var type = templates.classifyScriptPubKey(script) + + assert.equal(type, f.type) + }) + }) + }) + + // FIXME: bad + describe('pay-to-pubKeyHash', function() { + it('matches the test data', function() { + var f = fixtures.valid[2] + var address = Address.fromBase58Check('19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu') + var script = templates.createPubKeyHashScriptPubKey(address.hash) + + assert.equal(script.toHex(), f.hex) + }) + }) + + // FIXME: bad + describe('pay-to-pubkey', function() { + it('matches the test data', function() { + var f = fixtures.valid[0] + var pubKey = ECPubKey.fromHex(f.pubKey) + var script = templates.createPubKeyScriptPubKey(pubKey) + + assert.equal(script.toHex(), f.hex) + }) + }) + + // FIXME: bad + describe('pay-to-scriptHash', function() { + it('matches the test data', function() { + var f = fixtures.valid[1] + var address = Address.fromBase58Check('3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8') + var script = templates.createP2SHScriptPubKey(address.hash) + + assert.equal(script.toHex(), f.hex) + }) + }) + + // FIXME: bad + describe('2-of-3 Multi-Signature scriptPubKey', function() { + var pubKeys + + beforeEach(function() { + pubKeys = [ + '02ea1297665dd733d444f31ec2581020004892cdaaf3dd6c0107c615afb839785f', + '02fab2dea1458990793f56f42e4a47dbf35a12a351f26fa5d7e0cc7447eaafa21f', + '036c6802ce7e8113723dd92cdb852e492ebb157a871ca532c3cb9ed08248ff0e19' + ].map(ECPubKey.fromHex) + }) + + it('should create valid redeemScript', function() { + var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + + var hash160 = crypto.hash160(new Buffer(redeemScript.buffer)) + var multisigAddress = new Address(hash160, networks.bitcoin.scriptHash) + + assert.equal(multisigAddress.toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v') + }) + + it('should throw on not enough pubKeys provided', function() { + assert.throws(function() { + templates.createMultisigScriptPubKey(4, pubKeys) + }, /Not enough pubKeys provided/) + }) + }) + + // FIXME: bad + describe('2-of-2 Multisig scriptSig', function() { + var pubKeys = [ + '02359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1', + '0395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a' + ].map(ECPubKey.fromHex) + var signatures = [ + '304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801', + '3045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d140501' + ].map(h2b) + var expected = '0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae' + + it('should create a valid P2SH multisig scriptSig', function() { + var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + var redeemScriptSig = templates.createMultisigScriptSig(signatures) + + var scriptSig = templates.createP2SHScriptSig(redeemScriptSig, redeemScript) + + assert.equal(b2h(scriptSig.buffer), expected) + }) + + it('should throw on not enough signatures', function() { + var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + + assert.throws(function() { + templates.createMultisigScriptSig(signatures.slice(1), redeemScript) + }, /Not enough signatures provided/) + }) + }) +}) diff --git a/test/transaction.js b/test/transaction.js index 69f6b16..3485435 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,5 +1,6 @@ var assert = require('assert') var networks = require('../src/networks') +var templates = require('../src/templates') var Address = require('../src/address') var ECKey = require('../src/eckey') @@ -255,14 +256,14 @@ describe('Transaction', function() { return ECKey.fromWIF(wif) }) var pubKeys = privKeys.map(function(eck) { return eck.pub }) - var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys) + var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) var signatures = privKeys.map(function(privKey) { return tx.signScriptSig(0, redeemScript, privKey) }) - var redeemScriptSig = Script.createMultisigScriptSig(signatures) - var scriptSig = Script.createP2SHScriptSig(redeemScriptSig, redeemScript) + var redeemScriptSig = templates.createMultisigScriptSig(signatures) + var scriptSig = templates.createP2SHScriptSig(redeemScriptSig, redeemScript) tx.setScriptSig(0, scriptSig) signatures.forEach(function(sig, i){ diff --git a/test/wallet.js b/test/wallet.js index 955eba1..763c19d 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -2,10 +2,10 @@ var assert = require('assert') var crypto = require('../src/crypto') var networks = require('../src/networks') var sinon = require('sinon') +var templates = require('../src/templates') var Address = require('../src/address') var HDNode = require('../src/hdnode') -var Script = require('../src/script') var Transaction = require('../src/transaction').Transaction var Wallet = require('../src/wallet') @@ -268,7 +268,7 @@ describe('Wallet', function() { it('does not fail on scripts with no corresponding Address', function() { var pubKey = wallet.getPrivateKey(0).pub - var script = Script.createPubKeyScriptPubKey(pubKey) + var script = templates.createPubKeyScriptPubKey(pubKey) var tx2 = new Transaction() tx2.addInput(fakeTxHash(1), 0) From be29f504579df126c8c9a0f5bc3f34f258de4e0c Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 09:33:58 +1000 Subject: [PATCH 09/18] Address: fix consistent casing --- src/address.js | 8 ++++---- test/fixtures/address.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/address.js b/src/address.js index c54b94e..3da5dbd 100644 --- a/src/address.js +++ b/src/address.js @@ -11,7 +11,7 @@ function findScriptTypeByVersion(queryVersion) { var version = network[versionName] if (version === queryVersion) { - return versionName + return versionName.toLowerCase() } } } @@ -62,14 +62,14 @@ Address.prototype.toBase58Check = function () { Address.prototype.toScriptPubKey = function() { var scriptType = findScriptTypeByVersion(this.version) - if (scriptType === 'pubKeyHash') { + if (scriptType === 'pubkeyhash') { return templates.createPubKeyHashScriptPubKey(this.hash) - } else if (scriptType === 'scriptHash') { + } else if (scriptType === 'scripthash') { return templates.createP2SHScriptPubKey(this.hash) } - assert(false, this.toString() + ' has no matching script') + assert(false, this.toString() + ' has no matching Script') } Address.prototype.toString = Address.prototype.toBase58Check diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 06557af..83b6a3a 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -62,7 +62,7 @@ ], "toScriptPubKey": [ { - "description": "24kPZCmVgzfkpGdXExy56234MRHrsqQxNWE has no matching script", + "description": "24kPZCmVgzfkpGdXExy56234MRHrsqQxNWE has no matching Script", "hex": "751e76e8199196d454941c45d1b3a323f1433bd6", "version": 153 } From de1571647a8f8fd420ce533b710771f08ccf0c5c Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 09:36:11 +1000 Subject: [PATCH 10/18] Script: early exit in chunk.reduce --- src/script.js | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/src/script.js b/src/script.js index 57e2ebe..95866b9 100644 --- a/src/script.js +++ b/src/script.js @@ -43,17 +43,11 @@ Script.fromChunks = function(chunks) { assert(Array.isArray(chunks), 'Expected Array, got ' + chunks) var bufferSize = chunks.reduce(function(accum, chunk) { - var chunkSize - if (Buffer.isBuffer(chunk)) { - chunkSize = bufferutils.pushDataSize(chunk.length) + chunk.length - - } else { - chunkSize = 1 - + return accum + bufferutils.pushDataSize(chunk.length) + chunk.length } - return accum + chunkSize + return accum + 1 }, 0.0) var buffer = new Buffer(bufferSize) @@ -67,8 +61,6 @@ Script.fromChunks = function(chunks) { offset += chunk.length } else { - assert(typeof chunk == 'number') - buffer.writeUInt8(chunk, offset) offset += 1 } From 41c3b68293f3b3ba44860bee66aba6a39f63f8f9 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 09:58:52 +1000 Subject: [PATCH 11/18] templates: rename to scripts --- src/address.js | 9 +++---- src/index.js | 1 + src/{templates.js => scripts.js} | 40 +++++++++++++++---------------- src/transaction.js | 4 ++-- test/integration/p2sh.js | 9 +++---- test/{templates.js => scripts.js} | 32 ++++++++++++------------- test/transaction.js | 8 +++---- test/wallet.js | 4 ++-- 8 files changed, 55 insertions(+), 52 deletions(-) rename src/{templates.js => scripts.js} (81%) rename test/{templates.js => scripts.js} (79%) diff --git a/src/address.js b/src/address.js index 3da5dbd..8990089 100644 --- a/src/address.js +++ b/src/address.js @@ -1,7 +1,7 @@ var assert = require('assert') var base58check = require('./base58check') var networks = require('./networks') -var templates = require('./templates') +var scripts = require('./scripts') function findScriptTypeByVersion(queryVersion) { for (var networkName in networks) { @@ -38,7 +38,7 @@ Address.fromBase58Check = function(string) { Address.fromScriptPubKey = function(script, network) { network = network || networks.bitcoin - var type = templates.classifyScriptPubKey(script) + var type = scripts.classifyOutput(script) if (type === 'pubkeyhash') { return new Address(script.chunks[2], network.pubKeyHash) @@ -63,10 +63,11 @@ Address.prototype.toScriptPubKey = function() { var scriptType = findScriptTypeByVersion(this.version) if (scriptType === 'pubkeyhash') { - return templates.createPubKeyHashScriptPubKey(this.hash) + return scripts.pubKeyHashOutput(this.hash) } else if (scriptType === 'scripthash') { - return templates.createP2SHScriptPubKey(this.hash) + return scripts.scriptHashOutput(this.hash) + } assert(false, this.toString() + ' has no matching Script') diff --git a/src/index.js b/src/index.js index b46f2c5..5af8210 100644 --- a/src/index.js +++ b/src/index.js @@ -14,6 +14,7 @@ module.exports = { opcodes: require('./opcodes'), HDNode: require('./hdnode'), Script: require('./script'), + scripts: require('./scripts'), Transaction: T.Transaction, TransactionIn: T.TransactionIn, TransactionOut: T.TransactionOut, diff --git a/src/templates.js b/src/scripts.js similarity index 81% rename from src/templates.js rename to src/scripts.js index 6eecc19..b561819 100644 --- a/src/templates.js +++ b/src/scripts.js @@ -2,7 +2,7 @@ var assert = require('assert') var opcodes = require('./opcodes') var Script = require('./script') -function classifyScriptPubKey(script) { +function classifyOutput(script) { if (isPubkeyhash.call(script)) { return 'pubkeyhash' } else if (isPubkey.call(script)) { @@ -18,7 +18,7 @@ function classifyScriptPubKey(script) { } } -function classifyScriptSig(script) { +function classifyInput(script) { if (script.chunks.length == 1 && Buffer.isBuffer(script.chunks[0])) { return 'pubkey' } else if (script.chunks.length == 2 && Buffer.isBuffer(script.chunks[0]) && Buffer.isBuffer(script.chunks[1])) { @@ -82,7 +82,7 @@ function isSmallIntOp(opcode) { // Standard Script Templates // {pubKey} OP_CHECKSIG -function createPubKeyScriptPubKey(pubKey) { +function pubKeyOutput(pubKey) { return Script.fromChunks([ pubKey.toBuffer(), opcodes.OP_CHECKSIG @@ -90,7 +90,7 @@ function createPubKeyScriptPubKey(pubKey) { } // OP_DUP OP_HASH160 {pubKeyHash} OP_EQUALVERIFY OP_CHECKSIG -function createPubKeyHashScriptPubKey(hash) { +function pubKeyHashOutput(hash) { assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) return Script.fromChunks([ @@ -103,7 +103,7 @@ function createPubKeyHashScriptPubKey(hash) { } // OP_HASH160 {scriptHash} OP_EQUAL -function createP2SHScriptPubKey(hash) { +function scriptHashOutput(hash) { assert(Buffer.isBuffer(hash), 'Expected Buffer, got ' + hash) return Script.fromChunks([ @@ -114,7 +114,7 @@ function createP2SHScriptPubKey(hash) { } // m [pubKeys ...] n OP_CHECKMULTISIG -function createMultisigScriptPubKey(m, pubKeys) { +function multisigOutput(m, pubKeys) { assert(Array.isArray(pubKeys), 'Expected Array, got ' + pubKeys) assert(pubKeys.length >= m, 'Not enough pubKeys provided') @@ -132,21 +132,21 @@ function createMultisigScriptPubKey(m, pubKeys) { } // {signature} -function createPubKeyScriptSig(signature) { +function pubKeyInput(signature) { assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) return Script.fromChunks(signature) } // {signature} {pubKey} -function createPubKeyHashScriptSig(signature, pubKey) { +function pubKeyHashInput(signature, pubKey) { assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) return Script.fromChunks([signature, pubKey.toBuffer()]) } // {serialized scriptPubKey script} -function createP2SHScriptSig(scriptSig, scriptPubKey) { +function scriptHashInput(scriptSig, scriptPubKey) { return Script.fromChunks([].concat( scriptSig.chunks, scriptPubKey.toBuffer() @@ -154,7 +154,7 @@ function createP2SHScriptSig(scriptSig, scriptPubKey) { } // OP_0 [signatures ...] -function createMultisigScriptSig(signatures, scriptPubKey) { +function multisigInput(signatures, scriptPubKey) { if (scriptPubKey) { assert(isMultisig.call(scriptPubKey)) @@ -167,14 +167,14 @@ function createMultisigScriptSig(signatures, scriptPubKey) { } module.exports = { - classifyScriptPubKey: classifyScriptPubKey, - classifyScriptSig: classifyScriptSig, - createMultisigScriptPubKey: createMultisigScriptPubKey, - createMultisigScriptSig: createMultisigScriptSig, - createP2SHScriptPubKey: createP2SHScriptPubKey, - createP2SHScriptSig: createP2SHScriptSig, - createPubKeyHashScriptPubKey: createPubKeyHashScriptPubKey, - createPubKeyHashScriptSig: createPubKeyHashScriptSig, - createPubKeyScriptPubKey: createPubKeyScriptPubKey, - createPubKeyScriptSig: createPubKeyScriptSig + classifyInput: classifyInput, + classifyOutput: classifyOutput, + multisigInput: multisigInput, + multisigOutput: multisigOutput, + pubKeyHashInput: pubKeyHashInput, + pubKeyHashOutput: pubKeyHashOutput, + pubKeyInput: pubKeyInput, + pubKeyOutput: pubKeyOutput, + scriptHashInput: scriptHashInput, + scriptHashOutput: scriptHashOutput } diff --git a/src/transaction.js b/src/transaction.js index d0de5c5..6b9cfda 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -5,7 +5,7 @@ var bufferutils = require('./bufferutils') var crypto = require('./crypto') var ecdsa = require('./ecdsa') var opcodes = require('./opcodes') -var templates = require('./templates') +var scripts = require('./scripts') var Address = require('./address') var ECKey = require('./eckey') @@ -346,7 +346,7 @@ Transaction.prototype.sign = function(index, key, type) { var signature = this.signScriptSig(index, script, key, type) // FIXME: Assumed prior TX was pay-to-pubkey-hash - var scriptSig = templates.createPubKeyHashScriptSig(signature, key.pub) + var scriptSig = scripts.pubKeyHashInput(signature, key.pub) this.setScriptSig(index, scriptSig) } diff --git a/test/integration/p2sh.js b/test/integration/p2sh.js index dbc8fe8..4639eb7 100644 --- a/test/integration/p2sh.js +++ b/test/integration/p2sh.js @@ -3,6 +3,7 @@ var assert = require('assert') var bitcoin = require('../../') var crypto = bitcoin.crypto var networks = bitcoin.networks +var scripts = bitcoin.scripts var Address = bitcoin.Address var ECKey = bitcoin.ECKey @@ -28,8 +29,8 @@ describe('Bitcoin-js', function() { var outputAmount = 1e4 var pubKeys = privKeys.map(function(eck) { return eck.pub }) - var redeemScript = Script.createMultisigScriptPubKey(2, pubKeys) - var scriptPubKey = Script.createP2SHScriptPubKey(redeemScript.getHash()) + var redeemScript = scripts.multisigOutput(2, pubKeys) + var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) var multisigAddress = Address.fromScriptPubKey(scriptPubKey, networks.testnet).toString() @@ -57,8 +58,8 @@ describe('Bitcoin-js', function() { return tx.signScriptSig(0, redeemScript, privKey) }) - var redeemScriptSig = Script.createMultisigScriptSig(signatures) - var scriptSig = Script.createP2SHScriptSig(redeemScriptSig, redeemScript) + var redeemScriptSig = scripts.multisigInput(signatures) + var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) tx.setScriptSig(0, scriptSig) // broadcast our transaction diff --git a/test/templates.js b/test/scripts.js similarity index 79% rename from test/templates.js rename to test/scripts.js index b969e53..36583c0 100644 --- a/test/templates.js +++ b/test/scripts.js @@ -1,7 +1,7 @@ var assert = require('assert') var crypto = require('../src/crypto') var networks = require('../src/networks') -var templates = require('../src/templates') +var scripts = require('../src/scripts') var Address = require('../src/address') var ECPubKey = require('../src/ecpubkey') @@ -12,27 +12,27 @@ var fixtures = require('./fixtures/script.json') function b2h(b) { return new Buffer(b).toString('hex') } function h2b(h) { return new Buffer(h, 'hex') } -describe('Templates', function() { - describe('classifyScriptSig', function() { +describe('Scripts', function() { + describe('classifyInput', function() { fixtures.valid.forEach(function(f) { if (f.scriptPubKey) return it('supports ' + f.type, function() { var script = Script.fromHex(f.hex) - var type = templates.classifyScriptSig(script) + var type = scripts.classifyInput(script) assert.equal(type, f.type) }) }) }) - describe('classifyScriptPubKey', function() { + describe('classifyOutput', function() { fixtures.valid.forEach(function(f) { if (!f.scriptPubKey) return it('supports ' + f.type, function() { var script = Script.fromHex(f.hex) - var type = templates.classifyScriptPubKey(script) + var type = scripts.classifyOutput(script) assert.equal(type, f.type) }) @@ -44,7 +44,7 @@ describe('Templates', function() { it('matches the test data', function() { var f = fixtures.valid[2] var address = Address.fromBase58Check('19E6FV3m3kEPoJD5Jz6dGKdKwTVvjsWUvu') - var script = templates.createPubKeyHashScriptPubKey(address.hash) + var script = scripts.pubKeyHashOutput(address.hash) assert.equal(script.toHex(), f.hex) }) @@ -55,7 +55,7 @@ describe('Templates', function() { it('matches the test data', function() { var f = fixtures.valid[0] var pubKey = ECPubKey.fromHex(f.pubKey) - var script = templates.createPubKeyScriptPubKey(pubKey) + var script = scripts.pubKeyOutput(pubKey) assert.equal(script.toHex(), f.hex) }) @@ -66,7 +66,7 @@ describe('Templates', function() { it('matches the test data', function() { var f = fixtures.valid[1] var address = Address.fromBase58Check('3NukJ6fYZJ5Kk8bPjycAnruZkE5Q7UW7i8') - var script = templates.createP2SHScriptPubKey(address.hash) + var script = scripts.scriptHashOutput(address.hash) assert.equal(script.toHex(), f.hex) }) @@ -85,7 +85,7 @@ describe('Templates', function() { }) it('should create valid redeemScript', function() { - var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + var redeemScript = scripts.multisigOutput(2, pubKeys) var hash160 = crypto.hash160(new Buffer(redeemScript.buffer)) var multisigAddress = new Address(hash160, networks.bitcoin.scriptHash) @@ -95,7 +95,7 @@ describe('Templates', function() { it('should throw on not enough pubKeys provided', function() { assert.throws(function() { - templates.createMultisigScriptPubKey(4, pubKeys) + scripts.multisigOutput(4, pubKeys) }, /Not enough pubKeys provided/) }) }) @@ -113,19 +113,19 @@ describe('Templates', function() { var expected = '0047304402207515cf147d201f411092e6be5a64a6006f9308fad7b2a8fdaab22cd86ce764c202200974b8aca7bf51dbf54150d3884e1ae04f675637b926ec33bf75939446f6ca2801483045022100ef253c1faa39e65115872519e5f0a33bbecf430c0f35cf562beabbad4da24d8d02201742be8ee49812a73adea3007c9641ce6725c32cd44ddb8e3a3af460015d14050147522102359c6e3f04cefbf089cf1d6670dc47c3fb4df68e2bad1fa5a369f9ce4b42bbd1210395a9d84d47d524548f79f435758c01faec5da2b7e551d3b8c995b7e06326ae4a52ae' it('should create a valid P2SH multisig scriptSig', function() { - var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) - var redeemScriptSig = templates.createMultisigScriptSig(signatures) + var redeemScript = scripts.multisigOutput(2, pubKeys) + var redeemInput = scripts.multisigInput(signatures) - var scriptSig = templates.createP2SHScriptSig(redeemScriptSig, redeemScript) + var scriptSig = scripts.scriptHashInput(redeemInput, redeemScript) assert.equal(b2h(scriptSig.buffer), expected) }) it('should throw on not enough signatures', function() { - var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + var redeemScript = scripts.multisigOutput(2, pubKeys) assert.throws(function() { - templates.createMultisigScriptSig(signatures.slice(1), redeemScript) + scripts.multisigInput(signatures.slice(1), redeemScript) }, /Not enough signatures provided/) }) }) diff --git a/test/transaction.js b/test/transaction.js index 3485435..cdfb403 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -1,6 +1,6 @@ var assert = require('assert') var networks = require('../src/networks') -var templates = require('../src/templates') +var scripts = require('../src/scripts') var Address = require('../src/address') var ECKey = require('../src/eckey') @@ -256,14 +256,14 @@ describe('Transaction', function() { return ECKey.fromWIF(wif) }) var pubKeys = privKeys.map(function(eck) { return eck.pub }) - var redeemScript = templates.createMultisigScriptPubKey(2, pubKeys) + var redeemScript = scripts.multisigOutput(2, pubKeys) var signatures = privKeys.map(function(privKey) { return tx.signScriptSig(0, redeemScript, privKey) }) - var redeemScriptSig = templates.createMultisigScriptSig(signatures) - var scriptSig = templates.createP2SHScriptSig(redeemScriptSig, redeemScript) + var redeemScriptSig = scripts.multisigInput(signatures) + var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) tx.setScriptSig(0, scriptSig) signatures.forEach(function(sig, i){ diff --git a/test/wallet.js b/test/wallet.js index 763c19d..436f436 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -2,7 +2,7 @@ var assert = require('assert') var crypto = require('../src/crypto') var networks = require('../src/networks') var sinon = require('sinon') -var templates = require('../src/templates') +var scripts = require('../src/scripts') var Address = require('../src/address') var HDNode = require('../src/hdnode') @@ -268,7 +268,7 @@ describe('Wallet', function() { it('does not fail on scripts with no corresponding Address', function() { var pubKey = wallet.getPrivateKey(0).pub - var script = templates.createPubKeyScriptPubKey(pubKey) + var script = scripts.pubKeyOutput(pubKey) var tx2 = new Transaction() tx2.addInput(fakeTxHash(1), 0) From 8929c51d98f532fc8878a28d16e1de7279de1343 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 10:39:49 +1000 Subject: [PATCH 12/18] Script: remove clone --- src/script.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/script.js b/src/script.js index 95866b9..f0d6757 100644 --- a/src/script.js +++ b/src/script.js @@ -78,10 +78,6 @@ Script.fromHex = function(hex) { Script.EMPTY = Script.fromChunks([]) // Operations -Script.prototype.clone = function() { - return new Script(this.buffer, this.chunks) -} - Script.prototype.getHash = function() { return crypto.hash160(this.buffer) } From 5a96df1ec121feebe7a02132c2847ec54e92c1d1 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 11:09:21 +1000 Subject: [PATCH 13/18] Script: stop jshint complaining about missing-assignment --- src/script.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/script.js b/src/script.js index f0d6757..700b7ff 100644 --- a/src/script.js +++ b/src/script.js @@ -32,7 +32,7 @@ Script.fromBuffer = function(buffer) { } else { chunks.push(opcode) - ++i + i += 1 } } From 7e5af52cd1c3fa121e07f7dad94e87c6d834484a Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 11:30:07 +1000 Subject: [PATCH 14/18] Transaction: mass rename from SPK/SS to Input/Output I think it is important we maintain some reasoning that an Input script is actually a script signature, but in the end, these names are more coherent and understandable when reasoning with our code. So I think its OK we break tradition with bitcoind. --- src/address.js | 4 ++-- src/transaction.js | 30 ++++++++++++++---------------- src/wallet.js | 2 +- test/address.js | 16 ++++++++-------- test/fixtures/address.json | 4 ++-- test/integration/p2sh.js | 6 +++--- test/transaction.js | 16 ++++++++-------- test/wallet.js | 2 +- 8 files changed, 39 insertions(+), 41 deletions(-) diff --git a/src/address.js b/src/address.js index 8990089..f5439e7 100644 --- a/src/address.js +++ b/src/address.js @@ -35,7 +35,7 @@ Address.fromBase58Check = function(string) { return new Address(hash, version) } -Address.fromScriptPubKey = function(script, network) { +Address.fromOutputScript = function(script, network) { network = network || networks.bitcoin var type = scripts.classifyOutput(script) @@ -59,7 +59,7 @@ Address.prototype.toBase58Check = function () { return base58check.encode(payload) } -Address.prototype.toScriptPubKey = function() { +Address.prototype.toOutputScript = function() { var scriptType = findScriptTypeByVersion(this.version) if (scriptType === 'pubkeyhash') { diff --git a/src/transaction.js b/src/transaction.js index 6b9cfda..d9c83a7 100644 --- a/src/transaction.js +++ b/src/transaction.js @@ -110,7 +110,7 @@ Transaction.prototype.addOutput = function (address, value) { this.outs.push(new TransactionOut({ value: value, - script: address.toScriptPubKey(), + script: address.toOutputScript(), address: address // TODO: Remove me })) } @@ -196,13 +196,13 @@ var SIGHASH_ANYONECANPAY = 0x80 * hashType, serializes and finally hashes the result. This hash can then be * used to sign the transaction input in question. */ -Transaction.prototype.hashForSignature = function(scriptPubKey, inIndex, hashType) { +Transaction.prototype.hashForSignature = function(prevOutScript, inIndex, hashType) { assert(inIndex >= 0, 'Invalid vin index') assert(inIndex < this.ins.length, 'Invalid vin index') - assert(scriptPubKey instanceof Script, 'Invalid Script object') + assert(prevOutScript instanceof Script, 'Invalid Script object') var txTmp = this.clone() - var hashScript = scriptPubKey.without(opcodes.OP_CODESEPARATOR) + var hashScript = prevOutScript.without(opcodes.OP_CODESEPARATOR) // Blank out other inputs' signatures txTmp.ins.forEach(function(txin) { @@ -239,8 +239,7 @@ Transaction.prototype.getHash = function () { return buffer.toString('hex') } -Transaction.prototype.clone = function () -{ +Transaction.prototype.clone = function () { var newTx = new Transaction() newTx.version = this.version newTx.locktime = this.locktime @@ -337,24 +336,22 @@ Transaction.fromHex = function(hex) { } /** - * Signs a standard output at some index with the given key + * Signs a pubKeyHash output at some index with the given key */ Transaction.prototype.sign = function(index, key, type) { - assert(key instanceof ECKey) - - var script = key.pub.getAddress().toScriptPubKey() - var signature = this.signScriptSig(index, script, key, type) + var prevOutScript = key.pub.getAddress().toOutputScript() + var signature = this.signInput(index, prevOutScript, key, type) // FIXME: Assumed prior TX was pay-to-pubkey-hash var scriptSig = scripts.pubKeyHashInput(signature, key.pub) - this.setScriptSig(index, scriptSig) + this.setInputScript(index, scriptSig) } -Transaction.prototype.signScriptSig = function(index, scriptPubKey, key, type) { +Transaction.prototype.signInput = function(index, prevOutScript, key, type) { type = type || SIGHASH_ALL assert(key instanceof ECKey, 'Invalid private key') - var hash = this.hashForSignature(scriptPubKey, index, type) + var hash = this.hashForSignature(prevOutScript, index, type) var signature = key.sign(hash) var DERencoded = ecdsa.serializeSig(signature) @@ -364,11 +361,12 @@ Transaction.prototype.signScriptSig = function(index, scriptPubKey, key, type) { ]) } -Transaction.prototype.setScriptSig = function(index, script) { +Transaction.prototype.setInputScript = function(index, script) { this.ins[index].script = script } -Transaction.prototype.validateSig = function(index, script, pub, DERsig) { +// FIXME: should probably be validateInput(index, pub) +Transaction.prototype.validateInput = function(index, script, pub, DERsig) { var type = DERsig.readUInt8(DERsig.length - 1) DERsig = DERsig.slice(0, -1) diff --git a/src/wallet.js b/src/wallet.js index b144206..445bbf9 100644 --- a/src/wallet.js +++ b/src/wallet.js @@ -143,7 +143,7 @@ function Wallet(seed, network) { var address try { - address = Address.fromScriptPubKey(txOut.script, network).toString() + address = Address.fromOutputScript(txOut.script, network).toString() } catch(e) { if (!(e.message.match(/has no matching Address/))) throw e } diff --git a/test/address.js b/test/address.js index fb066fe..50ff895 100644 --- a/test/address.js +++ b/test/address.js @@ -38,23 +38,23 @@ describe('Address', function() { }) }) - describe('fromScriptPubKey', function() { + describe('fromOutputScript', function() { fixtures.valid.forEach(function(f) { it('imports ' + f.description + '(' + f.network + ') correctly', function() { var script = Script.fromHex(f.script) - var addr = Address.fromScriptPubKey(script, networks[f.network]) + var addr = Address.fromOutputScript(script, networks[f.network]) assert.equal(addr.version, f.version) assert.equal(addr.hash.toString('hex'), f.hex) }) }) - fixtures.invalid.fromScriptPubKey.forEach(function(f) { + fixtures.invalid.fromOutputScript.forEach(function(f) { it('throws when ' + f.description, function() { var script = Script.fromHex(f.hex) assert.throws(function() { - Address.fromScriptPubKey(script) + Address.fromOutputScript(script) }, new RegExp(f.description)) }) }) @@ -71,22 +71,22 @@ describe('Address', function() { }) }) - describe('toScriptPubKey', function() { + describe('toOutputScript', function() { fixtures.valid.forEach(function(f) { it('imports ' + f.description + '(' + f.network + ') correctly', function() { var addr = Address.fromBase58Check(f.base58check) - var script = addr.toScriptPubKey() + var script = addr.toOutputScript() assert.equal(script.toHex(), f.script) }) }) - fixtures.invalid.toScriptPubKey.forEach(function(f) { + fixtures.invalid.toOutputScript.forEach(function(f) { it('throws when ' + f.description, function() { var addr = new Address(new Buffer(f.hex, 'hex'), f.version) assert.throws(function() { - addr.toScriptPubKey() + addr.toOutputScript() }, new RegExp(f.description)) }) }) diff --git a/test/fixtures/address.json b/test/fixtures/address.json index 83b6a3a..cedd17d 100644 --- a/test/fixtures/address.json +++ b/test/fixtures/address.json @@ -46,7 +46,7 @@ "exception": "Invalid hash length" } ], - "fromScriptPubKey": [ + "fromOutputScript": [ { "description": "pubkey has no matching Address", "hex": "21031f1e68f82112b373f0fe980b3a89d212d2b5c01fb51eb25acb8b4c4b4299ce95ac" @@ -60,7 +60,7 @@ "hex": "6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474" } ], - "toScriptPubKey": [ + "toOutputScript": [ { "description": "24kPZCmVgzfkpGdXExy56234MRHrsqQxNWE has no matching Script", "hex": "751e76e8199196d454941c45d1b3a323f1433bd6", diff --git a/test/integration/p2sh.js b/test/integration/p2sh.js index 4639eb7..ff7946c 100644 --- a/test/integration/p2sh.js +++ b/test/integration/p2sh.js @@ -32,7 +32,7 @@ describe('Bitcoin-js', function() { var redeemScript = scripts.multisigOutput(2, pubKeys) var scriptPubKey = scripts.scriptHashOutput(redeemScript.getHash()) - var multisigAddress = Address.fromScriptPubKey(scriptPubKey, networks.testnet).toString() + var multisigAddress = Address.fromOutputScript(scriptPubKey, networks.testnet).toString() // Attempt to send funds to the source address, providing some unspents for later helloblock.faucet.withdraw(multisigAddress, coldAmount, function(err) { @@ -55,12 +55,12 @@ describe('Bitcoin-js', function() { tx.addOutput(targetAddress, spendAmount) var signatures = privKeys.map(function(privKey) { - return tx.signScriptSig(0, redeemScript, privKey) + return tx.signInput(0, redeemScript, privKey) }) var redeemScriptSig = scripts.multisigInput(signatures) var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - tx.setScriptSig(0, scriptSig) + tx.setInputScript(0, scriptSig) // broadcast our transaction helloblock.transactions.propagate(tx.toHex(), function(err, resp, resource) { diff --git a/test/transaction.js b/test/transaction.js index cdfb403..e9bbec6 100644 --- a/test/transaction.js +++ b/test/transaction.js @@ -71,7 +71,7 @@ describe('Transaction', function() { var output = tx.outs[0] assert.equal(output.value, 5000000000) - assert.deepEqual(output.script, Address.fromBase58Check('n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9').toScriptPubKey()) + assert.deepEqual(output.script, Address.fromBase58Check('n1gqLjZbRH1biT5o4qiVMiNig8wcCPQeB9').toOutputScript()) }) it('assigns hash to deserialized object', function(){ @@ -200,11 +200,11 @@ describe('Transaction', function() { var script = prevTx.outs[0].script var sig = new Buffer(tx.ins[0].script.chunks[0]) - assert.equal(tx.validateSig(0, script, key.pub, sig), true) + assert.equal(tx.validateInput(0, script, key.pub, sig), true) }) }) - describe('validateSig', function(){ + describe('validateInput', function(){ var validTx beforeEach(function() { @@ -216,7 +216,7 @@ describe('Transaction', function() { var script = prevTx.outs[0].script var sig = new Buffer(validTx.ins[0].script.chunks[0]) - assert.equal(validTx.validateSig(0, script, key.pub, sig), true) + assert.equal(validTx.validateInput(0, script, key.pub, sig), true) }) }) @@ -243,7 +243,7 @@ describe('Transaction', function() { }) }) - describe('signScriptSig', function() { + describe('signInput', function() { it('works for multi-sig redeem script', function() { var tx = new Transaction() tx.addInput('d6f72aab8ff86ff6289842a0424319bf2ddba85dc7c52757912297f948286389', 0) @@ -259,15 +259,15 @@ describe('Transaction', function() { var redeemScript = scripts.multisigOutput(2, pubKeys) var signatures = privKeys.map(function(privKey) { - return tx.signScriptSig(0, redeemScript, privKey) + return tx.signInput(0, redeemScript, privKey) }) var redeemScriptSig = scripts.multisigInput(signatures) var scriptSig = scripts.scriptHashInput(redeemScriptSig, redeemScript) - tx.setScriptSig(0, scriptSig) + tx.setInputScript(0, scriptSig) signatures.forEach(function(sig, i){ - assert(tx.validateSig(0, redeemScript, privKeys[i].pub, sig)) + assert(tx.validateInput(0, redeemScript, privKeys[i].pub, sig)) }) var expected = '010000000189632848f99722915727c5c75da8db2dbf194342a0429828f66ff88fab2af7d600000000fd1b0100483045022100e5be20d440b2bbbc886161f9095fa6d0bca749a4e41d30064f30eb97adc7a1f5022061af132890d8e4e90fedff5e9365aeeb77021afd8ef1d5c114d575512e9a130a0147304402205054e38e9d7b5c10481b6b4991fde5704cd94d49e344406e3c2ce4d18a43bf8e022051d7ba8479865b53a48bee0cce86e89a25633af5b2918aa276859489e232f51c014c8752410479be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b84104c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee51ae168fea63dc339a3c58419466ceaeef7f632653266d0e1236431a950cfe52a52aeffffffff0101000000000000001976a914751e76e8199196d454941c45d1b3a323f1433bd688ac00000000' diff --git a/test/wallet.js b/test/wallet.js index 436f436..9448e5d 100644 --- a/test/wallet.js +++ b/test/wallet.js @@ -313,7 +313,7 @@ describe('Wallet', function() { assert.equal(output.receive, key) assert.equal(output.value, txOut.value) - var txOutAddress = Address.fromScriptPubKey(txOut.script).toString() + var txOutAddress = Address.fromOutputScript(txOut.script).toString() assert.equal(output.address, txOutAddress) } }) From 9d5d18b2d8eff659b084549927f692b91474160f Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Fri, 13 Jun 2014 16:24:55 +1000 Subject: [PATCH 15/18] Scripts: adds assertion for enforcing a Script input --- src/scripts.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/scripts.js b/src/scripts.js index b561819..fca4ba7 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -3,6 +3,8 @@ var opcodes = require('./opcodes') var Script = require('./script') function classifyOutput(script) { + assert(script instanceof Script, 'Expected Script, got ', script) + if (isPubkeyhash.call(script)) { return 'pubkeyhash' } else if (isPubkey.call(script)) { @@ -19,6 +21,8 @@ function classifyOutput(script) { } function classifyInput(script) { + assert(script instanceof Script, 'Expected Script, got ', script) + if (script.chunks.length == 1 && Buffer.isBuffer(script.chunks[0])) { return 'pubkey' } else if (script.chunks.length == 2 && Buffer.isBuffer(script.chunks[0]) && Buffer.isBuffer(script.chunks[1])) { From b68b1d5da4724bb0525e6138f2ed93e0ae0f011e Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 14 Jun 2014 00:30:01 +1000 Subject: [PATCH 16/18] Script: fixes pubKeyInput and adds a test --- src/scripts.js | 2 +- test/fixtures/script.json | 9 +++++++++ test/scripts.js | 22 +++++++++++++++++----- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/scripts.js b/src/scripts.js index fca4ba7..fc5d1e1 100644 --- a/src/scripts.js +++ b/src/scripts.js @@ -139,7 +139,7 @@ function multisigOutput(m, pubKeys) { function pubKeyInput(signature) { assert(Buffer.isBuffer(signature), 'Expected Buffer, got ' + signature) - return Script.fromChunks(signature) + return Script.fromChunks([signature]) } // {signature} {pubKey} diff --git a/test/fixtures/script.json b/test/fixtures/script.json index bb42dbe..edabed8 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -33,6 +33,15 @@ "asm": "72 304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301 65 040cd2d2ce17a1e9b2b3b2cb294d40eecf305a25b7e7bfdafae6bb2639f4ee399b3637706c3d377ec4ab781355add443ae864b134c5e523001c442186ea60f0eb8", "scriptPubKey": false }, + { + "description": "pubKey scriptSig", + "hex": "48304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", + "type": "pubkey", + "hash": "44d9982c3e79452e02ef5816976a0e20a0ec1cba", + "signature": "304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", + "asm": "72 304502206becda98cecf7a545d1a640221438ff8912d9b505ede67e0138485111099f696022100ccd616072501310acba10feb97cecc918e21c8e92760cd35144efec7622938f301", + "scriptPubKey": false + }, { "description": "Valid multisig script", "hex": "5121032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca330162102308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a52ae", diff --git a/test/scripts.js b/test/scripts.js index 36583c0..c100e76 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -52,12 +52,24 @@ describe('Scripts', function() { // FIXME: bad describe('pay-to-pubkey', function() { - it('matches the test data', function() { - var f = fixtures.valid[0] - var pubKey = ECPubKey.fromHex(f.pubKey) - var script = scripts.pubKeyOutput(pubKey) + describe('input', function() { + it('matches the test data', function() { + var f = fixtures.valid[4] + var signature = new Buffer(f.signature, 'hex') + var script = scripts.pubKeyInput(signature) - assert.equal(script.toHex(), f.hex) + assert.equal(script.toHex(), f.hex) + }) + }) + + describe('output', function() { + it('matches the test data', function() { + var f = fixtures.valid[0] + var pubKey = ECPubKey.fromHex(f.pubKey) + var script = scripts.pubKeyOutput(pubKey) + + assert.equal(script.toHex(), f.hex) + }) }) }) From 553ade12080717ee9f6489a81640e181889a91a8 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 14 Jun 2014 00:30:13 +1000 Subject: [PATCH 17/18] network: always use lowercase --- src/address.js | 6 +++--- src/ecpubkey.js | 2 +- src/message.js | 2 +- src/networks.js | 24 ++++++++++++------------ test/bitcoin.core.js | 12 ++++++------ test/ecpubkey.js | 2 +- test/hdnode.js | 2 +- test/scripts.js | 2 +- 8 files changed, 26 insertions(+), 26 deletions(-) diff --git a/src/address.js b/src/address.js index f5439e7..b1e6aeb 100644 --- a/src/address.js +++ b/src/address.js @@ -11,7 +11,7 @@ function findScriptTypeByVersion(queryVersion) { var version = network[versionName] if (version === queryVersion) { - return versionName.toLowerCase() + return versionName } } } @@ -41,10 +41,10 @@ Address.fromOutputScript = function(script, network) { var type = scripts.classifyOutput(script) if (type === 'pubkeyhash') { - return new Address(script.chunks[2], network.pubKeyHash) + return new Address(script.chunks[2], network.pubkeyhash) } else if (type === 'scripthash') { - return new Address(script.chunks[1], network.scriptHash) + return new Address(script.chunks[1], network.scripthash) } assert(false, type + ' has no matching Address') diff --git a/src/ecpubkey.js b/src/ecpubkey.js index 3716729..61a6666 100644 --- a/src/ecpubkey.js +++ b/src/ecpubkey.js @@ -32,7 +32,7 @@ ECPubKey.fromHex = function(hex) { ECPubKey.prototype.getAddress = function(network) { network = network || networks.bitcoin - return new Address(crypto.hash160(this.toBuffer()), network.pubKeyHash) + return new Address(crypto.hash160(this.toBuffer()), network.pubkeyhash) } ECPubKey.prototype.verify = function(hash, signature) { diff --git a/src/message.js b/src/message.js index 02b0b71..a70b9a2 100644 --- a/src/message.js +++ b/src/message.js @@ -13,7 +13,7 @@ var ecurve = require('ecurve') var ecparams = ecurve.getCurveByName('secp256k1') function magicHash(message, network) { - var magicPrefix = new Buffer(network.magicPrefix) + var magicPrefix = new Buffer(network.magicprefix) var messageBuffer = new Buffer(message) var lengthBuffer = new Buffer(bufferutils.varIntSize(messageBuffer.length)) bufferutils.writeVarInt(lengthBuffer, messageBuffer.length, 0) diff --git a/src/networks.js b/src/networks.js index da71d23..2b85818 100644 --- a/src/networks.js +++ b/src/networks.js @@ -2,43 +2,43 @@ // Dogecoin BIP32 is a proposed standard: https://bitcointalk.org/index.php?topic=409731 module.exports = { bitcoin: { - magicPrefix: '\x18Bitcoin Signed Message:\n', + magicprefix: '\x18Bitcoin Signed Message:\n', bip32: { public: 0x0488b21e, private: 0x0488ade4 }, - pubKeyHash: 0x00, - scriptHash: 0x05, + pubkeyhash: 0x00, + scripthash: 0x05, wif: 0x80 }, dogecoin: { - magicPrefix: '\x19Dogecoin Signed Message:\n', + magicprefix: '\x19Dogecoin Signed Message:\n', bip32: { public: 0x02facafd, private: 0x02fac398 }, - pubKeyHash: 0x1e, - scriptHash: 0x16, + pubkeyhash: 0x1e, + scripthash: 0x16, wif: 0x9e }, litecoin: { - magicPrefix: '\x19Litecoin Signed Message:\n', + magicprefix: '\x19Litecoin Signed Message:\n', bip32: { public: 0x019da462, private: 0x019d9cfe }, - pubKeyHash: 0x30, - scriptHash: 0x05, + pubkeyhash: 0x30, + scripthash: 0x05, wif: 0xb0 }, testnet: { - magicPrefix: '\x18Bitcoin Signed Message:\n', + magicprefix: '\x18Bitcoin Signed Message:\n', bip32: { public: 0x043587cf, private: 0x04358394 }, - pubKeyHash: 0x6f, - scriptHash: 0xc4, + pubkeyhash: 0x6f, + scripthash: 0xc4, wif: 0xef } } diff --git a/test/bitcoin.core.js b/test/bitcoin.core.js index b246f64..4cd8238 100644 --- a/test/bitcoin.core.js +++ b/test/bitcoin.core.js @@ -56,10 +56,10 @@ describe('Bitcoin-core', function() { assert.equal(address.hash.toString('hex'), hex) if (params.addrType === 'pubkey') { - assert.equal(address.version, network.pubKeyHash) + assert.equal(address.version, network.pubkeyhash) } else if (params.addrType === 'script') { - assert.equal(address.version, network.scriptHash) + assert.equal(address.version, network.scripthash) } }) }) @@ -68,10 +68,10 @@ describe('Bitcoin-core', function() { // base58_keys_invalid describe('Address', function() { var allowedNetworks = [ - networks.bitcoin.pubKeyHash, - networks.bitcoin.scriptHash, - networks.testnet.pubKeyHash, - networks.testnet.scriptHash + networks.bitcoin.pubkeyhash, + networks.bitcoin.scripthash, + networks.testnet.pubkeyhash, + networks.testnet.scripthash ] base58_keys_invalid.forEach(function(f) { diff --git a/test/ecpubkey.js b/test/ecpubkey.js index 62609ef..e6dcbbd 100644 --- a/test/ecpubkey.js +++ b/test/ecpubkey.js @@ -72,7 +72,7 @@ describe('ECPubKey', function() { var pubKey = new ECPubKey(Q) var address = pubKey.getAddress(networks.testnet) - assert.equal(address.version, networks.testnet.pubKeyHash) + assert.equal(address.version, networks.testnet.pubkeyhash) assert.equal(address.hash.toString('hex'), fixtures.compressed.hash160) }) }) diff --git a/test/hdnode.js b/test/hdnode.js index 44ef320..fb9a29f 100644 --- a/test/hdnode.js +++ b/test/hdnode.js @@ -204,7 +204,7 @@ describe('HDNode', function() { var hd = HDNode.fromBase58(f.master.base58) hd.network = networks.testnet - assert.equal(hd.getAddress().version, networks.testnet.pubKeyHash) + assert.equal(hd.getAddress().version, networks.testnet.pubkeyhash) }) }) diff --git a/test/scripts.js b/test/scripts.js index c100e76..19222e5 100644 --- a/test/scripts.js +++ b/test/scripts.js @@ -100,7 +100,7 @@ describe('Scripts', function() { var redeemScript = scripts.multisigOutput(2, pubKeys) var hash160 = crypto.hash160(new Buffer(redeemScript.buffer)) - var multisigAddress = new Address(hash160, networks.bitcoin.scriptHash) + var multisigAddress = new Address(hash160, networks.bitcoin.scripthash) assert.equal(multisigAddress.toString(), '32vYjxBb7pHJJyXgNk8UoK3BdRDxBzny2v') }) From 38a2475bf518ccbdc05deec867b843713730c5d2 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Sat, 14 Jun 2014 01:00:31 +1000 Subject: [PATCH 18/18] scripts: adds example multisig inputScript --- test/fixtures/script.json | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/test/fixtures/script.json b/test/fixtures/script.json index edabed8..a393444 100644 --- a/test/fixtures/script.json +++ b/test/fixtures/script.json @@ -50,6 +50,14 @@ "asm": "OP_TRUE 33 032487c2a32f7c8d57d2a93906a6457afd00697925b0e6e145d89af6d3bca33016 33 02308673d16987eaa010e540901cc6fe3695e758c19f46ce604e174dac315e685a OP_2 OP_CHECKMULTISIG", "scriptPubKey": true }, + { + "description": "mutisig scriptSig", + "hex": "0047304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101483045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", + "type": "multisig", + "hash": "b1ef3ae2c77b356eff81049aad7dfd2eeb34c6f5", + "asm": "00 47 304402202b29881db1b4cc128442d955e906d41c77365ed9a8392b584be12d980b236459022009da4bc60d09280aa26f4f981bfbed94eb7263d92920961e48a7f3f0991895b101 48 3045022100871708a7597c1dbebff2a5527a56a1f2b49d73e35cd825a07285f5f29f5766d8022003bd7ac25334e9a6d6020cc8ba1be67a8c70dca8e7063ea0547d79c45b9bc12601", + "scriptPubKey": false + }, { "description": "OP_RETURN script", "hex":"6a2606deadbeef03f895a2ad89fb6d696497af486cb7c644a27aa568c7a18dd06113401115185474",