diff --git a/lib/errors/spec.js b/lib/errors/spec.js index 8413fd5..3815b59 100644 --- a/lib/errors/spec.js +++ b/lib/errors/spec.js @@ -106,6 +106,9 @@ module.exports = [{ }, { name: 'CantDeriveAddress', message: 'Can\'t derive address associated with script {0}, needs to be p2pkh in, p2pkh out, p2sh in, or p2sh out.' + }, { + name: 'InvalidBuffer', + message: 'Invalid script buffer: can\'t parse valid script from given buffer {0}' }] }, { name: 'HDPrivateKey', diff --git a/lib/script/script.js b/lib/script/script.js index af5ce61..06401b4 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -57,44 +57,51 @@ Script.fromBuffer = function(buffer) { var br = new BufferReader(buffer); while (!br.finished()) { - var opcodenum = br.readUInt8(); + try { + var opcodenum = br.readUInt8(); - var len, buf; - if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) { - len = opcodenum; - script.chunks.push({ - buf: br.read(len), - len: len, - opcodenum: opcodenum - }); - } else if (opcodenum === Opcode.OP_PUSHDATA1) { - len = br.readUInt8(); - buf = br.read(len); - script.chunks.push({ - buf: buf, - len: len, - opcodenum: opcodenum - }); - } else if (opcodenum === Opcode.OP_PUSHDATA2) { - len = br.readUInt16LE(); - buf = br.read(len); - script.chunks.push({ - buf: buf, - len: len, - opcodenum: opcodenum - }); - } else if (opcodenum === Opcode.OP_PUSHDATA4) { - len = br.readUInt32LE(); - buf = br.read(len); - script.chunks.push({ - buf: buf, - len: len, - opcodenum: opcodenum - }); - } else { - script.chunks.push({ - opcodenum: opcodenum - }); + var len, buf; + if (opcodenum > 0 && opcodenum < Opcode.OP_PUSHDATA1) { + len = opcodenum; + script.chunks.push({ + buf: br.read(len), + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.OP_PUSHDATA1) { + len = br.readUInt8(); + buf = br.read(len); + script.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.OP_PUSHDATA2) { + len = br.readUInt16LE(); + buf = br.read(len); + script.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + } else if (opcodenum === Opcode.OP_PUSHDATA4) { + len = br.readUInt32LE(); + buf = br.read(len); + script.chunks.push({ + buf: buf, + len: len, + opcodenum: opcodenum + }); + } else { + script.chunks.push({ + opcodenum: opcodenum + }); + } + } catch (e) { + if (e instanceof RangeError) { + throw new errors.Script.InvalidBuffer(buffer); + } + throw e; } } @@ -291,7 +298,16 @@ Script.prototype.isScriptHashIn = function() { if (!redeemBuf) { return false; } - var redeemScript = new Script(redeemBuf); + + var redeemScript; + try { + redeemScript = Script.fromBuffer(redeemBuf); + } catch (e) { + if (e instanceof errors.Script.InvalidBuffer) { + return false; + } + throw e; + } var type = redeemScript.classify(); return type !== Script.types.UNKNOWN; }; diff --git a/test/script/script.js b/test/script/script.js index 64416dd..6de5476 100644 --- a/test/script/script.js +++ b/test/script/script.js @@ -308,6 +308,16 @@ describe('Script', function() { it('should identify this known non-scripthashin', function() { Script('20 0000000000000000000000000000000000000000 OP_CHECKSIG').isScriptHashIn().should.equal(false); }); + + it('should identify this problematic non-scripthashin scripts', function() { + var s = new Script('71 0x3044022017053dad84aa06213749df50a03330cfd24d6' + + 'b8e7ddbb6de66c03697b78a752a022053bc0faca8b4049fb3944a05fcf7c93b2861' + + '734d39a89b73108f605f70f5ed3401 33 0x0225386e988b84248dc9c30f784b06e' + + '02fdec57bbdbd443768eb5744a75ce44a4c'); + var s2 = new Script('OP_RETURN 32 0x19fdb20634911b6459e6086658b3a6ad2dc6576bd6826c73ee86a5f9aec14ed9'); + s.isScriptHashIn().should.equal(false); + s2.isScriptHashIn().should.equal(false); + }); }); describe('#isScripthashOut', function() { @@ -670,25 +680,28 @@ describe('Script', function() { Script().toAddress(Networks.livenet).should.equal(false); }); - // taken from txid 7e519caca256423320b92e3e17be5701f87afecbdb3f53af598032bfd8d164f5 it('works for p2pkh output', function() { + // taken from tx 7e519caca256423320b92e3e17be5701f87afecbdb3f53af598032bfd8d164f5 var script = new Script('OP_DUP OP_HASH160 20 ' + '0xc8e11b0eb0d2ad5362d894f048908341fa61b6e1 OP_EQUALVERIFY OP_CHECKSIG'); script.toAddress().toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc'); }); it('works for p2pkh input', function() { + // taken from tx 7e519caca256423320b92e3e17be5701f87afecbdb3f53af598032bfd8d164f5 var script = new Script('72 0x3045022100eff96230ca0f55b1e8c7a63e014f48611ff1af40875ecd33dee9062d7a6f5e2002206320405b5f6992c756e03e66b21a05a812b60996464ac6af815c2638b930dd7a01 65 0x04150defa035a2c7d826d7d5fc8ab2154bd1bb832f1a5c8ecb338f436362ad232e428b57db44677c5a8bd42c5ed9e2d7e04e742c59bee1b40080cfd57dec64b23a'); script.toAddress().toString().should.equal('1KK9oz4bFH8c1t6LmighHaoSEGx3P3FEmc'); + // taken from tx 7f8f95752a59d715dae9e0008a42e7968d2736741591bbfc6685f6e1649c21ed var s2 = new Script('71 0x3044022017053dad84aa06213749df50a03330cfd24d6b8e7ddbb6de66c03697b78a752a022053bc0faca8b4049fb3944a05fcf7c93b2861734d39a89b73108f605f70f5ed3401 33 0x0225386e988b84248dc9c30f784b06e02fdec57bbdbd443768eb5744a75ce44a4c'); s2.toAddress().toString().should.equal('17VArX6GRE6i6MVscBUZoXwi6NhnHa68B7'); }); - // taken from txid fe1f764299dc7f3b5a8fae912050df2b633bf99554c68bf1c456edb9c2b63585 it('works for p2sh output', function() { + // taken from tx fe1f764299dc7f3b5a8fae912050df2b633bf99554c68bf1c456edb9c2b63585 var script = new Script('OP_HASH160 20 0x99d29051af0c29adcb9040034752bba7dde33e35 OP_EQUAL'); script.toAddress().toString().should.equal('3FiMZ7stbfH2WG5JQ7CiuzrFo7CEnGUcAP'); }); it('works for p2sh input', function() { + // taken from tx fe1f764299dc7f3b5a8fae912050df2b633bf99554c68bf1c456edb9c2b63585 var script = new Script('OP_FALSE 72 0x3045022100e824fbe979fac5834d0062dd5a4e82a898e00ac454bd254cd708ad28530816f202206251ff0fa4dd70c0524c690d4e4deb2bd167297e7bbdf6743b4a8050d681555001 37 0x512102ff3ae0aaa4679ea156d5581dbe6695cc0c311df0aa42af76670d0debbd8f672951ae'); script.toAddress().toString().should.equal('3GYicPxCvsKvbJmZNBBeWkC3cLuGFhtrQi'); });