parsing and serialization.

This commit is contained in:
Christopher Jeffrey 2016-04-02 04:12:50 -07:00
parent 8289544b81
commit 47160c1cfe
4 changed files with 167 additions and 318 deletions

View File

@ -8,6 +8,8 @@ var bcoin = require('../bcoin');
var utils = bcoin.utils; var utils = bcoin.utils;
var assert = utils.assert; var assert = utils.assert;
var network = bcoin.protocol.network; var network = bcoin.protocol.network;
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
/** /**
* Address * Address
@ -65,8 +67,6 @@ Address.prototype.getID = function getID() {
}; };
Address.prototype.addKey = function addKey(key) { Address.prototype.addKey = function addKey(key) {
key = utils.ensureBuffer(key);
if (utils.indexOf(this.keys, key) !== -1) if (utils.indexOf(this.keys, key) !== -1)
return; return;
@ -76,11 +76,7 @@ Address.prototype.addKey = function addKey(key) {
}; };
Address.prototype.removeKey = function removeKey(key) { Address.prototype.removeKey = function removeKey(key) {
var index; var index = utils.indexOf(this.keys, key);
key = utils.ensureBuffer(key);
index = utils.indexOf(this.keys, key);
if (index === -1) if (index === -1)
return; return;
@ -210,7 +206,7 @@ Address.prototype.getScriptAddress = function getScriptAddress() {
if (this.witness) { if (this.witness) {
this._scriptAddress = this._scriptAddress =
Address.compileHash(this.getScriptHash256(), 'witnessscripthash'); Address.compileHash(this.getScriptHash256(), 'witnessscripthash', 0);
} else { } else {
this._scriptAddress = this._scriptAddress =
Address.compileHash(this.getScriptHash160(), 'scripthash'); Address.compileHash(this.getScriptHash160(), 'scripthash');
@ -233,7 +229,7 @@ Address.prototype.getKeyAddress = function getKeyAddress() {
return this._address; return this._address;
if (this.witness) if (this.witness)
this._address = Address.compileHash(this.getKeyHash(), 'witnesspubkeyhash'); this._address = Address.compileHash(this.getKeyHash(), 'witnesspubkeyhash', 0);
else else
this._address = Address.compileHash(this.getKeyHash(), 'pubkeyhash'); this._address = Address.compileHash(this.getKeyHash(), 'pubkeyhash');
@ -421,125 +417,108 @@ Address.prototype.__defineGetter__('address', function() {
}); });
Address.hash160 = function hash160(key) { Address.hash160 = function hash160(key) {
key = utils.ensureBuffer(key);
return utils.ripesha(key); return utils.ripesha(key);
}; };
Address.sha256 = function sha256(key) { Address.sha256 = function sha256(key) {
key = utils.ensureBuffer(key);
return utils.sha256(key); return utils.sha256(key);
}; };
Address.compileHash = function compileHash(hash, prefixType) { Address.compileHash = function compileHash(hash, type, version) {
var prefix, version, size, off, addr; var p, prefix;
if (!Buffer.isBuffer(hash)) if (!Buffer.isBuffer(hash))
hash = new Buffer(hash, 'hex'); hash = new Buffer(hash, 'hex');
if (!prefixType) if (!type)
prefixType = 'pubkeyhash'; type = 'pubkeyhash';
prefix = network.address.prefixes[prefixType]; prefix = network.address.prefixes[type];
version = network.address.versions[prefixType];
assert(prefix != null, 'Not a valid address prefix.'); if (version == null)
assert(hash.length === 20 || hash.length === 32, version = network.address.versions[type];
'Hash is the wrong length.');
size = 1 + hash.length + 4;
if (version != null)
size += 2;
addr = new Buffer(size);
off = 0;
off += utils.writeU8(addr, prefix, off);
if (version != null) {
off += utils.writeU8(addr, version, off);
off += utils.writeU8(addr, 0, off);
}
off += utils.copy(hash, addr, off);
off += utils.copy(utils.checksum(addr.slice(0, off)), addr, off);
return utils.toBase58(addr);
};
Address.compileData = function compileData(key, prefix) {
if (prefix === 'witnessscripthash')
key = Address.sha256(key);
else
key = Address.hash160(key);
return Address.compileHash(key, prefix);
};
Address.parse = function parse(addr, prefixType) {
var chk, prefix, version, size, hash;
if (!Buffer.isBuffer(addr))
addr = utils.fromBase58(addr);
if (prefixType == null)
prefixType = network.address.prefixesByVal[addr[0]];
if (!prefixType)
prefixType = 'pubkeyhash';
prefix = network.address.prefixes[prefixType];
version = network.address.versions[prefixType];
assert(prefix != null, 'Not a valid address prefix.'); assert(prefix != null, 'Not a valid address prefix.');
// prefix if (version == null)
size = 1; assert(hash.length === 20, 'Hash is the wrong size.');
else if (version === 0 && type === 'witnesspubkeyhash')
assert(hash.length === 20, 'Hash is the wrong size.');
else if (version === 0 && type === 'witnessscripthash')
assert(hash.length === 32, 'Hash is the wrong size.');
// version + nul byte p = new BufferWriter();
if (version != null)
size += 2;
hash = addr.slice(size, -4); p.writeU8(prefix);
if (version != null) {
p.writeU8(version);
p.writeU8(0)
}
p.writeBytes(hash);
p.writeChecksum();
// hash return utils.toBase58(p.render());
if (prefixType === 'witnessscripthash') };
size += 32;
Address.compileData = function compileData(data, type, version) {
if (type === 'witnessscripthash')
data = Address.sha256(data);
else else
size += 20; data = Address.hash160(data);
assert(addr.length === size + 4, 'Address is not the right length.'); return Address.compileHash(data, type, version);
};
assert(addr[0] === prefix, 'Address is not the right prefix.'); Address.parse = function parse(address) {
var prefix, type, version, hash;
if (!Buffer.isBuffer(address))
address = utils.fromBase58(address);
p = new BufferReader(address, true);
prefix = p.readU8();
type = network.address.prefixesByVal[prefix];
version = network.address.versions[type];
assert(type != null, 'Not a valid address prefix.');
if (version != null) { if (version != null) {
assert(addr[1] === version, version = p.readU8();
'Address is not the right program version.'); assert(version >= 0 && version <= 16, 'Bad program version.');
assert(addr[2] === 0, assert(p.readU8() === 0, 'Address version padding is zero.');
'Address version padding is zero.');
} }
chk = utils.checksum(addr.slice(0, -4)); if (type === 'witnessscripthash')
hash = p.readBytes(32);
else
hash = p.readBytes(20);
assert(utils.readU32(chk, 0) === utils.readU32(addr, size), p.verifyChecksum();
'Address checksum failed.');
return { return {
type: prefixType, type: type,
hash: hash, hash: hash,
version: version == null ? -1 : version version: version == null ? -1 : version
}; };
}; };
Address.validate = function validate(addr, prefix) { Address.validate = function validate(address, type) {
if (!addr) if (!address)
return false;
if (!Buffer.isBuffer(address) && typeof address !== 'string')
return false; return false;
try { try {
Address.parse(addr, prefix); address = Address.parse(address);
} catch (e) { } catch (e) {
return false; return false;
} }
if (type && address.type !== type)
return false;
return true; return true;
}; };

View File

@ -8,6 +8,8 @@ var bcoin = require('../bcoin');
var utils = require('./utils'); var utils = require('./utils');
var assert = utils.assert; var assert = utils.assert;
var network = bcoin.protocol.network; var network = bcoin.protocol.network;
var BufferWriter = require('./writer');
var BufferReader = require('./reader');
/** /**
* KeyPair * KeyPair
@ -87,45 +89,44 @@ KeyPair.prototype.toSecret = function toSecret() {
}; };
KeyPair.toSecret = function toSecret(privateKey, compressed) { KeyPair.toSecret = function toSecret(privateKey, compressed) {
var buf = new Buffer(1 + privateKey.length + (compressed ? 1 : 0) + 4); var p = new BufferWriter();
var off = 0;
off += utils.writeU8(buf, network.prefixes.privkey, 0); p.writeU8(network.prefixes.privkey);
off += utils.copy(privateKey, buf, off); p.writeBytes(privateKey);
if (compressed !== false) if (compressed !== false)
off += utils.writeU8(buf, 1, off); p.writeU8(1);
utils.copy(utils.checksum(buf.slice(0, off)), buf, off); p.writeChecksum();
return utils.toBase58(buf); return utils.toBase58(p.render());
}; };
KeyPair._fromSecret = function _fromSecret(privateKey) { KeyPair._fromSecret = function _fromSecret(secret) {
var key, compressed; var data = utils.fromBase58(secret);
var p = new BufferReader(data, true);
var compressed = false;
var privateKey;
key = utils.fromBase58(privateKey); assert(p.readU8() === network.prefixes.privkey, 'Bad network.');
assert(utils.isEqual(key.slice(-4), utils.checksum(key.slice(0, -4))));
assert.equal(key[0], network.prefixes.privkey);
key = key.slice(0, -4); privateKey = p.readBytes(32);
if (key.length === 34) {
assert.equal(key[33], 1); if (p.left() > 1) {
privateKey = key.slice(1, -1); assert(p.readU8() === 1);
compressed = true; compressed = true;
} else {
privateKey = key.slice(1);
compressed = false;
} }
p.verifyChecksum();
return { return {
privateKey: privateKey, privateKey: privateKey,
compressed: compressed compressed: compressed
}; };
}; };
KeyPair.fromSecret = function fromSecret(privateKey) { KeyPair.fromSecret = function fromSecret(secret) {
return new KeyPair(KeyPair._fromSecret(privateKey)); return new KeyPair(KeyPair._fromSecret(secret));
}; };
KeyPair.prototype.toJSON = function toJSON(passphrase) { KeyPair.prototype.toJSON = function toJSON(passphrase) {
@ -148,7 +149,7 @@ KeyPair.prototype.toJSON = function toJSON(passphrase) {
}; };
KeyPair._fromJSON = function _fromJSON(json, passphrase) { KeyPair._fromJSON = function _fromJSON(json, passphrase) {
var privateKey; var privateKey, publicKey;
assert.equal(json.v, 1); assert.equal(json.v, 1);
assert.equal(json.name, 'keypair'); assert.equal(json.name, 'keypair');
@ -164,13 +165,14 @@ KeyPair._fromJSON = function _fromJSON(json, passphrase) {
} }
if (json.publicKey) { if (json.publicKey) {
publicKey = utils.fromBase58(json.publicKey);
return { return {
publicKey: utils.fromBase58(json.publicKey), publicKey: publicKey,
compressed: publicKey[0] !== 0x04 compressed: publicKey[0] !== 0x04
}; };
} }
assert(false); assert(false, 'Could not parse KeyPair JSON.');
}; };
KeyPair.fromJSON = function fromJSON(json, passphrase) { KeyPair.fromJSON = function fromJSON(json, passphrase) {

View File

@ -1489,16 +1489,26 @@ Script.getInputAddress = function getInputAddress(code, prev, isWitness) {
return; return;
if (Script.isPubkeyhashInput(code)) { if (Script.isPubkeyhashInput(code)) {
return bcoin.address.compileData(code[1], if (isWitness) {
isWitness ? 'witnesspubkeyhash' : 'pubkeyhash'); return bcoin.address.compileData(
code[1],
'witnesspubkeyhash',
0);
}
return bcoin.address.compileData(code[1], 'pubkeyhash');
} }
if (Script.isMultisigInput(code, null, isWitness)) if (Script.isMultisigInput(code, null, isWitness))
return; return;
if (Script.isScripthashInput(code, null, isWitness)) { if (Script.isScripthashInput(code, null, isWitness)) {
return bcoin.address.compileData(code[code.length - 1], if (isWitness) {
isWitness ? 'witnessscripthash' : 'scripthash'); return bcoin.address.compileData(
code[code.length - 1],
'witnessscripthash',
0);
}
return bcoin.address.compileData(code[code.length - 1], 'scripthash');
} }
}; };
@ -1509,7 +1519,10 @@ Script.prototype.getAddress = function getAddress() {
program = this.getWitnessProgram(); program = this.getWitnessProgram();
if (!program.type || program.type === 'unknown') if (!program.type || program.type === 'unknown')
return; return;
return bcoin.address.compileHash(program.data, program.type); return bcoin.address.compileHash(
program.data,
program.type,
program.version);
} }
// Convert p2pk to p2pkh addresses // Convert p2pk to p2pkh addresses
@ -1822,26 +1835,14 @@ Script.isNonstandardInput = function isNonstandardInput(code, prev, isWitness) {
}; };
Script.createOutputScript = function(options) { Script.createOutputScript = function(options) {
var script, keys, m, n, hash, flags, address, redeem; var script, m, n, hash, flags, address, redeem;
if (!options) if (!options)
options = {}; options = {};
if (options.keys) { if (options.address) {
keys = options.keys.map(utils.ensureBuffer);
m = options.m;
n = options.n || keys.length;
assert(m >= 1 && m <= n, 'm must be between 1 and n');
assert(
n >= 1 && n <= (options.scriptHash ? 15 : 3),
'n must be between 1 and 15');
script = Script.createMultisig(keys, m, n);
} else if (options.address) {
address = bcoin.address.parse(options.address); address = bcoin.address.parse(options.address);
if (address.type === 'pubkeyhash') if (address.type === 'pubkeyhash')
script = Script.createPubkeyhash(address.hash); script = Script.createPubkeyhash(address.hash);
else if (address.type === 'scripthash') else if (address.type === 'scripthash')
@ -1849,29 +1850,62 @@ Script.createOutputScript = function(options) {
else if (address.version !== -1) else if (address.version !== -1)
script = Script.createWitnessProgram(address.version, address.hash); script = Script.createWitnessProgram(address.version, address.hash);
else else
assert(false); assert(false, 'Unknown address type.');
} else if (options.key) {
script = Script.createPubkey(utils.ensureBuffer(options.key)); return script;
} else if (options.flags) { }
if (options.flags) {
flags = options.flags; flags = options.flags;
if (typeof flags === 'string') if (typeof flags === 'string')
flags = new Buffer(flags, 'ascii'); flags = new Buffer(flags, 'utf8');
assert(Buffer.isBuffer(flags)); assert(Buffer.isBuffer(flags));
assert(flags.length <= constants.script.maxOpReturn); assert(flags.length <= constants.script.maxOpReturn, 'Nulldata too large.');
script = Script.createNulldata(flags); return Script.createNulldata(flags);
}
if (options.key) {
script = Script.createPubkey(options.key);
} else if (options.keyHash) {
assert(options.keyHash.length === 20);
if (options.version != null)
script = Script.createWitnessProgram(options.version, options.keyHash);
else
script = Script.createPubkeyhash(options.keyHash);
} else if (options.keys) {
m = options.m;
n = options.n || options.keys.length;
assert(m >= 1 && m <= n, 'm must be between 1 and n');
assert(n >= 1 && n <= (options.scriptHash ? 15 : 3),
'n must be between 1 and 15');
script = Script.createMultisig(options.keys, m, n);
} else if (Buffer.isBuffer(options.scriptHash)) {
if (options.version != null) {
assert(options.scriptHash.length === 32);
return Script.createWitnessProgram(options.version, options.scriptHash);
}
assert(options.scriptHash.length === 20);
return Script.createScripthash(options.scriptHash);
}
if (!script)
return new Script([]);
if (options.locktime != null) {
script.code.unshift(opcodes.OP_DROP);
script.code.unshift(opcodes.OP_CHECKLOCKTIMEVERIFY);
script.code.unshift(Script.array(options.locktime));
} }
if (options.scriptHash) { if (options.scriptHash) {
if (options.locktime != null) {
script = new Script([
Script.array(options.locktime),
opcodes.OP_CHECKLOCKTIMEVERIFY,
opcodes.OP_DROP
].concat(script.code));
}
redeem = script; redeem = script;
hash = utils.ripesha(script.encode()); if (options.version != null) {
script = Script.createScripthash(hash); hash = utils.sha256(redeem.encode());
script = Script.createWitnessProgram(options.version, hash);
} else {
hash = utils.ripesha(redeem.encode());
script = Script.createScripthash(hash);
}
script.redeem = redeem; script.redeem = redeem;
} }

View File

@ -66,56 +66,6 @@ utils.toBuffer = function toBuffer(msg, enc) {
assert(false); assert(false);
}; };
utils.toArray = function toArray(msg, enc) {
var res = [];
var i, c, hi, lo, slice, num;
if (!msg)
return res;
if (Buffer.isBuffer(msg))
return Array.prototype.slice.call(msg);
if (Array.isArray(msg))
return msg.slice();
if (typeof msg === 'string') {
if (!enc) {
for (i = 0; i < msg.length; i++) {
c = msg.charCodeAt(i);
hi = c >> 8;
lo = c & 0xff;
if (hi)
res.push(hi, lo);
else
res.push(lo);
}
} else if (enc === 'hex') {
msg = msg.replace(/[^a-z0-9]+/ig, '');
if (msg.length % 2 !== 0)
msg = '0' + msg;
for (i = 0; i < msg.length; i += 8) {
slice = msg.slice(i, i + 8);
num = parseInt(slice, 16);
if (slice.length === 8)
res.push((num >>> 24) & 0xff);
if (slice.length >= 6)
res.push((num >>> 16) & 0xff);
if (slice.length >= 4)
res.push((num >>> 8) & 0xff);
res.push(num & 0xff);
}
}
} else {
for (i = 0; i < msg.length; i++)
res[i] = msg[i] | 0;
}
return res;
};
var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ' var base58 = '123456789ABCDEFGHJKLMNPQRSTUVWXYZ'
+ 'abcdefghijkmnopqrstuvwxyz'; + 'abcdefghijkmnopqrstuvwxyz';
@ -399,34 +349,6 @@ utils.isHex = function isHex(msg) {
return typeof msg === 'string' && /^[0-9a-f]+$/i.test(msg); return typeof msg === 'string' && /^[0-9a-f]+$/i.test(msg);
}; };
function binaryInsert(list, item, compare, search) {
var start = 0;
var end = list.length;
var pos, cmp;
while (start < end) {
pos = (start + end) >> 1;
cmp = compare(item, list[pos]);
if (cmp === 0) {
start = pos;
end = pos;
break;
} else if (cmp < 0) {
end = pos;
} else {
start = pos + 1;
}
}
if (!search)
list.splice(start, 0, item);
return start;
}
utils.binaryInsert = binaryInsert;
utils.isEqual = function isEqual(a, b) { utils.isEqual = function isEqual(a, b) {
var i; var i;
@ -457,51 +379,13 @@ if (typeof setImmediate === 'function') {
}; };
} }
function RequestCache() {
this.map = {};
this.count = 0;
}
RequestCache.prototype.add = function add(id, cb) {
id = utils.toHex(id);
if (this.map[id]) {
this.map[id].push(cb);
return false;
} else {
this.map[id] = [ cb ];
this.count++;
return true;
}
};
RequestCache.prototype.fulfill = function fulfill(id, err, data) {
var cbs;
id = utils.toHex(id);
cbs = this.map[id];
if (!this.map[id])
return;
delete this.map[id];
this.count--;
cbs.forEach(function(cb) {
cb(err, data);
});
};
utils.RequestCache = RequestCache;
utils.asyncify = function asyncify(callback) { utils.asyncify = function asyncify(callback) {
if (callback && callback._asyncified) if (callback && callback._asyncified)
return callback; return callback;
function asyncifyFn(err, result1, result2) { function asyncifyFn(err, result1, result2) {
if (!callback) if (!callback)
return err || result1; return;
utils.nextTick(function() { utils.nextTick(function() {
callback(err, result1, result2); callback(err, result1, result2);
}); });
@ -811,37 +695,6 @@ utils.array2ip = function array2ip(ip, version) {
} }
}; };
utils.isArrayLike = function isArrayLike(msg) {
return msg
&& !Array.isArray(msg)
&& typeof msg === 'object'
&& typeof msg.length === 'number';
};
utils.isArray = function isArray(msg) {
return Array.isArray(msg);
};
utils.isBuffer = function isBuffer(msg) {
return Buffer.isBuffer(msg);
};
utils.ensureBuffer = function ensureBuffer(msg) {
if (Buffer.isBuffer(msg))
return msg;
if (Array.isArray(msg))
return new Buffer(msg);
if (utils.isHex(msg))
return new Buffer(msg, 'hex');
if (utils.isBase58(msg))
return utils.fromBase58(msg);
throw new Error('Cannot ensure buffer');
};
utils._inspect = function _inspect(obj, color) { utils._inspect = function _inspect(obj, color) {
return typeof obj !== 'string' return typeof obj !== 'string'
? util.inspect(obj, null, 20, color !== false) ? util.inspect(obj, null, 20, color !== false)
@ -986,10 +839,6 @@ utils.now = function now() {
return +new Date() / 1000 | 0; return +new Date() / 1000 | 0;
}; };
utils.host = function host(addr) {
return addr.split(':')[0];
};
utils.U32 = new bn(0xffffffff); utils.U32 = new bn(0xffffffff);
utils.U64 = new bn('ffffffffffffffff', 'hex'); utils.U64 = new bn('ffffffffffffffff', 'hex');
@ -1421,9 +1270,6 @@ utils.write32BE = function write32BE(dst, num, off) {
utils.write64 = function write64(dst, num, off) { utils.write64 = function write64(dst, num, off) {
var i; var i;
// if (!bn.isBN(num))
// num = new bn(+num);
if (!bn.isBN(num)) if (!bn.isBN(num))
return utils.write64N(dst, num, off); return utils.write64N(dst, num, off);
@ -1441,9 +1287,7 @@ utils.write64 = function write64(dst, num, off) {
if (num.bitLength() > 64) if (num.bitLength() > 64)
num = num.uand(utils.U64); num = num.uand(utils.U64);
num = num.toArray('le', 8); num = num.toBuffer('le', 8);
assert.equal(num.length, 8);
for (i = 0; i < num.length; i++) for (i = 0; i < num.length; i++)
dst[off++] = num[i] & 0xff; dst[off++] = num[i] & 0xff;
@ -1454,9 +1298,6 @@ utils.write64 = function write64(dst, num, off) {
utils.write64BE = function write64BE(dst, num, off) { utils.write64BE = function write64BE(dst, num, off) {
var i; var i;
// if (!bn.isBN(num))
// num = new bn(+num);
if (!bn.isBN(num)) if (!bn.isBN(num))
return utils.write64NBE(dst, num, off); return utils.write64NBE(dst, num, off);
@ -1474,9 +1315,7 @@ utils.write64BE = function write64BE(dst, num, off) {
if (num.bitLength() > 64) if (num.bitLength() > 64)
num = num.uand(utils.U64); num = num.uand(utils.U64);
num = num.toArray('be', 8); num = num.toBuffer('be', 8);
assert.equal(num.length, 8);
for (i = 0; i < num.length; i++) for (i = 0; i < num.length; i++)
dst[off++] = num[i] & 0xff; dst[off++] = num[i] & 0xff;
@ -2009,12 +1848,6 @@ utils.revMap = function revMap(map) {
return reversed; return reversed;
}; };
if (utils.isBrowser) {
bn.prototype.toBuffer = function toBuffer(order, size) {
return this.toArrayLike(Buffer, order, size);
};
}
/** /**
* VerifyError * VerifyError
*/ */
@ -2036,7 +1869,8 @@ function VerifyError(object, code, reason, score) {
this.code = code; this.code = code;
this.reason = score === -1 ? null : reason; this.reason = score === -1 ? null : reason;
this.score = score; this.score = score;
this.message = reason this.message = 'Verification failure: '
+ reason
+ ' (code=' + code + ' (code=' + code
+ ', score=' + score + ', score=' + score
+ ', height=' + this.height + ', height=' + this.height