Skip to content

Commit

Permalink
Add linting using prettier (#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
Amxx authored Feb 24, 2024
1 parent 0d55cb0 commit 4fa27de
Show file tree
Hide file tree
Showing 8 changed files with 131 additions and 100 deletions.
1 change: 1 addition & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,5 @@ jobs:
- name: Set up environment
uses: ./.github/actions/setup
- run: npm run coverage -- --forbid-only
- run: npm run lint
- uses: codecov/codecov-action@v3
6 changes: 6 additions & 0 deletions .prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"printWidth": 120,
"singleQuote": true,
"trailingComma": "all",
"arrowParens": "avoid"
}
22 changes: 22 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
"prepublishOnly": "npm run clean",
"prepare": "tsc",
"clean": "rm -rf dist",
"lint": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --check",
"lint:fix": "prettier --log-level warn --ignore-path .gitignore '**/*.{js,ts}' --write",
"test": "mocha",
"coverage": "c8 npm run test --"
},
Expand All @@ -29,6 +31,7 @@
"c8": "^7.12.0",
"fast-check": "^3.3.0",
"mocha": "^10.1.0",
"prettier": "^3.2.5",
"ts-node": "^10.9.1",
"typescript": "^4.8.4"
}
Expand Down
31 changes: 13 additions & 18 deletions src/core.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
import fc from 'fast-check';
import assert from 'assert/strict';
import { equalsBytes } from 'ethereum-cryptography/utils';
import { makeMerkleTree, getProof, processProof, getMultiProof, processMultiProof, isValidMerkleTree, renderMerkleTree } from './core';
import {
makeMerkleTree,
getProof,
processProof,
getMultiProof,
processMultiProof,
isValidMerkleTree,
renderMerkleTree,
} from './core';
import { compareBytes, hex } from './bytes';
import { keccak256 } from 'ethereum-cryptography/keccak';

