From 1997864ec240ac1c93eadf6aac4e0b84b9938cb8 Mon Sep 17 00:00:00 2001 From: Christopher Jeffrey Date: Sun, 27 Nov 2016 22:23:12 -0800 Subject: [PATCH] coins: refactor compression. --- lib/blockchain/coins.js | 4 +- lib/blockchain/compress.js | 79 ++++++++++++++++++++++---------------- lib/primitives/coin.js | 2 +- lib/script/script.js | 16 ++++++-- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/lib/blockchain/coins.js b/lib/blockchain/coins.js index 591400d1..cce0501c 100644 --- a/lib/blockchain/coins.js +++ b/lib/blockchain/coins.js @@ -383,7 +383,7 @@ Coins.parseCoin = function parseCoin(data, hash, index) { if (pos === index) { if (spent) return; - decompress.script(br, coin.script); + decompress.script(coin.script, br); coin.value = br.readVarint(); return coin; } @@ -505,7 +505,7 @@ CoinEntry.prototype.toCoin = function toCoin(coins, index) { // Seek to the coin's offset. br.seek(this.offset); - decompress.script(br, coin.script); + decompress.script(coin.script, br); coin.value = br.readVarint(); diff --git a/lib/blockchain/compress.js b/lib/blockchain/compress.js index 13a61964..32ff8756 100644 --- a/lib/blockchain/compress.js +++ b/lib/blockchain/compress.js @@ -20,7 +20,6 @@ var ec = require('../crypto/ec'); */ function compressScript(script, bw) { - var prefix = 0; var data; // Attempt to compress the output scripts. @@ -28,62 +27,80 @@ function compressScript(script, bw) { // they are serialized as minimaldata, as // we need to recreate them when we read // them. + + // P2PKH -> 1 | key-hash + // Saves 5 bytes. if (script.isPubkeyhash(true)) { - prefix = 1; data = script.code[2].data; - } else if (script.isScripthash()) { - prefix = 2; - data = script.code[1].data; - } else if (script.isPubkey(true)) { - prefix = 3; - data = script.code[0].data; - - // Try to compress the key. - data = compressKey(data); - - // If we can't compress it, - // just store the script. - if (!data) - prefix = 0; + bw.writeU8(1); + bw.writeBytes(data); + return bw; } - bw.writeU8(prefix); - - if (prefix === 0) - bw.writeVarBytes(script.toRaw()); - else + // P2SH -> 2 | script-hash + // Saves 3 bytes. + if (script.isScripthash()) { + data = script.code[1].data; + bw.writeU8(2); bw.writeBytes(data); + return bw; + } + + // P2PK -> 3 | compressed-key + // Only works if the key is valid. + // Saves up to 34 bytes. + if (script.isPubkey(true)) { + data = script.code[0].data; + if (ec.publicKeyVerify(data)) { + data = compressKey(data); + bw.writeU8(3); + bw.writeBytes(data); + return bw; + } + } + + // Raw -> 0 | varlen | script + bw.writeU8(0); + bw.writeVarBytes(script.toRaw()); + + return bw; } /** * Decompress a script from buffer reader. - * @param {BufferReader} br * @param {Script} script + * @param {BufferReader} br */ -function decompressScript(br, script) { - var key; +function decompressScript(script, br) { + var data; // Decompress the script. switch (br.readU8()) { case 0: - script.fromRaw(br.readVarBytes()); + data = br.readVarBytes(); + script.fromRaw(data); break; case 1: - script.fromPubkeyhash(br.readBytes(20)); + data = br.readBytes(20, true); + script.fromPubkeyhash(data); break; case 2: - script.fromScripthash(br.readBytes(20)); + data = br.readBytes(20, true); + script.fromScripthash(data); break; case 3: + data = br.readBytes(33, true); // Decompress the key. If this fails, // we have database corruption! - key = decompressKey(br.readBytes(33)); - script.fromPubkey(key); + data = decompressKey(data); + script.fromPubkey(data); break; default: throw new Error('Bad prefix.'); } + + return script; } /** @@ -157,10 +174,6 @@ function decompressValue(value) { function compressKey(key) { var out; - // We can't compress it if it's not valid. - if (!ec.publicKeyVerify(key)) - return; - switch (key[0]) { case 0x02: case 0x03: diff --git a/lib/primitives/coin.js b/lib/primitives/coin.js index f4a767c4..a561e5ac 100644 --- a/lib/primitives/coin.js +++ b/lib/primitives/coin.js @@ -303,7 +303,7 @@ Coin.prototype.fromCompressed = function fromCompressed(data) { this.height = bits >>> 1; this.coinbase = (bits & 1) !== 0; this.value = br.readVarint(); - decompress.script(br, this.script); + decompress.script(this.script, br); if (this.height === 0x7fffffff) this.height = -1; diff --git a/lib/script/script.js b/lib/script/script.js index 07e327a4..266261d8 100644 --- a/lib/script/script.js +++ b/lib/script/script.js @@ -1507,10 +1507,18 @@ Script.isCode = function isCode(raw) { */ Script.prototype.fromPubkey = function fromPubkey(key) { - assert(Buffer.isBuffer(key) && key.length >= 33); - this.push(key); - this.push(opcodes.OP_CHECKSIG); - this.compile(); + assert(Buffer.isBuffer(key) && key.length >= 33 && key.length <= 65); + + this.raw = new Buffer(1 + key.length + 1); + this.raw[0] = key.length; + key.copy(this.raw, 1); + this.raw[1 + key.length] = opcodes.OP_CHECKSIG; + + key = this.raw.slice(1, 1 + key.length); + + this.code.push(new Opcode(key.length, key)); + this.code.push(new Opcode(opcodes.OP_CHECKSIG)); + return this; };