Skip to content

Commit

Permalink
state: fix and test nibble functions
Browse files Browse the repository at this point in the history
  • Loading branch information
ScottyPoi committed Feb 7, 2024
1 parent ba8437d commit a61ee80
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 7 deletions.
24 changes: 18 additions & 6 deletions packages/portalnetwork/src/networks/state/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -132,17 +132,29 @@ export function calculateAddressRange(
* [1, 2, a, b] -> Nibbles(is_odd_length=false, packed_nibbles=[0x12, 0xab])
* [1, 2, a, b, c] -> Nibbles(is_odd_length=true, packed_nibbles=[0x01, 0x2a, 0xbc])
*/
export const tightlyPackNibbles = (nibbles: TNibble[]): TNibbles => {
if (!nibbles.every((nibble) => Nibble[nibble] !== undefined)) {
throw new Error(`path: [${nibbles}] must be an array of nibbles`)
export const tightlyPackNibbles = (_nibbles: TNibble[]): TNibbles => {
if (!_nibbles.every((nibble) => Nibble[nibble] !== undefined)) {
throw new Error(`path: [${_nibbles}] must be an array of nibbles`)
}
const nibbles: number[] = _nibbles.map((nibble) =>
typeof nibble === 'string' ? parseInt(nibble, 16) : nibble,
)
const isOddLength = nibbles.length % 2 !== 0
const nibbleArray = isOddLength ? ['0', ...nibbles] : nibbles
const nibbleArray = isOddLength ? [0, ...nibbles] : nibbles
const nibblePairs = Array.from({ length: nibbleArray.length / 2 }, (_, idx) => idx).map((i) => {
return nibbleArray.slice(2 * i, 2 * i + 2) as [TNibble, TNibble]
})
const packedBytes = nibblePairs.map((nibbles) => {
return parseInt(nibbles.join(''), 16)
const packedBytes = nibblePairs.map(([a, b]) => {
return parseInt(a.toString(16) + b.toString(16), 16)
})
return { isOddLength, packedNibbles: Uint8Array.from(packedBytes) }
}

export const unpackNibbles = (packedNibbles: Uint8Array, isOddLength: boolean): TNibble[] => {
const unpacked = packedNibbles.reduce((acc, byte, _idx, _array) => {
acc.push((byte >>> 4) as TNibble)
acc.push((byte & 0x0f) as TNibble)
return acc
}, [] as TNibble[])
return isOddLength ? unpacked.slice(1) : unpacked
}
39 changes: 38 additions & 1 deletion packages/portalnetwork/test/networks/state/utils.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import { randomBytes } from '@ethereumjs/util'
import { assert, describe, it } from 'vitest'
import { assert, describe, expect, it } from 'vitest'

import { StateNetworkContentType } from '../../../src/networks/state/types.js'
import {
MODULO,
calculateAddressRange,
distance,
keyType,
tightlyPackNibbles,
unpackNibbles,
} from '../../../src/networks/state/util.js'

import type { TNibble } from '../../../src/networks/state/types.js'

describe('distance()', () => {
it('should calculate distance between two values', () => {
assert.ok(distance(10n, 10n) === 0n, 'calculates correct distance')
Expand Down Expand Up @@ -197,3 +201,36 @@ describe('calculateAddressRange: ' + address.slice(0, 18) + '...', () => {
})
})
})

describe('Nibbles', () => {
const nibbleArrays: TNibble[][] = [
[0],
[0, 1],
[1, 2, 3],
[1, 2, 3, 4],
['a', 'b', 'c'],
['a', 'b', 'c', 9],
['a', 'b', 'c', 10],
['a', 'b', 'c', 10, 11],
['a', 'b', 'c', 10, 11, 'd'],
]

for (const nibbles of nibbleArrays) {
const packed = tightlyPackNibbles(nibbles)
const unpacked = unpackNibbles(packed.packedNibbles, packed.isOddLength)
it('should calculate packed nibbles', () => {
expect(packed.isOddLength).toEqual(nibbles.length % 2 !== 0)
expect(packed.packedNibbles.length).toEqual(Math.ceil(nibbles.length / 2))
})
it('should unpack packed nibbles', () => {
expect(unpacked.length).toEqual(nibbles.length)
for (const [idx, nibble] of nibbles.entries()) {
if (typeof nibble === 'string') {
expect(parseInt(nibble, 16)).toEqual(unpacked[idx])
} else {
expect(nibble).toEqual(unpacked[idx])
}
}
})
}
})

0 comments on commit a61ee80

Please sign in to comment.