@libp2p/pubsub
Version:
libp2p pubsub base class
129 lines • 4.16 kB
JavaScript
import { randomBytes } from '@libp2p/crypto';
import { publicKeyFromProtobuf, publicKeyToProtobuf } from '@libp2p/crypto/keys';
import { InvalidMessageError } from '@libp2p/interface';
import { peerIdFromMultihash, peerIdFromPublicKey } from '@libp2p/peer-id';
import * as Digest from 'multiformats/hashes/digest';
import { sha256 } from 'multiformats/hashes/sha2';
import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
import { toString as uint8ArrayToString } from 'uint8arrays/to-string';
/**
* Generate a random sequence number
*/
export function randomSeqno() {
return BigInt(`0x${uint8ArrayToString(randomBytes(8), 'base16')}`);
}
/**
* Generate a message id, based on the `key` and `seqno`
*/
export const msgId = (key, seqno) => {
const seqnoBytes = uint8ArrayFromString(seqno.toString(16).padStart(16, '0'), 'base16');
const keyBytes = publicKeyToProtobuf(key);
const msgId = new Uint8Array(keyBytes.byteLength + seqnoBytes.length);
msgId.set(keyBytes, 0);
msgId.set(seqnoBytes, keyBytes.byteLength);
return msgId;
};
/**
* Generate a message id, based on message `data`
*/
export const noSignMsgId = (data) => {
return sha256.encode(data);
};
/**
* Check if any member of the first set is also a member
* of the second set
*/
export const anyMatch = (a, b) => {
let bHas;
if (Array.isArray(b)) {
bHas = (val) => b.includes(val);
}
else {
bHas = (val) => b.has(val);
}
for (const val of a) {
if (bHas(val)) {
return true;
}
}
return false;
};
/**
* Make everything an array
*/
export const ensureArray = function (maybeArray) {
if (!Array.isArray(maybeArray)) {
return [maybeArray];
}
return maybeArray;
};
const isSigned = async (message) => {
if ((message.sequenceNumber == null) || (message.from == null) || (message.signature == null)) {
return false;
}
// if a public key is present in the `from` field, the message should be signed
const fromID = peerIdFromMultihash(Digest.decode(message.from));
if (fromID.publicKey != null) {
return true;
}
if (message.key != null) {
const signingKey = message.key;
const signingID = peerIdFromPublicKey(publicKeyFromProtobuf(signingKey));
return signingID.equals(fromID);
}
return false;
};
export const toMessage = async (message) => {
if (message.from == null) {
throw new InvalidMessageError('RPC message was missing from');
}
if (!await isSigned(message)) {
return {
type: 'unsigned',
topic: message.topic ?? '',
data: message.data ?? new Uint8Array(0)
};
}
const from = peerIdFromMultihash(Digest.decode(message.from));
const key = message.key ?? from.publicKey;
if (key == null) {
throw new InvalidMessageError('RPC message was missing public key');
}
const msg = {
type: 'signed',
from,
topic: message.topic ?? '',
sequenceNumber: bigIntFromBytes(message.sequenceNumber ?? new Uint8Array(0)),
data: message.data ?? new Uint8Array(0),
signature: message.signature ?? new Uint8Array(0),
key: key instanceof Uint8Array ? publicKeyFromProtobuf(key) : key
};
return msg;
};
export const toRpcMessage = (message) => {
if (message.type === 'signed') {
return {
from: message.from.toMultihash().bytes,
data: message.data,
sequenceNumber: bigIntToBytes(message.sequenceNumber),
topic: message.topic,
signature: message.signature,
key: message.key ? publicKeyToProtobuf(message.key) : undefined
};
}
return {
data: message.data,
topic: message.topic
};
};
export const bigIntToBytes = (num) => {
let str = num.toString(16);
if (str.length % 2 !== 0) {
str = `0${str}`;
}
return uint8ArrayFromString(str, 'base16');
};
export const bigIntFromBytes = (num) => {
return BigInt(`0x${uint8ArrayToString(num, 'base16')}`);
};
//# sourceMappingURL=utils.js.map