program. addresses.

This commit is contained in:
Christopher Jeffrey 2016-06-17 06:32:00 -07:00
parent 32e26446b1
commit 7862418ccc
No known key found for this signature in database
GPG Key ID: 8962AB9DE6666BBD
10 changed files with 271 additions and 209 deletions

View File

@ -7,6 +7,7 @@ var utils = bcoin.utils;
var assert = utils.assert;
process.on('uncaughtException', function(err) {
bcoin.debug(err.stack);
bcoin.error(err);
process.exit(1);
});

View File

@ -35,6 +35,16 @@ function Address(options) {
if (!(this instanceof Address))
return new Address(options);
this.hash = null;
this.type = null;
this.version = null;
this.network = bcoin.network.get().type;
if (options)
this.fromOptions(options);
}
Address.prototype.fromOptions = function fromOptions(options) {
this.hash = options.hash;
this.type = options.type || 'pubkeyhash';
this.version = options.version == null ? -1 : options.version;
@ -42,7 +52,11 @@ function Address(options) {
if (!Buffer.isBuffer(this.hash))
this.hash = new Buffer(this.hash, 'hex');
}
};
Address.fromOptions = function fromOptions(options) {
return new Address().fromOptions(options);
};
/**
* Get the address hash.
@ -161,7 +175,7 @@ Address.toBase58 = function toBase58(hash, type, version, network) {
* @throws Parse error
*/
Address.parseBase58 = function parseBase58(address) {
Address.prototype.fromBase58 = function fromBase58(address) {
var i, prefix, type, version, hash, network, p;
if (!Buffer.isBuffer(address))
@ -194,12 +208,12 @@ Address.parseBase58 = function parseBase58(address) {
p.verifyChecksum();
return {
network: network.type,
type: type,
hash: hash,
version: version == null ? -1 : version
};
this.network = network.type;
this.type = type;
this.hash = hash;
this.version = version == null ? -1 : version;
return this;
};
/**
@ -210,7 +224,7 @@ Address.parseBase58 = function parseBase58(address) {
*/
Address.fromBase58 = function fromBase58(address) {
return new Address(Address.parseBase58(address));
return new Address().fromBase58(address);
};
/**
@ -221,50 +235,61 @@ Address.fromBase58 = function fromBase58(address) {
* @returns {ParsedAddress|null}
*/
Address.parseScript = function parseScript(script) {
Address.prototype.fromScript = function fromScript(script) {
var program, hash;
if (script.isWitnessProgram()) {
program = script.getWitnessProgram();
if (!program.type || program.type === 'unknown')
program = script.toProgram();
if (program.isUnknown())
return;
return {
hash: program.data,
type: program.type,
version: program.version
};
this.hash = program.data;
this.type = program.type;
this.version = program.version;
return this;
}
// Fast case
if (script.isPubkey()) {
hash = utils.ripesha(script.raw.slice(1, script.raw[0] + 1));
return { hash: hash, type: 'pubkeyhash', version: -1 };
this.hash = utils.ripesha(script.raw.slice(1, script.raw[0] + 1));
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isPubkeyhash()) {
hash = script.raw.slice(3, 23);
return { hash: hash, type: 'pubkeyhash', version: -1 };
this.hash = script.raw.slice(3, 23);
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isScripthash()) {
hash = script.raw.slice(2, 22);
return { hash: hash, type: 'scripthash', version: -1 };
this.hash = script.raw.slice(2, 22);
this.type = 'scripthash';
this.version = -1;
return this;
}
// Slow case (allow non-minimal data and parse script)
if (script.isPubkey(true)) {
hash = utils.ripesha(script.code[0].data);
return { hash: hash, type: 'pubkeyhash', version: -1 };
this.hash = utils.ripesha(script.code[0].data);
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isPubkeyhash(true)) {
hash = script.code[2].data;
return { hash: hash, type: 'pubkeyhash', version: -1 };
this.hash = script.code[2].data;
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isMultisig()) {
hash = utils.ripesha(script.raw);
return { hash: hash, type: 'scripthash', version: -1 };
this.hash = utils.ripesha(script.raw);
this.type = 'scripthash';
this.version = -1;
return this;
}
};
@ -275,17 +300,21 @@ Address.parseScript = function parseScript(script) {
* @returns {ParsedAddress|null}
*/
Address.parseWitness = function parseWitness(witness) {
Address.prototype.fromWitness = function fromWitness(witness) {
var hash;
if (witness.isPubkeyhashInput()) {
hash = utils.ripesha(witness.items[1]);
return { hash: hash, type: 'witnesspubkeyhash', version: 0 };
this.hash = utils.ripesha(witness.items[1]);
this.type = 'witnesspubkeyhash';
this.version = 0;
return this;
}
if (witness.isScripthashInput()) {
hash = utils.sha256(witness.items[witness.items.length - 1]);
return { hash: hash, type: 'witnessscripthash', version: 0 };
this.hash = utils.sha256(witness.items[witness.items.length - 1]);
this.type = 'witnessscripthash';
this.version = 0;
return this;
}
};
@ -296,17 +325,21 @@ Address.parseWitness = function parseWitness(witness) {
* @returns {ParsedAddress|null}
*/
Address.parseInputScript = function parseInputScript(script) {
Address.prototype.fromInputScript = function fromInputScript(script) {
var hash;
if (script.isPubkeyhashInput()) {
hash = utils.ripesha(script.code[1].data);
return { hash: hash, type: 'pubkeyhash', version: -1 };
this.hash = utils.ripesha(script.code[1].data);
this.type = 'pubkeyhash';
this.version = -1;
return this;
}
if (script.isScripthashInput()) {
hash = utils.ripesha(script.code[script.code.length - 1].data);
return { hash: hash, type: 'scripthash', version: -1 };
this.hash = utils.ripesha(script.code[script.code.length - 1].data);
this.type = 'scripthash';
this.version = -1;
return this;
}
};
@ -317,12 +350,7 @@ Address.parseInputScript = function parseInputScript(script) {
*/
Address.fromWitness = function fromWitness(witness) {
var data = Address.parseWitness(witness);
if (!data)
return;
return new Address(data);
return new Address().fromWitness(witness);
};
/**
@ -332,12 +360,7 @@ Address.fromWitness = function fromWitness(witness) {
*/
Address.fromInputScript = function fromInputScript(script) {
var data = Address.parseInputScript(script);
if (!data)
return;
return new Address(data);
return new Address().fromInputScript(script);
};
/**
@ -347,12 +370,7 @@ Address.fromInputScript = function fromInputScript(script) {
*/
Address.fromScript = function fromScript(script) {
var data = Address.parseScript(script);
if (!data)
return;
return new Address(data);
return new Address().fromScript(script);
};
/**
@ -363,15 +381,16 @@ Address.fromScript = function fromScript(script) {
* @returns {ParsedAddress}
*/
Address.parseHash = function parseHash(hash, type, version) {
if (!Buffer.isBuffer(hash))
Address.prototype.fromHash = function fromHash(hash, type, version, network) {
if (typeof hash === 'string')
hash = new Buffer(hash, 'hex');
return {
hash: hash,
type: type || 'pubkeyhash',
version: version == null ? -1 : version
};
this.hash = hash;
this.type = type || 'pubkeyhash';
this.version = version == null ? -1 : version;
this.network = bcoin.network.get(network).type;
return this;
};
/**
@ -382,8 +401,8 @@ Address.parseHash = function parseHash(hash, type, version) {
* @returns {Address}
*/
Address.fromHash = function fromHash(hash, type, version) {
return new Address(Address.parseHash(hash, type, version));
Address.fromHash = function fromHash(hash, type, version, network) {
return new Address().fromHash(hash, type, version, network);
};
/**
@ -394,13 +413,13 @@ Address.fromHash = function fromHash(hash, type, version) {
* @returns {ParsedAddress}
*/
Address.parseData = function parseData(data, type, version) {
Address.prototype.fromData = function fromData(data, type, version, network) {
if (type === 'witnessscripthash')
data = utils.sha256(data);
else
data = utils.ripesha(data);
return Address.parseHash(data, type, version);
return this.fromHash(data, type, version, network);
};
/**
@ -411,8 +430,8 @@ Address.parseData = function parseData(data, type, version) {
* @returns {Address}
*/
Address.fromData = function fromData(data, type, version) {
return new Address(Address.parseData(data, type, version));
Address.fromData = function fromData(data, type, version, network) {
return new Address().fromData(data, type, version, network);
};
/**
@ -430,7 +449,7 @@ Address.validate = function validate(address, type) {
return false;
try {
address = Address.parseBase58(address);
address = Address.fromBase58(address);
} catch (e) {
return false;
}
@ -460,7 +479,7 @@ Address.getHash = function getHash(data, enc) {
hash = data.hash;
} else {
try {
hash = Address.parseBase58(data).hash;
hash = Address.fromBase58(data).hash;
} catch (e) {
return;
}

View File

@ -182,6 +182,7 @@ function Environment(options) {
this.walletdb = require('./walletdb');
this.provider = this.walletdb.Provider;
this.peer = require('./peer');
this.networkaddress = this.peer.NetworkAddress;
this.pool = require('./pool');
this.miner = require('./miner');
this.minerblock = this.miner.MinerBlock;

View File

@ -275,12 +275,8 @@ Peer.prototype._onConnect = function _onConnect() {
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
remote: {},
local: {
services: constants.LOCAL_SERVICES,
host: this.pool.host,
port: this.pool.port
},
remote: new NetworkAddress(),
local: this.pool.address,
nonce: this.pool.localNonce,
agent: constants.USER_AGENT,
height: this.chain.height,
@ -288,15 +284,10 @@ Peer.prototype._onConnect = function _onConnect() {
}));
// Advertise our address.
if (this.pool.host !== '0.0.0.0'
if (this.pool.address.host !== '0.0.0.0'
&& !this.pool.options.selfish
&& this.pool.server) {
this.write(this.framer.addr([{
ts: utils.now() - (process.uptime() | 0),
services: constants.LOCAL_SERVICES,
host: this.pool.host,
port: this.pool.port
}]));
this.write(this.framer.addr([this.pool.address]));
}
};
@ -1443,7 +1434,7 @@ Peer.prototype._handleAddr = function _handleAddr(addrs) {
var i, addr;
for (i = 0; i < addrs.length; i++) {
addr = new NetworkAddress(addrs[i]);
addr = addrs[i];
this.addrFilter.add(addr.host, 'ascii');
hosts.push(addr);
}
@ -1866,17 +1857,23 @@ Peer.prototype.inspect = function inspect() {
*/
function NetworkAddress(options) {
var host, ts, now;
if (!(this instanceof NetworkAddress))
return new NetworkAddress(options);
now = utils.now();
host = options.host;
ts = options.ts;
this.id = NetworkAddress.uid++;
this.host = '0.0.0.0';
this.port = 0;
this.services = 0;
this.ts = 0;
if (ts <= 100000000 || ts > now + 10 * 60)
ts = now - 5 * 24 * 60 * 60;
if (options)
this.fromOptions(options);
}
NetworkAddress.uid = 0;
NetworkAddress.prototype.fromOptions = function fromOptions(options) {
var host = options.host;
if (IP.version(host) !== -1)
host = IP.normalize(host);
@ -1886,14 +1883,17 @@ function NetworkAddress(options) {
assert(typeof options.services === 'number');
assert(typeof options.ts === 'number');
this.id = NetworkAddress.uid++;
this.host = host;
this.port = options.port;
this.services = options.services;
this.ts = ts;
}
this.ts = options.ts;
NetworkAddress.uid = 0;
return this;
};
NetworkAddress.fromOptions = function fromOptions(options) {
return NetworkAddress().fromOptions(options);
};
/**
* Test whether the `host` field is an ip address.
@ -1962,18 +1962,63 @@ NetworkAddress.prototype.inspect = function inspect() {
* @returns {NetworkAddress}
*/
NetworkAddress.fromHostname = function fromHostname(hostname, network) {
NetworkAddress.prototype.fromHostname = function fromHostname(hostname, network) {
var address = IP.parseHost(hostname);
network = bcoin.network.get(network);
return new NetworkAddress({
host: address.host,
port: address.port || network.port,
version: constants.VERSION,
services: constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS,
ts: utils.now()
});
this.host = address.host;
this.port = address.port || network.port;
this.version = constants.VERSION;
this.services = constants.services.NETWORK
| constants.services.BLOOM
| constants.services.WITNESS;
this.ts = bcoin.now();
return this;
};
NetworkAddress.fromHostname = function fromHostname(hostname, network) {
return new NetworkAddress().fromHostname(hostname, network);
};
NetworkAddress.prototype.fromRaw = function fromRaw(data, full) {
var p = bcoin.reader(data);
var now = bcoin.now();
if (full) // only version >= 31402
this.ts = p.readU32();
else
this.ts = 0;
this.services = p.readU53();
this.host = IP.toString(p.readBytes(16));
this.port = p.readU16BE();
if (this.ts <= 100000000 || this.ts > now + 10 * 60)
this.ts = now - 5 * 24 * 60 * 60;
return this;
};
NetworkAddress.fromRaw = function fromRaw(data, full) {
return new NetworkAddress().fromRaw(data, full);
};
NetworkAddress.prototype.toRaw = function toRaw(full, writer) {
var p = bcoin.writer(writer);
if (full)
p.writeU32(this.ts);
p.writeU64(this.services);
p.writeBytes(IP.toBuffer(this.host));
p.writeU16BE(this.port);
if (!writer)
p = p.render();
return p;
};
/*

View File

@ -116,8 +116,13 @@ function Pool(options) {
this.hosts = [];
this.hostMap = {};
this.host = '0.0.0.0';
this.port = this.network.port;
this.address = new NetworkAddress({
ts: utils.now() - (process.uptime() | 0),
services: constants.LOCAL_SERVICES,
host: '0.0.0.0',
port: this.network.port
});
this.server = null;
this.destroyed = false;
@ -326,7 +331,7 @@ Pool.prototype._init = function _init() {
bcoin.error(err);
if (ip) {
self.host = ip;
self.address.host = ip;
bcoin.debug('External IP found: %s.', ip);
}

View File

@ -407,34 +407,6 @@ Framer.prototype.feeFilter = function feeFilter(options) {
return this.packet('feefilter', Framer.feeFilter(options));
};
/**
* Serialize an address.
* @param {NakedNetworkAddress} data
* @param {Boolean?} full - Whether to include the timestamp.
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
Framer.address = function address(data, full, writer) {
var p = new BufferWriter(writer);
if (full) {
if (!data.ts)
p.writeU32(utils.now() - (process.uptime() | 0));
else
p.writeU32(data.ts);
}
p.writeU64(data.services || 0);
p.writeBytes(IP.toBuffer(data.host));
p.writeU16BE(data.port || 0);
if (!writer)
p = p.render();
return p;
};
/**
* Create a version packet (without a header).
* @param {VersionPacket} options
@ -446,8 +418,8 @@ Framer.version = function version(options, writer) {
var p = new BufferWriter(writer);
var agent = options.agent || constants.USER_AGENT;
var services = options.services;
var remote = options.remote || {};
var local = options.local || {};
var remote = options.remote || new bcoin.networkaddress();
var local = options.local || new bcoin.networkaddress();
var nonce = options.nonce;
if (services == null)
@ -462,8 +434,8 @@ Framer.version = function version(options, writer) {
p.write32(options.version || constants.VERSION);
p.writeU64(services);
p.write64(options.ts || bcoin.now());
Framer.address(remote, false, p);
Framer.address(local, false, p);
remote.toRaw(false, p);
local.toRaw(false, p);
p.writeU64(nonce);
p.writeVarString(agent, 'ascii');
p.write32(options.height || 0);
@ -765,7 +737,7 @@ Framer.reject = function reject(details, writer) {
/**
* Create an addr packet (without a header).
* @param {NakedNetworkAddress[]} hosts
* @param {NetworkAddress[]} hosts
* @param {BufferWriter?} writer - A buffer writer to continue writing from.
* @returns {Buffer} Returns a BufferWriter if `writer` was passed in.
*/
@ -777,7 +749,7 @@ Framer.addr = function addr(hosts, writer) {
p.writeVarint(hosts.length);
for (i = 0; i < hosts.length; i++)
Framer.address(hosts[i], true, p);
hosts[i].toRaw(true, p);
if (!writer)
p = p.render();

View File

@ -481,10 +481,10 @@ Parser.parseVersion = function parseVersion(p) {
version = p.read32();
services = p.readU53();
ts = p.read53();
recv = Parser.parseAddress(p, false);
recv = bcoin.networkaddress.fromRaw(p, false);
if (p.left() > 0) {
from = Parser.parseAddress(p, false);
from = bcoin.networkaddress.fromRaw(p, false);
nonce = p.readU64();
} else {
from = {};
@ -720,40 +720,10 @@ Parser.parseReject = function parseReject(p) {
};
};
/**
* Parse serialized network address.
* @param {Buffer|BufferReader} p
* @returns {NakedNetworkAddress}
*/
Parser.parseAddress = function parseAddress(p, full) {
var ts, services, ip, port;
p = new BufferReader(p);
if (full) // only version >= 31402
ts = p.readU32();
else
ts = 0;
services = p.readU53();
ip = p.readBytes(16);
port = p.readU16BE();
return {
ts: ts,
services: services,
host: IP.toString(ip),
port: port
};
};
/**
* Parse addr packet.
* @param {Buffer|BufferReader} p
* @returns {NakedNetworkAddress[]}
* @returns {NetworkAddress[]}
*/
Parser.parseAddr = function parseAddr(p) {
@ -767,7 +737,7 @@ Parser.parseAddr = function parseAddr(p) {
assert(count <= 10000, 'Too many addresses.');
for (i = 0; i < count; i++)
addrs.push(Parser.parseAddress(p, true));
addrs.push(bcoin.networkaddress.fromRaw(p, true));
return addrs;
};

View File

@ -2771,7 +2771,7 @@ Script.prototype.isWitnessProgram = function isWitnessProgram() {
* @returns {Program|null}
*/
Script.prototype.getWitnessProgram = function getWitnessProgram() {
Script.prototype.toProgram = function toProgram() {
var version, data, type;
if (!this.isWitnessProgram())
@ -2780,23 +2780,7 @@ Script.prototype.getWitnessProgram = function getWitnessProgram() {
version = Script.getSmall(this.raw[0]);
data = this.raw.slice(2);
if (version > 0) {
// No interpretation of script (anyone can spend)
type = 'unknown';
} else if (version === 0 && data.length === 20) {
type = 'witnesspubkeyhash';
} else if (version === 0 && data.length === 32) {
type = 'witnessscripthash';
} else {
// Fail on bad version=0
type = null;
}
return {
version: version,
type: type,
data: data
};
return new Program(version, data);
};
/**
@ -3819,7 +3803,7 @@ Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flag
assert((flags & constants.flags.VERIFY_P2SH) !== 0);
if (output.isWitnessProgram())
return Script.witnessSigops(output.getWitnessProgram(), witness, flags);
return Script.witnessSigops(output.toProgram(), witness, flags);
// This is a unique situation in terms of consensus
// rules. We can just grab the redeem script without
@ -3831,7 +3815,7 @@ Script.getWitnessSigops = function getWitnessSigops(input, output, witness, flag
if (output.isScripthash() && input.isPushOnly()) {
redeem = input.getRedeem();
if (redeem && redeem.isWitnessProgram())
return Script.witnessSigops(redeem.getWitnessProgram(), witness, flags);
return Script.witnessSigops(redeem.toProgram(), witness, flags);
}
return 0;
@ -4050,7 +4034,7 @@ Script.verify = function verify(input, witness, output, tx, i, flags) {
*/
Script.verifyProgram = function verifyProgram(witness, output, flags, tx, i) {
var program = output.getWitnessProgram();
var program = output.toProgram();
var stack = witness.toStack();
var witnessScript, redeem, j;
@ -4505,6 +4489,47 @@ Opcode.isOpcode = function isOpcode(obj) {
&& (Buffer.isBuffer(obj.data) || obj.data === null);
};
/**
* Witness Program
* @constructor
* @private
* @param {Number} version
* @param {Buffer} data
*/
function Program(version, data) {
if (!(this instanceof Program))
return new Program(version, data);
this.version = version;
this.data = data;
this.type = null;
if (version > 0) {
// No interpretation of script (anyone can spend)
this.type = 'unknown';
} else if (version === 0 && data.length === 20) {
this.type = 'witnesspubkeyhash';
} else if (version === 0 && data.length === 32) {
this.type = 'witnessscripthash';
} else {
// Fail on bad version=0
this.type = null;
}
}
Program.prototype.isUnknown = function isUnknown() {
return !this.type || this.type === 'unknown';
};
Program.prototype.inspect = function inspect() {
return '<Program:'
+ ' version=' + this.version
+ ' data=' + this.data.toString('hex')
+ ' type=' + this.type
+ '>';
};
/*
* Expose
*/

View File

@ -31,14 +31,38 @@ describe('Protocol', function() {
});
}
packetTest('version', {}, function(payload) {
var v1 = {
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
remote: new bcoin.networkaddress(),
local: new bcoin.networkaddress(),
nonce: utils.nonce(),
agent: constants.USER_AGENT,
height: 0,
relay: false
};
packetTest('version', v1, function(payload) {
assert.equal(payload.version, constants.VERSION);
assert.equal(payload.agent, agent);
assert.equal(payload.height, 0);
assert.equal(payload.relay, false);
});
packetTest('version', { relay: true, height: 10 }, function(payload) {
var v2 = {
version: constants.VERSION,
services: constants.LOCAL_SERVICES,
ts: bcoin.now(),
remote: new bcoin.networkaddress(),
local: new bcoin.networkaddress(),
nonce: utils.nonce(),
agent: constants.USER_AGENT,
height: 10,
relay: true
};
packetTest('version', v2, function(payload) {
assert.equal(payload.version, constants.VERSION);
assert.equal(payload.agent, agent);
assert.equal(payload.height, 10);
@ -49,18 +73,18 @@ describe('Protocol', function() {
});
var hosts = [
{
new bcoin.networkaddress({
services: constants.LOCAL_SERVICES,
host: '127.0.0.1',
port: 8333,
ts: Date.now() / 1000 | 0
},
{
}),
new bcoin.networkaddress({
services: constants.LOCAL_SERVICES,
host: '::123:456:789a',
port: 18333,
ts: Date.now() / 1000 | 0
}
})
];
packetTest('addr', hosts, function(payload) {

View File

@ -105,9 +105,9 @@ describe('Wallet', function() {
assert.ifError(err);
if (witness)
assert(bcoin.address.parseBase58(w.getAddress()).type === 'witnesspubkeyhash');
assert(bcoin.address.fromBase58(w.getAddress()).type === 'witnesspubkeyhash');
else
assert(bcoin.address.parseBase58(w.getAddress()).type === 'pubkeyhash');
assert(bcoin.address.fromBase58(w.getAddress()).type === 'pubkeyhash');
var src = bcoin.mtx({
outputs: [{
@ -606,9 +606,9 @@ describe('Wallet', function() {
var addr = w1.getAddress();
if (witness)
assert(bcoin.address.parseBase58(addr).type === 'witnessscripthash');
assert(bcoin.address.fromBase58(addr).type === 'witnessscripthash');
else
assert(bcoin.address.parseBase58(addr).type === 'scripthash');
assert(bcoin.address.fromBase58(addr).type === 'scripthash');
assert.equal(w1.getAddress(), addr);
assert.equal(w2.getAddress(), addr);