UNPKG

@ethereumjs/devp2p

Version:
190 lines 6.47 kB
import { RLP } from '@ethereumjs/rlp'; import { EthereumJSErrorWithoutCode, bytesToHex, bytesToUnprefixedHex, concatBytes, equalsBytes, } from '@ethereumjs/util'; import debug from 'debug'; import { publicKeyConvert } from 'ethereum-cryptography/secp256k1-compat.js'; import { secp256k1 } from 'ethereum-cryptography/secp256k1.js'; // Do not use :# here, no logging without sub namespace occurring and current code structure // otherwise creates loggers like `devp2p:#:eth` export const devp2pDebug = debug('devp2p'); export function genPrivateKey() { const privateKey = secp256k1.utils.randomPrivateKey(); return secp256k1.utils.isValidPrivateKey(privateKey) === true ? privateKey : genPrivateKey(); } export function pk2id(pk) { if (pk.length === 33) { pk = publicKeyConvert(pk, false); } return pk.subarray(1); } export function id2pk(id) { return concatBytes(Uint8Array.from([0x04]), id); } export function zfill(bytes, size, leftpad = true) { if (bytes.length >= size) return bytes; if (leftpad === undefined) leftpad = true; const pad = new Uint8Array(size - bytes.length).fill(0x00); return leftpad ? concatBytes(pad, bytes) : concatBytes(bytes, pad); } export function xor(a, b) { const length = Math.min(a.length, b.length); const bytes = new Uint8Array(length); for (let i = 0; i < length; ++i) bytes[i] = a[i] ^ b[i]; return bytes; } export function assertEq(expected, actual, msg, debug, messageName) { let fullMsg; if (expected instanceof Uint8Array && actual instanceof Uint8Array) { if (equalsBytes(expected, actual)) return; fullMsg = `${msg}: ${bytesToHex(expected)} / ${bytesToHex(actual)}`; const debugMsg = `[ERROR] ${fullMsg}`; if (messageName !== undefined) { debug(messageName, debugMsg); } else { debug(debugMsg); } throw EthereumJSErrorWithoutCode(fullMsg); } if (expected === actual) return; fullMsg = `${msg}: ${expected} / ${actual}`; if (messageName !== undefined) { debug(messageName, fullMsg); } else { debug(fullMsg); } throw EthereumJSErrorWithoutCode(fullMsg); } export function formatLogId(id, verbose) { const numChars = 7; if (verbose) { return id; } else { return `${id.substring(0, numChars)}`; } } export function formatLogData(data, verbose) { const maxChars = 60; if (verbose || data.length <= maxChars) { return data; } else { return `${data.substring(0, maxChars)}...`; } } export class Deferred { constructor() { this.resolve = () => { }; this.reject = () => { }; this.promise = new Promise((resolve, reject) => { this.resolve = resolve; this.reject = reject; }); } } export function createDeferred() { return new Deferred(); } export function unstrictDecode(value) { // rlp library throws on remainder.length !== 0 // this utility function bypasses that return RLP.decode(value, true).data; } /*************************** ************************************************************/ // Methods borrowed from `node-ip` by Fedor Indutny (https://github.com/indutny/node-ip) // and modified to use Uint8Arrays instead of Buffers export const ipToString = (bytes, offset, length) => { offset = offset !== undefined ? ~~offset : 0; length = length ?? bytes.length - offset; const tempArray = []; let result = ''; if (length === 4) { // IPv4 for (let i = 0; i < length; i++) { tempArray.push(bytes[offset + i]); } result = tempArray.join('.'); } else if (length === 16) { // IPv6 for (let i = 0; i < length; i += 2) { tempArray.push(new DataView(bytes.buffer).getUint16(offset + i).toString(16)); } result = tempArray.join(':'); result = result.replace(/(^|:)0(:0)*:0(:|$)/, '$1::$3'); result = result.replace(/:{3,4}/, '::'); } return result; }; const ipv4Regex = /^(\d{1,3}\.){3,3}\d{1,3}$/; const ipv6Regex = /^(::)?(((\d{1,3}\.){3}(\d{1,3}){1})?([0-9a-f]){0,4}:{0,2}){1,8}(::)?$/i; export const isV4Format = function (ip) { return ipv4Regex.test(ip); }; export const isV6Format = function (ip) { return ipv6Regex.test(ip); }; export const ipToBytes = (ip, bytes, offset = 0) => { offset = ~~offset; let result; if (isV4Format(ip)) { result = bytes ?? new Uint8Array(offset + 4); ip.split(/\./g).map((byte) => { result[offset++] = parseInt(byte, 10) & 0xff; }); } else if (isV6Format(ip)) { const sections = ip.split(':', 8); let i; for (i = 0; i < sections.length; i++) { const isv4 = isV4Format(sections[i]); let v4Bytes = new Uint8Array([]); if (isv4) { v4Bytes = ipToBytes(sections[i]); sections[i] = bytesToUnprefixedHex(v4Bytes.subarray(0, 2)); } if (v4Bytes.length > 0 && ++i < 8) { sections.splice(i, 0, bytesToUnprefixedHex(v4Bytes.subarray(2, 4))); } } if (sections[0] === '') { while (sections.length < 8) sections.unshift('0'); } else if (sections[sections.length - 1] === '') { while (sections.length < 8) sections.push('0'); } else if (sections.length < 8) { for (i = 0; i < sections.length && sections[i] !== ''; i++) ; const argv = [i, 1]; for (i = 9 - sections.length; i > 0; i--) { argv.push('0'); } // eslint-disable-next-line prefer-spread sections.splice.apply(sections, argv); } result = bytes ?? new Uint8Array(offset + 16); for (i = 0; i < sections.length; i++) { const word = parseInt(sections[i], 16); result[offset++] = (word >> 8) & 0xff; result[offset++] = word & 0xff; } } else { throw Error(`Invalid ip format: ${ip}`); } if (result === undefined) { throw Error(`Invalid ip address: ${ip}`); } return result; }; /************ End of methods borrowed from `node-ip` ***************************/ //# sourceMappingURL=util.js.map