From e5e783fbf9ab286552760c22bb0e0ea1c35e307e Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Thu, 2 Jul 2015 19:26:39 -0400 Subject: [PATCH 1/5] Optimized performance of script.isPublicKeyHashIn() --- benchmark/script.js | 79 ++++++++++++++++++++++++++++++++++++++++++++ lib/script/script.js | 32 ++++++++++++++---- 2 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 benchmark/script.js diff --git a/benchmark/script.js b/benchmark/script.js new file mode 100644 index 0000000..76a6320 --- /dev/null +++ b/benchmark/script.js @@ -0,0 +1,79 @@ +'use strict'; + +var benchmark = require('benchmark'); +var bitcore = require('..'); +var async = require('async'); +var blockData = require('./block-357238.json'); + +var maxTime = 10; + +console.log('Benchmarking Script'); +console.log('---------------------------------------'); + +async.series([ + function(next) { + + var c = 0; + var scripts = []; + var block = bitcore.Block.fromString(blockData); + for (var i = 0; i < block.transactions.length; i++) { + var tx = block.transactions[i]; + for (var j = 0; j < tx.inputs.length; j++) { + var input = tx.inputs[j]; + if (input.script) { + scripts.push(input.script); + } + } + } + + function isPublicKeyHashIn() { + if (c >= scripts.length) { + c = 0; + } + scripts[c].isPublicKeyHashIn(true); + c++; + } + + function isPublicKeyHashInAccurate() { + if (c >= scripts.length) { + c = 0; + } + scripts[c].isPublicKeyHashIn(); + c++; + } + + function toAddress() { + if (c >= scripts.length) { + c = 0; + } + scripts[c].toAddress(); + c++; + } + + function getAddressInfo() { + if (c >= scripts.length) { + c = 0; + } + scripts[c].getAddressInfo(); + c++; + } + + var suite = new benchmark.Suite(); + suite.add('isPublicKeyHashIn', isPublicKeyHashIn, {maxTime: maxTime}); + suite.add('isPublicKeyHashIn (accurate)', isPublicKeyHashInAccurate, {maxTime: maxTime}); + suite.add('toAddress', toAddress, {maxTime: maxTime}); + suite.add('getAddressInfo', getAddressInfo, {maxTime: maxTime}); + suite + .on('cycle', function(event) { + console.log(String(event.target)); + }) + .on('complete', function() { + console.log('Done'); + console.log('----------------------------------------------------------------------'); + next(); + }) + .run(); + } +], function(err) { + console.log('Finished'); +}); diff --git a/lib/script/script.js b/lib/script/script.js index f93ace2..3f25faa 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -245,13 +245,33 @@ Script.prototype.isPublicKeyHashOut = function() { }; /** + * @param {boolean} inaccurate - option to disable full (slow) public key validation * @returns {boolean} if this is a pay to public key hash input script */ -Script.prototype.isPublicKeyHashIn = function() { - return this.chunks.length === 2 && - this.chunks[0].buf && - this.chunks[0].buf.length <= 0x49 && - PublicKey.isValid(this.chunks[1].buf); +Script.prototype.isPublicKeyHashIn = function(inaccurate) { + if (this.chunks.length === 2) { + var signatureBuf = this.chunks[0].buf; + var pubkeyBuf = this.chunks[1].buf; + if (signatureBuf && + signatureBuf.length && + signatureBuf[0] === 0x30 && + pubkeyBuf.length + ) { + var version = pubkeyBuf[0]; + var isVersion = false; + if (version === 0x04 && pubkeyBuf.length === 65) { + isVersion = true; + } else if ((version === 0x03 || version === 0x02) && pubkeyBuf.length === 33) { + isVersion = true; + } + if (inaccurate) { + return isVersion; + } else { + return PublicKey.isValid(pubkeyBuf); + } + } + } + return false; }; Script.prototype.getPublicKeyHash = function() { @@ -753,7 +773,7 @@ Script.prototype.getAddressInfo = function() { } else if (this.isPublicKeyHashOut()) { info.hashBuffer = this.getData(); info.type = Address.PayToPublicKeyHash; - } else if (this.isPublicKeyHashIn()) { + } else if (this.isPublicKeyHashIn(true)) { // hash the publickey found in the scriptSig info.hashBuffer = Hash.sha256ripemd160(this.chunks[1].buf); info.type = Address.PayToPublicKeyHash; From febbcc6a6e0011e3ddc24d21334f6a2eaa43687a Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 6 Jul 2015 10:26:18 -0400 Subject: [PATCH 2/5] Check pubkeyBuf before pubkeyBuf.length --- lib/script/script.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/script/script.js b/lib/script/script.js index 3f25faa..544471d 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -255,6 +255,7 @@ Script.prototype.isPublicKeyHashIn = function(inaccurate) { if (signatureBuf && signatureBuf.length && signatureBuf[0] === 0x30 && + pubkeyBuf && pubkeyBuf.length ) { var version = pubkeyBuf[0]; From 770e0e3a7f3e56258f7bbce7471b6a61ec87f7c9 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 6 Jul 2015 12:52:11 -0400 Subject: [PATCH 3/5] Full public key validation isn't necessary. --- lib/script/script.js | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/lib/script/script.js b/lib/script/script.js index 544471d..48dad2a 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -245,7 +245,6 @@ Script.prototype.isPublicKeyHashOut = function() { }; /** - * @param {boolean} inaccurate - option to disable full (slow) public key validation * @returns {boolean} if this is a pay to public key hash input script */ Script.prototype.isPublicKeyHashIn = function(inaccurate) { @@ -259,16 +258,12 @@ Script.prototype.isPublicKeyHashIn = function(inaccurate) { pubkeyBuf.length ) { var version = pubkeyBuf[0]; - var isVersion = false; - if (version === 0x04 && pubkeyBuf.length === 65) { - isVersion = true; + if ((version === 0x04 || + version === 0x06 || + version === 0x07) && pubkeyBuf.length === 65) { + return true; } else if ((version === 0x03 || version === 0x02) && pubkeyBuf.length === 33) { - isVersion = true; - } - if (inaccurate) { - return isVersion; - } else { - return PublicKey.isValid(pubkeyBuf); + return true; } } } @@ -774,7 +769,7 @@ Script.prototype.getAddressInfo = function() { } else if (this.isPublicKeyHashOut()) { info.hashBuffer = this.getData(); info.type = Address.PayToPublicKeyHash; - } else if (this.isPublicKeyHashIn(true)) { + } else if (this.isPublicKeyHashIn()) { // hash the publickey found in the scriptSig info.hashBuffer = Hash.sha256ripemd160(this.chunks[1].buf); info.type = Address.PayToPublicKeyHash; From 7e23109f9b8f1abfcc40110a909ba0c4587486b7 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 6 Jul 2015 13:12:20 -0400 Subject: [PATCH 4/5] Added more tests around script.isPublicKeyHashIn --- lib/script/script.js | 2 +- test/script/script.js | 32 ++++++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 5 deletions(-) diff --git a/lib/script/script.js b/lib/script/script.js index 48dad2a..21fe7fb 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -247,7 +247,7 @@ Script.prototype.isPublicKeyHashOut = function() { /** * @returns {boolean} if this is a pay to public key hash input script */ -Script.prototype.isPublicKeyHashIn = function(inaccurate) { +Script.prototype.isPublicKeyHashIn = function() { if (this.chunks.length === 2) { var signatureBuf = this.chunks[0].buf; var pubkeyBuf = this.chunks[1].buf; diff --git a/test/script/script.js b/test/script/script.js index dd138a2..cd94da8 100644 --- a/test/script/script.js +++ b/test/script/script.js @@ -233,19 +233,27 @@ describe('Script', function() { describe('#isPublicKeyHashIn', function() { - it('should identify this known pubkeyhashin', function() { + it('should identify this known pubkeyhashin (uncompressed pubkey version)', function() { Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPublicKeyHashIn().should.equal(true); }); - it('should identify this known pubkeyhashin starting with 0x02', function() { + it('should identify this known pubkeyhashin (hybrid pubkey version w/06)', function() { + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x06e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPublicKeyHashIn().should.equal(true); + }); + + it('should identify this known pubkeyhashin (hybrid pubkey version w/07)', function() { + Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x07e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6').isPublicKeyHashIn().should.equal(true); + }); + + it('should identify this known pubkeyhashin (compressed pubkey w/ 0x02)', function() { Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 21 0x02aec6b86621e7fef63747fbfd6a6e7d54c8e1052044ef2dd2c5e46656ef1194d4').isPublicKeyHashIn().should.equal(true); }); - it('should identify this known pubkeyhashin starting with 0x03', function() { + it('should identify this known pubkeyhashin (compressed pubkey w/ 0x03)', function() { Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 21 0x03e724d93c4fda5f1236c525de7ffac6c5f1f72b0f5cdd1fc4b4f5642b6d055fcc').isPublicKeyHashIn().should.equal(true); }); - it('should identify this known non-pubkeyhashin', function() { + it('should identify this known non-pubkeyhashin (bad ops length)', function() { Script('73 0x3046022100bb3c194a30e460d81d34be0a230179c043a656f67e3c5c8bf47eceae7c4042ee0221008bf54ca11b2985285be0fd7a212873d243e6e73f5fad57e8eb14c4f39728b8c601 65 0x04e365859b3c78a8b7c202412b949ebca58e147dba297be29eee53cd3e1d300a6419bc780cc9aec0dc94ed194e91c8f6433f1b781ee00eac0ead2aae1e8e0712c6 OP_CHECKSIG').isPublicKeyHashIn().should.equal(false); }); @@ -253,6 +261,22 @@ describe('Script', function() { Script('70 0x3043021f336721e4343f67c835cbfd465477db09073dc38a936f9c445d573c1c8a7fdf022064b0e3cb6892a9ecf870030e3066bc259e1f24841c9471d97f9be08b73f6530701 33 0x0370b2e1dcaa8f51cb0ead1221dd8cb31721502b3b5b7d4b374d263dfec63a4369').isPublicKeyHashIn().should.equal(true); }); + it('should identify this known non-pubkeyhashin (bad version)', function() { + Script('70 0x3043021f336721e4343f67c835cbfd465477db09073dc38a936f9c445d573c1c8a7fdf022064b0e3cb6892a9ecf870030e3066bc259e1f24841c9471d97f9be08b73f6530701 33 0x1270b2e1dcaa8f51cb0ead1221dd8cb31721502b3b5b7d4b374d263dfec63a4369').isPublicKeyHashIn().should.equal(false); + }); + + it('should identify this known non-pubkeyhashin (bad signature version)', function() { + Script('70 0x4043021f336721e4343f67c835cbfd465477db09073dc38a936f9c445d573c1c8a7fdf022064b0e3cb6892a9ecf870030e3066bc259e1f24841c9471d97f9be08b73f6530701 33 0x0370b2e1dcaa8f51cb0ead1221dd8cb31721502b3b5b7d4b374d263dfec63a4369').isPublicKeyHashIn().should.equal(false); + }); + + it('should identify this known non-pubkeyhashin (no public key)', function() { + Script('70 0x3043021f336721e4343f67c835cbfd465477db09073dc38a936f9c445d573c1c8a7fdf022064b0e3cb6892a9ecf870030e3066bc259e1f24841c9471d97f9be08b73f6530701 OP_CHECKSIG').isPublicKeyHashIn().should.equal(false); + }); + + it('should identify this known non-pubkeyhashin (no signature)', function() { + Script('OP_DROP OP_CHECKSIG').isPublicKeyHashIn().should.equal(false); + }); + }); describe('#isPublicKeyHashOut', function() { From 468f87d69408815c99808240629571362d088f00 Mon Sep 17 00:00:00 2001 From: Braydon Fuller Date: Mon, 6 Jul 2015 13:14:44 -0400 Subject: [PATCH 5/5] Update benchmarks for isPublicKeyHashIn --- benchmark/script.js | 9 --------- 1 file changed, 9 deletions(-) diff --git a/benchmark/script.js b/benchmark/script.js index 76a6320..33229dc 100644 --- a/benchmark/script.js +++ b/benchmark/script.js @@ -27,14 +27,6 @@ async.series([ } function isPublicKeyHashIn() { - if (c >= scripts.length) { - c = 0; - } - scripts[c].isPublicKeyHashIn(true); - c++; - } - - function isPublicKeyHashInAccurate() { if (c >= scripts.length) { c = 0; } @@ -60,7 +52,6 @@ async.series([ var suite = new benchmark.Suite(); suite.add('isPublicKeyHashIn', isPublicKeyHashIn, {maxTime: maxTime}); - suite.add('isPublicKeyHashIn (accurate)', isPublicKeyHashInAccurate, {maxTime: maxTime}); suite.add('toAddress', toAddress, {maxTime: maxTime}); suite.add('getAddressInfo', getAddressInfo, {maxTime: maxTime}); suite