Skip to content

Commit

Permalink
add meta code verify (#311)
Browse files Browse the repository at this point in the history
Add meta code verify to check integrity of dashboard
  • Loading branch information
spiritbroski authored and m00n620 committed May 4, 2023
1 parent fa0f74a commit 7a90acf
Show file tree
Hide file tree
Showing 65 changed files with 6,946 additions and 87 deletions.
22 changes: 22 additions & 0 deletions .github/workflows/ci-test-meta-code-verify.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: meta-code-verify CI

on:
push:
branches:
- "main"
pull_request:
workflow_dispatch:

jobs:
meta-code-verify-test:
name: meta-code-verify Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: npm install --global yarn && yarn
name: Install dependencies
- run: yarn compile
name: Compile smart contracts
working-directory: ./packages/core
- run: yarn meta-code-verify:test
name: Run meta-code-verify test
6 changes: 4 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
"sdk:lint": "yarn workspace @human-protocol/sdk lint",
"basemodels:test": "yarn workspace @human-protocol/basemodels test",
"basemodels:lint": "yarn workspace @human-protocol/basemodels lint",
"test": "concurrently npm:core:test npm:subgraph:test npm:escrow-dashboard:test npm:fortune:test npm:sdk:test npm:basemodels:test npm:faucet-server:test",
"lint": "concurrently npm:core:lint npm:subgraph:lint npm:escrow-dashboard:lint npm:fortune:lint npm:sdk:lint npm:basemodels:lint npm:faucet-server:lint",
"meta-code-verify:test": "yarn workspace @human-protocol/meta-code-verify test",
"meta-code-verify:lint": "yarn workspace @human-protocol/meta-code-verify lint",
"test": "concurrently npm:core:test npm:subgraph:test npm:escrow-dashboard:test npm:fortune:test npm:sdk:test npm:basemodels:test npm:faucet-server:test npm:meta-code-verify:test",
"lint": "concurrently npm:core:lint npm:subgraph:lint npm:escrow-dashboard:lint npm:fortune:lint npm:sdk:lint npm:basemodels:lint npm:faucet-server:lint npm:meta-code-verify:lint",
"prepare": "husky install"
},
"workspaces": {
Expand Down
10 changes: 9 additions & 1 deletion packages/apps/escrow-dashboard/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,15 @@ You may also see any lint errors in the console.

### `yarn run build`

Builds the app for production to the `build` folder.\
Before building you need to set environment variable in `.env`:

```
VITE_APP_NFT_STORAGE_API=
VITE_APP_FAUCET_SERVER_URL=
VITE_APP_WALLETCONNECT_PROJECT_ID=
```

Builds the app for production to the `dist` folder.\
It correctly bundles React in production mode and optimizes the build for the best performance.

The build is minified and the filenames include the hashes.
Expand Down
1 change: 1 addition & 0 deletions packages/apps/escrow-dashboard/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
<script id="binary-transparency-manifest" type="application/json"></script>
</body>
</html>
6 changes: 6 additions & 0 deletions packages/apps/escrow-dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@
"devDependencies": {
"@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^14.0.0",
"@types/crypto-js": "^4.1.1",
"@types/glob": "^8.1.0",
"@types/numeral": "^2.0.2",
"@types/react": "^18.0.25",
"@types/react-dom": "^18.0.9",
"@types/react-test-renderer": "^18.0.0",
"@vitejs/plugin-react": "^3.1.0",
"crypto-js": "^4.1.1",
"dotenv": "^16.0.3",
"eslint-config-react-app": "^7.0.1",
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.26.0",
Expand All @@ -49,7 +53,9 @@
"happy-dom": "^8.2.6",
"identity-obj-proxy": "^3.0.0",
"jsdom": "^21.1.0",
"merkletreejs": "^0.3.9",
"resize-observer-polyfill": "^1.5.1",
"sinon": "^15.0.4",
"vite": "^4.1.4",
"vite-plugin-node-polyfills": "^0.7.0",
"vitest": "^0.29.2"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import * as fs from 'node:fs';
import * as path from 'path';
import { NFTStorage } from 'nft.storage';
import sinon from 'sinon';
import tmp from 'tmp';
import { test, assert } from 'vitest';
import generateMerkleTree from '../generateMerkleTree';
const mockToken = 'test-token';

test('generateMerkleTree should return a valid Merkle tree JSON', async () => {
// Create a temporary directory for the dist/assets folder
const tmpDir = tmp.dirSync();
const tmpDistAssetsPath = path.join(tmpDir.name, 'dist/assets');
fs.mkdirSync(tmpDistAssetsPath, { recursive: true });

// Create some JS files in the temporary dist/assets folder
fs.writeFileSync(
path.join(tmpDistAssetsPath, 'test.js'),
'console.log("Hello, world!");'
);
fs.writeFileSync(
path.join(tmpDistAssetsPath, 'test2.js'),
'console.log("Another file!");'
);

// Mock NFTStorage.storeBlob method
const fakeCid = 'bafybeih42y6g7zkr76j6ax7z6wjc5d56xazsrtxzp6f7j6fsk67djnppmq';
sinon.stub(NFTStorage.prototype, 'storeBlob').resolves(fakeCid);

const origin = 'https://example.com';

const merkleTreeJson = await generateMerkleTree(
origin,
mockToken,
tmpDistAssetsPath
);
const merkleTreeData = JSON.parse(merkleTreeJson);

// Cleanup and restore the actual file system and NFTStorage.storeBlob method
fs.rmdirSync(tmpDir.name, { recursive: true });
sinon.restore();

// Assertions
assert(
merkleTreeData.hasOwnProperty('version'),
'Merkle tree JSON should have a "version" property'
);
assert(
merkleTreeData.version === fakeCid,
'Merkle tree JSON "version" should match the fake CID'
);
assert(
merkleTreeData.hasOwnProperty('root'),
'Merkle tree JSON should have a "root" property'
);
assert(
merkleTreeData.hasOwnProperty('leaves'),
'Merkle tree JSON should have a "leaves" property'
);
assert(
merkleTreeData.root.startsWith('0x'),
'Merkle tree root hash should start with "0x"'
);
assert(
merkleTreeData.leaves.length > 0,
'Merkle tree leaves should not be empty'
);
assert(
merkleTreeData.leaves.every((leaf) => leaf.startsWith('0x')),
'Each Merkle tree leaf hash should start with "0x"'
);
});
51 changes: 51 additions & 0 deletions packages/apps/escrow-dashboard/scripts/generateMerkleTree.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import * as fs from 'node:fs';
import * as path from 'path';
import { SHA256 } from 'crypto-js';
import * as glob from 'glob';
import { MerkleTree } from 'merkletreejs';
import { NFTStorage } from 'nft.storage';

export default async function generateMerkleTree(
origin: string,
token: string,
buildPath?: string
): Promise<string> {
if (!buildPath) {
buildPath = path.join(__dirname, '../dist/assets');
}

const allFiles = glob.sync('**/*.js', { cwd: buildPath });
const NFT_STORAGE_CLIENT = new NFTStorage({
token,
});
const fileHashes = allFiles.map((file) => {
const filePath = path.join(buildPath, file);
const fileContent = fs.readFileSync(filePath, 'utf-8');
return SHA256(fileContent).toString();
});

const merkleTree = new MerkleTree(fileHashes, SHA256);
const merkleRoot = '0x' + merkleTree.getRoot().toString('hex');

// Add the '0x' prefix to each leaf
const leaves = merkleTree
.getLeaves()
.map((leaf) => '0x' + leaf.toString('hex'));

const someData = new Blob([
JSON.stringify({
origin,
root_hash: merkleRoot,
published_date: Date.now(),
}) as string,
]);
const cid = await NFT_STORAGE_CLIENT.storeBlob(someData);

const merkleTreeJson = JSON.stringify({
version: cid,
root: merkleRoot,
leaves: leaves,
});

return merkleTreeJson;
}
75 changes: 50 additions & 25 deletions packages/apps/escrow-dashboard/vite.config.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,60 @@
/// <reference types="vitest" />
/// <reference types="vite/client" />

import * as fs from 'fs';
import path from 'path';
import react from '@vitejs/plugin-react';
import dotenv from 'dotenv';
import { defineConfig } from 'vite';
import { nodePolyfills } from 'vite-plugin-node-polyfills';
import generateMerkleTree from './scripts/generateMerkleTree';

dotenv.config();
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
react({ fastRefresh: false }),
nodePolyfills({
// Whether to polyfill `node:` protocol imports.
protocolImports: true,
}),
],
worker: {
plugins: [react()],
},
resolve: {
alias: [{ find: 'src', replacement: path.resolve(__dirname, 'src') }],
},
test: {
globals: true,
environment: 'happy-dom',
setupFiles: './tests/setup.ts',
coverage: {
reporter: ['text', 'json', 'html'],
export default defineConfig(({ mode }) => {
return {
plugins: [
react({ fastRefresh: false }),
nodePolyfills({
// Whether to polyfill `node:` protocol imports.
protocolImports: true,
}),
{
name: 'generate-merkle-tree',
apply: 'build',
async writeBundle() {
const merkleTreeJson = await generateMerkleTree(
mode === 'development'
? 'localhost'
: 'dashboard.humanprotocol.org',
process.env.VITE_APP_NFT_STORAGE_API as string
);

const indexPath = path.resolve(__dirname, './dist/index.html');
const indexContent = fs.readFileSync(indexPath, 'utf-8');
const newIndexContent = indexContent.replace(
'<script id="binary-transparency-manifest" type="application/json"></script>',
`<script id="binary-transparency-manifest" type="application/json">${merkleTreeJson}</script>`
);
fs.writeFileSync(indexPath, newIndexContent);
},
},
],
worker: {
plugins: [react()],
},
resolve: {
alias: [{ find: 'src', replacement: path.resolve(__dirname, 'src') }],
},
test: {
globals: true,
environment: 'happy-dom',
setupFiles: './tests/setup.ts',
coverage: {
reporter: ['text', 'json', 'html'],
},
},
server: {
port: 3002,
},
},
server: {
port: 3002,
},
};
});
21 changes: 21 additions & 0 deletions packages/apps/meta-code-verify/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
{
"root": true,
"env": {
"browser": true,
"es2021": true,
"jest": true,
"webextensions": true
},
"parserOptions": { "project": ["./tsconfig.json"] },
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended"
],
"parser": "@typescript-eslint/parser",
"plugins": ["@typescript-eslint"],
"rules": {
"@typescript-eslint/no-unused-vars": ["error", {"argsIgnorePattern": "^_"}],
"@typescript-eslint/no-empty-function": 0,
"@typescript-eslint/no-extra-semi": 0
}
}
5 changes: 5 additions & 0 deletions packages/apps/meta-code-verify/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
dist/chrome
dist/edge
dist/firefox
node_modules
.env
12 changes: 12 additions & 0 deletions packages/apps/meta-code-verify/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"printWidth": 80,
"tabWidth": 2,
"useTabs": false,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"jsxBracketSameLine": false,
"arrowParens": "avoid",
"proseWrap": "preserve"
}
Loading

0 comments on commit 7a90acf

Please sign in to comment.