Expand Down Expand Up @@ -49,29 +57,20 @@ describe('core properties', () => {

describe('core error conditions', () => {
it('zero leaves', () => {
assert.throws(
() => makeMerkleTree([]),
/^Error: Expected non-zero number of leaves$/,
);
assert.throws(() => makeMerkleTree([]), /^Error: Expected non-zero number of leaves$/);
});

it('multiproof duplicate index', () => {
const tree = makeMerkleTree(new Array(2).fill(zero));
assert.throws(
() => getMultiProof(tree, [1, 1]),
/^Error: Cannot prove duplicated index$/,
);
assert.throws(() => getMultiProof(tree, [1, 1]), /^Error: Cannot prove duplicated index$/);
});

it('tree validity', () => {
assert(!isValidMerkleTree([]), 'empty tree');
assert(!isValidMerkleTree([zero, zero]), 'even number of nodes');
assert(!isValidMerkleTree([zero, zero, zero]), 'inner node not hash of children');

assert.throws(
() => renderMerkleTree([]),
/^Error: Expected non-zero number of nodes$/,
);
assert.throws(() => renderMerkleTree([]), /^Error: Expected non-zero number of nodes$/);
});

it('multiproof invariants', () => {
Expand All @@ -84,12 +83,8 @@ describe('core error conditions', () => {
proofFlags: [true, true, false],
};

assert.throws(
() => processMultiProof(badMultiProof),
/^Error: Broken invariant$/,
);
assert.throws(() => processMultiProof(badMultiProof), /^Error: Broken invariant$/);
});

});

class PrettyBytes extends Uint8Array {
Expand Down
44 changes: 25 additions & 19 deletions src/core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,22 @@ import { throwError } from './utils/throw-error';

const hashPair = (a: Bytes, b: Bytes) => keccak256(concatBytes(...[a, b].sort(compareBytes)));

const leftChildIndex = (i: number) => 2 * i + 1;
const leftChildIndex = (i: number) => 2 * i + 1;
const rightChildIndex = (i: number) => 2 * i + 2;
const parentIndex = (i: number) => i > 0 ? Math.floor((i - 1) / 2) : throwError('Root has no parent');
const siblingIndex = (i: number) => i > 0 ? i - (-1) ** (i % 2) : throwError('Root has no siblings');
const parentIndex = (i: number) => (i > 0 ? Math.floor((i - 1) / 2) : throwError('Root has no parent'));
const siblingIndex = (i: number) => (i > 0 ? i - (-1) ** (i % 2) : throwError('Root has no siblings'));

const isTreeNode = (tree: unknown[], i: number) => i >= 0 && i < tree.length;
const isInternalNode = (tree: unknown[], i: number) => isTreeNode(tree, leftChildIndex(i));
const isLeafNode = (tree: unknown[], i: number) => isTreeNode(tree, i) && !isInternalNode(tree, i);
const isTreeNode = (tree: unknown[], i: number) => i >= 0 && i < tree.length;
const isInternalNode = (tree: unknown[], i: number) => isTreeNode(tree, leftChildIndex(i));
const isLeafNode = (tree: unknown[], i: number) => isTreeNode(tree, i) && !isInternalNode(tree, i);
const isValidMerkleNode = (node: Bytes) => node instanceof Uint8Array && node.length === 32;

const checkTreeNode = (tree: unknown[], i: number) => void (isTreeNode(tree, i) || throwError('Index is not in tree'));
const checkInternalNode = (tree: unknown[], i: number) => void (isInternalNode(tree, i) || throwError('Index is not an internal tree node'));
const checkLeafNode = (tree: unknown[], i: number) => void (isLeafNode(tree, i) || throwError('Index is not a leaf'));
const checkValidMerkleNode = (node: Bytes) => void (isValidMerkleNode(node) || throwError('Merkle tree nodes must be Uint8Array of length 32'));
const checkTreeNode = (tree: unknown[], i: number) => void (isTreeNode(tree, i) || throwError('Index is not in tree'));
const checkInternalNode = (tree: unknown[], i: number) =>
void (isInternalNode(tree, i) || throwError('Index is not an internal tree node'));
const checkLeafNode = (tree: unknown[], i: number) => void (isLeafNode(tree, i) || throwError('Index is not a leaf'));
const checkValidMerkleNode = (node: Bytes) =>
void (isValidMerkleNode(node) || throwError('Merkle tree nodes must be Uint8Array of length 32'));

export function makeMerkleTree(leaves: Bytes[]): Bytes[] {
leaves.forEach(checkValidMerkleNode);
Expand All @@ -33,10 +35,7 @@ export function makeMerkleTree(leaves: Bytes[]): Bytes[] {
tree[tree.length - 1 - i] = leaf;
}
for (let i = tree.length - 1 - leaves.length; i >= 0; i--) {
tree[i] = hashPair(
tree[leftChildIndex(i)]!,
tree[rightChildIndex(i)]!,
);
tree[i] = hashPair(tree[leftChildIndex(i)]!, tree[rightChildIndex(i)]!);
}

return tree;
Expand Down Expand Up @@ -129,7 +128,7 @@ export function processMultiProof(multiproof: MultiProof<Bytes>): Bytes {
}

if (stack.length + proof.length !== 1) {
throw new Error('Broken invariant');
throw new Error('Broken invariant');
}

return stack.pop() ?? proof.shift()!;
Expand Down Expand Up @@ -169,10 +168,17 @@ export function renderMerkleTree(tree: Bytes[]): string {
const [i, path] = stack.pop()!;

lines.push(
path.slice(0, -1).map(p => [' ', '│ '][p]).join('') +
path.slice(-1).map(p => ['└─ ', '├─ '][p]).join('') +
i + ') ' +
bytesToHex(tree[i]!)
path
.slice(0, -1)
.map(p => [' ', '│ '][p])
.join('') +
path
.slice(-1)
.map(p => ['└─ ', '├─ '][p])
.join('') +
i +
') ' +
bytesToHex(tree[i]!),
);

if (rightChildIndex(i) < tree.length) {
Expand Down
71 changes: 29 additions & 42 deletions src/standard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@ const makeTree = (s: string, opts: MerkleTreeOptions = {}) => {
const l = s.split('').map(c => [c]);
const t = StandardMerkleTree.of(l, ['string'], opts);
return { l, t };
}
};

describe('standard merkle tree', () => {
for (const opts of [
{},
{ sortLeaves: true },
{ sortLeaves: false },
]) {
for (const opts of [{}, { sortLeaves: true }, { sortLeaves: false }]) {
describe(`with options '${JSON.stringify(opts)}'`, () => {
const { l: leaves, t: tree } = makeTree('abcdef', opts);
const { l: otherLeaves, t: otherTree } = makeTree('abc', opts);
Expand Down Expand Up @@ -72,31 +68,31 @@ describe('standard merkle tree', () => {
tree.render(),
opts.sortLeaves == false
? [
"0) 23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b",
"├─ 1) 8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9",
"│ ├─ 3) 03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9",
"│ │ ├─ 7) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b",
"│ │ └─ 8) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b",
"│ └─ 4) fa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016",
"│ ├─ 9) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681",
"│ └─ 10) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c",
"└─ 2) 7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece",
" ├─ 5) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848",
" └─ 6) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e",
].join("\n")
'0) 23be0977360f08bb0bd7f709a7d543d2cd779c79c66d74e0441919871647de2b',
'├─ 1) 8f7234e8cfe39c08ca84a3a3e3274f574af26fd15165fe29e09cbab742daccd9',
'│ ├─ 3) 03707d7802a71ca56a8ad8028da98c4f1dbec55b31b4a25d536b5309cc20eda9',
'│ │ ├─ 7) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b',
'│ │ └─ 8) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b',
'│ └─ 4) fa914d99a18dc32d9725b3ef1c50426deb40ec8d0885dac8edcc5bfd6d030016',
'│ ├─ 9) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681',
'│ └─ 10) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c',
'└─ 2) 7b0c6cd04b82bfc0e250030a5d2690c52585e0cc6a4f3bc7909d7723b0236ece',
' ├─ 5) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848',
' └─ 6) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e',
].join('\n')
: [
"0) 6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8",
"├─ 1) 52426e0f1f65ff7e209a13b8c29cffe82e3acaf3dad0a9b9088f3b9a61a929c3",
"│ ├─ 3) 8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f",
"│ │ ├─ 7) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b",
"│ │ └─ 8) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c",
"│ └─ 4) 965b92c6cf08303cc4feb7f3e0819c436c2cec17c6f0688a6af139c9a368707c",
"│ ├─ 9) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e",
"│ └─ 10) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681",
"└─ 2) fd3cf45654e88d1cc5d663578c82c76f4b5e3826bacaa1216441443504538f51",
" ├─ 5) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b",
" └─ 6) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848",
].join("\n"),
'0) 6deb52b5da8fd108f79fab00341f38d2587896634c646ee52e49f845680a70c8',
'├─ 1) 52426e0f1f65ff7e209a13b8c29cffe82e3acaf3dad0a9b9088f3b9a61a929c3',
'│ ├─ 3) 8076923e76cf01a7c048400a2304c9a9c23bbbdac3a98ea3946340fdafbba34f',
'│ │ ├─ 7) 9cf5a63718145ba968a01c1d557020181c5b252f665cf7386d370eddb176517b',
'│ │ └─ 8) 9c15a6a0eaeed500fd9eed4cbeab71f797cefcc67bfd46683e4d2e6ff7f06d1c',
'│ └─ 4) 965b92c6cf08303cc4feb7f3e0819c436c2cec17c6f0688a6af139c9a368707c',
'│ ├─ 9) 9a4f64e953595df82d1b4f570d34c4f4f0cfaf729a61e9d60e83e579e1aa283e',
'│ └─ 10) 19ba6c6333e0e9a15bf67523e0676e2f23eb8e574092552d5e888c64a4bb3681',
'└─ 2) fd3cf45654e88d1cc5d663578c82c76f4b5e3826bacaa1216441443504538f51',
' ├─ 5) eba909cf4bb90c6922771d7f126ad0fd11dfde93f3937a196274e1ac20fd2f5b',
' └─ 6) c62a8cfa41edc0ef6f6ae27a2985b7d39c7fea770787d7e104696c6e81f64848',
].join('\n'),
);
});

Expand All @@ -108,10 +104,7 @@ describe('standard merkle tree', () => {
});

it('reject out of bounds value index', () => {
assert.throws(
() => tree.getProof(leaves.length),
/^Error: Index out of bounds$/,
);
assert.throws(() => tree.getProof(leaves.length), /^Error: Index out of bounds$/);
});

it('reject unrecognized tree dump', () => {
Expand All @@ -128,21 +121,15 @@ describe('standard merkle tree', () => {
values: [{ value: ['0'], treeIndex: 0 }],
leafEncoding: ['uint256'],
});
assert.throws(
() => loadedTree1.getProof(0),
/^Error: Merkle tree does not contain the expected value$/,
);
assert.throws(() => loadedTree1.getProof(0), /^Error: Merkle tree does not contain the expected value$/);

const loadedTree2 = StandardMerkleTree.load({
format: 'standard-v1',
tree: [zero, zero, hex(keccak256(keccak256(zeroBytes)))],
values: [{ value: ['0'], treeIndex: 2 }],
leafEncoding: ['uint256'],
});
assert.throws(
() => loadedTree2.getProof(0),
/^Error: Unable to prove value$/,
);
assert.throws(() => loadedTree2.getProof(0), /^Error: Unable to prove value$/);
});
});
}
Expand Down
Loading

0 comments on commit 4fa27de

Please sign in to comment.