From 92a232586afe545dfc2e5a7fbb3ea6f2b4d33266 Mon Sep 17 00:00:00 2001 From: achingbrain Date: Mon, 16 Jun 2025 14:31:11 +0300 Subject: [PATCH] fix: solve circular import --- src/convert.ts | 237 ------------------------------------------ src/registry.ts | 2 +- src/utils.ts | 239 +++++++++++++++++++++++++++++++++++++++++++ test/convert.spec.ts | 95 +---------------- test/utils.spec.ts | 98 ++++++++++++++++++ 5 files changed, 339 insertions(+), 332 deletions(-) create mode 100644 src/utils.ts create mode 100644 test/utils.spec.ts diff --git a/src/convert.ts b/src/convert.ts index 84da3069..18ff7be9 100644 --- a/src/convert.ts +++ b/src/convert.ts @@ -1,245 +1,8 @@ -import { isIPv4 } from '@chainsafe/is-ip' import { IpNet } from '@chainsafe/netmask' -import { base32 } from 'multiformats/bases/base32' -import { bases } from 'multiformats/basics' -import { concat as uint8ArrayConcat } from 'uint8arrays/concat' import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' import { toString as uint8ArrayToString } from 'uint8arrays/to-string' -import { InvalidMultiaddrError } from './errors.ts' import { registry } from './registry.js' import type { Multiaddr } from './index.ts' -import type { MultibaseCodec } from 'multiformats' -import type { SupportedEncodings } from 'uint8arrays/to-string' - -export function bytesToString (base: SupportedEncodings): (buf: Uint8Array) => string { - return (buf) => { - return uint8ArrayToString(buf, base) - } -} - -export function stringToBytes (base: SupportedEncodings): (value: string) => Uint8Array { - return (buf) => { - return uint8ArrayFromString(buf, base) - } -} - -export function bytes2port (buf: Uint8Array): string { - const view = new DataView(buf.buffer) - return view.getUint16(buf.byteOffset).toString() -} - -export function port2bytes (port: string | number): Uint8Array { - const buf = new ArrayBuffer(2) - const view = new DataView(buf) - view.setUint16(0, typeof port === 'string' ? parseInt(port) : port) - - return new Uint8Array(buf) -} - -export function onion2bytes (str: string): Uint8Array { - const addr = str.split(':') - - if (addr.length !== 2) { - throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) - } - - if (addr[0].length !== 16) { - throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`) - } - - // onion addresses do not include the multibase prefix, add it before decoding - const buf = uint8ArrayFromString(addr[0], 'base32') - - // onion port number - const port = parseInt(addr[1], 10) - - if (port < 1 || port > 65536) { - throw new Error('Port number is not in range(1, 65536)') - } - - const portBuf = port2bytes(port) - - return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) -} - -export function onion32bytes (str: string): Uint8Array { - const addr = str.split(':') - - if (addr.length !== 2) { - throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) - } - - if (addr[0].length !== 56) { - throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion3 address.`) - } - - // onion addresses do not include the multibase prefix, add it before decoding - const buf = base32.decode(`b${addr[0]}`) - - // onion port number - const port = parseInt(addr[1], 10) - - if (port < 1 || port > 65536) { - throw new Error('Port number is not in range(1, 65536)') - } - - const portBuf = port2bytes(port) - - return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) -} - -export function bytes2onion (buf: Uint8Array): string { - const addrBytes = buf.subarray(0, buf.length - 2) - const portBytes = buf.subarray(buf.length - 2) - const addr = uint8ArrayToString(addrBytes, 'base32') - const port = bytes2port(portBytes) - return `${addr}:${port}` -} - -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 -// but with buf/offset args removed because we don't use them -export const ip4ToBytes = function (ip: string): Uint8Array { - ip = ip.toString().trim() - - const bytes = new Uint8Array(4) - - ip.split(/\./g).forEach((byte, index) => { - const value = parseInt(byte, 10) - - if (isNaN(value) || value < 0 || value > 0xff) { - throw new InvalidMultiaddrError('Invalid byte value in IP address') - } - - bytes[index] = value - }) - - return bytes -} - -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 -// but with buf/offset args removed because we don't use them -export const ip6ToBytes = function (ip: string): Uint8Array { - let offset = 0 - ip = ip.toString().trim() - - const sections = ip.split(':', 8) - - let i - for (i = 0; i < sections.length; i++) { - const isv4 = isIPv4(sections[i]) - let v4Buffer: Uint8Array | undefined - - if (isv4) { - v4Buffer = ip4ToBytes(sections[i]) - sections[i] = uint8ArrayToString(v4Buffer.subarray(0, 2), 'base16') - } - - if (v4Buffer != null && ++i < 8) { - sections.splice(i, 0, uint8ArrayToString(v4Buffer.subarray(2, 4), 'base16')) - } - } - - 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: [number, number, ...string[]] = [i, 1] - for (i = 9 - sections.length; i > 0; i--) { - argv.push('0') - } - sections.splice.apply(sections, argv) - } - - const bytes = new Uint8Array(offset + 16) - - for (i = 0; i < sections.length; i++) { - if (sections[i] === '') { - sections[i] = '0' - } - - const word = parseInt(sections[i], 16) - - if (isNaN(word) || word < 0 || word > 0xffff) { - throw new InvalidMultiaddrError('Invalid byte value in IP address') - } - - bytes[offset++] = (word >> 8) & 0xff - bytes[offset++] = word & 0xff - } - - return bytes -} - -// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63 -export const ip4ToString = function (buf: Uint8Array): string { - if (buf.byteLength !== 4) { - throw new InvalidMultiaddrError('IPv4 address was incorrect length') - } - - const result = [] - - for (let i = 0; i < buf.byteLength; i++) { - result.push(buf[i]) - } - - return result.join('.') -} - -export const ip6ToString = function (buf: Uint8Array): string { - if (buf.byteLength !== 16) { - throw new InvalidMultiaddrError('IPv6 address was incorrect length') - } - - const result: string[] = [] - - for (let i = 0; i < buf.byteLength; i += 2) { - const byte1 = buf[i] - const byte2 = buf[i + 1] - - const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}` - - result.push(tuple) - } - - const ip = result.join(':') - - try { - const url = new URL(`http://[${ip}]`) - - return url.hostname.substring(1, url.hostname.length - 1) - } catch { - throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`) - } -} - -export function ip6StringToValue (str: string): string { - try { - const url = new URL(`http://[${str}]`) - - return url.hostname.substring(1, url.hostname.length - 1) - } catch { - throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`) - } -} - -const decoders = Object.values(bases).map((c) => c.decoder) -const anybaseDecoder = (function () { - let acc = decoders[0].or(decoders[1]) - decoders.slice(2).forEach((d) => (acc = acc.or(d))) - return acc -})() - -export function mb2bytes (mbstr: string): Uint8Array { - return anybaseDecoder.decode(mbstr) -} - -export function bytes2mb (base: MultibaseCodec): (buf: Uint8Array) => string { - return (buf) => { - return base.encoder.encode(buf) - } -} export function convertToIpNet (multiaddr: Multiaddr): IpNet { let mask: string | undefined diff --git a/src/registry.ts b/src/registry.ts index 6a10c191..da40475d 100644 --- a/src/registry.ts +++ b/src/registry.ts @@ -2,8 +2,8 @@ import { isIPv4, isIPv6 } from '@chainsafe/is-ip' import { CID } from 'multiformats' import { base64url } from 'multiformats/bases/base64' import { CODE_CERTHASH, CODE_DCCP, CODE_DNS, CODE_DNS4, CODE_DNS6, CODE_DNSADDR, CODE_GARLIC32, CODE_GARLIC64, CODE_HTTP, CODE_HTTP_PATH, CODE_HTTPS, CODE_IP4, CODE_IP6, CODE_IP6ZONE, CODE_IPCIDR, CODE_MEMORY, CODE_NOISE, CODE_ONION, CODE_ONION3, CODE_P2P, CODE_P2P_CIRCUIT, CODE_P2P_STARDUST, CODE_P2P_WEBRTC_DIRECT, CODE_P2P_WEBRTC_STAR, CODE_P2P_WEBSOCKET_STAR, CODE_QUIC, CODE_QUIC_V1, CODE_SCTP, CODE_SNI, CODE_TCP, CODE_TLS, CODE_UDP, CODE_UDT, CODE_UNIX, CODE_UTP, CODE_WEBRTC, CODE_WEBRTC_DIRECT, CODE_WEBTRANSPORT, CODE_WS, CODE_WSS } from './constants.ts' -import { bytes2mb, bytes2onion, bytes2port, bytesToString, ip4ToBytes, ip4ToString, ip6StringToValue, ip6ToBytes, ip6ToString, mb2bytes, onion2bytes, onion32bytes, port2bytes, stringToBytes } from './convert.ts' import { InvalidProtocolError, ValidationError } from './errors.ts' +import { bytes2mb, bytes2onion, bytes2port, bytesToString, ip4ToBytes, ip4ToString, ip6StringToValue, ip6ToBytes, ip6ToString, mb2bytes, onion2bytes, onion32bytes, port2bytes, stringToBytes } from './utils.ts' import { validatePort } from './validation.ts' export const V = -1 diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 00000000..d75f6b71 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,239 @@ +import { isIPv4 } from '@chainsafe/is-ip' +import { base32 } from 'multiformats/bases/base32' +import { bases } from 'multiformats/basics' +import { concat as uint8ArrayConcat } from 'uint8arrays/concat' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import { toString as uint8ArrayToString } from 'uint8arrays/to-string' +import { InvalidMultiaddrError } from './errors.ts' +import type { MultibaseCodec } from 'multiformats' +import type { SupportedEncodings } from 'uint8arrays/to-string' + +export function bytesToString (base: SupportedEncodings): (buf: Uint8Array) => string { + return (buf) => { + return uint8ArrayToString(buf, base) + } +} + +export function stringToBytes (base: SupportedEncodings): (value: string) => Uint8Array { + return (buf) => { + return uint8ArrayFromString(buf, base) + } +} + +export function bytes2port (buf: Uint8Array): string { + const view = new DataView(buf.buffer) + return view.getUint16(buf.byteOffset).toString() +} + +export function port2bytes (port: string | number): Uint8Array { + const buf = new ArrayBuffer(2) + const view = new DataView(buf) + view.setUint16(0, typeof port === 'string' ? parseInt(port) : port) + + return new Uint8Array(buf) +} + +export function onion2bytes (str: string): Uint8Array { + const addr = str.split(':') + + if (addr.length !== 2) { + throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) + } + + if (addr[0].length !== 16) { + throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion address.`) + } + + // onion addresses do not include the multibase prefix, add it before decoding + const buf = uint8ArrayFromString(addr[0], 'base32') + + // onion port number + const port = parseInt(addr[1], 10) + + if (port < 1 || port > 65536) { + throw new Error('Port number is not in range(1, 65536)') + } + + const portBuf = port2bytes(port) + + return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) +} + +export function onion32bytes (str: string): Uint8Array { + const addr = str.split(':') + + if (addr.length !== 2) { + throw new Error(`failed to parse onion addr: ["'${addr.join('", "')}'"]' does not contain a port number`) + } + + if (addr[0].length !== 56) { + throw new Error(`failed to parse onion addr: ${addr[0]} not a Tor onion3 address.`) + } + + // onion addresses do not include the multibase prefix, add it before decoding + const buf = base32.decode(`b${addr[0]}`) + + // onion port number + const port = parseInt(addr[1], 10) + + if (port < 1 || port > 65536) { + throw new Error('Port number is not in range(1, 65536)') + } + + const portBuf = port2bytes(port) + + return uint8ArrayConcat([buf, portBuf], buf.length + portBuf.length) +} + +export function bytes2onion (buf: Uint8Array): string { + const addrBytes = buf.subarray(0, buf.length - 2) + const portBytes = buf.subarray(buf.length - 2) + const addr = uint8ArrayToString(addrBytes, 'base32') + const port = bytes2port(portBytes) + return `${addr}:${port}` +} + +// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 +// but with buf/offset args removed because we don't use them +export const ip4ToBytes = function (ip: string): Uint8Array { + ip = ip.toString().trim() + + const bytes = new Uint8Array(4) + + ip.split(/\./g).forEach((byte, index) => { + const value = parseInt(byte, 10) + + if (isNaN(value) || value < 0 || value > 0xff) { + throw new InvalidMultiaddrError('Invalid byte value in IP address') + } + + bytes[index] = value + }) + + return bytes +} + +// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L7 +// but with buf/offset args removed because we don't use them +export const ip6ToBytes = function (ip: string): Uint8Array { + let offset = 0 + ip = ip.toString().trim() + + const sections = ip.split(':', 8) + + let i + for (i = 0; i < sections.length; i++) { + const isv4 = isIPv4(sections[i]) + let v4Buffer: Uint8Array | undefined + + if (isv4) { + v4Buffer = ip4ToBytes(sections[i]) + sections[i] = uint8ArrayToString(v4Buffer.subarray(0, 2), 'base16') + } + + if (v4Buffer != null && ++i < 8) { + sections.splice(i, 0, uint8ArrayToString(v4Buffer.subarray(2, 4), 'base16')) + } + } + + 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: [number, number, ...string[]] = [i, 1] + for (i = 9 - sections.length; i > 0; i--) { + argv.push('0') + } + sections.splice.apply(sections, argv) + } + + const bytes = new Uint8Array(offset + 16) + + for (i = 0; i < sections.length; i++) { + if (sections[i] === '') { + sections[i] = '0' + } + + const word = parseInt(sections[i], 16) + + if (isNaN(word) || word < 0 || word > 0xffff) { + throw new InvalidMultiaddrError('Invalid byte value in IP address') + } + + bytes[offset++] = (word >> 8) & 0xff + bytes[offset++] = word & 0xff + } + + return bytes +} + +// Copied from https://github.com/indutny/node-ip/blob/master/lib/ip.js#L63 +export const ip4ToString = function (buf: Uint8Array): string { + if (buf.byteLength !== 4) { + throw new InvalidMultiaddrError('IPv4 address was incorrect length') + } + + const result = [] + + for (let i = 0; i < buf.byteLength; i++) { + result.push(buf[i]) + } + + return result.join('.') +} + +export const ip6ToString = function (buf: Uint8Array): string { + if (buf.byteLength !== 16) { + throw new InvalidMultiaddrError('IPv6 address was incorrect length') + } + + const result: string[] = [] + + for (let i = 0; i < buf.byteLength; i += 2) { + const byte1 = buf[i] + const byte2 = buf[i + 1] + + const tuple = `${byte1.toString(16).padStart(2, '0')}${byte2.toString(16).padStart(2, '0')}` + + result.push(tuple) + } + + const ip = result.join(':') + + try { + const url = new URL(`http://[${ip}]`) + + return url.hostname.substring(1, url.hostname.length - 1) + } catch { + throw new InvalidMultiaddrError(`Invalid IPv6 address "${ip}"`) + } +} + +export function ip6StringToValue (str: string): string { + try { + const url = new URL(`http://[${str}]`) + + return url.hostname.substring(1, url.hostname.length - 1) + } catch { + throw new InvalidMultiaddrError(`Invalid IPv6 address "${str}"`) + } +} + +const decoders = Object.values(bases).map((c) => c.decoder) +const anybaseDecoder = (function () { + let acc = decoders[0].or(decoders[1]) + decoders.slice(2).forEach((d) => (acc = acc.or(d))) + return acc +})() + +export function mb2bytes (mbstr: string): Uint8Array { + return anybaseDecoder.decode(mbstr) +} + +export function bytes2mb (base: MultibaseCodec): (buf: Uint8Array) => string { + return (buf) => { + return base.encoder.encode(buf) + } +} diff --git a/test/convert.spec.ts b/test/convert.spec.ts index 33abed54..642776df 100644 --- a/test/convert.spec.ts +++ b/test/convert.spec.ts @@ -1,102 +1,9 @@ /* eslint-env mocha */ import { expect } from 'aegir/chai' -import { base64url } from 'multiformats/bases/base64' -import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' -import { bytes2mb, bytes2port, convertToIpNet, ip4ToBytes, ip4ToString, ip6ToBytes, ip6ToString, mb2bytes, port2bytes } from '../src/convert.ts' +import { convertToIpNet } from '../src/convert.ts' import { multiaddr } from '../src/index.js' describe('convert', () => { - it('handles ip4 buffers', () => { - expect(ip4ToString(uint8ArrayFromString('c0a80001', 'base16'))).to.equal('192.168.0.1') - }) - - it('handles ip6 buffers', () => { - expect(ip6ToString(uint8ArrayFromString('abcd0000000100020003000400050006', 'base16'))).to.equal('abcd:0:1:2:3:4:5:6') - }) - - it('handles ipv6 strings', () => { - expect(ip6ToBytes('ABCD::1:2:3:4:5:6')).to.equalBytes(uint8ArrayFromString('ABCD0000000100020003000400050006', 'base16upper')) - }) - - it('handles ip4 strings', () => { - expect(ip4ToBytes('192.168.0.1')).to.equalBytes(uint8ArrayFromString('c0a80001', 'base16')) - }) - - it('throws on invalid ip4 conversion', () => { - expect(() => ip4ToBytes('555.168.0.1')).to.throw() - .with.property('name', 'InvalidMultiaddrError') - }) - - it('throws on invalid ip6 conversion', () => { - expect(() => ip6ToBytes('FFFF::GGGG')).to.throw() - .with.property('name', 'InvalidMultiaddrError') - }) - - it('round trips loopback addresses', () => { - const address = '127.0.0.1' - const bytes = ip4ToBytes(address) - - expect(ip4ToString(bytes)).to.equal(address) - }) - - it('round trips class C addresses', () => { - const address = '192.168.1.1' - const bytes = ip4ToBytes(address) - - expect(ip4ToString(bytes)).to.equal(address) - }) - - /* - describe('.toBytes', () => { - it('defaults to hex conversion', () => { - expect( - convertToBytes(CODE_WS, 'c0a80001') - ).to.eql( - Uint8Array.from([192, 168, 0, 1]) - ) - }) - }) -*/ - describe('.toString', () => { - /* - it('throws on inconsistent ipfs links', () => { - const valid = uint8ArrayFromString('03221220d52ebb89d85b02a284948203a62ff28389c57c9f42beec4ec20db76a68911c0b', 'base16') - expect( - () => bytesToString(valid.slice(0, valid.length - 8)) - ).to.throw( - /inconsistent length/ - ) - }) - - it('defaults to hex conversion', () => { - expect( - convertToString('ws', Uint8Array.from([192, 168, 0, 1])) - ).to.eql( - 'c0a80001' - ) - }) -*/ - it('respects byteoffset during conversion', () => { - const bytes = port2bytes('1234') - const buffer = new Uint8Array(bytes.byteLength + 5) - buffer.set(bytes, 5) - expect(bytes2port(buffer.subarray(5))).to.equal('1234') - }) - }) - - it('can round-trip certhash, though encoding base may change', () => { - const myCertFingerprint = { - algorithm: 'sha-256', - value: 'f4:32:a0:45:34:62:85:e0:d8:d7:75:36:84:72:8e:b2:aa:9e:71:64:e4:eb:fe:06:51:64:42:64:fe:04:a8:d0' - } - const mb = 'f' + myCertFingerprint.value.replaceAll(':', '') - const bytes = mb2bytes(mb) - const outcome = bytes2mb(base64url)(bytes) - expect(outcome).to.equal('u9DKgRTRiheDY13U2hHKOsqqecWTk6_4GUWRCZP4EqNA') - const bytesOut = mb2bytes(outcome) - expect(bytesOut).to.equalBytes(bytes) - }) - it('convertToIpNet ip4', function () { const ipnet = convertToIpNet(multiaddr('/ip4/192.0.2.0/ipcidr/24')) expect(ipnet.toString()).equal('192.0.2.0/24') diff --git a/test/utils.spec.ts b/test/utils.spec.ts new file mode 100644 index 00000000..64492d9e --- /dev/null +++ b/test/utils.spec.ts @@ -0,0 +1,98 @@ +/* eslint-env mocha */ +import { expect } from 'aegir/chai' +import { base64url } from 'multiformats/bases/base64' +import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string' +import { bytes2mb, bytes2port, ip4ToBytes, ip4ToString, ip6ToBytes, ip6ToString, mb2bytes, port2bytes } from '../src/utils.ts' + +describe('utils', () => { + it('handles ip4 buffers', () => { + expect(ip4ToString(uint8ArrayFromString('c0a80001', 'base16'))).to.equal('192.168.0.1') + }) + + it('handles ip6 buffers', () => { + expect(ip6ToString(uint8ArrayFromString('abcd0000000100020003000400050006', 'base16'))).to.equal('abcd:0:1:2:3:4:5:6') + }) + + it('handles ipv6 strings', () => { + expect(ip6ToBytes('ABCD::1:2:3:4:5:6')).to.equalBytes(uint8ArrayFromString('ABCD0000000100020003000400050006', 'base16upper')) + }) + + it('handles ip4 strings', () => { + expect(ip4ToBytes('192.168.0.1')).to.equalBytes(uint8ArrayFromString('c0a80001', 'base16')) + }) + + it('throws on invalid ip4 conversion', () => { + expect(() => ip4ToBytes('555.168.0.1')).to.throw() + .with.property('name', 'InvalidMultiaddrError') + }) + + it('throws on invalid ip6 conversion', () => { + expect(() => ip6ToBytes('FFFF::GGGG')).to.throw() + .with.property('name', 'InvalidMultiaddrError') + }) + + it('round trips loopback addresses', () => { + const address = '127.0.0.1' + const bytes = ip4ToBytes(address) + + expect(ip4ToString(bytes)).to.equal(address) + }) + + it('round trips class C addresses', () => { + const address = '192.168.1.1' + const bytes = ip4ToBytes(address) + + expect(ip4ToString(bytes)).to.equal(address) + }) + + /* + describe('.toBytes', () => { + it('defaults to hex conversion', () => { + expect( + convertToBytes(CODE_WS, 'c0a80001') + ).to.eql( + Uint8Array.from([192, 168, 0, 1]) + ) + }) + }) +*/ + describe('.toString', () => { + /* + it('throws on inconsistent ipfs links', () => { + const valid = uint8ArrayFromString('03221220d52ebb89d85b02a284948203a62ff28389c57c9f42beec4ec20db76a68911c0b', 'base16') + expect( + () => bytesToString(valid.slice(0, valid.length - 8)) + ).to.throw( + /inconsistent length/ + ) + }) + + it('defaults to hex conversion', () => { + expect( + convertToString('ws', Uint8Array.from([192, 168, 0, 1])) + ).to.eql( + 'c0a80001' + ) + }) +*/ + it('respects byteoffset during conversion', () => { + const bytes = port2bytes('1234') + const buffer = new Uint8Array(bytes.byteLength + 5) + buffer.set(bytes, 5) + expect(bytes2port(buffer.subarray(5))).to.equal('1234') + }) + }) + + it('can round-trip certhash, though encoding base may change', () => { + const myCertFingerprint = { + algorithm: 'sha-256', + value: 'f4:32:a0:45:34:62:85:e0:d8:d7:75:36:84:72:8e:b2:aa:9e:71:64:e4:eb:fe:06:51:64:42:64:fe:04:a8:d0' + } + const mb = 'f' + myCertFingerprint.value.replaceAll(':', '') + const bytes = mb2bytes(mb) + const outcome = bytes2mb(base64url)(bytes) + expect(outcome).to.equal('u9DKgRTRiheDY13U2hHKOsqqecWTk6_4GUWRCZP4EqNA') + const bytesOut = mb2bytes(outcome) + expect(bytesOut).to.equalBytes(bytes) + }) +})