coins: refactor compression.

This commit is contained in:
Christopher Jeffrey 2016-11-27 22:23:12 -08:00
parent 7fd2c409ae
commit 1997864ec2
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
4 changed files with 61 additions and 40 deletions

View File

@ -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();

View File

@ -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:

View File

@ -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;

View File

@ -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;
};