├── .fatherrc.ts
├── .gitignore
├── .prettierignore
├── .prettierrc
├── LICENSE
├── README.md
├── examples
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .umirc.ts
├── README.md
├── package.json
├── src
│ ├── myconfig_template.ts
│ └── pages
│ │ ├── quickStart.tsx
│ │ ├── useUtils.ts
│ │ ├── use_case_1.tsx
│ │ ├── use_case_2.tsx
│ │ └── use_case_3.tsx
├── tsconfig.json
└── typings.d.ts
├── examples_static
├── .gitignore
├── .prettierignore
├── .prettierrc
├── .umirc.ts
├── Dockerfile_use_case1
├── Dockerfile_use_case2
├── Dockerfile_use_case3
├── README.md
├── build.js
├── build_arweave.js
├── ngnix
│ ├── nginx_case_1.conf
│ ├── nginx_case_2.conf
│ └── nginx_case_3.conf
├── package-lock.json
├── package.json
├── src
│ ├── myconfig_template.ts
│ └── pages
│ │ ├── useUtils.ts
│ │ ├── use_case_1.tsx
│ │ ├── use_case_2.tsx
│ │ └── use_case_3.tsx
├── tsconfig.json
└── typings.d.ts
├── jest.config.ts
├── package-lock.json
├── package.json
├── src
├── Crypto.ts
├── DataLake.ts
├── MindLake.ts
├── Permission.ts
├── index.ts
├── request
│ ├── index.ts
│ └── request.ts
├── types
│ └── index.ts
└── util
│ ├── abi.ts
│ ├── bcl.ts
│ ├── cipher.ts
│ ├── clerk.ts
│ ├── constant.ts
│ ├── result.ts
│ ├── util.ts
│ └── web3.ts
├── tsconfig.json
├── tutorial
├── Configure_AppKey.md
├── Configure_Docker_Case.md
├── Configure_Node.md
├── Configure_Wallet.md
├── README.md
└── imgs
│ ├── alice_connect.png
│ ├── bob_connect.png
│ ├── brew.png
│ ├── case_2.png
│ ├── case_2_logout.png
│ ├── case_3.png
│ ├── case_3_result.png
│ ├── change_chain.png
│ ├── change_wallet_alice.png
│ ├── change_wallet_bob.png
│ ├── change_wallet_charlie.png
│ ├── charlie_connect.png
│ ├── charlie_out_put.png
│ ├── create_dapp.png
│ ├── create_dapp_confirm.png
│ ├── create_wallet.png
│ ├── create_wallet_confirm.png
│ ├── dapp_list.png
│ ├── decrypt_request.png
│ ├── docker-app-search.png
│ ├── docker_app.png
│ ├── docker_app_top.webp
│ ├── docker_bac.jpeg
│ ├── docker_bak.png
│ ├── docker_case1_img.png
│ ├── docker_info.png
│ ├── docker_run.png
│ ├── docker_run1.png
│ ├── docker_run2.png
│ ├── docker_win_download.png
│ ├── docker_windows_win.jpeg
│ ├── img.png
│ ├── insert_data_done.png
│ ├── install_brew.png
│ ├── metamask_testnet_enable.png
│ ├── myDapp_menu.png
│ ├── nounce_sign.png
│ ├── nvm_1.png
│ ├── nvm_2.png
│ ├── nvm_3.png
│ ├── open_mac_terminal.png
│ ├── quickStart.png
│ ├── quickStart_logout.png
│ ├── request_publickey.png
│ ├── sign_scan.png
│ ├── tutorial-architecture.jpeg
│ ├── tutorial-architecture.jpg
│ ├── upload_chain.png
│ ├── white_list_popup.png
│ ├── window_download_nvm.png
│ ├── windows_open_terminal.png
│ └── windows_terminal.png
└── yarn.lock
/.fatherrc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'father';
2 |
3 | export default defineConfig({
4 | esm: {output: 'dist'},
5 | cjs: {output: 'dist'},
6 | prebundle: {},
7 | });
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | /node_modules
2 | /dist
3 | .DS_Store
4 | .idea
5 | .editorconfig
6 | examples/src/myconfig.ts
7 | .npmrc
8 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.html
5 | package.json
6 | .umi
7 | .umi-production
8 | .umi-test
9 | .dist
10 | .node_modules
11 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 80,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 The Typescript Packaging Authority
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
7 | furnished to do so, subject to the following conditions:
8 |
9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
10 |
11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THESOFTWARE.
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mind Lake Typescript SDK
2 |
3 | An Typescript implementation for Mind Lake
4 |
5 | ## Description
6 |
7 | The Mind Lake SDK utilizes Mind Lake's encryption storage and privacy computing capabilities to provide secure data management.
8 | * Mind Lake is the backbone of Mind Network.
9 | * All data is end-to-end encrypted at the client-side SDK side, ensuring that plaintext data never leaves the user's client.
10 | * Cryptographic principles ensure that only the data owner can access their own plaintext data.
11 | * Additionally, Mind Lake's powerful privacy computing capabilities enable the performance of calculations and querying of encrypted data.
12 |
13 | ## Getting Started
14 |
15 | ### Dependencies
16 |
17 | * node [16, 18)
18 | * web3js
19 |
20 | ### Installing
21 |
22 | ```
23 | $ npm install --save mind-lake-sdk
24 | # or
25 | $ yarn add mind-lake-sdk
26 | ```
27 |
28 | ### Import
29 | ```
30 | import { MindLake } from "mind-lake-sdk";
31 | const mindLake = await MindLake.getInstance("YOUR OWN APP KEY")
32 | ...
33 | ```
34 |
35 | ### Executing program
36 | * [step-by-step tutorial](/tutorial/README.md)
37 | * [quick starts](https://mind-network.gitbook.io/mind-lake-sdk/get-started)
38 | * [more examples](https://mind-network.gitbook.io/mind-lake-sdk/use-cases)
39 |
40 |
41 | ## code
42 | ```
43 | mind-lake-sdk-typescript
44 | |-- src # source code
45 | | |-- MindLake.ts
46 | | |-- DataLake.ts
47 | | |-- Permission.ts
48 | | |-- Cryptor.ts
49 | |-- examples # use case examples
50 | |-- tutorial # step-by-step tutorial
51 | |-- README.md
52 | └--- LICENSE
53 |
54 | ```
55 |
56 | ## Help
57 |
58 | Full doc: [https://mind-network.gitbook.io/mind-lake-sdk](https://mind-network.gitbook.io/mind-lake-sdk)
59 |
60 | ## Authors
61 | * Joshua [@JoshuaW55818202](https://twitter.com/JoshuaW55818202)
62 | * Lee [@LeeTan853917](https://twitter.com/LeeTan853917)
63 |
64 | ## Version History
65 |
66 | * v1.0.0
67 | * Initial Release
68 | * v1.0.1
69 | * Fix bug
70 | * v1.0.2
71 | * Fix bug
72 | * v1.0.4
73 | * Add listOwner method to Permission Class
74 | * Add listOwnerColumn method to Permission Class
75 | * v1.0.5
76 | * Fix bug
77 | * v1.0.6
78 | * Update the connect function
79 | * v1.0.7
80 | * Add support for Mind DataPack
81 | * v1.0.13
82 | * Support multiple chains
83 | * Support clerk
84 | ## License
85 |
86 | This project is licensed under the [MIT] License - see the LICENSE.md file for details
87 |
--------------------------------------------------------------------------------
/examples/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /npm-debug.log*
6 | /yarn-error.log
7 | /yarn.lock
8 | /package-lock.json
9 |
10 | # production
11 | /dist
12 |
13 | # misc
14 | .DS_Store
15 |
16 | # umi
17 | /src/.umi
18 | /src/.umi-production
19 | /src/.umi-test
20 | /.env.local
21 |
--------------------------------------------------------------------------------
/examples/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.html
5 | package.json
6 | .umi
7 | .umi-production
8 | .umi-test
9 |
--------------------------------------------------------------------------------
/examples/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 80,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/examples/.umirc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'umi';
2 |
3 | export default defineConfig({
4 | nodeModulesTransform: {
5 | type: 'none',
6 | },
7 | routes: [
8 | { path: '/', component: '@/pages/quickStart' },
9 | { path: '/use_case_1', component: '@/pages/use_case_1' },
10 | { path: '/use_case_2', component: '@/pages/use_case_2' },
11 | { path: '/use_case_3', component: '@/pages/use_case_3' },
12 | ],
13 | fastRefresh: {},
14 | });
15 |
--------------------------------------------------------------------------------
/examples/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | A quick example on how to use mind-lake-sdk TypeScript library
3 |
4 | ## Getting Started
5 |
6 | ### Install dependencies
7 |
8 | install mind-lake-sdk manual
9 | ```bash
10 | $ npm install --save mind-lake-sdk
11 | ```
12 |
13 | install other all packages required for this quick demo. mind-lake-sdk (TypeScript) is defined in package.json
14 |
15 | use npm
16 | ```bash
17 | $ npm install
18 | ```
19 |
20 | or use yarn
21 | ```bash
22 | $ yarn
23 | ```
24 |
25 | ### request and change to your own app_key
26 | ```
27 | cp myconfig_template.ts myconfig.ts
28 | ```
29 |
30 | ### Start the dev server
31 |
32 | use npm
33 | ```bash
34 | $ npm start
35 | ```
36 |
37 | or use npm
38 | ```bash
39 | $ yarn start
40 | ```
41 |
42 | ### checkout in browser: http://localhost:8000
43 |
--------------------------------------------------------------------------------
/examples/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start": "umi dev"
5 | },
6 | "dependencies": {
7 | "js-md5": "^0.7.3",
8 | "mind-lake-sdk": "^1.0.5",
9 | "react": "17.x",
10 | "react-dom": "17.x",
11 | "umi": "^3.5.23"
12 | },
13 | "devDependencies": {
14 | "@types/react": "^17.0.0",
15 | "@types/react-dom": "^17.0.0",
16 | "typescript": "^4.1.2",
17 | "yorkie": "^2.0.0"
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/examples/src/myconfig_template.ts:
--------------------------------------------------------------------------------
1 | export const appKey = "YOUR_APP_KEY=";
2 | export const nodeUrl = "https://sdk.mindnetwork.xyz"; // or change to other node url
3 | export const aliceWalletAddress = "Alice_Wallet_Address";
4 | export const bobWalletAddress = "Bob_Wallet_Address";
5 | export const charlieWalletAddress = "Charlie_Wallet_Address";
6 |
--------------------------------------------------------------------------------
/examples/src/pages/quickStart.tsx:
--------------------------------------------------------------------------------
1 | import { MindLake } from 'mind-lake-sdk';
2 | import React from 'react';
3 | import useUtils, { resultFormat } from '@/pages/useUtils';
4 |
5 | const tableName = "table_encrypt";
6 | const columns = [{columnName: 'id', type: MindLake.DataType.int4, encrypt: true}, {columnName: 'name', type: MindLake.DataType.text, encrypt: true}];
7 |
8 |
9 | const Index = () => {
10 |
11 | const { result, login, logger} = useUtils();
12 |
13 | const quickStart = async () => {
14 | const mindLake = await login();
15 | if(!mindLake) {
16 | return ;
17 | }
18 |
19 | // create a table
20 | const dataLake = mindLake.dataLake;
21 | await dataLake.dropTable(tableName);
22 |
23 | let result = await dataLake.createTable(tableName, columns);
24 | logger(`create Table "${tableName}" columns "${JSON.stringify(columns)}" >>> ${resultFormat(result)}`);
25 | if(result.code !== 0) {
26 | return
27 | }
28 |
29 | // encrypt data
30 | const crypto = mindLake.crypto;
31 | result = await crypto.encrypt(1, `${tableName}.id`);
32 | logger(`encrypt(${tableName}.id, 1) >>> ${resultFormat(result)}`);
33 | if(result.code !== 0) {
34 | return
35 | }
36 | const encryptId = result.result;
37 | result = await crypto.encrypt("tom", `${tableName}.name`);
38 | logger(`encrypt(${tableName}.name, "tom") >>> ${resultFormat(result)}`);
39 | if(result.code !== 0) {
40 | return
41 | }
42 | const encryptName = result.result;
43 | // insert encrypted data
44 | const sql = `insert into ${tableName} (id, name) values ('${encryptId}', '${encryptName}')`;
45 | result = await dataLake.query(sql);
46 | logger(`${sql} >>> ${resultFormat(result)}`);
47 | if(result.code !== 0){
48 | return
49 | }
50 |
51 | //query encrypted data;
52 | const selectSql = `select * from ${tableName}`;
53 | result = await dataLake.query(selectSql);
54 | logger(`${selectSql} >>> ${resultFormat(result)}`);
55 | if(result.code === 0) {
56 | const columnList = result.result.columnList;
57 | for (const row of result.result.data) {
58 | for (const index in row) {
59 | const encryptData = row[index];
60 | const column = columnList[index];
61 | const decryptRes = await crypto.decrypt(encryptData);
62 | logger(`decrypt(${tableName}.${column}) >>> ${resultFormat(decryptRes)}`)
63 | }
64 | }
65 | }
66 | };
67 |
68 |
69 | return (
70 |
71 |
Quick start with your MetaMask (for development only)
72 |
73 | Logs output:
74 | {
75 | result.map((log, k) =>
{ log }
)
76 | }
77 |
78 |
79 | )
80 |
81 |
82 | };
83 |
84 | export default Index;
85 |
--------------------------------------------------------------------------------
/examples/src/pages/useUtils.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { MindLake } from 'mind-lake-sdk';
3 | import { appKey, nodeUrl } from '@/myconfig';
4 |
5 | export const resultFormat = (result: any) => {
6 | if(typeof result === 'string') {
7 | return result
8 | }
9 | if(result.code === 0) {
10 | return result.result ? JSON.stringify(result.result) : true
11 | }else {
12 | return result.message
13 | }
14 | };
15 |
16 |
17 | const useUtils = () => {
18 |
19 | const [result, setResult] = useState>([]);
20 |
21 | const logger = (info: string | object, init?: boolean) => {
22 | setResult(pre => !init ? [...pre, info] : [info]);
23 | window.scrollTo(0, document.documentElement.scrollHeight)
24 | };
25 |
26 | const login = async (init = true) => {
27 | const mindLake = await MindLake.getInstance(appKey, nodeUrl);
28 | if(!mindLake) {
29 | return logger("mind lake init error", init)
30 | }
31 | logger("logging...please wait...", init);
32 | let result = await mindLake.connect();
33 | if(result.code === 403) {
34 | logger(`login >>> ${JSON.stringify({message: `Thanks for your interest. The product is under active development and limit to invited users only until full release. Your wallets are currently not in the early trial and you can apply via: https://bit.ly/mindalphatest`})}`);
35 | return ;
36 | }else if(result.code !==0 ){
37 | logger(`login >>> ${JSON.stringify(result.result)}`);
38 | return
39 | }
40 |
41 | logger(`login >>> ${JSON.stringify(result.result)}`);
42 | return mindLake;
43 | };
44 |
45 | return {
46 | result,
47 | logger,
48 | login
49 | }
50 | };
51 |
52 | export default useUtils;
53 |
--------------------------------------------------------------------------------
/examples/src/pages/use_case_1.tsx:
--------------------------------------------------------------------------------
1 | import { MindLake } from 'mind-lake-sdk';
2 | import React from 'react';
3 | import useUtils, { resultFormat } from '@/pages/useUtils';
4 |
5 | const tableName1 = "wallet_balance";
6 | const columns1 = [{columnName: 'WalletAddress', type: MindLake.DataType.text, encrypt: false}, {columnName: 'Name', type: MindLake.DataType.text, encrypt: true}, {columnName: 'Balance', type: MindLake.DataType.float4, encrypt: true}];
7 |
8 |
9 | const Index = () => {
10 |
11 | const { result, login, logger} = useUtils();
12 |
13 | const useCase1 = async () => {
14 | const mindLake = await login();
15 | if(!mindLake) {
16 | return ;
17 | }
18 |
19 | // create a table
20 | const dataLake = mindLake.dataLake;
21 | await dataLake.dropTable(tableName1);
22 | let result = await dataLake.createTable("wallet_balance", columns1, ["WalletAddress"]);
23 | logger(`create Table "${tableName1}" columns "${JSON.stringify(columns1)}" >>> ${resultFormat(result)}`);
24 | if(result.code !== 0) {
25 | return
26 | }
27 |
28 | // encrypt data
29 | const crypto = mindLake.crypto;
30 | result = await crypto.encrypt("Alice", `${tableName1}.Name`);
31 | logger(`encrypt(${tableName1}.Name, "Alice") >>> ${resultFormat(result)}`);
32 | if(result.code !== 0) {
33 | return
34 | }
35 | let encryptedName = result.result;
36 | result = await crypto.encrypt(10.5, `${tableName1}.Balance`);
37 | logger(`encrypt(${tableName1}.Balance, 10.5") >>> ${resultFormat(result)}`);
38 | if(result.code !==0 ) {
39 | return
40 | }
41 | let encryptedBalance = result.result;
42 |
43 | // insert encrypted data
44 | const sql_alice = `insert into wallet_balance ("WalletAddress", "Name", "Balance") values ('0xB2F588A50E43f58FEb0c05ff86a30D0d0b1BF065', '${encryptedName}', '${encryptedBalance}')`;
45 | result = await dataLake.query(sql_alice);
46 | logger(`${sql_alice} >>> ${resultFormat(result)}`);
47 | if(result.code !== 0){
48 | return
49 | }
50 |
51 | result = await crypto.encrypt("Bob", `${tableName1}.Name`);
52 | logger(`encrypt(${tableName1}.Name, "Bob") >>> ${resultFormat(result)}`);
53 | if(result.code !== 0) {
54 | return
55 | }
56 | encryptedName = result.result;
57 |
58 | result = await crypto.encrypt(20.8, `${tableName1}.Balance`);
59 | logger(`encrypt(${tableName1}.Balance, 20.8") >>> ${resultFormat(result)}`);
60 | if(result.code !==0 ) {
61 | return
62 | }
63 | encryptedBalance = result.result;
64 |
65 | // insert encrypted data
66 | const sql_bob = `insert into wallet_balance ("WalletAddress", "Name", "Balance") values ('0x420c08373E2ba9C7566Ba0D210fB42A20a1eD2f8', '${encryptedName}', '${encryptedBalance}')`;
67 | result = await dataLake.query(sql_bob);
68 | logger(`${sql_bob} >>> ${resultFormat(result)}`);
69 | if(result.code !== 0){
70 | return
71 | }
72 | //query encrypted data;
73 | const selectSql = `select * from ${tableName1}`;
74 | result = await dataLake.query(selectSql);
75 | logger(`${selectSql} >>> ${resultFormat(result)}`);
76 | if(result.code === 0) {
77 | const columnList = result.result.columnList;
78 | for (const row of result.result.data) {
79 | for (const index in row) {
80 | // @ts-ignore
81 | if(index > 0) {
82 | const encryptData = row[index];
83 | const column = columnList[index];
84 | const decryptRes = await crypto.decrypt(encryptData);
85 | logger(`decrypt(${tableName1}.${column}) >>> ${resultFormat(decryptRes)}`)
86 | }
87 | }
88 | }
89 | }
90 |
91 | };
92 |
93 |
94 | return (
95 |
96 |
Test case one with your MetaMask (for development only)
97 |
98 | Logs output:
99 | {
100 | result.map((log, k) =>
{ log }
)
101 | }
102 |
103 |
104 | )
105 |
106 |
107 | };
108 |
109 | export default Index;
110 |
--------------------------------------------------------------------------------
/examples/src/pages/use_case_2.tsx:
--------------------------------------------------------------------------------
1 | import useUtils, { resultFormat } from '@/pages/useUtils';
2 | import React from 'react';
3 | import { MindLake } from 'mind-lake-sdk';
4 | // @ts-ignore
5 | import md5 from 'js-md5';
6 |
7 | const tableName2 = "album_1";
8 | const columns2 = [{columnName: 'name', type: MindLake.DataType.text, encrypt: false}, {columnName: 'picture', type: MindLake.DataType.text, encrypt: true}];
9 |
10 |
11 | const Index = () => {
12 |
13 | const { result, login, logger} = useUtils();
14 |
15 | const useCase2 = async () => {
16 | const mindLake = await login();
17 | if(!mindLake) {
18 | return ;
19 | }
20 |
21 | const response = await fetch("https://avatars.githubusercontent.com/u/97393721");
22 | if(!response || response.status !== 200) {
23 | return logger("Failed to get picture from github")
24 | }
25 | const image = await response.arrayBuffer();
26 | logger(`MD5 of the original picture pic_origin.png: >>> ${md5(image)}`);
27 | const base64 = Buffer.from(image).toString('base64');
28 | logger('get picture from github >>>');
29 | logger( )
30 | // create a table
31 | const dataLake = mindLake.dataLake;
32 | await dataLake.dropTable(tableName2);
33 | let result = await dataLake.createTable(tableName2, columns2);
34 | logger(`create Table "${tableName2}" columns "${JSON.stringify(columns2)}" >>> ${resultFormat(result)}`);
35 | if(result.code !==0 ) {
36 | return
37 | }
38 | const crypto = mindLake.crypto;
39 | result = await crypto.encrypt(base64, `${tableName2}.picture`);
40 | logger(`encrypt(${tableName2}.picture) >>>${resultFormat(result)}`);
41 | if(result.code !== 0) {
42 | return
43 | }
44 | const sql = `insert into ${tableName2} (name, picture) values ('mind.png', '${result.result}')`;
45 | result = await dataLake.query(sql);
46 | logger(`${sql} >>> ${resultFormat(result)}`);
47 | if(result.code !== 0) {
48 | return
49 | }
50 |
51 | const selectSql = `select * from ${tableName2}`;
52 | result = await dataLake.query(selectSql);
53 | logger(`${selectSql} >>> ${resultFormat(result)}`);
54 | if(result.code !== 0) {
55 | return
56 | }
57 | for (const row of result.result.data) {
58 | const decrypt = await crypto.decrypt(row[1]);
59 | logger(`decrypt(${tableName2}.picture) >>> ${JSON.stringify(md5(Buffer.from(decrypt.result, 'base64')))}`);
60 | logger('decrypt picture from mind lake >>>');
61 | logger( )
62 | }
63 | };
64 |
65 | return (
66 |
67 |
Test case two with your MetaMask (for development only)
68 |
69 | Logs output:
70 | {
71 | result.map((log, k) =>
{ log }
)
72 | }
73 |
74 |
75 | )
76 | };
77 |
78 | export default Index;
79 |
--------------------------------------------------------------------------------
/examples/src/pages/use_case_3.tsx:
--------------------------------------------------------------------------------
1 | import useUtils, { resultFormat } from '@/pages/useUtils';
2 | import React, { useRef } from 'react';
3 | import { MindLake } from 'mind-lake-sdk';
4 | import { aliceWalletAddress as alice, bobWalletAddress as bob, charlieWalletAddress as charlie} from '@/myconfig';
5 |
6 | const tableName3 = "transaction";
7 | const columns3 = [{columnName: 'WalletAddress', type: MindLake.DataType.text, encrypt: false}, {columnName: 'Token', type: MindLake.DataType.text, encrypt: true}, {columnName: 'Volume', type: MindLake.DataType.float4, encrypt: true}];
8 |
9 | const dataAlice = [
10 | { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'USDT', Volume: 5.6 },
11 | { WalletAddress: "0xD862D48f36ce6298eFD00474eC852b8838a54F66", Token: 'BUSD', Volume: 6.3 },
12 | { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'BUSD', Volume: 10.3},
13 | ];
14 |
15 | const dataBob = [
16 | { WalletAddress: '0xD862D48f36ce6298eFD00474eC852b8838a54F66', Token: 'USDT', Volume: 3.3},
17 | { WalletAddress: '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', Token: 'BUSD', Volume: 9.8},
18 | { WalletAddress: '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', Token: 'USDT', Volume: 7.7}
19 | ];
20 |
21 | const Index = () => {
22 |
23 | const { result: resultAlice, login: loginAlice, logger: loggerAlice } = useUtils();
24 |
25 | const { result: resultBob, login: loginBob, logger: loggerBob } = useUtils();
26 |
27 | const { result: resultCharlie, login: loginCharlie, logger: loggerCharlie } = useUtils();
28 |
29 | const policyList = useRef>([]);
30 |
31 | const insertData = async (data: Array, role: string) => {
32 | let login,logger;
33 | if(role === 'alice') {
34 | login = loginAlice;
35 | logger = loggerAlice;
36 | }else {
37 | login = loginBob;
38 | logger = loggerBob;
39 | }
40 |
41 | const mindLake = await login(false);
42 | if(!mindLake) {
43 | return
44 | }
45 | // create a table
46 | const dataLake = mindLake.dataLake;
47 | await dataLake.dropTable(tableName3);
48 | let result = await dataLake.createTable(tableName3, columns3);
49 | logger(`create Table ${tableName3} columns ${JSON.stringify(columns3)} >>> ${resultFormat(result)}`);
50 | if(result.code !==0 ) {
51 | return
52 | }
53 |
54 | // encrypt data
55 | const crypto = mindLake.crypto;
56 | for (const row of data) {
57 | const walletAddress = row.WalletAddress;
58 | const encryptToken = await crypto.encrypt(row.Token, `${tableName3}.Token`);
59 | logger(`encrypt(${walletAddress}.${tableName3}.Token, ${row.Token}) >>> ${encryptToken.result}`);
60 | const encryptVolume = await crypto.encrypt(row.Volume, `${tableName3}.Volume`);
61 | logger(`encrypt(${walletAddress}.${tableName3}.Volume, ${row.Volume}) >>> ${encryptVolume.result}`);
62 | const sql = `insert into transaction ("WalletAddress", "Token", "Volume") values ('${walletAddress}', '${encryptToken.result}', '${encryptVolume.result}')`;
63 | result = await dataLake.query(sql);
64 | logger(`${sql} >>> ${resultFormat(result)}`);
65 | if(result.code !== 0) {
66 | return
67 | }
68 | }
69 | const permission = mindLake.permission;
70 | result = await permission.grant(charlie, [`${tableName3}.Token`, `${tableName3}.Volume`]);
71 | logger(`grant columns ${JSON.stringify([`${tableName3}.Token`, `${tableName3}.Volume`])} to charlie >>> ${resultFormat(result)}`);
72 | if(result.code !==0 ) {
73 | return
74 | }
75 | logger(`Insert data done`);
76 | await mindLake.disConnect();
77 | if(policyList.current) {
78 | if(policyList.current.length > 2) {
79 | policyList.current = [];
80 | }
81 | policyList.current.push(result.result)
82 | }
83 | };
84 |
85 | const charlieQuery = async () => {
86 | const logger = loggerCharlie;
87 | const login = loginCharlie;
88 | if(!policyList.current || policyList.current.length != 2) {
89 | return logger(`Please wait Alice or Bob grant data`)
90 | }
91 | console.log(policyList.current);
92 | // @ts-ignore
93 | const [policyAliceID, policyBobID] = policyList.current;
94 | const mindLake = await login(false);
95 | if(!mindLake) {
96 | return ;
97 | }
98 | const permission = mindLake.permission;
99 | const dataLake = mindLake.dataLake;
100 | const crypto = mindLake.crypto;
101 | let result = await permission.confirm(policyAliceID);
102 | logger(`charlie confirm grant policyAliceId=${policyAliceID} >>> ${resultFormat(result)}`);
103 | if(result.code !== 0) {
104 | return
105 | }
106 | result = await permission.confirm(policyBobID);
107 | logger(`charlie confirm grant policyAliceId=${policyBobID} >>> ${resultFormat(result)}`);
108 | if(result.code !== 0) {
109 | return
110 | }
111 |
112 | const sql = `SELECT combine."WalletAddress", SUM(combine."Volume") FROM
113 | (SELECT "WalletAddress","Volume" FROM "${alice.slice(2).toLocaleLowerCase()}"."transaction"
114 | UNION ALL
115 | SELECT "WalletAddress","Volume" FROM "${bob.slice(2).toLocaleLowerCase()}"."transaction") as combine
116 | GROUP BY "WalletAddress"`;
117 | result = await dataLake.query(sql);
118 | logger(`${sql} >>> ${resultFormat(result)}`);
119 | if(result.code !== 0) {
120 | return
121 | }
122 | const columnList = result.result.columnList;
123 | for (const row of result.result.data) {
124 | const walletAddress = row[0];
125 | result = await crypto.decrypt(row[1]);
126 | logger(`${walletAddress}.${columnList[1]} >>> ${resultFormat(result)}`);
127 | if(result.code !== 0) {
128 | return
129 | }
130 | }
131 | };
132 |
133 |
134 | return (
135 |
136 |
137 |
insertData(dataAlice, 'alice')}>Insert Alice Data And Share To Charlie
138 |
139 | Logs output:
140 | {
141 | resultAlice.map((log, k) =>
{ log }
)
142 | }
143 |
144 |
145 |
146 |
insertData(dataBob, 'bob')}>Insert Bob Data And Share To Charlie
147 |
148 | Logs output:
149 | {
150 | resultBob.map((log, k) =>
{ log }
)
151 | }
152 |
153 |
154 |
155 |
charlieQuery()}>Charlie Select Data And Decrypt Data
156 |
157 | Logs output:
158 | {
159 | resultCharlie.map((log, k) =>
{ log }
)
160 | }
161 |
162 |
163 |
164 |
165 | )
166 |
167 | };
168 |
169 | export default Index;
170 |
--------------------------------------------------------------------------------
/examples/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "resolveJsonModule": true,
7 | "importHelpers": true,
8 | "jsx": "react-jsx",
9 | "esModuleInterop": true,
10 | "sourceMap": true,
11 | "baseUrl": "./",
12 | "strict": true,
13 | "paths": {
14 | "@/*": ["src/*"],
15 | "@@/*": ["src/.umi/*"]
16 | },
17 | "allowSyntheticDefaultImports": true
18 | },
19 | "include": [
20 | "mock/**/*",
21 | "src/**/*",
22 | "config/**/*",
23 | ".umirc.ts",
24 | "typings.d.ts"
25 | ],
26 | "exclude": [
27 | "node_modules",
28 | "lib",
29 | "es",
30 | "dist",
31 | "typings",
32 | "**/__test__",
33 | "test",
34 | "docs",
35 | "tests"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/examples/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css';
2 | declare module '*.less';
3 | declare module '*.png';
4 | declare module '*.svg' {
5 | export function ReactComponent(
6 | props: React.SVGProps,
7 | ): React.ReactElement;
8 | const url: string;
9 | export default url;
10 | }
11 |
--------------------------------------------------------------------------------
/examples_static/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /npm-debug.log*
6 | /yarn-error.log
7 | /yarn.lock
8 |
9 | # production
10 | /dist
11 |
12 | # misc
13 | .DS_Store
14 |
15 | # umi
16 | /src/.umi
17 | /src/.umi-production
18 | /src/.umi-test
19 | /.env.local
20 |
--------------------------------------------------------------------------------
/examples_static/.prettierignore:
--------------------------------------------------------------------------------
1 | **/*.md
2 | **/*.svg
3 | **/*.ejs
4 | **/*.html
5 | package.json
6 | .umi
7 | .umi-production
8 | .umi-test
9 |
--------------------------------------------------------------------------------
/examples_static/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "singleQuote": true,
3 | "trailingComma": "all",
4 | "printWidth": 80,
5 | "overrides": [
6 | {
7 | "files": ".prettierrc",
8 | "options": { "parser": "json" }
9 | }
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/examples_static/.umirc.ts:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'umi';
2 |
3 | export default defineConfig({
4 | routes: [
5 | { path: '/use_case_1', component: '@/pages/use_case_1' },
6 | { path: '/use_case_2', component: '@/pages/use_case_2' },
7 | { path: '/use_case_3', component: '@/pages/use_case_3' },
8 | ],
9 | // exportStatic: {
10 | // dynamicRoot: true,
11 | // htmlSuffix: true
12 | // },
13 | mpa: {},
14 | publicPath: './'
15 | });
16 |
--------------------------------------------------------------------------------
/examples_static/Dockerfile_use_case1:
--------------------------------------------------------------------------------
1 | # Dockerfile_use_case1
2 | # build front-end
3 | FROM node:16 AS frontend
4 |
5 | WORKDIR /app
6 |
7 | COPY ./ /app
8 |
9 | #COPY ./pnpm-lock.yaml /app
10 | RUN node -v
11 |
12 | #RUN npm install --registry=https://registry.npmmirror.com/
13 | RUN npm install --registry=https://registry.npmmirror.com/
14 |
15 | #ENV NODE_OPTIONS=--openssl-legacy-provider
16 |
17 | RUN npm run build
18 |
19 | COPY ./ /app
20 |
21 | FROM nginx:stable-alpine
22 | #
23 | COPY --from=frontend /app/dist/use_case_1 /usr/share/nginx/html
24 | ##
25 | COPY --from=frontend /app/ngnix/nginx_case_1.conf /etc/nginx/conf.d/default.conf
26 |
27 | EXPOSE 80
28 |
--------------------------------------------------------------------------------
/examples_static/Dockerfile_use_case2:
--------------------------------------------------------------------------------
1 | # Dockerfile_use_case2
2 | # build front-end
3 | FROM node:16 AS frontend
4 |
5 | WORKDIR /app
6 |
7 | COPY ./ /app
8 |
9 | #COPY ./pnpm-lock.yaml /app
10 | RUN node -v
11 |
12 | #RUN npm install --registry=https://registry.npmmirror.com/
13 | RUN npm install --registry=https://registry.npmmirror.com/
14 |
15 | #ENV NODE_OPTIONS=--openssl-legacy-provider
16 |
17 | RUN npm run build
18 |
19 | COPY ./ /app
20 |
21 | FROM nginx:stable-alpine
22 | #
23 | COPY --from=frontend /app/dist/use_case_2 /usr/share/nginx/html
24 | ##
25 | COPY --from=frontend /app/ngnix/nginx_case_2.conf /etc/nginx/conf.d/default.conf
26 |
27 | EXPOSE 80
28 |
--------------------------------------------------------------------------------
/examples_static/Dockerfile_use_case3:
--------------------------------------------------------------------------------
1 | # Dockerfile_use_case2=3
2 | # build front-end
3 | FROM node:16 AS frontend
4 |
5 | WORKDIR /app
6 |
7 | COPY ./ /app
8 |
9 | #COPY ./pnpm-lock.yaml /app
10 | RUN node -v
11 |
12 | #RUN npm install --registry=https://registry.npmmirror.com/
13 | RUN npm install --registry=https://registry.npmmirror.com/
14 |
15 | #ENV NODE_OPTIONS=--openssl-legacy-provider
16 |
17 | RUN npm run build
18 |
19 | COPY ./ /app
20 |
21 | FROM nginx:stable-alpine
22 | #
23 | COPY --from=frontend /app/dist/use_case_3 /usr/share/nginx/html
24 | ##
25 | COPY --from=frontend /app/ngnix/nginx_case_3.conf /etc/nginx/conf.d/default.conf
26 |
27 | EXPOSE 80
28 |
--------------------------------------------------------------------------------
/examples_static/README.md:
--------------------------------------------------------------------------------
1 | # Overview
2 | A quick example on how to use mind-lake-sdk TypeScript library
3 |
4 | ## Getting Started
5 |
6 | ### Install dependencies
7 |
8 | install mind-lake-sdk manual
9 | ```bash
10 | $ npm install --save mind-lake-sdk
11 | ```
12 |
13 | install other all packages required for this quick demo. mind-lake-sdk (TypeScript) is defined in package.json
14 |
15 | use npm
16 | ```bash
17 | $ npm install
18 | ```
19 |
20 | or use yarn
21 | ```bash
22 | $ yarn
23 | ```
24 |
25 | ### request and change to your own app_key
26 | ```
27 | cp myconfig_template.ts myconfig.ts
28 | ```
29 |
30 | ### Start the dev server
31 |
32 | use npm
33 | ```bash
34 | $ npm start
35 | ```
36 |
37 | or use npm
38 | ```bash
39 | $ yarn start
40 | ```
41 | ### checkout in browser: http://localhost:8000
42 |
43 | ### build to IPFS
44 |
45 | use npm
46 | ```
47 | $ npm run build-to-ipfs
48 | ```
49 |
50 | ### build to arweave
51 | use npm
52 | ```
53 | $ npm run build-to-arweave
54 | ```
55 |
--------------------------------------------------------------------------------
/examples_static/build.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 |
3 | const basePath = __dirname+ '/dist/';
4 |
5 | //创建3个文件夹
6 | for (let i = 1; i < 4; i ++) {
7 | const oldPath = basePath + `use_case_${i}.html`;
8 | const newPath = basePath + 'use_case_'+ i;
9 | fs.mkdirSync(newPath);
10 | fs.renameSync(oldPath, newPath+`/use_case_${i}.html`);
11 | fs.copyFileSync(basePath + 'umi.js', newPath+ '/umi.js')
12 | }
13 |
14 | fs.unlinkSync(basePath + 'umi.js');
15 |
16 |
--------------------------------------------------------------------------------
/examples_static/build_arweave.js:
--------------------------------------------------------------------------------
1 | const fs = require("fs");
2 |
3 | const basePath = __dirname+ '/dist/';
4 |
5 | const jsFile = fs.readFileSync(basePath + 'umi.js').toString();
6 |
7 | for (let i = 1; i < 2; i ++) {
8 | const filePath = basePath +`use_case_${i}.html`;
9 | const htmlFile = fs.readFileSync(filePath).toString();
10 | const result = htmlFile.replace('',
11 | ``
14 | );
15 | fs.writeFileSync('test.txt', result, {encoding: 'utf-8', flag: 'w'})
16 | }
17 |
18 | fs.unlinkSync(basePath + 'umi.js');
19 |
--------------------------------------------------------------------------------
/examples_static/ngnix/nginx_case_1.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 | charset utf-8;
5 | error_page 500 502 503 504 /50x.html;
6 | location / {
7 | root /usr/share/nginx/html;
8 | index index.html;
9 | }
10 | rewrite ^/index\.html /use_case_1.html permanent;
11 |
12 | proxy_set_header Host $host;
13 | proxy_set_header X-Real-IP $remote_addr;
14 | proxy_set_header REMOTE-HOST $remote_addr;
15 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
16 | }
17 |
--------------------------------------------------------------------------------
/examples_static/ngnix/nginx_case_2.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 | charset utf-8;
5 | error_page 500 502 503 504 /50x.html;
6 | location / {
7 | root /usr/share/nginx/html;
8 | index index.html;
9 | }
10 | rewrite ^/index\.html /use_case_2.html permanent;
11 |
12 | proxy_set_header Host $host;
13 | proxy_set_header X-Real-IP $remote_addr;
14 | proxy_set_header REMOTE-HOST $remote_addr;
15 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
16 | }
17 |
--------------------------------------------------------------------------------
/examples_static/ngnix/nginx_case_3.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 80;
3 | server_name localhost;
4 | charset utf-8;
5 | error_page 500 502 503 504 /50x.html;
6 | location / {
7 | root /usr/share/nginx/html;
8 | index index.html;
9 | }
10 | rewrite ^/index\.html /use_case_3.html permanent;
11 |
12 | proxy_set_header Host $host;
13 | proxy_set_header X-Real-IP $remote_addr;
14 | proxy_set_header REMOTE-HOST $remote_addr;
15 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
16 | }
17 |
--------------------------------------------------------------------------------
/examples_static/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "private": true,
3 | "scripts": {
4 | "start": "umi dev",
5 | "build": "umi build",
6 | "build-to-ipfs": "umi build",
7 | "build-to-arweave": "umi build && node build_arweave.js",
8 | "analyze": "cross-env ANALYZE=1 umi build"
9 | },
10 | "dependencies": {
11 | "js-md5": "^0.7.3",
12 | "mind-lake-sdk": "latest",
13 | "react": "17.x",
14 | "react-dom": "17.x",
15 | "umi": "^3.5.23"
16 | },
17 | "devDependencies": {
18 | "@types/react": "^17.0.0",
19 | "@types/react-dom": "^17.0.0",
20 | "typescript": "^4.1.2",
21 | "yorkie": "^2.0.0",
22 | "cross-env": "^7.0.0"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/examples_static/src/myconfig_template.ts:
--------------------------------------------------------------------------------
1 | export const appKey = "YOUR_APP_KEY=";
2 | export const nodeUrl = "https://sdk.mindnetwork.xyz"; // or change to other node url
3 | export const aliceWalletAddress = "Alice_Wallet_Address";
4 | export const bobWalletAddress = "Bob_Wallet_Address";
5 | export const charlieWalletAddress = "Charlie_Wallet_Address";
6 |
--------------------------------------------------------------------------------
/examples_static/src/pages/useUtils.ts:
--------------------------------------------------------------------------------
1 | import { useState } from 'react';
2 | import { MindLake } from 'mind-lake-sdk';
3 | import { appKey, nodeUrl } from '@/myconfig';
4 |
5 | export const resultFormat = (result: any) => {
6 | if(typeof result === 'string') {
7 | return result
8 | }
9 | if(result.code === 0) {
10 | return result.result ? JSON.stringify(result.result) : true
11 | }else {
12 | return result.message
13 | }
14 | };
15 |
16 |
17 | const useUtils = () => {
18 | const [loading, setLoading] = useState(false);
19 | const [result, setResult] = useState>([]);
20 |
21 | const logger = (info: string | object, init?: boolean) => {
22 | setResult(pre => !init ? [...pre, info] : [info]);
23 | window.scrollTo(0, document.documentElement.scrollHeight)
24 | };
25 |
26 | const login = async (init = true) => {
27 | const mindLake = await MindLake.getInstance(appKey, nodeUrl).catch(e => logger(`mind lake init error >>> ${JSON.stringify(e)}`, init));
28 | if(!mindLake) {
29 | return
30 | }
31 | setLoading(true);
32 | logger("logging...please wait...", init);
33 | let result = await mindLake.connect();
34 | setLoading(false);
35 | if(result.code === 403) {
36 | logger(`login >>> ${JSON.stringify({message: `Thanks for your interest. The product is under active development and limit to invited users only until full release. Your wallets are currently not in the early trial and you can apply via: https://bit.ly/mindalphatest`})}`);
37 | return ;
38 | }else if(result.code !==0 ){
39 | logger(`login >>> ${JSON.stringify(result)}`);
40 | return
41 | }
42 |
43 | logger(`login >>> ${JSON.stringify(result.result)}`);
44 | return mindLake;
45 | };
46 |
47 | return {
48 | result,
49 | logger,
50 | login,
51 | loading
52 | }
53 | };
54 |
55 | export default useUtils;
56 |
--------------------------------------------------------------------------------
/examples_static/src/pages/use_case_1.tsx:
--------------------------------------------------------------------------------
1 | import { MindLake } from 'mind-lake-sdk';
2 | import React from 'react';
3 | import useUtils, { resultFormat } from '@/pages/useUtils';
4 |
5 | const tableName1 = "wallet_balance";
6 | const columns1 = [{columnName: 'WalletAddress', type: MindLake.DataType.text, encrypt: false}, {columnName: 'Name', type: MindLake.DataType.text, encrypt: true}, {columnName: 'Balance', type: MindLake.DataType.float4, encrypt: true}];
7 |
8 |
9 | const Index = () => {
10 |
11 | const { result, login, logger, loading} = useUtils();
12 |
13 | const useCase1 = async () => {
14 | const mindLake = await login();
15 | if(!mindLake) {
16 | return ;
17 | }
18 |
19 | // create a table
20 | const dataLake = mindLake.dataLake;
21 | await dataLake.dropTable(tableName1);
22 | let result = await dataLake.createTable("wallet_balance", columns1, ["WalletAddress"]);
23 | logger(`create Table "${tableName1}" columns "${JSON.stringify(columns1)}" >>> ${resultFormat(result)}`);
24 | if(result.code !== 0) {
25 | return
26 | }
27 |
28 | // encrypt data
29 | const crypto = mindLake.crypto;
30 | result = await crypto.encrypt("Alice", `${tableName1}.Name`);
31 | logger(`encrypt(${tableName1}.Name, "Alice") >>> ${resultFormat(result)}`);
32 | if(result.code !== 0) {
33 | return
34 | }
35 | let encryptedName = result.result;
36 | result = await crypto.encrypt(10.5, `${tableName1}.Balance`);
37 | logger(`encrypt(${tableName1}.Balance, 10.5") >>> ${resultFormat(result)}`);
38 | if(result.code !==0 ) {
39 | return
40 | }
41 | let encryptedBalance = result.result;
42 |
43 | // insert encrypted data
44 | const sql_alice = `insert into wallet_balance ("WalletAddress", "Name", "Balance") values ('0xB2F588A50E43f58FEb0c05ff86a30D0d0b1BF065', '${encryptedName}', '${encryptedBalance}')`;
45 | result = await dataLake.query(sql_alice);
46 | logger(`${sql_alice} >>> ${resultFormat(result)}`);
47 | if(result.code !== 0){
48 | return
49 | }
50 |
51 | result = await crypto.encrypt("Bob", `${tableName1}.Name`);
52 | logger(`encrypt(${tableName1}.Name, "Bob") >>> ${resultFormat(result)}`);
53 | if(result.code !== 0) {
54 | return
55 | }
56 | encryptedName = result.result;
57 |
58 | result = await crypto.encrypt(20.8, `${tableName1}.Balance`);
59 | logger(`encrypt(${tableName1}.Balance, 20.8") >>> ${resultFormat(result)}`);
60 | if(result.code !==0 ) {
61 | return
62 | }
63 | encryptedBalance = result.result;
64 |
65 | // insert encrypted data
66 | const sql_bob = `insert into wallet_balance ("WalletAddress", "Name", "Balance") values ('0x420c08373E2ba9C7566Ba0D210fB42A20a1eD2f8', '${encryptedName}', '${encryptedBalance}')`;
67 | result = await dataLake.query(sql_bob);
68 | logger(`${sql_bob} >>> ${resultFormat(result)}`);
69 | if(result.code !== 0){
70 | return
71 | }
72 | //query encrypted data;
73 | const selectSql = `select * from ${tableName1}`;
74 | result = await dataLake.query(selectSql);
75 | logger(`${selectSql} >>> ${resultFormat(result)}`);
76 | if(result.code === 0) {
77 | const columnList = result.result.columnList;
78 | for (const row of result.result.data) {
79 | for (const index in row) {
80 | // @ts-ignore
81 | if(index > 0) {
82 | const encryptData = row[index];
83 | const column = columnList[index];
84 | const decryptRes = await crypto.decrypt(encryptData);
85 | logger(`decrypt(${tableName1}.${column}) >>> ${resultFormat(decryptRes)}`)
86 | }
87 | }
88 | }
89 | }
90 |
91 | };
92 |
93 |
94 | return (
95 |
96 |
Test case one with your MetaMask (for development only)
97 |
98 | Logs output:
99 | {
100 | result.map((log, k) =>
{ log }
)
101 | }
102 |
103 |
104 | )
105 |
106 |
107 | };
108 |
109 | export default Index;
110 |
--------------------------------------------------------------------------------
/examples_static/src/pages/use_case_2.tsx:
--------------------------------------------------------------------------------
1 | import useUtils, { resultFormat } from '@/pages/useUtils';
2 | import React from 'react';
3 | import { MindLake } from 'mind-lake-sdk';
4 | // @ts-ignore
5 | import md5 from 'js-md5';
6 |
7 | const tableName2 = "album_1";
8 | const columns2 = [{columnName: 'name', type: MindLake.DataType.text, encrypt: false}, {columnName: 'picture', type: MindLake.DataType.text, encrypt: true}];
9 |
10 |
11 | const Index = () => {
12 |
13 | const { result, login, logger, loading} = useUtils();
14 |
15 | const useCase2 = async () => {
16 | const mindLake = await login();
17 | if(!mindLake) {
18 | return ;
19 | }
20 |
21 | const response = await fetch("https://avatars.githubusercontent.com/u/97393721");
22 | if(!response || response.status !== 200) {
23 | return logger("Failed to get picture from github")
24 | }
25 | const image = await response.arrayBuffer();
26 | logger(`MD5 of the original picture pic_origin.png: >>> ${md5(image)}`);
27 | const base64 = Buffer.from(image).toString('base64');
28 | logger('get picture from github >>>');
29 | logger( )
30 | // create a table
31 | const dataLake = mindLake.dataLake;
32 | await dataLake.dropTable(tableName2);
33 | let result = await dataLake.createTable(tableName2, columns2);
34 | logger(`create Table "${tableName2}" columns "${JSON.stringify(columns2)}" >>> ${resultFormat(result)}`);
35 | if(result.code !==0 ) {
36 | return
37 | }
38 | const crypto = mindLake.crypto;
39 | result = await crypto.encrypt(base64, `${tableName2}.picture`);
40 | logger(`encrypt(${tableName2}.picture) >>>${resultFormat(result)}`);
41 | if(result.code !== 0) {
42 | return
43 | }
44 | const sql = `insert into ${tableName2} (name, picture) values ('mind.png', '${result.result}')`;
45 | result = await dataLake.query(sql);
46 | logger(`${sql} >>> ${resultFormat(result)}`);
47 | if(result.code !== 0) {
48 | return
49 | }
50 |
51 | const selectSql = `select * from ${tableName2}`;
52 | result = await dataLake.query(selectSql);
53 | logger(`${selectSql} >>> ${resultFormat(result)}`);
54 | if(result.code !== 0) {
55 | return
56 | }
57 | for (const row of result.result.data) {
58 | const decrypt = await crypto.decrypt(row[1]);
59 | logger(`decrypt(${tableName2}.picture) >>> ${JSON.stringify(md5(Buffer.from(decrypt.result, 'base64')))}`);
60 | logger('decrypt picture from mind lake >>>');
61 | logger( )
62 | }
63 | };
64 |
65 | return (
66 |
67 |
Test case two with your MetaMask (for development only)
68 |
69 | Logs output:
70 | {
71 | result.map((log, k) =>
{ log }
)
72 | }
73 |
74 |
75 | )
76 | };
77 |
78 | export default Index;
79 |
--------------------------------------------------------------------------------
/examples_static/src/pages/use_case_3.tsx:
--------------------------------------------------------------------------------
1 | import useUtils, { resultFormat } from '@/pages/useUtils';
2 | import React, { useRef, useState } from 'react';
3 | import { MindLake } from 'mind-lake-sdk';
4 | // import { aliceWalletAddress as alice, bobWalletAddress as bob, charlieWalletAddress as charlie} from '@/myconfig';
5 |
6 | const tableName3 = "transaction";
7 | const columns3 = [{columnName: 'WalletAddress', type: MindLake.DataType.text, encrypt: false}, {columnName: 'Token', type: MindLake.DataType.text, encrypt: true}, {columnName: 'Volume', type: MindLake.DataType.float4, encrypt: true}];
8 |
9 | const dataAlice = [
10 | { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'USDT', Volume: 5.6 },
11 | { WalletAddress: "0xD862D48f36ce6298eFD00474eC852b8838a54F66", Token: 'BUSD', Volume: 6.3 },
12 | { WalletAddress: "0x8CFB38b2cba74757431B205612E349B8b9a9E661", Token: 'BUSD', Volume: 10.3},
13 | ];
14 |
15 | const dataBob = [
16 | { WalletAddress: '0xD862D48f36ce6298eFD00474eC852b8838a54F66', Token: 'USDT', Volume: 3.3},
17 | { WalletAddress: '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', Token: 'BUSD', Volume: 9.8},
18 | { WalletAddress: '0x70dBcC09edF6D9AdD4A235e2D8346E78A79ac770', Token: 'USDT', Volume: 7.7}
19 | ];
20 |
21 | const Index = () => {
22 |
23 | const { result: resultAlice, login: loginAlice, logger: loggerAlice, loading: loadingAlice } = useUtils();
24 |
25 | const { result: resultBob, login: loginBob, logger: loggerBob, loading: loadingBob } = useUtils();
26 |
27 | const { result: resultCharlie, login: loginCharlie, logger: loggerCharlie, loading: loadingCharlie } = useUtils();
28 |
29 | const policyList = useRef>([]);
30 |
31 | const [alice, setAlice] = useState("");
32 | const [bob, setBob] = useState("");
33 | const [charlie, setCharlie] = useState("");
34 |
35 | const insertData = async (data: Array, role: string) => {
36 | let login,logger;
37 | if(role === 'alice') {
38 | login = loginAlice;
39 | logger = loggerAlice;
40 | }else{
41 | login = loginBob;
42 | logger = loggerBob;
43 | }
44 | if(!alice || !bob || !charlie) {
45 | logger(`Please input alice , bob and charlie wallet address`);
46 | return ;
47 | }
48 |
49 | const mindLake = await login(false);
50 | if(!mindLake) {
51 | return
52 | }
53 | // create a table
54 | const dataLake = mindLake.dataLake;
55 | await dataLake.dropTable(tableName3);
56 | let result = await dataLake.createTable(tableName3, columns3);
57 | logger(`create Table ${tableName3} columns ${JSON.stringify(columns3)} >>> ${resultFormat(result)}`);
58 | if(result.code !==0 ) {
59 | return
60 | }
61 |
62 | // encrypt data
63 | const crypto = mindLake.crypto;
64 | for (const row of data) {
65 | const walletAddress = row.WalletAddress;
66 | const encryptToken = await crypto.encrypt(row.Token, `${tableName3}.Token`);
67 | logger(`encrypt(${walletAddress}.${tableName3}.Token, ${row.Token}) >>> ${encryptToken.result}`);
68 | const encryptVolume = await crypto.encrypt(row.Volume, `${tableName3}.Volume`);
69 | logger(`encrypt(${walletAddress}.${tableName3}.Volume, ${row.Volume}) >>> ${encryptVolume.result}`);
70 | const sql = `insert into transaction ("WalletAddress", "Token", "Volume") values ('${walletAddress}', '${encryptToken.result}', '${encryptVolume.result}')`;
71 | result = await dataLake.query(sql);
72 | logger(`${sql} >>> ${resultFormat(result)}`);
73 | if(result.code !== 0) {
74 | return
75 | }
76 | }
77 | const permission = mindLake.permission;
78 | result = await permission.grant(charlie, [`${tableName3}.Token`, `${tableName3}.Volume`]);
79 | logger(`grant columns ${JSON.stringify([`${tableName3}.Token`, `${tableName3}.Volume`])} to charlie >>> ${resultFormat(result)}`);
80 | if(result.code !==0 ) {
81 | return
82 | }
83 | logger(`Insert data done`);
84 | await mindLake.disConnect();
85 | if(policyList.current) {
86 | if(policyList.current.length > 2) {
87 | policyList.current = [];
88 | }
89 | policyList.current.push(result.result)
90 | }
91 | };
92 |
93 | const charlieQuery = async () => {
94 | const logger = loggerCharlie;
95 | const login = loginCharlie;
96 | if(!alice || !bob || !charlie) {
97 | logger(`Please input alice , bob and charlie wallet address`);
98 | return ;
99 | }
100 |
101 | if(!policyList.current || policyList.current.length != 2) {
102 | return logger(`Please wait Alice or Bob grant data`)
103 | }
104 | console.log(policyList.current);
105 | // @ts-ignore
106 | const [policyAliceID, policyBobID] = policyList.current;
107 | const mindLake = await login(false);
108 | if(!mindLake) {
109 | return ;
110 | }
111 | const permission = mindLake.permission;
112 | const dataLake = mindLake.dataLake;
113 | const crypto = mindLake.crypto;
114 | let result = await permission.confirm(policyAliceID);
115 | logger(`charlie confirm grant policyAliceId=${policyAliceID} >>> ${resultFormat(result)}`);
116 | if(result.code !== 0) {
117 | return
118 | }
119 | result = await permission.confirm(policyBobID);
120 | logger(`charlie confirm grant policyAliceId=${policyBobID} >>> ${resultFormat(result)}`);
121 | if(result.code !== 0) {
122 | return
123 | }
124 |
125 | const sql = `SELECT combine."WalletAddress", SUM(combine."Volume") FROM
126 | (SELECT "WalletAddress","Volume" FROM "${alice.slice(2).toLocaleLowerCase()}"."transaction"
127 | UNION ALL
128 | SELECT "WalletAddress","Volume" FROM "${bob.slice(2).toLocaleLowerCase()}"."transaction") as combine
129 | GROUP BY "WalletAddress"`;
130 | result = await dataLake.query(sql);
131 | logger(`${sql} >>> ${resultFormat(result)}`);
132 | if(result.code !== 0) {
133 | return
134 | }
135 | const columnList = result.result.columnList;
136 | for (const row of result.result.data) {
137 | const walletAddress = row[0];
138 | result = await crypto.decrypt(row[1]);
139 | logger(`${walletAddress}.${columnList[1]} >>> ${resultFormat(result)}`);
140 | if(result.code !== 0) {
141 | return
142 | }
143 | }
144 | };
145 |
146 |
147 | return (
148 | <>
149 |
150 |
155 |
156 |
157 |
insertData(dataAlice, 'alice')} disabled={loadingAlice}>
158 | Insert Alice Data And Share To Charlie
159 |
160 | (Please change wallet account to Alice)
161 |
162 |
163 | Logs output:
164 | {
165 | resultAlice.map((log, k) =>
{ log }
)
166 | }
167 |
168 |
169 |
170 |
insertData(dataBob, 'bob')} disabled={loadingBob}>
171 | Insert Bob Data And Share To Charlie
172 | (Please change wallet account to Bob)
173 |
174 |
175 | Logs output:
176 | {
177 | resultBob.map((log, k) =>
{ log }
)
178 | }
179 |
180 |
181 |
182 |
charlieQuery()} disabled={loadingCharlie}>
183 | Charlie Select Data And Decrypt Data
184 | (Please change wallet account to Charlie)
185 |
186 |
187 | Logs output:
188 | {
189 | resultCharlie.map((log, k) =>
{ log }
)
190 | }
191 |
192 |
193 |
194 | >
195 | )
196 |
197 | };
198 |
199 | export default Index;
200 |
--------------------------------------------------------------------------------
/examples_static/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "esnext",
4 | "module": "esnext",
5 | "moduleResolution": "node",
6 | "resolveJsonModule": true,
7 | "importHelpers": true,
8 | "jsx": "react-jsx",
9 | "esModuleInterop": true,
10 | "sourceMap": true,
11 | "baseUrl": "./",
12 | "strict": true,
13 | "paths": {
14 | "@/*": ["src/*"],
15 | "@@/*": ["src/.umi/*"]
16 | },
17 | "allowSyntheticDefaultImports": true
18 | },
19 | "include": [
20 | "mock/**/*",
21 | "src/**/*",
22 | "config/**/*",
23 | ".umirc.ts",
24 | "typings.d.ts"
25 | ],
26 | "exclude": [
27 | "node_modules",
28 | "lib",
29 | "es",
30 | "dist",
31 | "typings",
32 | "**/__test__",
33 | "test",
34 | "docs",
35 | "tests"
36 | ]
37 | }
38 |
--------------------------------------------------------------------------------
/examples_static/typings.d.ts:
--------------------------------------------------------------------------------
1 | declare module '*.css';
2 | declare module '*.less';
3 | declare module '*.png';
4 | declare module '*.svg' {
5 | export function ReactComponent(
6 | props: React.SVGProps,
7 | ): React.ReactElement;
8 | const url: string;
9 | export default url;
10 | }
11 |
--------------------------------------------------------------------------------
/jest.config.ts:
--------------------------------------------------------------------------------
1 | /** @type {import('ts-jest').JestConfigWithTsJest} */
2 | export default {
3 | preset: 'ts-jest',
4 | testEnvironment: 'node',
5 | moduleFileExtensions: [
6 | "ts",
7 | "tsx",
8 | "js",
9 | "jsx"
10 | ],
11 | "modulePaths": [
12 | ""
13 | ],
14 | moduleNameMapper: {
15 | '^src/(.*)': '/src/$1',
16 | },
17 | setupFiles: [
18 | ],
19 | testMatch: [
20 | ]
21 | };
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "mind-lake-sdk",
3 | "version": "1.0.13",
4 | "description": "",
5 | "module": "dist/index.js",
6 | "types": "dist/index.d.ts",
7 | "scripts": {
8 | "dev": "father dev",
9 | "prettier": "prettier --write src/**/*.{js,jsx,tsx,ts,json}",
10 | "test-all": "npm run test-create && npm run test-delete && npm run test-drop && npm run test-encrypt && npm run test-insert && npm run test-link && npm run test-update && npm run test-sharing",
11 | "test-create": "jest create",
12 | "test-delete": "jest delete",
13 | "test-drop": "jest drop",
14 | "test-encrypt": "jest encrypt",
15 | "test-insert": "jest insert",
16 | "test-link": "jest link --testNamePattern=alice_init_data && jest link --testNamePattern=test_link_table_to_otherwalletcocoon_bob",
17 | "test-sharing": "jest sharing --testNamePattern=Alice_init_encrypt_data && jest sharing --testNamePattern=no_grant_bob_try_to_decrypt && jest sharing --testNamePattern=grant_column_1 && jest sharing --testNamePattern=confirm_grant && jest sharing --testNamePattern=grant_column_3 && jest sharing --testNamePattern=confirm_grant_3",
18 | "test-update": "jest update",
19 | "build": "cross-env NODE_ENV=production && father build",
20 | "build:deps": "father prebundle",
21 | "prepublishOnly": "father doctor && npm run build"
22 | },
23 | "keywords": [
24 | "mind",
25 | "lake",
26 | "mind-lake"
27 | ],
28 | "authors": [],
29 | "license": "MIT",
30 | "files": [
31 | "dist",
32 | "compiled"
33 | ],
34 | "publishConfig": {
35 | "access": "public"
36 | },
37 | "repository": "https://github.com/mind-network/mind-lake-sdk-typescript",
38 | "homepage": "https://github.com/mind-network/mind-lake-sdk-typescript",
39 | "dependencies": {
40 | "@babel/runtime": "^7.21.5",
41 | "@clerk/clerk-js": "^4.56.1",
42 | "@ethersproject/providers": "^5.7.2",
43 | "@metamask/eth-sig-util": "^4.0.0",
44 | "@types/jest": "^29.5.1",
45 | "@types/node-rsa": "^1.1.1",
46 | "axios": "^1.3.6",
47 | "dayjs": "^1.11.7",
48 | "decimal.js": "^10.4.3",
49 | "js-base64": "^3.7.5",
50 | "node-forge": "^1.3.1",
51 | "node-rsa": "^1.1.1",
52 | "uuid": "^9.0.0",
53 | "web3": "^1.9.0"
54 | },
55 | "devDependencies": {
56 | "cross-env": "^7.0.3",
57 | "father": "^4.1.8",
58 | "jest": "^29.5.0",
59 | "log4js": "^6.9.1",
60 | "prettier": "2.8.8",
61 | "ts-jest": "^29.1.0",
62 | "ts-node": "^10.9.1"
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/src/Crypto.ts:
--------------------------------------------------------------------------------
1 | import { Service } from './request';
2 | import { CipherHelper } from './util/cipher';
3 | import { MindLake } from './MindLake';
4 | // @ts-ignore
5 | import { v4 as uuidv4 } from 'uuid';
6 | import { CCEntry, DataType, ResultType, MkManager } from './types';
7 | import Result from './util/result';
8 |
9 | export default class Crypto {
10 | private service!: Service;
11 |
12 | private mkManager!: MkManager;
13 |
14 | private sdk!: MindLake;
15 |
16 | constructor(sdk: MindLake) {
17 | this.service = sdk.service;
18 | this.mkManager = sdk.mkManager;
19 | this.sdk = sdk;
20 | }
21 |
22 | /**
23 | * encrypt method
24 | * @param tableName
25 | * @param columnName
26 | * @param data
27 | * @param schema
28 | */
29 | public async encrypt(
30 | data: any,
31 | tableNameColumnName: string | DataType,
32 | ): Promise {
33 | try {
34 | MindLake.checkLogin();
35 | this.sdk.checkRegistered();
36 | const walletAddress = await this.mkManager.getWalletAccount();
37 | let encType!: number;
38 | //const { ctxId, decryptedDek, algorithm }
39 | let ccEntry!: { ctxId: number; algorithm: number; decryptedDek: Buffer };
40 | if (typeof tableNameColumnName === 'string') {
41 | const [tableName, columnName] = tableNameColumnName.split('.');
42 | encType = await this.service.execute({
43 | bizType: 107,
44 | tableName,
45 | column: columnName,
46 | });
47 | // @ts-ignore
48 | ccEntry = await this._getOrGenDek(tableName, columnName, walletAddress);
49 | } else {
50 | encType = tableNameColumnName;
51 | const { ctxId, algorithm, encryptedDek } = await this.service.execute<
52 | any,
53 | { ctxId: number; algorithm: number; encryptedDek: string }
54 | >({ bizType: 108 });
55 | const mekBuffer = await this.mkManager.getMekBytes();
56 | const [_, decryptedDek] = CipherHelper.decryptDekToBase64(
57 | mekBuffer,
58 | encryptedDek,
59 | );
60 | ccEntry = { ctxId, algorithm, decryptedDek };
61 | }
62 |
63 | if (encType > 4) {
64 | encType += 1;
65 | }
66 | const encodeDataBuffer = CipherHelper.encodeDataByType(data, encType);
67 |
68 | const header = this._genCryptoHeader(ccEntry.ctxId, encType);
69 | const checkCode = this._genCheckCode(encodeDataBuffer, 1);
70 | const data_to_enc = Buffer.concat([
71 | encodeDataBuffer,
72 | Buffer.from(checkCode),
73 | ]);
74 | let encrypted_data;
75 | const iv = CipherHelper.randomBytes();
76 | if (ccEntry.algorithm === 3) {
77 | encrypted_data = CipherHelper.aesEncrypt(
78 | ccEntry.decryptedDek,
79 | iv,
80 | data_to_enc,
81 | );
82 | }
83 | if (!encrypted_data) {
84 | throw new Error('aesEncrypt error');
85 | }
86 | const buf = Buffer.concat([header, iv, encrypted_data]);
87 | const temp = buf.slice(1);
88 | const checkCode2 = this._genCheckCode(temp, 1);
89 | const result = Buffer.concat([checkCode2, temp]);
90 | return Result.success(`\\x${Buffer.from(result).toString('hex')}`);
91 | } catch (e) {
92 | console.error(e);
93 | return Result.fail(e);
94 | }
95 | }
96 |
97 | /**
98 | *
99 | * @param hex
100 | */
101 | public async decrypt(hex: string): Promise {
102 | try {
103 | MindLake.checkLogin();
104 | this.sdk.checkRegistered();
105 | const mek = await this.mkManager.getMekBytes();
106 | const encryptData = hex.replace('\\x', '');
107 | const encryptDataBuffer = Buffer.from(encryptData, 'hex');
108 | const header = this._extractCryptoHeader(encryptDataBuffer);
109 | const cxtId = this._extractCtxId(header);
110 | const { encryptedDek, algorithm } = await this.service.execute<
111 | any,
112 | { encryptedDek: string; algorithm: number }
113 | >({
114 | bizType: 111,
115 | ctxId: String(cxtId),
116 | });
117 | const [_, dek] = CipherHelper.decryptDekToBase64(mek, encryptedDek);
118 | const afterEncType = this._extractEncType(header);
119 | const idx = (header[1] & 0x7) + 2;
120 | const iv = encryptDataBuffer.slice(idx, idx + 16);
121 | const cipherBlob = encryptDataBuffer.slice(idx + 16);
122 | // @ts-ignore
123 | const plainBlob = CipherHelper.aesDecrypt(dek, iv, cipherBlob);
124 | const encodeResult = plainBlob.subarray(0, -1);
125 | const checkCode = plainBlob.slice(-1);
126 | const checkCode2 = this._genCheckCode(encodeResult, 1);
127 | if (checkCode[0] != checkCode2[0]) {
128 | throw new Error('Check code is not correct');
129 | }
130 | const decryptData = CipherHelper.decodeDataByType(
131 | Uint8Array.from(encodeResult),
132 | afterEncType,
133 | );
134 | return Result.success(decryptData);
135 | } catch (e) {
136 | console.error(e);
137 | return Result.fail(e);
138 | }
139 | }
140 |
141 | /**
142 | * get dek
143 | * @param schema
144 | * @param table
145 | * @param column
146 | * @param walletAddress
147 | * @private
148 | */
149 | private async _getOrGenDek(
150 | table: string,
151 | column: string,
152 | walletAddress: string,
153 | ) {
154 | const mekBuffer = await this.mkManager.getMekBytes();
155 | let cc = await this._queryCCEntryByName(table, column, walletAddress).catch(
156 | (e) => {},
157 | );
158 | if (!cc) {
159 | cc = await this._genCCEntryFromLocal(table, column);
160 | }
161 | const { ctxId, dekId, encryptedDek, algorithm } = cc;
162 | const [, decryptedDek] = CipherHelper.decryptDekToBase64(
163 | mekBuffer,
164 | encryptedDek,
165 | );
166 | return { ctxId: ctxId, decryptedDek, algorithm };
167 | }
168 |
169 | private async _queryCCEntryByName(
170 | table: string,
171 | column: string,
172 | walletAddress: string,
173 | ) {
174 | return await this.service.execute({
175 | bizType: 108,
176 | table: table,
177 | column: column,
178 | walletAddress,
179 | });
180 | }
181 |
182 | private async _genCCEntryFromLocal(table: string, column: string) {
183 | const dekId = await this.service.execute({
184 | bizType: 109,
185 | mekId: this.sdk.mekId,
186 | });
187 | const dek: Buffer = CipherHelper.randomBytes();
188 | const mek = await this.mkManager.getMekBytes();
189 | return this._genSQLInsertDek(
190 | this.sdk.mekId,
191 | dekId,
192 | mek,
193 | dek,
194 | 3,
195 | table,
196 | column,
197 | );
198 | }
199 |
200 | private async _genSQLInsertDek(
201 | mekId: string,
202 | dekId: number,
203 | mek: Buffer,
204 | dek: Buffer,
205 | alg = 3,
206 | table: string,
207 | column: string,
208 | ) {
209 | const dekCipherStr = CipherHelper.encryptDekToBase64(mek, dekId, dek);
210 | const grpIdStr = uuidv4();
211 | const uuidStringWithoutHyphen = grpIdStr.replace(/-/g, '');
212 | const _gripId = Buffer.from(
213 | uuidStringWithoutHyphen
214 | .match(/.{1,2}/g)
215 | ?.map((byte: string) => parseInt(byte, 16)) || [],
216 | );
217 | const gAuthStr = CipherHelper.digest_gAuth(mek, _gripId, dekId);
218 | return this.service.execute({
219 | bizType: 110,
220 | table: table,
221 | column: column,
222 | schema: 'public',
223 | mekId,
224 | dekId,
225 | dekCipherStr,
226 | grpIdStr,
227 | groupAuthStr: gAuthStr,
228 | });
229 | }
230 |
231 | private _genCryptoHeader(ctxId: number, encType: number): Buffer {
232 | let head = Buffer.from('0000', 'hex');
233 | let tmp_value = head[1];
234 | tmp_value = tmp_value & 0xffffff07;
235 | tmp_value = tmp_value | (encType << 3);
236 | head[1] = tmp_value;
237 | let tmp = ctxId;
238 | while (tmp != 0) {
239 | head = Buffer.concat([head, Buffer.alloc(1).fill(tmp & 0xff)]);
240 | tmp >>= 8;
241 | }
242 | const ctxLen = head.length - 2;
243 | let tmp_val = head[1];
244 | tmp_val = (tmp_val & 0xfffffff8) | (ctxLen & 0x7);
245 | head[1] = tmp_val;
246 | return head;
247 | }
248 |
249 | private _extractCryptoHeader(data: Uint8Array): Buffer {
250 | let header = [];
251 | let index = 0;
252 | for (let i = 0; i < 1; i++) {
253 | header.push(data[index]);
254 | // header[index] = data[index];
255 | index++;
256 | }
257 | if (index !== 1) {
258 | throw new Error('Invalid header index');
259 | }
260 | header.push(data[index]);
261 | //header[index] = data[index];
262 | index++;
263 | const rng = header[1] & 0x7;
264 | for (let i = 0; i < rng; i++) {
265 | header.push(data[index]);
266 | // header[index] = data[index];
267 | index++;
268 | }
269 | return Buffer.from(header);
270 | }
271 |
272 | private _extractEncType(header: Buffer): number {
273 | const tmp_value = header[1];
274 | const type_value = (tmp_value & 0xf8) >> 3;
275 | return type_value;
276 | }
277 |
278 | private _extractCtxId(header: Buffer): number {
279 | const ctxIdLen = header[1] & 0x7;
280 | if (header.length !== ctxIdLen + 2) {
281 | throw new Error('Invalid header length');
282 | }
283 | let ctxId = 0;
284 | for (let i = 0; i < ctxIdLen; i++) {
285 | const index = header.length - 1 - i;
286 | ctxId = (ctxId << 8) | (header[index] & 0xff);
287 | }
288 | return ctxId;
289 | }
290 |
291 | private _genCheckCode(encodeData: Uint8Array, resultSize: number) {
292 | let tmpCode = new Uint8Array(resultSize);
293 | for (let i = 0; i < encodeData.length; i++) {
294 | let n = i % resultSize;
295 | tmpCode[n] ^= encodeData[i];
296 | }
297 | return Buffer.from(tmpCode);
298 | }
299 | }
300 |
--------------------------------------------------------------------------------
/src/DataLake.ts:
--------------------------------------------------------------------------------
1 | import { Service } from './request';
2 | import { MindLake } from './MindLake';
3 | import Result from './util/result';
4 | import { ColumnType, ResultType } from './types';
5 |
6 | export default class DataLake {
7 | private service!: Service;
8 |
9 | constructor(sdk: MindLake) {
10 | this.service = sdk.service;
11 | }
12 |
13 | public async query(executeSql: string) {
14 | try {
15 | const res = await this.service.execute({ bizType: 114, executeSql });
16 | return Result.success(res);
17 | } catch (e) {
18 | console.error(e);
19 | return Result.fail(e);
20 | }
21 | }
22 |
23 | public async createCocoon(cocoonName: string): Promise {
24 | try {
25 | await this.service.execute({ bizType: 121, cocoonName });
26 | return Result.success(true);
27 | } catch (e) {
28 | console.error(e);
29 | return Result.fail(e);
30 | }
31 | }
32 |
33 | public async dropCocoon(cocoonName: string): Promise {
34 | try {
35 | await this.service.execute({ bizType: 129, cocoonName });
36 | return Result.success(true);
37 | } catch (e) {
38 | console.error(e);
39 | return Result.fail(e);
40 | }
41 | }
42 |
43 | public async createTable(
44 | tableName: string,
45 | columns: Array,
46 | pkColumns?: Array,
47 | ): Promise {
48 | try {
49 | await this.service.execute({
50 | bizType: 123,
51 | tableName,
52 | columns,
53 | pkColumns,
54 | });
55 | return Result.success(true);
56 | } catch (e) {
57 | console.error(e);
58 | return Result.fail(e);
59 | }
60 | }
61 |
62 | public async dropTable(tableName: string): Promise {
63 | try {
64 | await this.service.execute({ bizType: 128, tableName });
65 | return Result.success(true);
66 | } catch (e) {
67 | console.error(e);
68 | return Result.fail(e);
69 | }
70 | }
71 |
72 | public async listCocoon(): Promise {
73 | try {
74 | const data = await this.service.execute({ bizType: 122 });
75 | return Result.success(data);
76 | } catch (e) {
77 | console.error(e);
78 | return Result.fail(e);
79 | }
80 | }
81 |
82 | public async listTablesByCocoon(cocoonName: string): Promise {
83 | try {
84 | const data = await this.service.execute({
85 | bizType: 125,
86 | cocoonName,
87 | });
88 | return Result.success(data);
89 | } catch (e) {
90 | console.error(e);
91 | return Result.fail(e);
92 | }
93 | }
94 |
95 | public async linkTableToCocoon(
96 | tableName: string,
97 | cocoonName: string,
98 | ): Promise {
99 | try {
100 | const data = await this.service.execute({
101 | bizType: 124,
102 | tableName,
103 | cocoonName,
104 | });
105 | return Result.success(data);
106 | } catch (e) {
107 | console.error(e);
108 | return Result.fail(e);
109 | }
110 | }
111 |
112 | public async listTableByWalletAddress(): Promise {
113 | try {
114 | const data = await this.service.execute({
115 | bizType: 301,
116 | });
117 | return Result.success(data);
118 | } catch (e) {
119 | console.error(e);
120 | return Result.fail(e);
121 | }
122 | }
123 |
124 | public async queryForDataAndMeta(executeSql: string): Promise {
125 | try {
126 | const data = await this.service.execute({
127 | bizType: 113,
128 | executeSql,
129 | });
130 | return Result.success(data);
131 | } catch (e) {
132 | console.error(e);
133 | return Result.fail(e);
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/src/MindLake.ts:
--------------------------------------------------------------------------------
1 | import { Service } from './request';
2 | import { APP_KEY, CHAIN_KEY, TOKEN_KEY } from './util/constant';
3 | import { CipherHelper } from './util/cipher';
4 | import { Web3Interact } from './util/web3';
5 | import { Util } from './util/util';
6 | import Crypto from './Crypto';
7 | import DataLake from './DataLake';
8 | import { Bcl } from './util/bcl';
9 | import {
10 | ServerInfo,
11 | RequestMekProvisionBody,
12 | RequestRegisterCertificateBody,
13 | ResultType,
14 | DataType,
15 | MkManager,
16 | ChainInfo,
17 | ClerkConfig,
18 | } from './types';
19 | import Permission from './Permission';
20 | import Result from './util/result';
21 | import pkConfig from '../package.json';
22 | import Clerk from './util/clerk';
23 | import { SignInProps, UserButtonProps } from '@clerk/types';
24 | /**
25 | *
26 | */
27 | export class MindLake {
28 | public service!: Service;
29 |
30 | /**
31 | * mindLake version
32 | */
33 | public static readonly version = pkConfig.version;
34 |
35 | /**
36 | * isConnected mindDB
37 | */
38 | public static isConnected = false;
39 |
40 | /**
41 | * mind db is init
42 | */
43 | public isRegistered: boolean = false;
44 |
45 | /**
46 | * server publicKey
47 | * */
48 | public publicKey!: string;
49 |
50 | /**
51 | *registerPukId
52 | */
53 | public registerPukId!: string;
54 |
55 | /**
56 | * mek id
57 | */
58 | public mekId!: string;
59 |
60 | /**
61 | * crypto instance
62 | */
63 | public crypto!: Crypto;
64 |
65 | public mkManager!: MkManager;
66 |
67 | public dataLake!: DataLake;
68 |
69 | public permission!: Permission;
70 |
71 | private chainList: Array | undefined;
72 |
73 | private static instance: MindLake;
74 |
75 | public static log = false;
76 |
77 | public static readonly DataType = DataType;
78 |
79 | public static async getInstance(
80 | appKey: string,
81 | nodeUrl?: string,
82 | ): Promise {
83 | if (this.instance === undefined) {
84 | this.instance = new MindLake(appKey, nodeUrl);
85 | await this.instance._getServerInfo();
86 | await this.instance.supportChaninList();
87 | const chainStr = localStorage.getItem(CHAIN_KEY);
88 | let chain;
89 | try {
90 | chain = chainStr && JSON.parse(chainStr);
91 | } catch (error) {
92 | console.error(error);
93 | }
94 | if (chain?.clerk) {
95 | this.instance.mkManager = new Clerk(chain.clerk);
96 | let sessionToken;
97 | if (this.instance.mkManager instanceof Clerk) {
98 | sessionToken = await this.instance.mkManager.init();
99 | }
100 | if (sessionToken) {
101 | const res = await this.instance.service.execute<
102 | any,
103 | { token: string }
104 | >({
105 | bizType: 206,
106 | clerkToken: sessionToken,
107 | });
108 | if (res && res.token) {
109 | localStorage.setItem(TOKEN_KEY, res.token);
110 | await this.instance._init();
111 | MindLake.isConnected = true;
112 | }
113 | } else {
114 | MindLake.isConnected = false;
115 | }
116 | } else {
117 | this.instance.mkManager = new Web3Interact();
118 | await this.instance.mkManager.checkConnection();
119 | if (chain && this.instance.mkManager instanceof Web3Interact) {
120 | chain && this.instance.mkManager.setChain(chain);
121 | }
122 | }
123 | this.instance.crypto = new Crypto(this.instance);
124 | this.instance.dataLake = new DataLake(this.instance);
125 | this.instance.permission = new Permission(this.instance);
126 | }
127 | return this.instance;
128 | }
129 |
130 | private constructor(appKey: string, nodeUrl?: string) {
131 | localStorage.setItem(APP_KEY, appKey);
132 | this.service = new Service(nodeUrl);
133 | }
134 |
135 | /**
136 | *connect db
137 | */
138 | public async connect(chainId: string | number): Promise {
139 | try {
140 | if (!(this.mkManager instanceof Web3Interact)) {
141 | this.mkManager = new Web3Interact();
142 | this.crypto = new Crypto(this);
143 | this.permission = new Permission(this);
144 | }
145 | if (typeof chainId !== 'number' && typeof chainId !== 'string') {
146 | return Result.fail('chainId must be a number or string');
147 | }
148 | const chain = this.chainList?.find((c) => c.chainId == chainId);
149 | if (!chain) {
150 | return Result.fail('chain not supported');
151 | }
152 | if (this.mkManager instanceof Web3Interact) {
153 | this.mkManager.setChain(chain);
154 | await this.mkManager._changeChainToSupportChain();
155 | }
156 | const walletAddress = await this.mkManager.getWalletAccount();
157 | localStorage.setItem(CHAIN_KEY, JSON.stringify(chain));
158 | const nonce = await this.service.execute({
159 | bizType: 203,
160 | walletAddress,
161 | });
162 | const signature = await this.mkManager.personalSignature(nonce);
163 | const res = await this.service.execute({
164 | bizType: 201,
165 | walletAddress,
166 | signature,
167 | });
168 | if (res && res.token) {
169 | localStorage.setItem(TOKEN_KEY, res.token);
170 | await this._init();
171 | MindLake.isConnected = true;
172 | return Result.success(true);
173 | }
174 | return Result.fail(false);
175 | } catch (e) {
176 | console.error(e);
177 | localStorage.removeItem(TOKEN_KEY);
178 | return Result.fail(e);
179 | }
180 | }
181 |
182 | public async clerkConnect(
183 | config?: ClerkConfig,
184 | signProps?: SignInProps,
185 | ): Promise {
186 | try {
187 | this.mkManager = new Clerk(config);
188 | localStorage.setItem(CHAIN_KEY, JSON.stringify({ clerk: { ...config } }));
189 | if (this.mkManager instanceof Clerk) {
190 | await this.mkManager.init();
191 | await this.mkManager.signIn(signProps);
192 | }
193 | } catch (e) {
194 | return Result.fail(e);
195 | }
196 | }
197 |
198 | public renderClerkUserBotton(dom: HTMLDivElement, props?: UserButtonProps) {
199 | if (this.mkManager instanceof Clerk) {
200 | this.mkManager.renderUserButton(dom, props);
201 | }
202 | }
203 |
204 | public async supportChaninList(): Promise> {
205 | if (!this.chainList) {
206 | const result = await this.service.execute>({
207 | bizType: 205,
208 | });
209 | this.chainList = result;
210 | }
211 | return this.chainList;
212 | }
213 |
214 | /**
215 | * disconnect db
216 | */
217 | public async disConnect(): Promise {
218 | try {
219 | await this.service.execute({ bizType: 202 });
220 | return Result.success(true);
221 | } catch (e) {
222 | console.error(e);
223 | return Result.fail(e);
224 | }
225 | }
226 |
227 | /**
228 | * getEnclaveInfo
229 | */
230 | private async _getServerInfo() {
231 | const info = await this.service
232 | .execute({
233 | bizType: 120,
234 | })
235 | .catch((e) => console.error(e));
236 | if (info) {
237 | MindLake.isConnected = true;
238 | this.publicKey = info.publicKey;
239 | this.isRegistered =
240 | info.isRegistered && info.isMekProvision && info.isSelfBcl;
241 | this.mekId = info.mekId;
242 | if (this.isRegistered && this.mekId) {
243 | await this._getAccount();
244 | }
245 | }
246 | }
247 |
248 | private async _init() {
249 | await this._getServerInfo();
250 | const provisionRes = await this._mekProvision();
251 | if (!provisionRes) {
252 | throw new Error('Provision failed');
253 | }
254 | if (!this.isRegistered) {
255 | const { privateKeyPem, publicKeyPem } = await this.mkManager.getPkPem();
256 | const registerPukId = await this._registerCertificate(
257 | publicKeyPem,
258 | privateKeyPem,
259 | );
260 | if (!registerPukId) {
261 | throw new Error('Register key pair failed');
262 | }
263 | this.registerPukId = registerPukId;
264 | const sn = await this._issueBclForSelf(privateKeyPem);
265 | if (!sn) {
266 | throw new Error('Grant for self failed');
267 | }
268 | this.isRegistered = true;
269 | }
270 | }
271 |
272 | /**
273 | * mek provision
274 | * @private
275 | */
276 | private async _mekProvision() {
277 | if (!this.publicKey) {
278 | throw new Error('Provision failed: The public key is not empty');
279 | }
280 | const pubKey = this.publicKey.replace('\\n', '\n');
281 | const ephemeralKey = CipherHelper.randomBytes();
282 | const sealedEphemeralKey: Uint8Array = CipherHelper.rsaEncrypt(
283 | pubKey,
284 | ephemeralKey,
285 | );
286 | const sealedEphemeralKeyLenBytes = new Buffer([
287 | sealedEphemeralKey.length & 0xff,
288 | (sealedEphemeralKey.length >> 8) & 0xff,
289 | ]);
290 | const iv = CipherHelper.randomBytes();
291 | const envelope_json = await this._genEnvelope();
292 | const envelope_enc = CipherHelper.aesEncrypt(
293 | ephemeralKey,
294 | iv,
295 | envelope_json,
296 | );
297 | const big_envelope = Buffer.concat([
298 | sealedEphemeralKeyLenBytes,
299 | sealedEphemeralKey,
300 | iv,
301 | envelope_enc,
302 | ]);
303 | const big_envelopeBase64 = Buffer.from(big_envelope).toString('base64');
304 | return await this.service.execute({
305 | bizType: 102,
306 | databasePublicKey: this.publicKey,
307 | envelope: big_envelopeBase64,
308 | });
309 | }
310 |
311 | /**
312 | * registerCertificate pukId
313 | * @private
314 | */
315 | private async _registerCertificate(
316 | publicKeyPem: string,
317 | privateKeyPem: string,
318 | ): Promise {
319 | const mekId = this.mekId;
320 | const mek = await this.mkManager.getMekBytes();
321 | const pukId = CipherHelper.sha256Hash(publicKeyPem);
322 | const toBeSignedBytes = Buffer.concat([
323 | Buffer.from(Util.structPackq(mekId)),
324 | Buffer.from(pukId, 'latin1'), //important
325 | ]);
326 | const rsaSign = Buffer.concat([
327 | Buffer.from('01', 'hex'),
328 | CipherHelper.rsaSign(privateKeyPem, toBeSignedBytes),
329 | ]);
330 | const private_sig = rsaSign.toString('base64');
331 | const mekSign = Buffer.concat([
332 | Buffer.from('00', 'hex'),
333 | CipherHelper.hmacHash(mek, toBeSignedBytes),
334 | ]);
335 | const mek_sign = mekSign.toString('base64');
336 | const res = await this.service.execute<
337 | RequestRegisterCertificateBody,
338 | boolean
339 | >({
340 | bizType: 104,
341 | mekId,
342 | pukId,
343 | publicKey: publicKeyPem,
344 | privateSig: private_sig,
345 | mekSig: mek_sign,
346 | });
347 | if (res) {
348 | return pukId;
349 | }
350 | }
351 |
352 | /**
353 | * issueBclForSelf
354 | * @param privateKeyPem
355 | * @private
356 | */
357 | private async _issueBclForSelf(privateKeyPem: string) {
358 | //
359 | const bcl = new Bcl(this.service);
360 | await bcl.createBclBody(this.registerPukId, this.registerPukId, '');
361 | const defaultGroup = await this.service.execute({
362 | bizType: 108,
363 | });
364 | if (!defaultGroup || !defaultGroup.groupId) {
365 | throw new Error('Get default group error');
366 | }
367 | const selfDekGroup = {
368 | groupid: defaultGroup.groupId,
369 | min: 1,
370 | max: 1000,
371 | };
372 | bcl.addDekGroup(selfDekGroup, selfDekGroup);
373 | return await bcl.issueBcl(privateKeyPem, 106);
374 | }
375 |
376 | private async _genEnvelope() {
377 | const mek = await this.mkManager.getMekBytes();
378 | let envelope: any = {};
379 | const base64Mek = Buffer.from(mek).toString('base64');
380 | envelope['mek'] = base64Mek;
381 | envelope['expire'] = 0;
382 | let envelope_json = JSON.stringify(envelope).replace(' ', '');
383 | return envelope_json;
384 | }
385 |
386 | private async _getAccount() {
387 | const registerPukId = await this.service.execute({
388 | bizType: 103,
389 | mekId: this.mekId,
390 | });
391 | this.registerPukId = registerPukId;
392 | }
393 |
394 | public static checkLogin() {
395 | if (!MindLake.isConnected) {
396 | throw new Error('Please connect first');
397 | }
398 | }
399 |
400 | public checkRegistered() {
401 | if (!this.isRegistered) {
402 | throw new Error('Register error, please reLogin to register');
403 | }
404 | }
405 | }
406 |
--------------------------------------------------------------------------------
/src/Permission.ts:
--------------------------------------------------------------------------------
1 | import { MindLake } from './MindLake';
2 | import { Service } from './request';
3 | import { Bcl } from './util/bcl';
4 | import { ResultType } from './types';
5 | import Result from './util/result';
6 | import { MkManager } from './types';
7 |
8 | export default class Permission {
9 | private readonly service!: Service;
10 |
11 | private readonly mkManager!: MkManager;
12 |
13 | private readonly sdk!: MindLake;
14 |
15 | constructor(sdk: MindLake) {
16 | this.service = sdk.service;
17 | this.mkManager = sdk.mkManager;
18 | this.sdk = sdk;
19 | }
20 |
21 | /**
22 | *
23 | * @param targetWalletAddress
24 | */
25 | public async grant(
26 | targetChain: string,
27 | targetWalletAddress: string,
28 | columns: Array,
29 | ): Promise {
30 | try {
31 | MindLake.checkLogin();
32 | this.sdk.checkRegistered();
33 | if (!columns.length) {
34 | throw new Error('no columns to need grant');
35 | }
36 |
37 | const bcl = new Bcl(this.service);
38 | //get subjectPublicId
39 | const res = await this.service.execute({
40 | bizType: 119,
41 | targetWalletAddress: targetWalletAddress,
42 | targetChain,
43 | });
44 | if (!res || !res.publicKeyId) {
45 | throw new Error(
46 | "Peer user (Subject)'s certificate hasn't been registered.",
47 | );
48 | }
49 | await bcl.createBclBody(this.sdk.registerPukId, res.publicKeyId, '');
50 |
51 | const eachFunc = async (data: Array) => {
52 | for (const tableColumn of data) {
53 | const [table, column] = tableColumn.split('.');
54 | await this._addColumnIntoBcl(bcl, table, column);
55 | }
56 | };
57 |
58 | await eachFunc(columns);
59 | const { privateKeyPem } = await this.sdk.mkManager.getPkPem();
60 | const sn = await bcl.issueBcl(privateKeyPem, 115);
61 | return Result.success(sn);
62 | } catch (e) {
63 | console.error(e);
64 | return Result.fail(e);
65 | }
66 | }
67 |
68 | /**
69 | *
70 | * @param policyId
71 | */
72 | public async confirm(policyId: string): Promise {
73 | try {
74 | MindLake.checkLogin();
75 | this.sdk.checkRegistered();
76 | if (!policyId) {
77 | throw new Error('The policy id is empty');
78 | }
79 | const bcl = new Bcl(this.service);
80 | await bcl.loadBclBodyBySN(policyId);
81 | if (!bcl.bclBody || !bcl.bclBody.serial_num) {
82 | throw new Error('The policyID is not correct');
83 | }
84 | const { privateKeyPem } = await this.mkManager.getPkPem();
85 | const sn = await bcl.issueBcl(privateKeyPem, 117);
86 | return Result.success(sn);
87 | } catch (e) {
88 | console.error(e);
89 | return Result.fail(e);
90 | }
91 | }
92 |
93 | /**
94 | * revoke grant
95 | * @param targetWalletAddress
96 | * @param columns, if the columns is empty ,will revoke all
97 | */
98 | public async revoke(
99 | targetWalletAddress: string,
100 | targetChain: string,
101 | columns?: Array<{ table: string; column: string }>,
102 | ): Promise {
103 | try {
104 | MindLake.checkLogin();
105 | this.sdk.checkRegistered();
106 | const bcl = new Bcl(this.service);
107 | //get subjectPublicId
108 | const res = await this.service.execute({
109 | bizType: 119,
110 | targetWalletAddress: targetWalletAddress,
111 | targetChain,
112 | });
113 | if (!res || !res.publicKeyId) {
114 | throw new Error(
115 | "Peer user (Subject)'s certificate hasn't been registered.",
116 | );
117 | }
118 | await bcl.loadBclBodyByPukId(this.sdk.registerPukId, res.publicKeyId);
119 | if (!bcl.bclBody || !bcl.bclBody.serial_num) {
120 | throw new Error('No grant required to revoke!');
121 | }
122 | if (!columns || !columns.length) {
123 | bcl.removeDekGroupAll();
124 | } else {
125 | const groupIdArray = new Array();
126 | const eachFunc = async (
127 | data: Array<{ table: string; column: string }>,
128 | ) => {
129 | for (const column of data) {
130 | const ccSelf = await this.service.execute(
131 | {
132 | bizType: 108,
133 | schema: 'public',
134 | table: column.table,
135 | column: column.column,
136 | },
137 | );
138 | if (!ccSelf.groupId) {
139 | throw new Error('groupId is not exist');
140 | }
141 | groupIdArray.push(ccSelf.groupId);
142 | }
143 | };
144 | await eachFunc(columns);
145 | bcl.removeDekGroup(groupIdArray);
146 | }
147 | const { privateKeyPem } = await this.mkManager.getPkPem();
148 | const sn = await bcl.issueBcl(privateKeyPem, 115);
149 | return Result.success(sn);
150 | } catch (e) {
151 | console.error(e);
152 | return Result.fail(e);
153 | }
154 | }
155 |
156 | public async listGrantee(): Promise {
157 | try {
158 | const data = await this.service.execute({ bizType: 126 });
159 | return Result.success(data);
160 | } catch (e) {
161 | console.error(e);
162 | return Result.fail(e);
163 | }
164 | }
165 |
166 | public async listOwner(): Promise {
167 | try {
168 | const data = await this.service.execute({ bizType: 130 });
169 | return Result.success(data);
170 | } catch (e) {
171 | console.error(e);
172 | return Result.fail(e);
173 | }
174 | }
175 |
176 | public async listOwnerColumn(
177 | targetWalletAddress: string,
178 | targetChain: string,
179 | ): Promise {
180 | try {
181 | const data = await this.service.execute({
182 | bizType: 131,
183 | targetWalletAddress,
184 | targetChain,
185 | });
186 | return Result.success(data);
187 | } catch (e) {
188 | console.error(e);
189 | return Result.fail(e);
190 | }
191 | }
192 |
193 | public async listGrantedColumn(
194 | targetWalletAddress: string,
195 | targetChain: string,
196 | ): Promise {
197 | try {
198 | const data = await this.service.execute({
199 | bizType: 127,
200 | targetWalletAddress,
201 | targetChain,
202 | });
203 | return Result.success(data);
204 | } catch (e) {
205 | console.error(e);
206 | return Result.fail(e);
207 | }
208 | }
209 |
210 | public async _addColumnIntoBcl(
211 | bcl: Bcl,
212 | table: string,
213 | column: string,
214 | minId = 1,
215 | maxId = 1000,
216 | ) {
217 | const ccSelf = await this.service.execute({
218 | bizType: 108,
219 | table: table,
220 | column: column,
221 | });
222 | if (!ccSelf.groupId) {
223 | throw new Error('groupId is not exist');
224 | }
225 | const issuerDekGroup = { groupid: ccSelf.groupId, min: minId, max: maxId };
226 | bcl.addDekGroup(issuerDekGroup);
227 | }
228 | }
229 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import { MindLake } from './MindLake';
2 | import Crypto from './Crypto';
3 | import DataLake from './DataLake';
4 | import Permission from './Permission';
5 | import { DataType, ColumnType } from './types';
6 |
7 | export { MindLake, Crypto, DataLake, Permission, DataType, ColumnType };
8 |
--------------------------------------------------------------------------------
/src/request/index.ts:
--------------------------------------------------------------------------------
1 | import request from './request';
2 |
3 | export class Service {
4 |
5 | nodeUrl = "https://sdk.mindnetwork.xyz";
6 |
7 | constructor(nodeUrl?: string) {
8 | if(nodeUrl) {
9 | this.nodeUrl = nodeUrl
10 | }
11 | }
12 |
13 | async execute(data: R): Promise {
14 | return await request.post(this.nodeUrl + '/node', data);
15 | }
16 |
17 | }
18 |
19 | // Service.execute({databasePublicKey: '', envelope: ''});
20 |
--------------------------------------------------------------------------------
/src/request/request.ts:
--------------------------------------------------------------------------------
1 | import axios, {
2 | AxiosInstance,
3 | AxiosError,
4 | AxiosRequestConfig,
5 | AxiosResponse,
6 | } from 'axios';
7 | import { APP_KEY, CHAIN_KEY, TOKEN_KEY, WALLET_key } from '../util/constant';
8 | import { MindLake } from '../MindLake';
9 |
10 | const URL: string = `https://sdk.mindnetwork.xyz/node`;
11 | enum RequestEnums {
12 | TIMEOUT = 20000,
13 | OVERDUE = 401,
14 | SUCCESS = 0,
15 | }
16 |
17 | const NOT_LOGIN_CODE = [401, 402, 403, 40003];
18 |
19 | const config = {
20 | // baseURL: URL,
21 | timeout: RequestEnums.TIMEOUT,
22 | };
23 |
24 | class RequestHttp {
25 | service: AxiosInstance;
26 | public constructor(config: AxiosRequestConfig) {
27 | this.service = axios.create(config);
28 |
29 | this.service.interceptors.request.use(
30 | // @ts-ignore
31 | (config) => {
32 | // session token
33 | const token = localStorage.getItem(TOKEN_KEY) || '';
34 | // your wallet address
35 | const wa = localStorage.getItem(WALLET_key);
36 | //your dapp key
37 | const app = localStorage.getItem(APP_KEY);
38 | const chainStr = localStorage.getItem(CHAIN_KEY);
39 | let chain;
40 | try {
41 | chain = chainStr && JSON.parse(chainStr);
42 | } catch (error) {
43 | console.log(error);
44 | }
45 | const _config = {
46 | ...config,
47 | headers: {
48 | token: token,
49 | ver: `v${MindLake.version}`,
50 | wa,
51 | app,
52 | chain: chain?.clerk ? 0 : chain?.chainId,
53 | },
54 | };
55 | if (MindLake.log) {
56 | console.log('request data >>>', config.data);
57 | }
58 | return _config;
59 | },
60 | (error: AxiosError) => {
61 | Promise.reject(error);
62 | },
63 | );
64 |
65 | this.service.interceptors.response.use(
66 | (response: AxiosResponse) => {
67 | const { data, config } = response;
68 | if (MindLake.log) {
69 | console.log('response >>> ', JSON.stringify(data));
70 | }
71 | if (NOT_LOGIN_CODE.includes(data.code)) {
72 | localStorage.removeItem(TOKEN_KEY);
73 | MindLake.isConnected = false;
74 | return Promise.reject({ code: data.code, message: data.message });
75 | }
76 | if (data.code && data.code !== RequestEnums.SUCCESS) {
77 | return Promise.reject({ code: data.code, message: data.message });
78 | }
79 | return data.data;
80 | },
81 | (error: AxiosError) => {
82 | console.error(error);
83 | },
84 | );
85 | }
86 |
87 | get(url: string, params?: object): Promise {
88 | return this.service.get(url, { params });
89 | }
90 | post(url: string, data?: any): Promise {
91 | return this.service.post(url, data);
92 | }
93 | }
94 |
95 | export default new RequestHttp(config);
96 |
--------------------------------------------------------------------------------
/src/types/index.ts:
--------------------------------------------------------------------------------
1 | import { ClerkOptions, UserButtonProps } from '@clerk/types';
2 | export type ResultType = {
3 | code: number;
4 | message?: string;
5 | result?: any;
6 | };
7 |
8 | /**
9 | * This enum can be used in conjunction with the Column class,
10 | * which represents a single column in a database table, to specify the data type of the column.
11 | * By utilizing the DataType enum,
12 | * you can ensure that your data is stored in the appropriate format and can be processed correctly by the system.
13 | */
14 | export enum DataType {
15 | int4 = 1,
16 | int8 = 2,
17 | float4 = 3,
18 | float8 = 4,
19 | decimal = 5,
20 | text = 6,
21 | timestamp = 7,
22 | }
23 |
24 | export type ColumnType = {
25 | columnName: string;
26 | type: DataType;
27 | encrypt: boolean;
28 | };
29 |
30 | export type ServerInfo = {
31 | publicKey: string;
32 | mekId: string;
33 | isRegistered: boolean;
34 | isMekProvision: boolean;
35 | isSelfBcl: boolean;
36 | };
37 |
38 | export type ChainInfo = {
39 | id: number;
40 | chainId: string;
41 | chainName: string;
42 | hexadecimalChainId: string;
43 | currency: string;
44 | rpcNodeUrl: string;
45 | rpcOtherNodeUrl: string;
46 | abi: string;
47 | smartAddress: string;
48 | createTime: string;
49 | updateTime: string;
50 | status: number;
51 | feature: string;
52 | };
53 |
54 | export type RequestMekProvisionBody = {
55 | bizType: number;
56 | databasePublicKey: string;
57 | envelope: string;
58 | };
59 |
60 | export type RequestRegisterCertificateBody = {
61 | bizType: number;
62 | mekId: string;
63 | pukId: string;
64 | publicKey: string;
65 | privateSig: string;
66 | mekSig: string;
67 | };
68 |
69 | export type CCEntry = {
70 | ctxId: number;
71 | dekId: string;
72 | encryptedDek: string;
73 | algorithm: number;
74 | groupId: string;
75 | };
76 |
77 | export interface MkManager {
78 | getWalletAccount(): Promise;
79 | checkConnection(): Promise;
80 | getMekBytes(): Promise;
81 | getPkPem(): Promise<{
82 | privateKeyPem: string;
83 | publicKeyPem: string;
84 | }>;
85 | personalSignature(signData: string): Promise;
86 | encrypt(str: string): Promise;
87 | decrypt(cipher: Buffer): Promise;
88 | }
89 |
90 | /**
91 | * target userButton dom node render to
92 | */
93 | export type ClerkConfig = {
94 | target?: string | HTMLDivElement;
95 | options?: ClerkOptions;
96 | userButtonProps?: UserButtonProps;
97 | };
98 |
99 | export type MindLakeConfig = {
100 | clerk?: ClerkConfig;
101 | };
102 |
103 | export type ConnectChain = ChainInfo | { clerk: ClerkConfig };
104 |
--------------------------------------------------------------------------------
/src/util/abi.ts:
--------------------------------------------------------------------------------
1 | const Abi = [
2 | {
3 | anonymous: false,
4 | inputs: [
5 | {
6 | indexed: true,
7 | internalType: 'address',
8 | name: 'wallet',
9 | type: 'address',
10 | },
11 | {
12 | indexed: false,
13 | internalType: 'bytes',
14 | name: 'MK',
15 | type: 'bytes',
16 | },
17 | {
18 | indexed: false,
19 | internalType: 'bytes',
20 | name: 'SK',
21 | type: 'bytes',
22 | },
23 | ],
24 | name: 'KeysUpdated',
25 | type: 'event',
26 | },
27 | {
28 | inputs: [
29 | {
30 | internalType: 'bytes',
31 | name: '_mk',
32 | type: 'bytes',
33 | },
34 | {
35 | internalType: 'bytes',
36 | name: '_sk',
37 | type: 'bytes',
38 | },
39 | ],
40 | name: 'setKeys',
41 | outputs: [],
42 | stateMutability: 'nonpayable',
43 | type: 'function',
44 | },
45 | {
46 | inputs: [
47 | {
48 | internalType: 'address',
49 | name: '_wallet',
50 | type: 'address',
51 | },
52 | ],
53 | name: 'getKeys',
54 | outputs: [
55 | {
56 | internalType: 'bytes',
57 | name: 'MK',
58 | type: 'bytes',
59 | },
60 | {
61 | internalType: 'bytes',
62 | name: 'SK',
63 | type: 'bytes',
64 | },
65 | ],
66 | stateMutability: 'view',
67 | type: 'function',
68 | },
69 | ];
70 |
71 | export default Abi;
72 |
--------------------------------------------------------------------------------
/src/util/bcl.ts:
--------------------------------------------------------------------------------
1 | import { Service } from '../request';
2 | // @ts-ignore
3 | import dayjs from 'dayjs';
4 | // @ts-ignore
5 | import { v4 as uuidv4 } from 'uuid';
6 | import { CipherHelper } from './cipher';
7 |
8 | export interface IDekGroup {
9 | groupid: string;
10 | min: number;
11 | max: number;
12 | }
13 |
14 | export interface IPolicies {
15 | issuer_dek_group: Array;
16 | subject_dek_group: Array;
17 | result_dek: string;
18 | operation: Array;
19 | post_proc: string;
20 | pre_proc: string;
21 | }
22 |
23 | /**
24 | *
25 | */
26 | export class Bcl {
27 | public bclBody = {
28 | version: 1,
29 | serial_num: '',
30 | issuer_pukid: '',
31 | subject_pukid: '',
32 | validity: {
33 | not_after: '',
34 | not_before: '',
35 | },
36 | policies: {
37 | issuer_dek_group: new Array(),
38 | subject_dek_group: new Array(),
39 | result_dek: '',
40 | operation: '',
41 | postproc: '',
42 | preproc: '',
43 | },
44 | };
45 |
46 | private service!: Service;
47 |
48 | constructor(service: Service) {
49 | this.service = service;
50 | }
51 |
52 | /**
53 | *
54 | * @param issuerPukid
55 | * @param subjectPukid
56 | * @param serialNum
57 | * @param resultDek
58 | * @param operation
59 | * @param postProc
60 | * @param preProc
61 | * @param version
62 | * @param notBefore
63 | * @param notAfter
64 | */
65 | public async createBclBody(
66 | issuerPukid: string,
67 | subjectPukid: string,
68 | serialNum: string,
69 | version = 1,
70 | resultDek = 'SUBJECT',
71 | operation = ['*'],
72 | postProc = 'NULL',
73 | preProc = 'NULL',
74 | notBefore?: number,
75 | notAfter?: number,
76 | ): Promise {
77 | if (issuerPukid && subjectPukid) {
78 | await this.loadBclBodyByPukId(issuerPukid, subjectPukid);
79 | } else if (serialNum) {
80 | await this.loadBclBodyBySN(serialNum);
81 | }
82 |
83 | if (this.bclBody.serial_num) {
84 | return this;
85 | }
86 |
87 | if (!serialNum) {
88 | serialNum = uuidv4();
89 | }
90 | this.bclBody.version = version;
91 | this.bclBody['serial_num'] = serialNum;
92 | this.bclBody['issuer_pukid'] = issuerPukid;
93 | this.bclBody['subject_pukid'] = subjectPukid;
94 | const nowDate = dayjs();
95 | if (!notBefore) {
96 | notBefore = nowDate.valueOf();
97 | }
98 | if (!notAfter) {
99 | notAfter = nowDate.add(365, 'day').valueOf();
100 | }
101 |
102 | this.bclBody['validity']['not_after'] =
103 | dayjs(notAfter).format('YYYYMMDDHHmmssZZ');
104 | this.bclBody['validity']['not_before'] =
105 | dayjs(notBefore).format('YYYYMMDDHHmmssZZ');
106 | // @ts-ignore
107 | this.bclBody['policies'] = this._initBlankBclPolicies(
108 | resultDek,
109 | operation,
110 | postProc,
111 | preProc,
112 | );
113 | return this;
114 | }
115 |
116 | /**
117 | * get bcl body by serialNum
118 | * @param serialNum
119 | */
120 | public async loadBclBodyBySN(serialNum: string): Promise {
121 | const bclBodyJson = await this.service.execute({
122 | bizType: 116,
123 | serialNum,
124 | });
125 | if (bclBodyJson) {
126 | this.bclBody = JSON.parse(bclBodyJson);
127 | }
128 | return this;
129 | }
130 |
131 | public async loadBclBodyByPukId(issuePukId: string, subjectPukId: string) {
132 | const bclBodyJson = await this.service.execute({
133 | bizType: 118,
134 | issuePukId: issuePukId,
135 | subjectPukId: subjectPukId,
136 | });
137 | if (bclBodyJson) {
138 | this.bclBody = JSON.parse(bclBodyJson);
139 | }
140 | return this;
141 | }
142 |
143 | /**
144 | *
145 | * @param issuerDekGroup
146 | * @param subjectDekGroup
147 | */
148 | public addDekGroup(
149 | issuerDekGroup: IDekGroup,
150 | subjectDekGroup?: IDekGroup,
151 | ): Bcl {
152 | const preIssueDekGroup = this.bclBody['policies'][
153 | 'issuer_dek_group'
154 | ].filter((p) => p.groupid !== issuerDekGroup.groupid);
155 | preIssueDekGroup.push(issuerDekGroup);
156 | this.bclBody['policies']['issuer_dek_group'] = preIssueDekGroup;
157 | if (subjectDekGroup) {
158 | const preSubjectDekGroup = this.bclBody['policies'][
159 | 'subject_dek_group'
160 | ].filter((p) => p.groupid !== subjectDekGroup.groupid);
161 | preSubjectDekGroup.push(subjectDekGroup);
162 | this.bclBody['policies']['subject_dek_group'] = preSubjectDekGroup;
163 | }
164 | return this;
165 | }
166 |
167 | public removeDekGroup(
168 | issueGroupId: Array,
169 | subjectDekGroupId?: Array,
170 | ): Bcl {
171 | const newIssueDekGroup = this.bclBody['policies'][
172 | 'issuer_dek_group'
173 | ].filter((d) => !issueGroupId.includes(d.groupid));
174 | this.bclBody['policies']['issuer_dek_group'] = newIssueDekGroup;
175 | if (subjectDekGroupId && subjectDekGroupId.length) {
176 | const newSubjectDekGroup = this.bclBody['policies'][
177 | 'subject_dek_group'
178 | ].filter((d) => !subjectDekGroupId.includes(d.groupid));
179 | this.bclBody['policies']['subject_dek_group'] = newSubjectDekGroup;
180 | }
181 | return this;
182 | }
183 |
184 | public removeDekGroupAll(): Bcl {
185 | this.bclBody['policies']['issuer_dek_group'] = [];
186 | this.bclBody['policies']['subject_dek_group'] = [];
187 | return this;
188 | }
189 |
190 | /**
191 | *
192 | * @param privateKeyPem
193 | */
194 | private _signBclBody(privateKeyPem: string): Buffer {
195 | const toBeSignedBytes = Buffer.from(JSON.stringify(this.bclBody), 'utf8');
196 | const buffer = Buffer.concat([
197 | Buffer.from('01', 'hex'),
198 | CipherHelper.rsaSign(privateKeyPem, toBeSignedBytes),
199 | ]);
200 | return buffer;
201 | }
202 |
203 | /**
204 | *
205 | * @param privateKeyPem
206 | */
207 | public async issueBcl(
208 | privateKeyPem: string,
209 | bizType = 106,
210 | ): Promise {
211 | if (!this.bclBody.serial_num) {
212 | throw new Error('Bcl is not init');
213 | }
214 | const bclRequestJson = JSON.stringify(this.bclBody);
215 | const bclSignBuffer = this._signBclBody(privateKeyPem);
216 | return await this.service.execute({
217 | bizType,
218 | bclBody: bclRequestJson,
219 | privateSig: bclSignBuffer.toString('base64'),
220 | });
221 | }
222 |
223 | /**
224 | *
225 | * @param result_dek
226 | * @param operation
227 | * @param postproc
228 | * @param preproc
229 | * @private
230 | */
231 | private _initBlankBclPolicies(
232 | result_dek: string,
233 | operation: Array,
234 | postproc: string,
235 | preproc: string,
236 | ): IPolicies {
237 | const policies = {
238 | issuer_dek_group: [],
239 | subject_dek_group: [],
240 | result_dek,
241 | operation,
242 | post_proc: postproc,
243 | pre_proc: preproc,
244 | };
245 | return policies;
246 | }
247 | }
248 |
--------------------------------------------------------------------------------
/src/util/cipher.ts:
--------------------------------------------------------------------------------
1 | import * as CRYPTO from 'crypto';
2 | const Rsa = require('node-rsa');
3 | // @ts-ignore
4 | import forge from 'node-forge';
5 | import { Util } from './util';
6 | import Decimal from 'decimal.js';
7 | import { DataType } from '../types';
8 |
9 | /**
10 | * Helper related to encryption
11 | */
12 | export class CipherHelper {
13 | /**
14 | * Obtain bytes of random length
15 | * @param length
16 | * @return Buffer
17 | */
18 | static randomBytes(length = 16): Buffer {
19 | return CRYPTO.randomBytes(length);
20 | }
21 |
22 | /**
23 | * get hash
24 | * @param data
25 | */
26 | static sha256Hash(data: string): string {
27 | return CRYPTO.createHash('sha256').update(data).digest('base64');
28 | }
29 |
30 | /**
31 | * rsa sign
32 | * @param privateKeyPem
33 | * @param data
34 | */
35 | static rsaSign(privateKeyPem: string, data: string | Buffer): Buffer {
36 | const privateKey = forge.pki.privateKeyFromPem(privateKeyPem);
37 | const md = forge.md.sha256.create();
38 | if(typeof data === 'string') {
39 | md.update(data, 'utf8');
40 | }else {
41 | const str = data.toString('latin1');
42 | md.update(str, 'latin1');
43 | }
44 | const pss = forge.pss.create({
45 | md: forge.md.sha256.create(),
46 | mgf: forge.mgf.mgf1.create(forge.md.sha256.create()),
47 | saltLength: 32
48 | });
49 | const signature = privateKey.sign(md, pss);
50 | return Buffer.from(signature, "latin1");
51 | }
52 |
53 | static rsaEncrypt(pubKey: string, data: Buffer): Buffer {
54 | const forgePublicKey = forge.pki.publicKeyFromPem(pubKey);
55 | const byteString = Util.ab2str(data);
56 | const encrypted = forgePublicKey.encrypt(byteString, 'RSA-OAEP', {
57 | md: forge.md.sha256.create(),
58 | mgf1: {
59 | md: forge.md.sha256.create(),
60 | },
61 | });
62 | return Buffer.from(encrypted, 'latin1');
63 | }
64 |
65 | /**
66 | * get hmac hash
67 | * @param key
68 | * @param data
69 | */
70 | static hmacHash(key: Buffer, data: string | Uint8Array): Buffer {
71 | const h = CRYPTO.createHmac('sha256', key);
72 | h.update(data);
73 | return h.digest();
74 | }
75 |
76 | /**
77 | * create RSA keys
78 | * @param b
79 | */
80 | static createKeyPemString(b = 2048): {
81 | publicKeyPem: string;
82 | privateKeyPem: string;
83 | } {
84 | const key = new Rsa({ b: 2048 });
85 | const publicKeyPem = key.exportKey('pkcs8-public-der');
86 | const privateKeyPem = key.exportKey('pkcs8-private-der');
87 | return { publicKeyPem, privateKeyPem };
88 | }
89 |
90 | static getPublicKeyPemFromPrivate(privateKeyDer: Buffer) {
91 | const key = new Rsa(privateKeyDer, 'pkcs8-der');
92 | const publicKeyPem = key.exportKey('pkcs8-public-pem');
93 | const privateKeyPem = key.exportKey("pkcs8-private-pem");
94 | return {publicKeyPem, privateKeyPem};
95 | }
96 |
97 | /**
98 | * aes encrypt
99 | * @param key
100 | * @param iv
101 | * @param data
102 | */
103 | static aesEncrypt(
104 | key: Buffer,
105 | iv: Uint8Array,
106 | data: string | Buffer,
107 | ): Buffer {
108 | const cipher = CRYPTO.createCipheriv('aes-128-cbc', key, iv);
109 | let encrypted_data;
110 | cipher.setAutoPadding(true);
111 | encrypted_data = cipher.update(data);
112 | encrypted_data = Buffer.concat([encrypted_data, cipher.final()]);
113 | return encrypted_data as Buffer;
114 | }
115 |
116 | /**
117 | * aes decrypt
118 | * @param key
119 | * @param iv
120 | * @param data
121 | */
122 | static aesDecrypt(key: Buffer, iv: Buffer, data: Buffer): Buffer {
123 | const cipher = CRYPTO.createDecipheriv('aes-128-cbc', key, iv);
124 | const encrypted_data_b64 = Buffer.from(data).toString('base64');
125 | let decrypted_data = cipher.update(encrypted_data_b64, 'base64');
126 | decrypted_data = Buffer.concat([decrypted_data, cipher.final()]);
127 | return decrypted_data;
128 | }
129 |
130 | /**
131 | * Generate hexadecimal mk
132 | */
133 | static generateMk(): string {
134 | const mk = CipherHelper.randomBytes();
135 | return mk.toString('hex');
136 | }
137 |
138 | /**
139 | *
140 | * @param mk
141 | * @param dekId
142 | * @param dek
143 | */
144 | static encryptDekToBase64(mk: Buffer, dekId: number, dek: Buffer) {
145 | const buffer = new ArrayBuffer(8);
146 | const view = new DataView(buffer);
147 | view.setUint32(0, dekId & 0xffffffff, true);
148 | view.setUint32(4, Math.floor(dekId / 0x100000000), true);
149 | const dekid_dek = Buffer.concat([Buffer.from(buffer), dek], 24);
150 | const iv = CipherHelper.randomBytes();
151 | const encrypted_data = CipherHelper.aesEncrypt(mk, iv, dekid_dek);
152 | const dekCipher = Buffer.concat([Buffer.from([3]), iv, encrypted_data]);
153 | return dekCipher.toString('base64');
154 | }
155 |
156 | static decryptDekToBase64(mek: Buffer, dekCipherStr: string): [number, Buffer] {
157 | const dekCipher = Buffer.from(dekCipherStr, 'base64');
158 | const dekid_dek = CipherHelper.aesDecrypt(
159 | mek,
160 | dekCipher.slice(1, 17),
161 | dekCipher.slice(17),
162 | );
163 | const dekid = new DataView(dekid_dek.slice(0, 8).buffer).getUint16(0, true);
164 | const dek = dekid_dek.slice(8);
165 | return [dekid, dek];
166 | }
167 |
168 | static digest_gAuth(mek: Buffer, grp_id: Buffer, dek_id: number): string {
169 | const buffer = new ArrayBuffer(8);
170 | const view = new DataView(buffer);
171 | view.setBigInt64(0, BigInt(dek_id), true);
172 | const dek_id_array = Buffer.from(buffer);
173 | const buf = Buffer.concat([grp_id, dek_id_array]);
174 | const gAuth = CipherHelper.hmacHash(mek, buf);
175 | return gAuth.toString('base64');
176 | }
177 |
178 | public static encodeDataByType(data: any, encType: number): Buffer {
179 | let result!: Uint8Array;
180 | const buffer = new ArrayBuffer(8);
181 | const view = new DataView(buffer);
182 | switch (encType) {
183 | case DataType.int4:
184 | view.setInt32(0, data, true);
185 | result = new Uint8Array(buffer, 0, 4);
186 | break;
187 | case DataType.int8:
188 | view.setBigInt64(0, BigInt(data), true);
189 | result = new Uint8Array(buffer);
190 | break;
191 | case DataType.float4:
192 | view.setFloat32(0, data, true);
193 | result = new Uint8Array(buffer, 0, 4);
194 | break;
195 | case DataType.float8:
196 | view.setFloat64(0, data, true);
197 | result = new Uint8Array(buffer);
198 | break;
199 | case 6:
200 | const val = new Decimal(data);
201 | result = new TextEncoder().encode(val.toString());
202 | break;
203 | case 7:
204 | result = new TextEncoder().encode(data);
205 | break;
206 | case 8:
207 | const uSec = BigInt(Math.floor(data * 1000));
208 | const offset = BigInt(
209 | Math.floor(new Date().getTimezoneOffset() * 60 * 1000000),
210 | );
211 | const adjustedUSec = uSec - BigInt(946684800000000) + offset;
212 | view.setBigInt64(0, adjustedUSec, true);
213 | result = new Uint8Array(buffer);
214 | break;
215 | default:
216 | throw new Error('Unsupported encryption type');
217 | }
218 | return Buffer.from(result);
219 | }
220 |
221 | static decodeDataByType(data: any, encType: number): any {
222 | let result: any;
223 | if (encType === DataType.int4) {
224 | // enc_int4
225 | const size = 4;
226 | const buf = data.slice(0, size);
227 | result = new Int32Array(buf.buffer)[0];
228 | } else if (encType === DataType.int8) {
229 | // enc_int8
230 | const size = 8;
231 | const buf = data.slice(0, size);
232 | result = new BigInt64Array(buf.buffer)[0];
233 | result = result.toString();//end n ?
234 | } else if (encType === DataType.float4) {
235 | // enc_float4
236 | const size = 4;
237 | const buf = data.slice(0, size);
238 | result = new Float32Array(buf.buffer)[0];
239 | } else if (encType === DataType.float8) {
240 | // enc_float8
241 | const size = 8;
242 | const buf = data.slice(0, size);
243 | result = new Float64Array(buf.buffer)[0];
244 | } else if (encType === 6) {
245 | // enc_decimal
246 | result = new Decimal(Buffer.from(data).toString());
247 | result = result.toString();
248 | } else if (encType === 7) {
249 | // enc_text
250 | result = new TextDecoder().decode(data);
251 | } else if (encType === 8) {
252 | // enc_timestamp
253 | const size = 8;
254 | const buf = data.slice(0, size);
255 | let u_sec = new BigInt64Array(buf.buffer)[0];
256 | u_sec += BigInt(946684800000000);
257 | u_sec -= BigInt(new Date().getTimezoneOffset() * 60 * 1000000);
258 | const time_stamp = Number(u_sec) / 1000000.0;
259 | result = time_stamp * 1000;
260 | } else {
261 | throw new Error('Unsupported encryption type');
262 | }
263 | return result;
264 | }
265 | }
266 |
--------------------------------------------------------------------------------
/src/util/clerk.ts:
--------------------------------------------------------------------------------
1 | import ClerkJS from '@clerk/clerk-js';
2 | import { SignInProps, UserButtonProps } from '@clerk/types';
3 | import { ClerkConfig, MkManager } from 'src/types';
4 | import { CipherHelper } from './cipher';
5 | import { CLERK_PUBLISHABLE_KEY, TOKEN_KEY, WALLET_key } from './constant';
6 | import Web3 from 'web3';
7 | import {
8 | getEncryptionPublicKey,
9 | encrypt as metaMaskEncrypt,
10 | decrypt as metaMaskDecrypt,
11 | } from '@metamask/eth-sig-util';
12 |
13 | export default class Clerk implements MkManager {
14 | private mkBuffer: Buffer | undefined;
15 | private privateKeyCipherBuffer: Buffer | undefined;
16 | private waPk: string | undefined;
17 |
18 | private web3: Web3;
19 | public config: ClerkConfig | undefined;
20 | public clerkJs: ClerkJS;
21 |
22 | constructor(config?: ClerkConfig) {
23 | this.config = config;
24 | this.clerkJs = new ClerkJS(CLERK_PUBLISHABLE_KEY);
25 | this.web3 = new Web3();
26 | }
27 |
28 | async encrypt(str: string): Promise {
29 | if (!this.waPk) {
30 | throw new Error('Please connect first.');
31 | }
32 | const pubKey = getEncryptionPublicKey(this.waPk.substring(2));
33 | if (pubKey) {
34 | const enc = metaMaskEncrypt({
35 | publicKey: pubKey,
36 | data: Buffer.from(str, 'hex').toString('base64'),
37 | version: 'x25519-xsalsa20-poly1305',
38 | });
39 | const buf = Buffer.concat([
40 | Buffer.from(enc.ephemPublicKey, 'base64'),
41 | Buffer.from(enc.nonce, 'base64'),
42 | Buffer.from(enc.ciphertext, 'base64'),
43 | ]);
44 | return buf;
45 | }
46 | }
47 | async decrypt(cipher: Buffer): Promise {
48 | if (!this.waPk) {
49 | throw new Error('Please connect first.');
50 | }
51 | const encryptedData = {
52 | version: 'x25519-xsalsa20-poly1305',
53 | ephemPublicKey: cipher.slice(0, 32).toString('base64'),
54 | nonce: cipher.slice(32, 56).toString('base64'),
55 | ciphertext: cipher.slice(56).toString('base64'),
56 | };
57 | // Convert data to hex string required by MetaMask
58 | const decryptData = metaMaskDecrypt({
59 | encryptedData,
60 | privateKey: this.waPk.substring(2),
61 | });
62 | return Buffer.from(decryptData, 'base64');
63 | }
64 | async getWalletAccount(): Promise {
65 | if (!this.waPk) {
66 | throw new Error('Please connect first.');
67 | }
68 | const account = await this.web3.eth.accounts.privateKeyToAccount(this.waPk);
69 | return account.address;
70 | }
71 | async checkConnection(): Promise {}
72 | async getMekBytes(): Promise {
73 | const user = this.clerkJs.user;
74 | if (!user) {
75 | throw new Error('Please sign in first.');
76 | }
77 | if (this.mkBuffer) {
78 | return this.mkBuffer;
79 | }
80 | const mk = user.unsafeMetadata.mk as string;
81 | if (mk) {
82 | this.mkBuffer = Buffer.from(mk, 'hex');
83 | } else {
84 | this.mkBuffer = await this._setMk();
85 | }
86 | return this.mkBuffer;
87 | }
88 | async getPkPem(): Promise<{ privateKeyPem: string; publicKeyPem: string }> {
89 | const mkBuffer = await this.getMekBytes();
90 | const user = this.clerkJs.user;
91 | if (!this.privateKeyCipherBuffer) {
92 | const pk = user?.unsafeMetadata.pk as string;
93 | if (pk) {
94 | this.privateKeyCipherBuffer = Buffer.from(pk, 'hex');
95 | } else {
96 | this.privateKeyCipherBuffer = await this._setPk(mkBuffer);
97 | }
98 | }
99 | const iv = this.privateKeyCipherBuffer.slice(0, 16);
100 | const cipher = this.privateKeyCipherBuffer.slice(16);
101 | const decrypt = CipherHelper.aesDecrypt(mkBuffer, iv, cipher);
102 | const { publicKeyPem, privateKeyPem } =
103 | CipherHelper.getPublicKeyPemFromPrivate(decrypt);
104 | return { privateKeyPem, publicKeyPem };
105 | }
106 | async personalSignature(signData: string): Promise {
107 | if (!this.waPk) {
108 | throw new Error('Please connect first.');
109 | }
110 | const account = await this.web3.eth.accounts.privateKeyToAccount(this.waPk);
111 | const sign = await account.sign(signData);
112 | return sign.signature;
113 | }
114 |
115 | public async init(): Promise {
116 | await this.clerkJs.load(this.config?.options);
117 | if (this.clerkJs.user) {
118 | const email = this.clerkJs.user.primaryEmailAddress
119 | ?.emailAddress as string;
120 | localStorage.setItem(WALLET_key, email);
121 | const sesstion_token = (await this.clerkJs.session?.getToken({
122 | template: 'mindnetwork',
123 | })) as string;
124 | await this._initWaPk();
125 | return sesstion_token;
126 | } else {
127 | localStorage.removeItem(TOKEN_KEY);
128 | }
129 | }
130 |
131 | public renderUserButton(
132 | dom?: HTMLDivElement,
133 | userButtonProps?: UserButtonProps,
134 | ): void {
135 | const target = dom || this.config?.target;
136 | let compontens = null;
137 | if (typeof target === 'string') {
138 | compontens = document.querySelector(target) as HTMLDivElement;
139 | }
140 | if (target instanceof HTMLDivElement) {
141 | compontens = target;
142 | }
143 | if (!compontens) {
144 | throw new Error('clerk target dom node is not a HTMLDivElement');
145 | }
146 | this.clerkJs.mountUserButton(compontens, {
147 | ...this.config?.userButtonProps,
148 | ...userButtonProps,
149 | });
150 | }
151 |
152 | public async signIn(props?: SignInProps): Promise {
153 | await this.clerkJs.openSignIn(props);
154 | }
155 |
156 | private async _setMk(): Promise {
157 | const mk: string = CipherHelper.generateMk();
158 | const user = this.clerkJs.user;
159 | await user?.update({
160 | unsafeMetadata: {
161 | ...user?.unsafeMetadata,
162 | mk,
163 | },
164 | });
165 | return Buffer.from(mk, 'hex');
166 | }
167 |
168 | private async _setPk(mekBuffer: Buffer): Promise {
169 | const keys = CipherHelper.createKeyPemString();
170 | const iv = CipherHelper.randomBytes();
171 | const privateKeyCipherBuffer = Buffer.concat([
172 | iv,
173 | CipherHelper.aesEncrypt(mekBuffer, iv, keys.privateKeyPem),
174 | ]);
175 | const user = this.clerkJs.user;
176 | await user?.update({
177 | unsafeMetadata: {
178 | ...user?.unsafeMetadata,
179 | pk: privateKeyCipherBuffer.toString('hex'),
180 | },
181 | });
182 | return privateKeyCipherBuffer;
183 | }
184 |
185 | private async _initWaPk(): Promise {
186 | if (this.waPk) {
187 | return;
188 | }
189 | const user = this.clerkJs.user;
190 | if (!user) {
191 | return;
192 | }
193 | const waPk = user.unsafeMetadata.waPk as string;
194 | if (waPk) {
195 | this.waPk = waPk;
196 | return;
197 | }
198 | const privateKey = this.web3.eth.accounts.create().privateKey;
199 | await user?.update({
200 | unsafeMetadata: {
201 | ...user?.unsafeMetadata,
202 | waPk: privateKey,
203 | },
204 | });
205 | this.waPk = privateKey;
206 | }
207 | }
208 |
--------------------------------------------------------------------------------
/src/util/constant.ts:
--------------------------------------------------------------------------------
1 | export const TOKEN_KEY = 'JEnG1cYqXS3D3nPnx2KsHw==';
2 | export const WALLET_key = 'wa';
3 | export const APP_KEY = 'app';
4 | export const CLERK_PUBLISHABLE_KEY =
5 | 'pk_test_Zmx5aW5nLWJ1enphcmQtNTIuY2xlcmsuYWNjb3VudHMuZGV2JA';
6 | export const CHAIN_KEY = 'chain';
7 | export const LOGIN_METHOD_KEY = 'connect';
8 |
--------------------------------------------------------------------------------
/src/util/result.ts:
--------------------------------------------------------------------------------
1 | import { ResultType } from '../types';
2 |
3 | export default class Result {
4 | static success(data: any): ResultType {
5 | return { code: 0, result: data };
6 | }
7 |
8 | static fail(error: any): ResultType {
9 | if (error.code) {
10 | return { code: error.code, message: error.message };
11 | }
12 | return { code: 50000, message: error.toString() };
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/src/util/util.ts:
--------------------------------------------------------------------------------
1 | export class Util {
2 | static ab2str(buf: Buffer) {
3 | // @ts-ignore
4 | return String.fromCharCode.apply(null, buf);
5 | }
6 |
7 | static structPackq(data: string) {
8 | let result!: Uint8Array;
9 | const buffer = new ArrayBuffer(8);
10 | const view = new DataView(buffer);
11 | view.setBigInt64(0, BigInt(data), true);
12 | result = new Uint8Array(buffer);
13 | return result;
14 | }
15 |
16 | static stringToByte(str: string) {
17 | const bytes = new Array();
18 | let len, c;
19 | len = str.length;
20 | for (let i = 0; i < len; i++) {
21 | c = str.charCodeAt(i);
22 | if (c >= 0x010000 && c <= 0x10ffff) {
23 | bytes.push(((c >> 18) & 0x07) | 0xf0);
24 | bytes.push(((c >> 12) & 0x3f) | 0x80);
25 | bytes.push(((c >> 6) & 0x3f) | 0x80);
26 | bytes.push((c & 0x3f) | 0x80);
27 | } else if (c >= 0x000800 && c <= 0x00fff) {
28 | bytes.push(((c >> 12) & 0x07) | 0xf0);
29 | bytes.push(((c >> 6) & 0x3f) | 0x80);
30 | bytes.push((c & 0x3f) | 0x80);
31 | } else if (c >= 0x000800 && c <= 0x0007ff) {
32 | bytes.push(((c >> 6) & 0x3f) | 0x80);
33 | bytes.push((c & 0x3f) | 0x80);
34 | } else {
35 | bytes.push(c & 0xff);
36 | }
37 | }
38 | return bytes;
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/util/web3.ts:
--------------------------------------------------------------------------------
1 | import { Web3Provider } from '@ethersproject/providers';
2 | import web3 from 'web3';
3 | import { Contract } from 'web3-eth-contract';
4 | import { CipherHelper } from './cipher';
5 | import { encrypt } from '@metamask/eth-sig-util';
6 | import { CHAIN_KEY, WALLET_key } from './constant';
7 | import { MindLake } from '../MindLake';
8 | import { ChainInfo, MkManager } from '../types';
9 | // const encrypt = {};
10 |
11 | export interface RequestArguments {
12 | readonly method: string;
13 | readonly params?: readonly unknown[] | object;
14 | }
15 |
16 | export interface Web3WithWalletProvider extends Web3Provider {
17 | request(args: RequestArguments): Promise;
18 | selectedAddress: string;
19 | }
20 |
21 | /**
22 | * web3 helper
23 | */
24 | export class Web3Interact implements MkManager {
25 | private readonly provider: Web3WithWalletProvider;
26 |
27 | /**
28 | * wallet address
29 | */
30 | private account!: string;
31 |
32 | /**
33 | * web3 instance
34 | */
35 | private web3!: web3;
36 |
37 | /**
38 | * contract instance
39 | */
40 | private contract!: Contract;
41 |
42 | /**
43 | * contract address
44 | */
45 | public static CONTRACT_ADDRESS = '0xF5932e67e84F08965DC6D62C2B67f47a6826E5a7';
46 |
47 | public static SUPPORT_CHAIN = 5;
48 |
49 | /**
50 | * currnt chaind
51 | */
52 | public chain: ChainInfo | undefined;
53 |
54 | /**
55 | * wallet publicKe
56 | */
57 | private publicKey!: string;
58 |
59 | /**
60 | * encrypt mk buffer
61 | */
62 | private mkCipherBuffer!: Buffer;
63 |
64 | /**
65 | * decrypt mk buffer
66 | */
67 | private mkBuffer!: Buffer;
68 |
69 | /**
70 | * encrypt privateKey buffer
71 | */
72 | private privateKeyCipherBuffer!: Buffer;
73 |
74 | constructor() {
75 | if (!window.ethereum) {
76 | throw new Error('Please install a wallet');
77 | }
78 | let currentProvider;
79 | // @ts-ignore
80 | if (window.ethereum.providers) {
81 | // @ts-ignore
82 | currentProvider = window.ethereum.providers.find(
83 | (p: any) => p.isMetaMask,
84 | );
85 | } else {
86 | currentProvider = window.ethereum;
87 | }
88 | if (!currentProvider.isMetaMask) {
89 | throw new Error(
90 | 'Only MetamMask wallet is supported currently. Please install or replace',
91 | );
92 | }
93 | this.provider = currentProvider;
94 | this.web3 = new web3(currentProvider);
95 | this._onListen();
96 | }
97 | async encrypt(str: string): Promise {
98 | const pubKey = await this._getEncryptionPublicKey();
99 | if (pubKey) {
100 | const enc = encrypt({
101 | publicKey: pubKey,
102 | data: Buffer.from(str, 'hex').toString('base64'),
103 | version: 'x25519-xsalsa20-poly1305',
104 | });
105 | const buf = Buffer.concat([
106 | Buffer.from(enc.ephemPublicKey, 'base64'),
107 | Buffer.from(enc.nonce, 'base64'),
108 | Buffer.from(enc.ciphertext, 'base64'),
109 | ]);
110 | return buf;
111 | }
112 | }
113 |
114 | async decrypt(cipher: Buffer): Promise {
115 | const structuredData = {
116 | version: 'x25519-xsalsa20-poly1305',
117 | ephemPublicKey: cipher.slice(0, 32).toString('base64'),
118 | nonce: cipher.slice(32, 56).toString('base64'),
119 | ciphertext: cipher.slice(56).toString('base64'),
120 | };
121 | // Convert data to hex string required by MetaMask
122 | const ct = `0x${Buffer.from(
123 | JSON.stringify(structuredData),
124 | 'utf8',
125 | ).toString('hex')}`;
126 | const decrypt = (await this.provider.request({
127 | method: 'eth_decrypt',
128 | params: [ct, this.account],
129 | })) as string;
130 | return Buffer.from(decrypt, 'base64');
131 | }
132 |
133 | public setChain(chain: ChainInfo) {
134 | this.chain = chain;
135 | }
136 |
137 | private _onListen() {
138 | this.provider.on('accountsChanged', this._onAccountsChanged.bind(this));
139 | this.provider.on('chainChanged', this._onChainChanged.bind(this));
140 | this.provider.on('disconnect', this._onDisconnect.bind(this));
141 | }
142 |
143 | /**
144 | * connected wallet account change event
145 | * @param accounts
146 | * @private
147 | */
148 | private _onAccountsChanged(accounts: string[]) {
149 | console.log('Wallet address changed:', accounts && accounts[0]);
150 | localStorage.setItem(WALLET_key, accounts && accounts[0]);
151 | this.account = accounts && accounts[0];
152 | this._walletChange();
153 | }
154 |
155 | /**
156 | * chain change event
157 | * @param chainId
158 | * @private
159 | */
160 | private _onChainChanged(chainId: number) {
161 | console.log(
162 | 'Chain changed: ',
163 | web3.utils.hexToNumber(chainId),
164 | this.account,
165 | );
166 | localStorage.setItem(
167 | CHAIN_KEY,
168 | JSON.stringify({ chainId: web3.utils.hexToNumber(chainId) as string }),
169 | );
170 | this._walletChange();
171 | }
172 |
173 | private _walletChange() {
174 | if (this.account) {
175 | this.mkCipherBuffer = undefined!;
176 | this.mkBuffer = undefined!;
177 | this.privateKeyCipherBuffer = undefined!;
178 | this.publicKey = undefined!;
179 | }
180 | }
181 |
182 | /**
183 | * wallet disconnect
184 | * @private
185 | */
186 | private _onDisconnect() {
187 | console.log('Wallet disconnect: ');
188 | }
189 |
190 | /**
191 | * get walletAddress
192 | */
193 | public async getWalletAccount(): Promise {
194 | if (!this.account || !MindLake.isConnected) {
195 | const accounts = (await this.provider.request({
196 | method: 'eth_requestAccounts',
197 | })) as string[];
198 | if (!accounts.length) {
199 | throw new Error('No accounts returned');
200 | }
201 | this.account = accounts[0];
202 | }
203 | localStorage.setItem(WALLET_key, this.account);
204 | return this.account;
205 | }
206 |
207 | public async checkConnection() {
208 | try {
209 | const accounts = (await this.provider.request({
210 | method: 'eth_accounts',
211 | })) as Array;
212 | if (accounts.length) {
213 | this.account = accounts[0];
214 | localStorage.setItem(WALLET_key, accounts[0]);
215 | }
216 | } catch (error) {
217 | console.log(error);
218 | }
219 | }
220 |
221 | /**
222 | * wallet signature
223 | * @param signData
224 | */
225 | public async personalSignature(signData: string): Promise {
226 | const signature = await this.provider.request({
227 | method: 'personal_sign',
228 | params: [web3.utils.fromUtf8(signData), this.account],
229 | });
230 | return signature as string;
231 | }
232 |
233 | /**
234 | * get wallet publicKey
235 | * @private
236 | */
237 | public async _getEncryptionPublicKey(): Promise {
238 | if (this.provider) {
239 | if (this.publicKey) {
240 | return this.publicKey;
241 | }
242 | const keyB64: string = (await this.provider.request({
243 | method: 'eth_getEncryptionPublicKey',
244 | params: [this.account],
245 | })) as string;
246 | this.publicKey = keyB64;
247 | return this.publicKey;
248 | }
249 | }
250 |
251 | /**
252 | * get mk
253 | */
254 | public async getMekBytes(): Promise {
255 | await this._changeChainToSupportChain();
256 | await this._loadKeysCipherFromChain();
257 | if (!this.mkCipherBuffer) {
258 | await this._generateKeysCipherToChain();
259 | }
260 | if (!this.mkBuffer) {
261 | this.mkBuffer = await this._decryptMk(this.mkCipherBuffer);
262 | }
263 | // console.log("mk", this.mkBuffer.toString("hex"))
264 | return this.mkBuffer;
265 | }
266 |
267 | /**
268 | * get pk
269 | */
270 | public async getPkPem(): Promise<{
271 | privateKeyPem: string;
272 | publicKeyPem: string;
273 | }> {
274 | const mk = await this.getMekBytes();
275 | const iv = this.privateKeyCipherBuffer.slice(0, 16);
276 | const cipher = this.privateKeyCipherBuffer.slice(16);
277 | const decrypt = CipherHelper.aesDecrypt(mk, iv, cipher);
278 | const { publicKeyPem, privateKeyPem } =
279 | CipherHelper.getPublicKeyPemFromPrivate(decrypt);
280 | return { privateKeyPem, publicKeyPem };
281 | }
282 |
283 | /**
284 | *load keys form chain
285 | */
286 | private async _loadKeysCipherFromChain() {
287 | if (this.mkCipherBuffer && this.privateKeyCipherBuffer) {
288 | return;
289 | }
290 | const account = await this.getWalletAccount();
291 | const keys = await this.contract.methods.getKeys(account).call();
292 | if (keys && keys.MK && keys.SK) {
293 | this.mkCipherBuffer = Buffer.from(keys.MK.slice(2), 'hex');
294 | this.privateKeyCipherBuffer = Buffer.from(keys.SK.slice(2), 'hex');
295 | }
296 | }
297 |
298 | /**
299 | * generateKeys form local to chain
300 | * @private
301 | */
302 | private async _generateKeysCipherToChain() {
303 | if (this.mkCipherBuffer && this.privateKeyCipherBuffer) {
304 | return;
305 | }
306 | const account = await this.getWalletAccount();
307 | const checkAccount = await this.web3.utils.toChecksumAddress(account);
308 | const mk = CipherHelper.generateMk();
309 | const mekBuffer = Buffer.from(mk, 'hex');
310 | const keys = CipherHelper.createKeyPemString();
311 | const iv = CipherHelper.randomBytes();
312 | const privateKeyCipherBuffer = Buffer.concat([
313 | iv,
314 | CipherHelper.aesEncrypt(mekBuffer, iv, keys.privateKeyPem),
315 | ]);
316 | const mkCipherBuffer = await this._encryptMk(mk);
317 | if (!mkCipherBuffer) {
318 | throw new Error('mk encrypt error');
319 | }
320 | const txHash = await this.contract.methods
321 | .setKeys(mkCipherBuffer, privateKeyCipherBuffer)
322 | .send({ from: checkAccount });
323 | if (txHash && txHash.blockHash) {
324 | this.mkCipherBuffer = mkCipherBuffer;
325 | this.privateKeyCipherBuffer = privateKeyCipherBuffer;
326 | }
327 | }
328 |
329 | /**
330 | * encrypt mk
331 | * @param mk
332 | * @private
333 | */
334 | private async _encryptMk(mk: string): Promise {
335 | return this.encrypt(mk);
336 | }
337 |
338 | /**
339 | * decrypt mk
340 | * @param data
341 | * @private
342 | */
343 | private async _decryptMk(data: Buffer): Promise {
344 | return this.decrypt(data);
345 | }
346 |
347 | /**
348 | *
349 | * @param chainId support chainId
350 | * @private
351 | */
352 | public async _changeChainToSupportChain() {
353 | if (!this.chain) {
354 | throw new Error('chain not found');
355 | }
356 | const abi = JSON.parse(this.chain.abi);
357 | this.contract = new this.web3.eth.Contract(
358 | // @ts-ignore
359 | abi,
360 | this.chain.smartAddress,
361 | );
362 | const currentNetworkId = await this.web3.eth.net.getId();
363 | console.log(
364 | 'currentNetworkId',
365 | currentNetworkId,
366 | 'target chainId ',
367 | this.chain.chainId,
368 | );
369 | if (currentNetworkId != Number(this.chain.chainId)) {
370 | try {
371 | await this.provider.request({
372 | method: 'wallet_switchEthereumChain',
373 | params: [{ chainId: this.web3.utils.toHex(this.chain.chainId) }],
374 | });
375 | } catch (error: any) {
376 | if (error?.code === 4902) {
377 | // Add chain to MetaMask
378 | await this.provider.request({
379 | method: 'wallet_addEthereumChain',
380 | params: [
381 | {
382 | chainName: this.chain.chainName,
383 | chainId: web3.utils.toHex(this.chain.chainId),
384 | nativeCurrency: {
385 | name: this.chain.currency,
386 | decimals: 18,
387 | symbol: this.chain.currency,
388 | },
389 | rpcUrls: [this.chain.rpcNodeUrl],
390 | },
391 | ],
392 | });
393 | } else {
394 | throw new Error(error);
395 | }
396 | }
397 | }
398 | }
399 | }
400 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "declaration": true,
5 | "skipLibCheck": true,
6 | "baseUrl": "./",
7 | "esModuleInterop": true
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/tutorial/Configure_AppKey.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
MindLake Tutorial: Mind appKey
5 |
6 |
7 | A step-by-step cookbook for beginner to create Mind appKey !
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | You need to register an appKey in order to access Mind Lake during testing period.
16 | Here is the steps and screenshort.
17 |
18 | 1. Visit https://scan.mindnetwork.xyz in your browser
19 |
20 | 2. Login with your MetaMask Wallet
21 |
22 | 3. Click `myDapp` in left side manu
23 |
24 | 
25 |
26 | 3. Click "Create Dapp"
27 |
28 | 
29 |
30 | 4. Input your Dapp name and then click "Create"
31 |
32 | 
33 |
34 | 5. copy appKey value into myconfig.ts to update "appKey"
35 |
36 | 
37 |
--------------------------------------------------------------------------------
/tutorial/Configure_Docker_Case.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
MindLake Tutorial: Configure Docker Case
5 |
6 |
7 | A step-by-step cookbook for Docker Case Configuration !
8 |
9 |
10 |
11 | - [:star2: 0. Step by step tutorial](#star2-0-step-by-step-tutorial)
12 | - [:star2: 1. Install docker](#star2-1-install-docker)
13 | - [:art: 1.1 For Mac OS](#art-11-for-mac-os)
14 | - [:dart: 1.1.2 Install docker with HomeBrew](#dart-112-install-docker-with-homebrew)
15 | - [:gear: 1.1.2.1 Step1: Install HomeBrew (If you don't have Homebrew installed)](#gear-1121-step1-install-homebrew-if-you-dont-have-homebrew-installed)
16 | - [:gear: 1.1.2.2 Step2: Install docker](#gear-1122-step2-install-docker)
17 | - [:art: 1.2 For Windows](#art-12-for-windows)
18 | - [:dart: 1.2.1 Download docker](#dart-121-download-docker)
19 | - [:dart: 1.2.2 Install docker](#dart-122-install-docker)
20 | - [:art: 2. Run docker case](#art-2-run-docker-case)
21 | - [:star2: 3. Explore the use cases](#star2-3-explore-the-use-cases)
22 |
23 | ## :star2: 0. Step by step tutorial
24 | This is part of support chapter for MindLake step-by-step tutorial for [Typescript](README.md)
25 |
26 | ## :star2: 1. Install docker
27 |
28 | ### :art: 1.1 For Mac OS
29 |
30 | #### :dart: 1.1.2 Install docker with HomeBrew
31 | If you need to install Nvm from the command line on macOS, the Homebrew package manager is a reliable option. Follow the steps below to install Nvm via Homebrew:
32 | ##### :gear: 1.1.2.1 Step1: Install HomeBrew (If you don't have Homebrew installed)
33 | 1. Open a browser and go to https://brew.sh.
34 |
35 | 
36 |
37 | 2. Under the "Install Homebrew" title, copy the command
38 | ```shell
39 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
40 | ```
41 |
42 | 
43 |
44 | 3. Then open a terminal window, paste the copied command, and press the 'Enter' or 'Return' button.
45 |
46 | 
47 |
48 | 4. Enter your macOS credentials if and when asked.
49 |
50 | 5. If prompted, install Apple's command line developer tools.
51 |
52 | ##### :gear: 1.1.2.2 Step2: Install docker
53 | 1. Enter the following command in terminal to upgrade Homebrew:
54 | ```shell
55 | brew install --cask docker
56 | ```
57 | 2. run docker app
58 | 
59 | Next, open docker app
60 | 
61 |
62 | 3. check docker install success
63 | ```shell
64 | docker info
65 | ```
66 | An example of the output is:
67 | ```shell
68 | Client:
69 | Version: 24.0.2
70 | Context: desktop-linux
71 | Debug Mode: false
72 |
73 | Server:
74 | Containers: 7
75 | Running: 5
76 | Paused: 0
77 | Stopped: 2
78 | Images: 197
79 | Server Version: 24.0.2
80 | Storage Driver: overlay2
81 | Backing Filesystem: extfs
82 | Supports d_type: true
83 | Using metacopy: false
84 | Native Overlay Diff: true
85 | userxattr: false
86 | Logging Driver: json-file
87 | Cgroup Driver: cgroupfs
88 | Cgroup Version: 2
89 | ```
90 |
91 | ### :art: 1.2 For Windows
92 |
93 | #### :dart: 1.2.1 Download docker
94 | 1. download Docker Desktop on Windows:
95 |
96 | https://docs.docker.com/desktop/install/windows-install/
97 |
98 | 
99 |
100 | 2. Double-click Docker Desktop `Installer.exe` to run the installer.
101 |
102 | 3. When prompted, ensure the `Use WSL 2 instead of Hyper-V` option on the Configuration page is selected or not depending on your choice of backend.
103 |
104 | - If your system only supports one of the two options, you will not be able to select which backend to use.
105 |
106 | 4. Follow the instructions on the installation wizard to authorize the installer and proceed with the install.
107 |
108 | 5. When the installation is successful, click `Close` to complete the installation process.
109 |
110 | 6. If your admin account is different to your user account, you must add the user to the `docker-users` group. Run `Computer Management` as an `administrator` and navigate to `Local Users and Groups > Groups > docker-users`. Right-click to add the user to the group. Log out and log back in for the changes to take effect.
111 |
112 |
113 | #### :dart: 1.2.2 Install docker
114 |
115 | 1. Search for Docker, and select Docker Desktop in the search results.
116 | 
117 |
118 | 2. The Docker menu (whale menu) displays the Docker Subscription Service Agreement window.
119 | 
120 |
121 |
122 |
123 | ### :art: 2. Run docker case
124 | 1. Follow Step 1 to encure Docker is running.
125 |
126 |
127 | 2. In command, pull docker image by copying and execute the command
128 | ```shell
129 | docker pull mindnetwork/mind-lake-sdk-typescript-case1:main
130 | ```
131 |
132 | ```shell
133 | docker pull mindnetwork/mind-lake-sdk-typescript-case2:main
134 | ```
135 |
136 | ```shell
137 | docker pull mindnetwork/mind-lake-sdk-typescript-case3:main
138 | ```
139 |
140 |
141 | mind lake case has 3 images:
142 | - https://hub.docker.com/repository/docker/mindnetwork/mind-lake-sdk-typescript-case1
143 | - https://hub.docker.com/repository/docker/mindnetwork/mind-lake-sdk-typescript-case2
144 | - https://hub.docker.com/repository/docker/mindnetwork/mind-lake-sdk-typescript-case3
145 |
146 | 3. run docker image
147 | 
148 |
149 | 
150 |
151 |
152 | 4. bind port with docker image, copy the command
153 | ```shell
154 | docker run -d -p 90:80 mindnetwork/mind-lake-sdk-typescript-case1:main
155 | ```
156 |
157 | ```shell
158 | docker run -d -p 91:80 mindnetwork/mind-lake-sdk-typescript-case2:main
159 | ```
160 |
161 | ```shell
162 | docker run -d -p 92:80 mindnetwork/mind-lake-sdk-typescript-case3:main
163 | ```
164 |
165 | "90"、"91"、"92" are bind ports ,
166 | "mindnetwork/mind-lake-sdk-typescript-case1:main"、
167 | "mindnetwork/mind-lake-sdk-typescript-case2:main"、
168 | "mindnetwork/mind-lake-sdk-typescript-case3:main" are docker repositories
169 |
170 |
171 | 5. run mind-lake-sdk-typescript-case1
172 |
173 | Open your browser and enter localhost
174 | ```http
175 | http://localhost:90/use_case_1.html
176 | ```
177 | 
178 |
179 | ```http
180 | http://localhost:91/use_case_2.html
181 | ```
182 | ```http
183 | http://localhost:92/use_case_3.html
184 | ```
185 |
186 |
187 | At last, you have successfully executed case1 by use docker !!!
188 |
189 | ## :star2: 3. Explore the use cases
190 | You can jump to [:art: 6.2 Use Case 1](README.md#art-62-use-case-1-single-user-with-structured-data) to continue to explore the use cases.
--------------------------------------------------------------------------------
/tutorial/Configure_Node.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
MindLake Tutorial: Configure Node
5 |
6 |
7 | A step-by-step cookbook for Node Configuration !
8 |
9 |
10 |
11 | - [:star2: 0. Step by step tutorial](#star2-0-step-by-step-tutorial)
12 | - [:star2: 1. Install Node Package Management Tools: Nvm](#star2-1-install-node-package-management-tools-nvm)
13 | - [:art: 1.1 For Mac OS](#art-11-for-mac-os)
14 | - [:dart: 1.1.2 Install Nvm with HomeBrew](#dart-112-install-nvm-with-homebrew)
15 | - [:gear: 1.1.2.1 Step1: Install HomeBrew (If you don't have Homebrew installed)](#gear-1121-step1-install-homebrew-if-you-dont-have-homebrew-installed)
16 | - [:gear: 1.1.2.2 Step2: Install Nvm](#gear-1122-step2-install-nvm)
17 | - [:gear: 1.1.2.3 Step3: Verify the Nvm Installation](#gear-1123-step3-verify-the-nvm-installation)
18 | - [:art: 1.2 For Windows](#art-12-for-windows)
19 | - [:dart: 1.2.1 Download Nvm](#dart-121-download-nvm)
20 | - [:dart: 1.2.2 Run exe installation file](#dart-122-run-exe-installation-file)
21 | - [:dart: 1.2.3 Validate nvm installation](#dart-123-validate-nvm-installation)
22 | - [:star2: 2. Install Node](#star2-2-install-node)
23 | - [:art: 2.1 Install Node](#art-21-install-node)
24 | - [:art: 2.2 Select right Node 16 version if not correct](#art-22-select-right-node-16-version-if-not-correct)
25 | - [:art: 2.3 Validate if correct node version](#art-23-validate-if-correct-node-version)
26 |
27 |
28 | ## :star2: 0. Step by step tutorial
29 | This is part of support chapter for MindLake step-by-step tutorial for [Typescript](README.md)
30 |
31 | ## :star2: 1. Install Node Package Management Tools: Nvm
32 |
33 | ### :art: 1.1 For Mac OS
34 |
35 | #### :dart: 1.1.2 Install Nvm with HomeBrew
36 | If you need to install Nvm from the command line on macOS, the Homebrew package manager is a reliable option. Follow the steps below to install Nvm via Homebrew:
37 | ##### :gear: 1.1.2.1 Step1: Install HomeBrew (If you don't have Homebrew installed)
38 | 1. Open a browser and go to https://brew.sh.
39 |
40 | 
41 |
42 | 2. Under the "Install Homebrew" title, copy the command
43 | ```shell
44 | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
45 | ```
46 |
47 | 
48 |
49 | 3. Then open a terminal window, paste the copied command, and press the 'Enter' or 'Return' button.
50 |
51 | 
52 |
53 | 4. Enter your macOS credentials if and when asked.
54 | 5. If prompted, install Apple's command line developer tools.
55 |
56 | ##### :gear: 1.1.2.2 Step2: Install Nvm
57 | 1. Enter the following command in terminal to upgrade Homebrew:
58 | ```shell
59 | brew update && brew upgrade
60 | ```
61 | 2. Install `nvm` using this command:
62 | ```shell
63 | brew install nvm
64 | ```
65 | Next, create a directory for nvm at home.
66 | ```
67 | mkdir ~/.nvm
68 | ```
69 | Now, configure the required environment variables. Edit the following configuration file in your home directory
70 | ```
71 | vim ~/.bash_profile
72 | ```
73 | and, add the below lines to `~/.bash_profile` ( or `~/.zshrc` for macOS Catalina or newer versions)
74 | ```
75 | export NVM_DIR=~/.nvm
76 | source $(brew --prefix nvm)/nvm.sh
77 | ```
78 | Press `ESC` + `:wq` to save and close your file.
79 |
80 | Next, load the variable to the current shell environment. From the next login, it will automatically loaded.
81 | ```
82 | source ~/.bash_profile
83 | ```
84 | That’s it. The nvm has been installed on your macOS system.
85 |
86 | ##### :gear: 1.1.2.3 Step3: Verify the Nvm Installation
87 | Enter the following command in terminal:
88 | ```shell
89 | nvm --version
90 | ```
91 | An example of the output is:
92 | ```
93 | 1.1.7
94 | ```
95 |
96 |
97 | ### :art: 1.2 For Windows
98 | Open Terminal on Windows:
99 | 1. press `WIN` + `R` key
100 | 2. type `CMD` in the prompt
101 | 3. press `ENTRY` key
102 |
103 | 
104 |
105 | 
106 |
107 | #### :dart: 1.2.1 Download Nvm
108 | 1. Open a browser and go to [nvm downloads for Windows](https://github.com/coreybutler/nvm-windows/releases)
109 | 2. Download nvm-setup.exe
110 |
111 | 
112 |
113 | #### :dart: 1.2.2 Run exe installation file
114 | Double Click nvm-setup.exe to run
115 |
116 | 
117 |
118 | 
119 |
120 | 
121 |
122 | #### :dart: 1.2.3 Validate nvm installation
123 | ```cmd
124 | nvm version
125 | ```
126 | An example of the output is:
127 | ```
128 | 1.1.7
129 | ```
130 |
131 | ## :star2: 2. Install Node
132 | ### :art: 2.1 Install Node
133 | ```
134 | nvm install 16
135 | ```
136 | An example of the output is:
137 | ```
138 | Downloading node.js version 16.13.1 (64-bit)...
139 | Complete
140 | Creating D:\program\nvm\temp
141 |
142 | Downloading npm version 8.1.2... Complete
143 | Installing npm v8.1.2...
144 |
145 | Installation complete. If you want to use this version, type
146 |
147 | nvm use 16.13.1
148 | ```
149 | ### :art: 2.2 Select right Node 16 version if not correct
150 | `Node16` is recommended. `Node18` still have issues and is not recommeded right now,
151 | ```cmd
152 | nvm use 16
153 | ```
154 | An example of the output is:
155 | ```
156 | Now using node v16.13.1 (64-bit)
157 | ```
158 |
159 | ### :art: 2.3 Validate if correct node version
160 | ```cmd
161 | node -v
162 | ```
163 | An example of the output is:
164 | ```
165 | v16.13.1
166 | ```
167 |
--------------------------------------------------------------------------------
/tutorial/Configure_Wallet.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
MindLake Tutorial: Configure Wallet
5 |
6 |
7 | A step-by-step cookbook for Wallet Configuration to access Mind Lake !
8 |
9 |
10 |
11 |
12 |
13 | ## :star2: 0. Step by step tutorial
14 | This is part of support chapter for MindLake step-by-step tutorial for [Typescript](README.md)
15 |
16 | ### :star2: 1. Prepare Wallet
17 | #### :art: 1.1 Install Wallet
18 | 1. Install [MetaMask](https://metamask.io/download/) plugins in Chrome Browser
19 | 2. [Sign up a MetaMask Wallet](https://myterablock.medium.com/how-to-create-or-import-a-metamask-wallet-a551fc2f5a6b)
20 | 3. Change the network to Goerli TestNet. If the TestNets aren't displayed, turn on "Show test networks" in Settings.
21 |
22 | 
23 |
24 | 4. Change the network to Goerli TestNet
25 | 5. Goerli Faucet for later gas fee if does not have: [Alchemy Goerli Faucet](https://goerlifaucet.com/), [Quicknode Goerli Faucet](https://faucet.quicknode.com/ethereum/goerli), [Moralis Goerli Faucet](https://moralis.io/faucets/)
26 |
27 | 
28 |
29 | #### :art: 1.2 Wallet Sign In: https://scan.mindnetwork.xyz
30 | 1. Open a browser and visit [mind-scan](https://scan.mindnetwork.xyz/scan)
31 | 2. Click "Sign in" buttom
32 |
33 | 
34 |
35 | 2.1 During the 'Connect' procedure, the wallet will prompt the user 2-3 times as follows:
36 | Sign a nonce for login authentication.
37 |
38 | 
39 |
40 | 2.2 If the user's account keys are already on the chain: Decrypt the user's account keys using the wallet's private key.
41 |
42 | 
43 |
44 | 2.3 If the user's account keys do not exist yet: Obtain the public key of the wallet, which is used to encrypt the randomly generated account keys.
45 |
46 | 
47 |
48 | 2.4 Sign the transaction to upload the encrypted key ciphers to the smart contract on the chain.
49 |
50 | 
51 |
52 | #### :art: 1.3 Register wallets if not in whitelist during testing period
53 | 1. If not in whitelist, there will be a pop-up prompt
54 |
55 | 
56 |
57 | 2. Click [Apply for test link ](https://bit.ly/mindalphatest)
58 | 3. After successful application, Please be patient and wait for the review. You will get notification by email.
59 |
60 |
--------------------------------------------------------------------------------
/tutorial/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
MindLake Tutorial: Typescript SDK
5 |
6 |
7 | A step-by-step cookbook for beginner to access Mind Lake !
8 |
9 |
10 |
11 |
12 |
13 | ## :notebook_with_decorative_cover: Table of Contents
14 | - [:notebook\_with\_decorative\_cover: Table of Contents](#notebook_with_decorative_cover-table-of-contents)
15 | - [:star2: 0. Other Programming Languages](#star2-0-other-programming-languages)
16 | - [:star2: 1. Prepare Wallet](#star2-1-prepare-wallet)
17 | - [:star2: 2. Choose a Method to Explore MindLake SDK](#star2-2-choose-a-method-to-explore-mindlake-sdk)
18 | - [:art: 2.1 Option 1: Use dApp Page Hosted on Arweave](#art-21-option-1-use-dapp-page-hosted-on-arweave)
19 | - [:art: 2.2 Option 2: Use Docker Image](#art-22-option-2-use-docker-image)
20 | - [:art: 2.3 Option 3: Compile with Node.js](#art-23-option-3-compile-with-nodejs)
21 | - [:star2: 3. Install Or Upgrade Node If Needed (for Option 3)](#star2-3-install-or-upgrade-node-if-needed-for-option-3)
22 | - [:star2: 4. Get Examples sourcecode and compile locally (for Option 3)](#star2-4-get-examples-sourcecode-and-compile-locally-for-option-3)
23 | - [:star2: 5. Prepare myconfig.ts (for Option 3)](#star2-5-prepare-myconfigts-for-option-3)
24 | - [:star2: 6. Execute the examples](#star2-6-execute-the-examples)
25 | - [:art: 6.1 QuickStart](#art-61-quickstart)
26 | - [:art: 6.2 Use Case 1: Single User with Structured Data](#art-62-use-case-1-single-user-with-structured-data)
27 | - [:art: 6.3 Use Case 2: Single User with Unstructured Data](#art-63-use-case-2-single-user-with-unstructured-data)
28 | - [:art: 6.4 Use Case 3: Multi Users with Permission Sharing](#art-64-use-case-3-multi-users-with-permission-sharing)
29 |
30 |
31 |
32 |
33 |
34 | ## :star2: 0. Other Programming Languages
35 | - [Python](https://github.com/mind-network/mind-lake-sdk-python/)
36 |
37 | ## :star2: 1. Prepare Wallet
38 | - Click [:star2: 1. Prepare Wallet](Configure_Wallet.md) to get your wallet ready for the use cases.
39 |
40 | ## :star2: 2. Choose a Method to Explore MindLake SDK
41 | In this tutorial, we provide 3 options to explore MindLake SDK with 3 use cases.
42 | All options has same dapp code but host in different ways.
43 | Here is the overall architecture:
44 | 
45 |
46 |
47 | ### :art: 2.1 Option 1: Use dApp Page Hosted on Arweave
48 | For beginners without tech background, we recommend to access the already compiled dApp page hosted on Arweave to have a quick glance at the use cases. You can jump to [:art: 6.2 Use Case 1](#art-62-use-case-1-single-user-with-structured-data)
49 |
50 | ### :art: 2.2 Option 2: Use Docker Image
51 | For developers who don't want to install Node.js, we recommend to use the docker image to run the examples. You can click [Use Docker Image](Configure_Docker_Case.md).
52 |
53 | ### :art: 2.3 Option 3: Compile with Node.js
54 | For developers who want to explore the source code, we recommend to compile the use cases with Node.js and run the examples. You can continue to read the following sections.
55 |
56 |
57 | ## :star2: 3. Install Or Upgrade Node If Needed (for Option 3)
58 | - If you are following Option 2 and Option 3, you can ignore this steps as envrioment is pre-configured.
59 | - If you are following Option 3 to compile sourcecode locally, please click to view [step-by-step to configure Node](Configure_Node.md) if Node is not installed or upgraded
60 |
61 | ## :star2: 4. Get Examples sourcecode and compile locally (for Option 3)
62 | - If you are following Option 2 and Option 3, you can ignore this steps as sourcecode are compiled and can run in your browser directly.
63 | - If you are following Option 3 to compile sourcecode locally, please follow bellow steps to build locally.
64 | 1. Enter the following command in the terminal window to fetch the example code from github:
65 | ```shell
66 | git clone https://github.com/mind-network/mind-lake-sdk-typescript.git
67 | ```
68 | 1. Enter the path of example code:
69 | ```shell
70 | cd mind-lake-sdk-typescript/examples
71 | ```
72 | 1. Install depedency
73 | ```cmd
74 | npm install
75 | ```
76 | 1. Check mind-lake-sdk depedency
77 | ```cmd
78 | npm info mind-lake-sdk version
79 | ```
80 | An example of the output is:
81 | ```cmd
82 | 1.0.2
83 | ```
84 |
85 | ## :star2: 5. Prepare myconfig.ts (for Option 3)
86 | - If you are following Option 2 and Option 3, you can ignore this steps as configuration is done and can run in your browser directly.
87 | - If you are following Option 3 to compile sourcecode locally, please follow bellow steps to configure locally.
88 |
89 | 1. `myconfig.ts` contains the settings of parameters used in examples and use cases, you can copy `myconfig_template.ts` to the name `myconfig.ts` and modify it as per your requirement.
90 | 2. `myconfig.ts` will need walletAddress and appKey.
91 | 3. If you want to run the examples of quickStart, Use Case 1 and Use Case 2, you only need to fill out `appKey`. You can click [Create Mind appKey](Configure_AppKey.md).
92 |
93 | 4. If you want to run Use Case 3, you need to fill out the wallets info for all of `Alice`, `Bob` and `Charlie`.
94 | - You can click [:star2: 1. Prepare Wallet](Configure_Wallet.md) again if you haven't get all the 3 wallets ready.
95 |
96 | ```
97 | export const appKey = "YOUR_APP_KEY";
98 | export const nodeUrl = "https://sdk.mindnetwork.xyz"; // or change to other node url
99 | export const aliceWalletAddress = "Alice_Wallet_Address";
100 | export const bobWalletAddress = "Bob_Wallet_Address";
101 | export const charlieWalletAddress = "Charlie_Wallet_Address";
102 | ```
103 |
104 | ## :star2: 6. Execute the examples
105 | If you are following Option 3, You can execute the following commands to run the quickstart and use cases.
106 | ```
107 | cd examples
108 | npm run start
109 | ```
110 | An example of the output is:
111 | ```
112 | App running at:
113 | - Local: http://localhost:8002 (copied to clipboard)
114 | - Network: http://192.168.137.1:8002
115 | ```
116 | By default use 8000 as port number. But will auto increase port number if 8000 is used. You may see 8001 or other incremental in your side. The example bellow 8002. Use the port number shown in your terminal.
117 | Open a browser and visit `http://localhost:8002`
118 |
119 | For bellow steps, we have list the command and screenshot in Option 3 by default, but also list the urls in Option 1 and Option 2.
120 |
121 | ### :art: 6.1 QuickStart
122 |
123 | 1. First, you should create a test wallet for test.
124 |
125 | 
126 |
127 | 
128 |
129 | 2. Click "Quick start with your MetaMask" and you will see the logs while login with your MetaMask wallet
130 |
131 | 
132 |
133 | 
134 |
135 | ### :art: 6.2 Use Case 1: Single User with Structured Data
136 | 1. Open a browser and visit `http://localhost:8002/use_case_1`
137 | - For Option 1, we hosted a same page on Arweave: https://arweave.net/ElzjXMVWIUMavlUbBhXHSl5JopcbL3H_E_0KehsMfPg
138 | - For Option 2, you may see docker instance and visit your browser in http://localhost:90/use_case_1.html
139 | 2. Click "Test case one with your MetaMask" and you will see the logs while executing Use Case 1.
140 |
141 | 
142 |
143 | ### :art: 6.3 Use Case 2: Single User with Unstructured Data
144 | 1. Open a browser and visit `http://localhost:8002/use_case_2`
145 | - For Option 1, we hosted a same page on Arweave: https://arweave.net/jvf-v7VImOAQ67uzG1CCGqZuVPXGvIE1S-l8MO3A75U
146 | - For Option 2, you may see docker instance and visit your browser in http://localhost:91/use_case_2.html
147 | 2. Click "Test case two with your MetaMask" and you will see the logs while executing Use Case 2
148 |
149 | 
150 |
151 | ### :art: 6.4 Use Case 3: Multi Users with Permission Sharing
152 | 1. You will need 3 wallets for Use Case 3: Alice, Bob, Charlie
153 | We show how to create a wallet for Alice for testing purpose.
154 |
155 | 
156 |
157 | 
158 |
159 | Using the same way to create wallet for Bob and Charlie
160 |
161 | Copy Alice,Bob,Charlie' wallet address into myconfig.ts to update `aliceWalletAddress` `bobWalletAddress` `charlieWalletAddress`
162 |
163 | > **Note**
164 | > During testing period, please make you have all wallet address are registered in into whitelist: https://sites.google.com/mindnetwork.xyz/mindnetwork/alpha-test.
165 | > Be more specific, please ensure your Alice, Bob and Charlie are registered and approved. Otherwise, you may experience the error on sharing.
166 |
167 |
168 | 2. Open a browser and visit `http://localhost:8002/use_case_3`
169 | - For Option 1, we hosted a same page on Arweave: https://arweave.net/QR03v_GcZlEnbuny9e5kb149WL4E2_gFe8pbFOMExbE
170 | - For Option 2, you may see docker instance and visit your browser in http://localhost:92/use_case_3.html
171 |
172 | 
173 |
174 | 1. Switch to Alice's wallet and perform actions as Alice
175 |
176 | 
177 |
178 | 
179 |
180 | Click "Insert Alice Data And Share To Charlie"
181 |
182 | 
183 |
184 | Wait until "insert data done" appears which means data insertion is completely.
185 |
186 | 4. Switch to BOb's wallet and performa actions as Bob
187 |
188 | 
189 |
190 | 
191 |
192 | Click "Insert Bob Data And Share To Charlie"
193 |
194 | 
195 |
196 | Wait until "insert data done" appears which means data insertion is completely.
197 |
198 | 5. Switch to Charlie's wallet and performa actions as Charlie
199 |
200 | 
201 |
202 | 
203 |
204 | Click "Charlie Select Data And Decrypt Data"
205 |
206 | 
207 |
208 | 6. Final output quick view
209 |
210 | 
211 |
212 |
213 |
--------------------------------------------------------------------------------
/tutorial/imgs/alice_connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/alice_connect.png
--------------------------------------------------------------------------------
/tutorial/imgs/bob_connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/bob_connect.png
--------------------------------------------------------------------------------
/tutorial/imgs/brew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/brew.png
--------------------------------------------------------------------------------
/tutorial/imgs/case_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/case_2.png
--------------------------------------------------------------------------------
/tutorial/imgs/case_2_logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/case_2_logout.png
--------------------------------------------------------------------------------
/tutorial/imgs/case_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/case_3.png
--------------------------------------------------------------------------------
/tutorial/imgs/case_3_result.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/case_3_result.png
--------------------------------------------------------------------------------
/tutorial/imgs/change_chain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/change_chain.png
--------------------------------------------------------------------------------
/tutorial/imgs/change_wallet_alice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/change_wallet_alice.png
--------------------------------------------------------------------------------
/tutorial/imgs/change_wallet_bob.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/change_wallet_bob.png
--------------------------------------------------------------------------------
/tutorial/imgs/change_wallet_charlie.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/change_wallet_charlie.png
--------------------------------------------------------------------------------
/tutorial/imgs/charlie_connect.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/charlie_connect.png
--------------------------------------------------------------------------------
/tutorial/imgs/charlie_out_put.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/charlie_out_put.png
--------------------------------------------------------------------------------
/tutorial/imgs/create_dapp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/create_dapp.png
--------------------------------------------------------------------------------
/tutorial/imgs/create_dapp_confirm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/create_dapp_confirm.png
--------------------------------------------------------------------------------
/tutorial/imgs/create_wallet.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/create_wallet.png
--------------------------------------------------------------------------------
/tutorial/imgs/create_wallet_confirm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/create_wallet_confirm.png
--------------------------------------------------------------------------------
/tutorial/imgs/dapp_list.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/dapp_list.png
--------------------------------------------------------------------------------
/tutorial/imgs/decrypt_request.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/decrypt_request.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker-app-search.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker-app-search.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_app.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_app.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_app_top.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_app_top.webp
--------------------------------------------------------------------------------
/tutorial/imgs/docker_bac.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_bac.jpeg
--------------------------------------------------------------------------------
/tutorial/imgs/docker_bak.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_bak.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_case1_img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_case1_img.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_info.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_info.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_run.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_run.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_run1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_run1.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_run2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_run2.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_win_download.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_win_download.png
--------------------------------------------------------------------------------
/tutorial/imgs/docker_windows_win.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/docker_windows_win.jpeg
--------------------------------------------------------------------------------
/tutorial/imgs/img.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/img.png
--------------------------------------------------------------------------------
/tutorial/imgs/insert_data_done.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/insert_data_done.png
--------------------------------------------------------------------------------
/tutorial/imgs/install_brew.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/install_brew.png
--------------------------------------------------------------------------------
/tutorial/imgs/metamask_testnet_enable.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/metamask_testnet_enable.png
--------------------------------------------------------------------------------
/tutorial/imgs/myDapp_menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/myDapp_menu.png
--------------------------------------------------------------------------------
/tutorial/imgs/nounce_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/nounce_sign.png
--------------------------------------------------------------------------------
/tutorial/imgs/nvm_1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/nvm_1.png
--------------------------------------------------------------------------------
/tutorial/imgs/nvm_2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/nvm_2.png
--------------------------------------------------------------------------------
/tutorial/imgs/nvm_3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/nvm_3.png
--------------------------------------------------------------------------------
/tutorial/imgs/open_mac_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/open_mac_terminal.png
--------------------------------------------------------------------------------
/tutorial/imgs/quickStart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/quickStart.png
--------------------------------------------------------------------------------
/tutorial/imgs/quickStart_logout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/quickStart_logout.png
--------------------------------------------------------------------------------
/tutorial/imgs/request_publickey.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/request_publickey.png
--------------------------------------------------------------------------------
/tutorial/imgs/sign_scan.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/sign_scan.png
--------------------------------------------------------------------------------
/tutorial/imgs/tutorial-architecture.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/tutorial-architecture.jpeg
--------------------------------------------------------------------------------
/tutorial/imgs/tutorial-architecture.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/tutorial-architecture.jpg
--------------------------------------------------------------------------------
/tutorial/imgs/upload_chain.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/upload_chain.png
--------------------------------------------------------------------------------
/tutorial/imgs/white_list_popup.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/white_list_popup.png
--------------------------------------------------------------------------------
/tutorial/imgs/window_download_nvm.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/window_download_nvm.png
--------------------------------------------------------------------------------
/tutorial/imgs/windows_open_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/windows_open_terminal.png
--------------------------------------------------------------------------------
/tutorial/imgs/windows_terminal.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mind-network/mind-lake-sdk-typescript/694eacad028d781032eef19cb6c9faaa204fa308/tutorial/imgs/windows_terminal.png
--------------------------------------------------------------------------------