From 00cec9ce649ba5939fe3ee47c2f8ef44bf227e14 Mon Sep 17 00:00:00 2001 From: Daniel Cousens Date: Thu, 12 Jun 2014 21:14:22 +1000 Subject: [PATCH] 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)