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

View File

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

View File

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

View File

@ -66,56 +66,6 @@ utils.toBuffer = function toBuffer(msg, enc) {
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'
+ 'abcdefghijkmnopqrstuvwxyz';
@ -399,34 +349,6 @@ utils.isHex = function isHex(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) {
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) {
if (callback && callback._asyncified)
return callback;
function asyncifyFn(err, result1, result2) {
if (!callback)
return err || result1;
return;
utils.nextTick(function() {
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) {
return typeof obj !== 'string'
? util.inspect(obj, null, 20, color !== false)
@ -986,10 +839,6 @@ utils.now = function now() {
return +new Date() / 1000 | 0;
};
utils.host = function host(addr) {
return addr.split(':')[0];
};
utils.U32 = new bn(0xffffffff);
utils.U64 = new bn('ffffffffffffffff', 'hex');
@ -1421,9 +1270,6 @@ utils.write32BE = function write32BE(dst, num, off) {
utils.write64 = function write64(dst, num, off) {
var i;
// if (!bn.isBN(num))
// num = new bn(+num);
if (!bn.isBN(num))
return utils.write64N(dst, num, off);
@ -1441,9 +1287,7 @@ utils.write64 = function write64(dst, num, off) {
if (num.bitLength() > 64)
num = num.uand(utils.U64);
num = num.toArray('le', 8);
assert.equal(num.length, 8);
num = num.toBuffer('le', 8);
for (i = 0; i < num.length; i++)
dst[off++] = num[i] & 0xff;
@ -1454,9 +1298,6 @@ utils.write64 = function write64(dst, num, off) {
utils.write64BE = function write64BE(dst, num, off) {
var i;
// if (!bn.isBN(num))
// num = new bn(+num);
if (!bn.isBN(num))
return utils.write64NBE(dst, num, off);
@ -1474,9 +1315,7 @@ utils.write64BE = function write64BE(dst, num, off) {
if (num.bitLength() > 64)
num = num.uand(utils.U64);
num = num.toArray('be', 8);
assert.equal(num.length, 8);
num = num.toBuffer('be', 8);
for (i = 0; i < num.length; i++)
dst[off++] = num[i] & 0xff;
@ -2009,12 +1848,6 @@ utils.revMap = function revMap(map) {
return reversed;
};
if (utils.isBrowser) {
bn.prototype.toBuffer = function toBuffer(order, size) {
return this.toArrayLike(Buffer, order, size);
};
}
/**
* VerifyError
*/
@ -2036,7 +1869,8 @@ function VerifyError(object, code, reason, score) {
this.code = code;
this.reason = score === -1 ? null : reason;
this.score = score;
this.message = reason
this.message = 'Verification failure: '
+ reason
+ ' (code=' + code
+ ', score=' + score
+ ', height=' + this.height