src/main/generic/network/address/PeerAddress.js
class PeerAddress {
/**
* @param {number} protocol
* @param {number} services
* @param {number} timestamp
* @param {NetAddress} netAddress
* @param {PublicKey} publicKey
* @param {number} distance
* @param {Signature} [signature]
*/
constructor(protocol, services, timestamp, netAddress, publicKey, distance, signature) {
if (!NumberUtils.isUint8(distance)) throw new Error('Malformed distance');
if (publicKey !== null && !(publicKey instanceof PublicKey)) throw new Error('Malformed publicKey');
/** @type {number} */
this._protocol = protocol;
/** @type {number} */
this._services = services;
/** @type {number} */
this._timestamp = timestamp;
/** @type {NetAddress} */
this._netAddress = netAddress || NetAddress.UNSPECIFIED;
/** @type {PublicKey} */
this._publicKey = publicKey;
/** @type {number} */
this._distance = distance;
/** @type {?Signature} */
this._signature = signature;
}
/**
* @param {SerialBuffer} buf
* @returns {PeerAddress}
*/
static unserialize(buf) {
const protocol = buf.readUint8();
switch (protocol) {
case Protocol.WS:
return WsPeerAddress.unserialize(buf);
case Protocol.RTC:
return RtcPeerAddress.unserialize(buf);
case Protocol.DUMB:
return DumbPeerAddress.unserialize(buf);
default:
throw `Malformed PeerAddress protocol ${protocol}`;
}
}
/**
* @param {SerialBuffer} [buf]
* @returns {SerialBuffer}
*/
serialize(buf) {
if (!this._publicKey) throw new Error('PeerAddress without publicKey may not be serialized.');
if (!this._signature) throw new Error('PeerAddress without signature may not be serialized.');
buf = buf || new SerialBuffer(this.serializedSize);
buf.writeUint8(this._protocol);
buf.writeUint32(this._services);
buf.writeUint64(this._timestamp);
// Never serialize private netAddresses.
if (this._netAddress.isPrivate()) {
NetAddress.UNSPECIFIED.serialize(buf);
} else {
this._netAddress.serialize(buf);
}
this._publicKey.serialize(buf);
buf.writeUint8(this._distance);
this._signature.serialize(buf);
return buf;
}
serializeContent(buf) {
buf = buf || new SerialBuffer(this.serializedContentSize);
buf.writeUint8(this._protocol);
buf.writeUint32(this._services);
buf.writeUint64(this._timestamp);
return buf;
}
/** @type {number} */
get serializedSize() {
return /*protocol*/ 1
+ /*services*/ 4
+ /*timestamp*/ 8
+ this._netAddress.serializedSize
+ this._publicKey.serializedSize
+ /*distance*/ 1
+ this._signature.serializedSize;
}
/** @type {number} */
get serializedContentSize() {
return /*protocol*/ 1
+ /*services*/ 4
+ /*timestamp*/ 8;
}
/**
* @param {PeerAddress|*} o
* @returns {boolean}
*/
equals(o) {
// We consider peer addresses to be equal if the public key or peer id is not known on one of them:
// Peers from the network always contain a peer id and public key, peers without peer id or public key
// are always set by the user.
return o instanceof PeerAddress
&& this.protocol === o.protocol
&& (!this.publicKey || !o.publicKey || this.publicKey.equals(o.publicKey))
&& (!this.peerId || !o.peerId || this.peerId.equals(o.peerId))
/* services is ignored */
/* timestamp is ignored */
/* netAddress is ignored */
/* distance is ignored */;
}
/**
* @returns {string}
*/
hashCode() {
throw new Error('unimplemented');
}
/**
* @returns {boolean}
*/
verifySignature() {
if (this._signatureVerified === undefined) {
this._signatureVerified = this.signature.verify(this.publicKey, this.serializeContent());
}
return this._signatureVerified;
}
/** @type {number} */
get protocol() {
return this._protocol;
}
/** @type {number} */
get services() {
return this._services;
}
/** @type {number} */
get timestamp() {
return this._timestamp;
}
/** @type {NetAddress} */
get netAddress() {
return this._netAddress.isPseudo() ? null : this._netAddress;
}
/** @type {NetAddress} */
set netAddress(value) {
this._netAddress = value || NetAddress.UNSPECIFIED;
}
/** @type {PublicKey} */
get publicKey() {
return this._publicKey;
}
/** @type {PeerId} */
get peerId() {
return this._publicKey ? this._publicKey.toPeerId() : null;
}
/** @type {number} */
get distance() {
return this._distance;
}
/** @type {Signature} */
get signature() {
return this._signature;
}
/** @type {Signature} */
set signature(signature) {
// Never change the signature of a remote address.
if (this._distance !== 0) {
return;
}
this._signature = signature;
this._signatureVerified = undefined;
}
// Changed when passed on to other peers.
/** @type {number} */
set distance(value) {
this._distance = value;
}
/**
* @returns {boolean}
*/
isSeed() {
return this._timestamp === 0;
}
/**
* @returns {boolean}
*/
exceedsAge() {
// Seed addresses are never too old.
if (this.isSeed()) {
return false;
}
const age = Date.now() - this.timestamp;
switch (this.protocol) {
case Protocol.WS:
return age > PeerAddressBook.MAX_AGE_WEBSOCKET;
case Protocol.RTC:
return age > PeerAddressBook.MAX_AGE_WEBRTC;
case Protocol.DUMB:
return age > PeerAddressBook.MAX_AGE_DUMB;
}
return false;
}
}
Class.register(PeerAddress);
class WsPeerAddress extends PeerAddress {
/**
* @param {string} host
* @param {number} port
* @param {string} [publicKeyHex]
* @returns {WsPeerAddress}
*/
static seed(host, port, publicKeyHex) {
const publicKey = publicKeyHex ? new PublicKey(BufferUtils.fromHex(publicKeyHex)) : null;
return new WsPeerAddress(Services.FULL, /*timestamp*/ 0, NetAddress.UNSPECIFIED, publicKey, 0, host, port);
}
/**
* @param {number} services
* @param {number} timestamp
* @param {NetAddress} netAddress
* @param {PublicKey} publicKey
* @param {number} distance
* @param {string} host
* @param {number} port
* @param {Signature} [signature]
*/
constructor(services, timestamp, netAddress, publicKey, distance, host, port, signature) {
super(Protocol.WS, services, timestamp, netAddress, publicKey, distance, signature);
if (!host) throw new Error('Malformed host');
if (!NumberUtils.isUint16(port)) throw new Error('Malformed port');
this._host = host;
this._port = port;
}
/**
* @param {SerialBuffer} buf
* @returns {WsPeerAddress}
*/
static unserialize(buf) {
const services = buf.readUint32();
const timestamp = buf.readUint64();
const netAddress = NetAddress.unserialize(buf);
const publicKey = PublicKey.unserialize(buf);
const distance = buf.readUint8();
const signature = Signature.unserialize(buf);
const host = buf.readVarLengthString();
const port = buf.readUint16();
return new WsPeerAddress(services, timestamp, netAddress, publicKey, distance, host, port, signature);
}
/**
* @param {SerialBuffer} [buf]
* @returns {SerialBuffer}
*/
serialize(buf) {
buf = buf || new SerialBuffer(this.serializedSize);
super.serialize(buf);
buf.writeVarLengthString(this._host);
buf.writeUint16(this._port);
return buf;
}
/**
* @param {SerialBuffer} [buf]
* @returns {SerialBuffer}
*/
serializeContent(buf) {
buf = buf || new SerialBuffer(this.serializedContentSize);
super.serializeContent(buf);
buf.writeVarLengthString(this._host);
buf.writeUint16(this._port);
return buf;
}
/**
* @returns {boolean}
*/
globallyReachable() {
return NetUtils.hostGloballyReachable(this.host);
}
/** @type {number} */
get serializedSize() {
return super.serializedSize
+ SerialBuffer.varLengthStringSize(this._host)
+ /*port*/ 2;
}
/** @type {number} */
get serializedContentSize() {
return super.serializedContentSize
+ SerialBuffer.varLengthStringSize(this._host)
+ /*port*/ 2;
}
/**
* @override
* @param {PeerAddress|*} o
* @returns {boolean}
*/
equals(o) {
return super.equals(o)
&& o instanceof WsPeerAddress
&& ((!!this.peerId && !!o.peerId) || (this._host === o.host && this._port === o.port));
}
/**
* @returns {string}
*/
hashCode() {
return this.peerId
? `wss:///${this.peerId}`
: `wss://${this._host}:${this._port}/`;
}
/**
* @returns {string}
*/
toString() {
return `wss://${this._host}:${this._port}/${this.peerId ? this.peerId : ''}`;
}
/**
* @returns {WsPeerAddress}
*/
withoutId() {
return new WsPeerAddress(this.services, this.timestamp, this.netAddress, null, this.distance, this.host, this.port);
}
/** @type {string} */
get host() {
return this._host;
}
/** @type {number} */
get port() {
return this._port;
}
}
Class.register(WsPeerAddress);
class RtcPeerAddress extends PeerAddress {
/**
* @param {number} services
* @param {number} timestamp
* @param {NetAddress} netAddress
* @param {PublicKey} publicKey
* @param {number} distance
* @param {Signature} [signature]
*/
constructor(services, timestamp, netAddress, publicKey, distance, signature) {
super(Protocol.RTC, services, timestamp, netAddress, publicKey, distance, signature);
}
/**
* @param {SerialBuffer} buf
* @returns {RtcPeerAddress}
*/
static unserialize(buf) {
const services = buf.readUint32();
const timestamp = buf.readUint64();
const netAddress = NetAddress.unserialize(buf);
const publicKey = PublicKey.unserialize(buf);
const distance = buf.readUint8();
const signature = Signature.unserialize(buf);
return new RtcPeerAddress(services, timestamp, netAddress, publicKey, distance, signature);
}
/**
* @param {SerialBuffer} [buf]
* @returns {SerialBuffer}
*/
serialize(buf) {
buf = buf || new SerialBuffer(this.serializedSize);
super.serialize(buf);
return buf;
}
/** @type {number} */
get serializedSize() {
return super.serializedSize;
}
/**
* @override
* @param {PeerAddress|*} o
* @returns {boolean}
*/
equals(o) {
return super.equals(o)
&& o instanceof RtcPeerAddress;
}
/**
* @returns {string}
*/
hashCode() {
return this.toString();
}
/**
* @returns {string}
*/
toString() {
return `rtc:///${this.peerId}`;
}
}
Class.register(RtcPeerAddress);
class DumbPeerAddress extends PeerAddress {
/**
* @param {number} services
* @param {number} timestamp
* @param {NetAddress} netAddress
* @param {PublicKey} publicKey
* @param {number} distance
* @param {Signature} [signature]
*/
constructor(services, timestamp, netAddress, publicKey, distance, signature) {
super(Protocol.DUMB, services, timestamp, netAddress, publicKey, distance, signature);
}
/**
* @param {SerialBuffer} buf
* @returns {DumbPeerAddress}
*/
static unserialize(buf) {
const services = buf.readUint32();
const timestamp = buf.readUint64();
const netAddress = NetAddress.unserialize(buf);
const publicKey = PublicKey.unserialize(buf);
const distance = buf.readUint8();
const signature = Signature.unserialize(buf);
return new DumbPeerAddress(services, timestamp, netAddress, publicKey, distance, signature);
}
/**
* @param {SerialBuffer} [buf]
* @returns {SerialBuffer}
*/
serialize(buf) {
buf = buf || new SerialBuffer(this.serializedSize);
super.serialize(buf);
return buf;
}
/** @type {number} */
get serializedSize() {
return super.serializedSize;
}
/**
* @override
* @param {PeerAddress} o
* @returns {boolean}
*/
equals(o) {
return super.equals(o)
&& o instanceof DumbPeerAddress;
}
/**
* @returns {string}
*/
hashCode() {
return this.toString();
}
/**
* @returns {string}
*/
toString() {
return `dumb:///${this.peerId}`;
}
}
Class.register(DumbPeerAddress);