├── .drone.yml ├── .eslintrc.json ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ └── test.yml ├── .gitignore ├── .nvmrc ├── .prettierrc.json ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── configs ├── debug.json └── template1.js ├── docs ├── ARCHITECTURE.md ├── DiscoveryBoot.png ├── FindProvidersAction.png ├── IPC_MESSAGES.md ├── IdentifySyncDiagram.png ├── MainController.jpg ├── NodeControllerDiagrams.jpg ├── ON_BOARDING.md ├── PersistentDiscovery.png ├── SyncHighLevel.png ├── TASKS_LIFE_CYCLE_DOCS.md ├── TrySyncReceiveOneContract.png ├── discovery_sequence ├── jsonrpc.png ├── overview1.jpg ├── overview2.jpg ├── overview3.jpg ├── start_flow.jpg ├── states_sync_sequence └── streams_diagram_sync.png ├── package-lock.json ├── package.json ├── scripts ├── deploy.sh └── test_tree_validation.js ├── src ├── cli │ ├── Parsers.js │ └── cli_app.js ├── client_api │ ├── JsonRpcServer.js │ └── README.md ├── common │ ├── CIDUtil.js │ ├── DbUtils.js │ ├── EncoderUtil.js │ ├── EngCID.js │ ├── StoppableTask.js │ ├── constants.js │ ├── cryptography.js │ ├── errors.js │ ├── logger.js │ └── utils.js ├── core │ ├── CoreRuntime.js │ ├── actions │ │ ├── DbRead │ │ │ └── GetDbAction.js │ │ ├── DbWrite │ │ │ └── UpdateDbAction.js │ │ ├── PreParseAction.js │ │ ├── SendToCoreAction.js │ │ └── deprecated │ │ │ ├── GetAllAddrsAction.js │ │ │ ├── GetContractCodeAction.js │ │ │ ├── GetDeltasAction.js │ │ │ └── NewTaskEncryptionKeyAction.js │ ├── core_messages_scheme.json │ ├── core_server_mock │ │ ├── core_server.js │ │ ├── data │ │ │ └── provider_db.js │ │ └── dataGenerator.js │ └── ipc.js ├── db │ ├── DbKey.js │ └── LevelDbApi.js ├── ethereum │ ├── EnigmaContractAPIBuilder.js │ ├── EnigmaContractReaderAPI.js │ ├── EnigmaContractWriterAPI.js │ ├── EthereumAPI.js │ ├── EthereumServices.js │ ├── EthereumVerifier.js │ ├── StateSync.js │ └── config.json ├── index.js ├── main_controller │ ├── DummyRuntime.js │ ├── EnvironmentBuilder.js │ ├── FacadeController.js │ ├── MainController.js │ ├── actions │ │ ├── DbAction.js │ │ ├── DummyAction.js │ │ └── ProxyAction.js │ ├── channels │ │ ├── Channel.js │ │ ├── Communicator.js │ │ └── Envelop.js │ └── mock_tests │ │ └── IdentifyMissingContentTest.js ├── policy │ ├── p2p_messages │ │ ├── messages.js │ │ ├── principal_messages.js │ │ ├── schemes │ │ │ ├── SchemeValidator.js │ │ │ └── state_sync_scheme.json │ │ └── sync_messages.js │ └── policy.js └── worker │ ├── EnigmaNode.js │ ├── builder │ └── WorkerBuilder.js │ ├── controller │ ├── NodeController.js │ └── actions │ │ ├── GetRegistrationParamsAction.js │ │ ├── GetStateKeysAction.js │ │ ├── GetStatusAction.js │ │ ├── HealthCheckAction.js │ │ ├── InitWorkerAction.js │ │ ├── NewTaskEncryptionKeyAction.js │ │ ├── PubSubUnsubscribeAction.js │ │ ├── PubsubPublishAction.js │ │ ├── PubsubSubscribeAction.js │ │ ├── SubscribeSelfSignKeyTopicPipelineAction.js │ │ ├── connectivity │ │ ├── BootstrapDiscoveredAction.js │ │ ├── GetPeers.js │ │ └── NewPeerAction.js │ │ ├── db │ │ ├── DbRequestAction.js │ │ ├── read │ │ │ ├── GetAllAddrsAction.js │ │ │ ├── GetAllTipsAction.js │ │ │ ├── GetContractCodeAction.js │ │ │ ├── GetDeltasAction.js │ │ │ └── GetTipsAction.js │ │ └── write │ │ │ └── UpdateDbAction.js │ │ ├── ethereum │ │ ├── CommitReceiptAction.js │ │ ├── GetWorkerParamsAction.js │ │ ├── LoginAction.js │ │ ├── LogoutAction.js │ │ ├── RegisterAction.js │ │ └── UnregisterAction.js │ │ ├── proxy │ │ ├── GetStatusProxyAction.js │ │ ├── ProxyDispatcherAction.js │ │ ├── RouteRpcBlockingAction.js │ │ └── RouteRpcNonBlockingAction.js │ │ ├── sync │ │ ├── AnnounceContentAction.js │ │ ├── AnnounceLocalStateAction.js │ │ ├── FindContentProviderAction.js │ │ ├── GetLocalTipsOfRemote.js │ │ ├── IdentifyMissingStatesAction.js │ │ ├── ProvideSyncStateAction.js │ │ ├── ReceiveAllPipelineAction.js │ │ └── TryReceiveAllAction.js │ │ └── tasks │ │ ├── ExecuteTaskAction.js │ │ ├── GetResultAction.js │ │ ├── HandleVerifiedTaskAction.js │ │ ├── PublishTaskResultAction.js │ │ ├── StartTaskExecutionAction.js │ │ ├── VerifyAndStoreResultAction.js │ │ └── VerifyNewTaskAction.js │ ├── handlers │ ├── ManagementServer.js │ ├── PrincipalNode.js │ ├── ProtocolHandler.js │ └── WebServer.js │ ├── libp2p-bundle.js │ ├── state_sync │ ├── provider │ │ └── Provider.js │ └── receiver │ │ ├── FindProviderResult.js │ │ ├── LocalMissingStatesResult.js │ │ ├── Receiver.js │ │ └── StateSyncReqVerifier.js │ └── tasks │ ├── ComputeTask.js │ ├── DeployTask.js │ ├── OutsideTask.js │ ├── Result.js │ ├── Task.js │ └── TaskManager.js └── test ├── all_actions_test.js ├── basic_test.js ├── db_cache_test.js ├── ethereum ├── EnigmaContractMock.js ├── EthereumAPIMock.js ├── advanced_eth_test.js ├── basic_eth_test.js ├── eth_verifier_test.js ├── scripts │ ├── contracts │ │ ├── Enigma.sol │ │ ├── EnigmaToken.sol │ │ ├── ExchangeRate.sol │ │ ├── Migrations.sol │ │ ├── impl │ │ │ ├── EnigmaCommon.sol │ │ │ ├── EnigmaEvents.sol │ │ │ ├── EnigmaState.sol │ │ │ ├── EnigmaStorage.sol │ │ │ ├── Getters.sol │ │ │ ├── PrincipalImpl.sol │ │ │ ├── SecretContractImpl.sol │ │ │ ├── TaskImpl.sol │ │ │ ├── UpgradeImpl.sol │ │ │ └── WorkersImpl.sol │ │ ├── interfaces │ │ │ ├── ERC20.sol │ │ │ ├── IEnigma.sol │ │ │ └── IExchangeRate.sol │ │ └── utils │ │ │ ├── Base64.sol │ │ │ ├── Bytes.sol │ │ │ ├── GetCode2.sol │ │ │ ├── Memory.sol │ │ │ └── SolRsaVerify.sol │ ├── env_initializer.js │ ├── migrations │ │ ├── 1_initial_migration.js │ │ └── 2_deploy_contracts.js │ └── truffle.js ├── test_parameters.json └── utils.js ├── init_worker_test.js ├── ipc_test.js ├── jsonrpc_advanced_test.js ├── jsonrpc_test.js ├── msg_test.js ├── nodeutils_test.js ├── principal_test.js ├── singleConfig ├── config_1.js ├── config_2_bootstrap.js ├── id-l.json └── single_config_test.js ├── sync_basic_test.js ├── sync_network_test.js ├── tasks ├── random_compute_tasks.json ├── random_deploy_tasks.json ├── task_flow_test.js ├── task_manager_test.js ├── task_test.js └── utils.js ├── testUtils ├── id-d.json ├── id-l.json ├── principal_mock.js ├── quickBuilderUtil.js └── utils.js ├── test_tree.js ├── utils_test.js ├── verifier_test.js └── webserver_test.js /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["prettier"], 3 | "plugins": ["prettier"], 4 | "rules": { 5 | "prettier/prettier": "error" 6 | }, 7 | "env": { 8 | "es6": true 9 | }, 10 | "parserOptions": { "ecmaVersion": 2019 } 11 | } 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "" 5 | labels: bug 6 | assignees: "" 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Desktop (please complete the following information):** 24 | 25 | - OS: [e.g. iOS] 26 | - Browser [e.g. chrome, safari] 27 | - Version [e.g. 22] 28 | 29 | **Additional context** 30 | Add any other context about the problem here. 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: enhancement 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Unit Tests 2 | 3 | on: [push] 4 | 5 | jobs: 6 | test: 7 | runs-on: ubuntu-latest 8 | 9 | steps: 10 | - uses: actions/checkout@v1 11 | - uses: actions/setup-node@v1.1.0 12 | with: 13 | node-version: 10.15.3 14 | - run: npm install 15 | - run: npm run test-tree 16 | - run: npm test 17 | - run: npm run report-coverage 18 | env: 19 | CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} 20 | -------------------------------------------------------------------------------- /.nvmrc: -------------------------------------------------------------------------------- 1 | lts/dubnium 2 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { "printWidth": 120 } 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | 3 | node_js: 4 | - "10.16" 5 | 6 | install: 7 | - npm install 8 | 9 | script: 10 | - npm test 11 | - if [ ${TRAVIS_BRANCH} == "develop" ] || [ ${TRAVIS_BRANCH} == "master" ]; then echo $TRAVIS_BRANCH ; npm run test-tree; fi 12 | 13 | after_success: 14 | - npm run report-coverage 15 | 16 | notifications: 17 | email: 18 | on_success: never 19 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:10 2 | MAINTAINER Isan-Rivkin 3 | WORKDIR /usr/src/app 4 | COPY package*.json ./ 5 | RUN npm -g config set user root 6 | RUN npm install 7 | COPY . . 8 | EXPOSE 8080 9 | RUN apt-get update -------------------------------------------------------------------------------- /configs/debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "bootstrapNodes": [], 3 | "port": "0", 4 | "nickname": "peer", 5 | "multiAddrs": ["/ip4/0.0.0.0/tcp/"], 6 | "namespace": "ipfs", 7 | "idPath": null, 8 | "isDiscover": true 9 | } 10 | -------------------------------------------------------------------------------- /configs/template1.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | logger: { 3 | level: "debug", // log level 4 | cli: true, // output to std 5 | file: false // output to file, if set path then will save to file else none. 6 | }, 7 | node: { 8 | network: { 9 | port: "0", // if 0 then chose random port, 10 | multiAddrs: ["/ip4/0.0.0.0/tcp/"], 11 | // TODO:: ignored because of constants/namespace 12 | namespace: "ipfs" 13 | }, 14 | idPath: null, // load PeerId, if null-> create one 15 | // TODO:: ignored currently cuz of implementation 16 | id: null, // either idPath or id -> actuall object here are acceptable if both are set, idPath is the default 17 | // TODO:: ignored libp2p-bundle 18 | isDiscover: true, // should do discovery ? 19 | // the inner task manager of the node controller 20 | taskManager: { 21 | dbPath: null // the db path for storage, if null saves in default 22 | }, 23 | // epoch related config 24 | // TODO:: ignored because of constants/PRINCIPAL_NODE 25 | principalNode: { 26 | uri: null, //principal node url, default if null 27 | retryOptions: { 28 | retries: 10, // try 1 time and retry 10 times if needed, total = 11 29 | factor: 1.7, // https://www.wolframalpha.com/input/?i=Sum%5B1000*x%5Ek,+%7Bk,+0,+9%7D%5D+%3D+5+*+60+*+1000 30 | minTimeout: 1 * 1000, // the number of milliseconds before starting the first retry 31 | maxTimeout: 2 * 60 * 1000, // the maximum number of milliseconds between two retries 32 | randomize: true 33 | } 34 | } 35 | }, 36 | // IPC 37 | core: { 38 | uri: "tcp://127.0.0.1:5552" // ipc uri 39 | }, 40 | // JsonRpc config 41 | proxy: { 42 | withProxy: true, // default serve as a proxy node 43 | port: null // integer or null will default in constants 44 | }, 45 | // Ethereum related configuration 46 | ethereum: { 47 | //default use ethereum or not 48 | withEthereum: false, 49 | // websocket provider 50 | ethereumWebsocketProvider: "", 51 | // enigma contract address 52 | enigmaContractAddress: "" 53 | }, 54 | // TODO:: CURRENTLY IGNORED 55 | "dev:": { 56 | truffleDir: "" 57 | } 58 | }; 59 | -------------------------------------------------------------------------------- /docs/DiscoveryBoot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/DiscoveryBoot.png -------------------------------------------------------------------------------- /docs/FindProvidersAction.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/FindProvidersAction.png -------------------------------------------------------------------------------- /docs/IdentifySyncDiagram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/IdentifySyncDiagram.png -------------------------------------------------------------------------------- /docs/MainController.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/MainController.jpg -------------------------------------------------------------------------------- /docs/NodeControllerDiagrams.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/NodeControllerDiagrams.jpg -------------------------------------------------------------------------------- /docs/PersistentDiscovery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/PersistentDiscovery.png -------------------------------------------------------------------------------- /docs/SyncHighLevel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/SyncHighLevel.png -------------------------------------------------------------------------------- /docs/TASKS_LIFE_CYCLE_DOCS.md: -------------------------------------------------------------------------------- 1 | # Tasks Life Cycle 2 | 3 | There are 2 type of tasks, Deploy Secret Contracts and Compute something with existing Secret Contracts. 4 | 5 | # Task Manager enigma-p2p 6 | 7 | The TaskManager class is responsible for all the things that are related to the business logic of the tasks life cycle. 8 | 9 | # Task ID 10 | 11 | Every task is identified by its unique global id both in the system and in the network. 12 | 13 | # Task Statuses 14 | 15 |
16 | Unverified 17 |

3 things needs to be verified: 18 |

23 | The task will not be even stored on disk (i.e stay in memory) until it is verified. 24 |

25 |
26 |
27 | In-Progress 28 |

Once a task is verified it is sent to `enigma-core` for execution and saved on disk for the purpose of persistence and reduced RAM usage.

29 |
30 |
31 | Success 32 |

Indicates that the task was finished successfully. Always includes a result attached.

33 |
34 |
35 | Failed 36 |

Indicates that the task execution failed. Always includes an error message.

37 |
38 |
39 | Ethereum-Failure 40 |

Indicates that the task failed due to a failure in the Ethereum callback.

41 |
42 | 43 | # Task Result Propagation in the network 44 | 45 | All the task results are either `Failed` or `Success`, and the result is published to a topic called `/task_status/0.1`. 46 | This is how the nodes including the **Gateway** node of the user will be informed once the result is ready. 47 | 48 | # Communication with the selected worker. 49 | 50 | Both for requests and status checks the communication is done via the `JsonRpc` component. 51 | The worker can respond to a status check at any time. 52 | -------------------------------------------------------------------------------- /docs/TrySyncReceiveOneContract.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/TrySyncReceiveOneContract.png -------------------------------------------------------------------------------- /docs/jsonrpc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/jsonrpc.png -------------------------------------------------------------------------------- /docs/overview1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/overview1.jpg -------------------------------------------------------------------------------- /docs/overview2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/overview2.jpg -------------------------------------------------------------------------------- /docs/overview3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/overview3.jpg -------------------------------------------------------------------------------- /docs/start_flow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/start_flow.jpg -------------------------------------------------------------------------------- /docs/states_sync_sequence: -------------------------------------------------------------------------------- 1 | /** State sync sequence **/ 2 | 3 | This part described the messages and their sequece of a node trying to synchronize its state. 4 | 5 | 6 | Assuming this data structure as a consensus mechanism on the Ethereum network 7 | 8 | // Enigma smart contract stores in some way or another: 9 | +--------------------+----------------+-----------+---------+------+ 10 | | secret-contracts | deployed code | delta 1 | delta 2 | ... | 11 | | address | + payload hash | hash | hash | ... | 12 | +--------------------+----------------+-----------+---------+------+ 13 | | 0x01... | asd.. | h(d1) | h(d2) | ... | 14 | | 0x02... | bk2.. | h(d1) | h(d2) | ... | 15 | | 0x03... | sg3.. | h(d1) | h(d2) | ... | 16 | | 0x04... | 4fh.. | h(d1) | h(d2) | ... | 17 | +--------------------+----------------+-----------+---------+------+ 18 | 19 | ### What does it mean for a worker to by synced ###: 20 | 21 | * CURRENT_ONCHAIN_CONTRACTS_NUM : The total number of contracts. 22 | * CURRENT_ONCHAIN_CONCTRACTS_ADDRS : List of all the contract addresses. 23 | * for each contract addr in CURRENT_ONCHAIN_CONCTRACTS_ADDRS: 24 | * CONTRACT_CODE 25 | * CURRENT_ONCHAIN_DELTAS : List of all the deltas hashes in a sequence order (delta 0 hash = initial state hash) 26 | 27 | 28 | 29 | ### The flow of a new node in the network ####: 30 | 31 | 0) Bootstrap 32 | 1) Load local state 33 | 2) Build "Hits Map" from RemoteState(Ethereum) - LocalState 34 | 3) Sync the "Hits Map" - synchronize from other peers // STATE_SYNC_REQ/RES 35 | 4) Announce (IPFS/CID) holdings (for each contract announce CID(address) ) 36 | 37 | 38 | ### State sync request - STATE_SYNC_REQ/RES ### 39 | 40 | // get the most recent tip from a peer 41 | // helpful to verify the peer is holding the state-delta that we need. 42 | 43 | GET_TIP_REQ 44 | 45 | { 46 | msgType : 'GET_TIP_REQ' 47 | contractAddress : '0x...' // the contract address we want to query the last delta from 48 | } 49 | GET_TIP_RES 50 | 51 | { 52 | msgType : 'GET_TIP_RES' 53 | contractAddress : '0x...' // the response include the contract address as well for verification 54 | tipIndex : '' // delta number 55 | tipHash : '' // delta hash keccack256 56 | } 57 | 58 | The message exchange process during #3 59 | 60 | 61 | 62 | After finding the peer CID provider of a contract , request : 63 | The providing peer will respond with up to 500 deltas in each range request, each response is a chunk 64 | 65 | 66 | 67 | SYNC_BCODE_REQ 68 | 69 | { 70 | msgType : 'SYNC_BCODE_REQ' 71 | contractAddress : '0x...' the address of the secret-contract 72 | } 73 | SYNC_BCODE_RES 74 | { 75 | msgType : 'SYNC_BCODE_RES' 76 | contractAddress : '0x...' the address of the secret-contract 77 | deployedByteCode : [] // byte array of the deployed byte code (does not include state[0]) 78 | } 79 | 80 | 81 | SYNC_STATE_REQ 82 | 83 | { 84 | msgType : 'SYNC_STATE_REQ' 85 | contractAddress : '0x...' the address of the secret-contract, 86 | fromIndex: '', // start index (include) 87 | toIndex : '', // end index (include) 88 | fromHash : '', 89 | toHash : '' 90 | } 91 | 92 | SYNC_STATE_RES 93 | 94 | { 95 | msgType : 'SYNC_STATE_RES' 96 | contractAddress : '0x...' the address of the secret-contract, 97 | states : [{index,hash,data}...] // the actual state deltas 98 | } 99 | 100 | 101 | 102 | 103 | ### Stream Flow ### 104 | 105 | OnBoot: 106 | 107 | 0) Bootstrap 108 | 1) Load current state from local db 109 | 2) get missing states from ethereum 110 | 3) request states from peers using IPFS 111 | 4) announce synched 112 | 113 | On Provide Request - Stream only flow : 114 | 115 | Requester -> LocalDB -> Requester -> repeat ? 116 | 117 | On Sync Request - Stream only flow : 118 | 119 | Missing state -> Provider Peer -> Sliding Window -> Validate Hashes -> repeat ? 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /docs/streams_diagram_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/scrtlabs/enigma-p2p/95529ba265693196d30844696861b71951d78839/docs/streams_diagram_sync.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "enigma-p2p", 3 | "version": "0.2.6", 4 | "description": "Enigma P2P library based on libp2p-js ", 5 | "main": "src/index.js", 6 | "bin": { 7 | "enigma-p2p-test": "./src/cli/cli_app.js" 8 | }, 9 | "nyc": { 10 | "temp-dir": "/tmp/enigma-p2p" 11 | }, 12 | "scripts": { 13 | "test": "find ./test -name '*_test.js' | npx nyc xargs env mocha --timeout 200000", 14 | "truffle-develop": "cd ./test/ethereum/scripts && truffle develop", 15 | "truffle-migrate-print-contract-address": "cd ./test/ethereum/scripts; truffle compile >/dev/null; truffle migrate --reset 2>/dev/null | grep -A 4 \"Replacing 'Enigma'\" | grep 'contract address' | awk '{print $NF}'", 16 | "kill-truffle": "ps aux | grep truffle | grep -v grep | awk '{print $2}' | xargs kill -9 || true", 17 | "compile-truffle": "cd test/ethereum/scripts && rm -rf build && truffle compile", 18 | "report-coverage": "npx nyc report --reporter=text-lcov > coverage.lcov && npx codecov", 19 | "lint": "eslint src", 20 | "lint:fix": "eslint --fix src", 21 | "start": "node src/cli/cli_app.js", 22 | "test-tree": "node scripts/test_tree_validation.js" 23 | }, 24 | "author": "isan rivkin", 25 | "license": "AGPL-3.0-or-later", 26 | "repository": { 27 | "type": "git", 28 | "url": "https://github.com/enigmampc/enigma-p2p.git" 29 | }, 30 | "dependencies": { 31 | "@nodeutils/defaults-deep": "^1.1.0", 32 | "async": "^2.6.1", 33 | "commander": "^2.18.0", 34 | "config": "^3.1.0", 35 | "cors": "^2.8.5", 36 | "ethereumjs-abi": "^0.6.7", 37 | "expect": "^24.1.0", 38 | "express": "^4.17.1", 39 | "fs": "0.0.1-security", 40 | "jayson": "^3.0.1", 41 | "jsbi": "^2.0.5", 42 | "jsonschema": "^1.2.4", 43 | "level": "^4.0.0", 44 | "libp2p": "^0.23.0", 45 | "libp2p-bootstrap": "^0.9.3", 46 | "libp2p-kad-dht": "^0.15.4", 47 | "libp2p-mdns": "^0.12.0", 48 | "libp2p-mplex": "^0.8.0", 49 | "libp2p-pnet": "^0.1.0", 50 | "libp2p-secio": "^0.10.0", 51 | "libp2p-spdy": "^0.12.1", 52 | "libp2p-tcp": "^0.12.1", 53 | "libp2p-websocket-star": "^0.10.0", 54 | "libp2p-websockets": "^0.12.0", 55 | "load-json-file": "^5.1.0", 56 | "log": "^1.4.0", 57 | "log4js": "^6.1.0", 58 | "mocha": "^6.1.4", 59 | "msgpack-lite": "^0.1.26", 60 | "openzeppelin-solidity": "2.1", 61 | "package.json": "^2.0.1", 62 | "peer-info": "^0.14.1", 63 | "pick-random": "^2.0.0", 64 | "randomatic": "^3.1.0", 65 | "readline": "^1.3.0", 66 | "retry": "^0.12.0", 67 | "rimraf": "^2.6.2", 68 | "unix-timestamp": "^0.2.0", 69 | "web3": "1.0.0-beta.37", 70 | "web3-utils": "1.0.0-beta.37", 71 | "zeromq": "^5.1.0", 72 | "date-format": "^3.0.0" 73 | }, 74 | "devDependencies": { 75 | "axios": "^0.18.1", 76 | "body-parser": "^1.18.3", 77 | "capture-console": "^1.0.1", 78 | "codecov": "^3.1.0", 79 | "connect": "^3.6.6", 80 | "eslint": "^6.7.2", 81 | "eslint-config-prettier": "^6.7.0", 82 | "eslint-plugin-prettier": "^3.1.2", 83 | "prettier": "^1.19.1", 84 | "tempdir": "^2.0.0", 85 | "truffle": "^5.1.1", 86 | "ws": "^7.0.0" 87 | }, 88 | "contributors": [ 89 | "Isan Rivkin ", 90 | "Lena Kleyner ", 91 | "Elichai Turkel ", 92 | "Avishai Weingarten ", 93 | "Victor Grau Serrat ", 94 | "Aditya Palepu ", 95 | "Assaf Morami " 96 | ] 97 | } 98 | -------------------------------------------------------------------------------- /scripts/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | git clone https://github.com/enigmampc/discovery-docker-network.git 3 | cd discovery-docker-network/enigma-p2p 4 | 5 | if [[ ${TRAVIS_BRANCH} == "master" ]]; then 6 | TAG=latest 7 | else 8 | # ${TRAVIS_BRANCH} == "develop" 9 | TAG=develop 10 | fi 11 | 12 | docker build --build-arg GIT_BRANCH_P2P=$TRAVIS_BRANCH -t enigmampc/enigma_p2p:${TAG} --no-cache . 13 | echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin 14 | docker push enigmampc/enigma_p2p:${TAG} 15 | -------------------------------------------------------------------------------- /scripts/test_tree_validation.js: -------------------------------------------------------------------------------- 1 | const tree = require("../test/test_tree").TEST_TREE; 2 | let topLevelKeys = Object.keys(tree); 3 | let shouldPanic = false; 4 | topLevelKeys.forEach(tk => { 5 | if (tree[tk].all === false) { 6 | console.log(`[ERROR] test_tree param ${tk} not true`); 7 | shouldPanic = true; 8 | } 9 | let localKeys = Object.keys(tk); 10 | localKeys.forEach(lk => { 11 | if (tree[tk]["#" + lk] === false) { 12 | console.log(`[ERROR] test_tree param ${tk}.#${lk} not true`); 13 | shouldPanic = true; 14 | } 15 | }); 16 | }); 17 | 18 | if (shouldPanic) { 19 | process.exit(1); 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/Parsers.js: -------------------------------------------------------------------------------- 1 | module.exports.list = function(val, params) { 2 | const parseVal = val.split(","); 3 | parseVal.forEach(ma => { 4 | let toReplace = ""; 5 | let val = ""; 6 | if (ma === "B1") { 7 | val = "B1"; 8 | toReplace = params.B1Addr; 9 | } 10 | if (ma === "B2") { 11 | val = "B2"; 12 | toReplace = params.B2Addr; 13 | } 14 | 15 | const idx = parseVal.indexOf(val); 16 | parseVal[idx] = toReplace; 17 | }); 18 | params.configObject.bootstrapNodes = parseVal; 19 | params.changedKeys.push("bootstrapNodes"); 20 | return parseVal; 21 | }; 22 | 23 | module.exports.nickname = function(val, params) { 24 | const parsedVal = val.toString(); 25 | params.configObject.nickname = parsedVal; 26 | params.changedKeys.push("nickname"); 27 | return parsedVal; 28 | }; 29 | 30 | module.exports.port = function(val, params) { 31 | let parseVal = val.toString(); 32 | if (parseVal === "B1") { 33 | parseVal = params.B1Port; 34 | } 35 | if (parseVal === "B2") { 36 | parseVal = params.B2Port; 37 | } 38 | params.configObject.port = parseVal; 39 | params.changedKeys.push("port"); 40 | return parseVal; 41 | }; 42 | 43 | module.exports.idPath = function(val, params) { 44 | let parsedVal = val.toString(); 45 | if (parsedVal === "B1") { 46 | parsedVal = params.B1Path; 47 | } 48 | if (parsedVal === "B2") { 49 | parsedVal = params.B2Path; 50 | } 51 | 52 | params.configObject.idPath = parsedVal; 53 | params.changedKeys.push("idPath"); 54 | return parsedVal; 55 | }; 56 | 57 | // module.exports.enigmaContractAddress = function(val,params) { 58 | // params.configObject.enigmaContractAddress = val; 59 | // params.changedKeys.push('enigmaContractAddress'); 60 | // return val; 61 | // }; 62 | 63 | module.exports.opener = 64 | " ______ _ _____ ___ _____ \n" + 65 | " | ____| (_) | __ \\__ \\| __ \\ \n" + 66 | " | |__ _ __ _ __ _ _ __ ___ __ _ | |__) | ) | |__) |\n" + 67 | " | __| | '_ \\| |/ _` | '_ ` _ \\ / _` | | ___/ / /| ___/ \n" + 68 | " | |____| | | | | (_| | | | | | | (_| | | | / /_| | \n" + 69 | " |______|_| |_|_|\\__, |_| |_| |_|\\__,_| |_| |____|_| \n" + 70 | " __/ | \n" + 71 | " |___/ "; 72 | -------------------------------------------------------------------------------- /src/common/CIDUtil.js: -------------------------------------------------------------------------------- 1 | const CID = require("cids"); 2 | const multihash = require("multihashes"); 3 | const Web3 = require("web3"); 4 | 5 | class CIDUtil { 6 | /** 7 | * The hashing function that is used currently is hashKeccack256 but there is not reason it cannot change. 8 | * @param {Array} delta 9 | * @return {string} hash 10 | * */ 11 | static hashByteArray(delta) { 12 | return CIDUtil.hashKeccack256(delta); 13 | } 14 | static hashKeccack256(value) { 15 | return new Web3().utils.sha3(value); 16 | } 17 | 18 | /** cast Ethereum keccack256 function into a CID 19 | * @param {String} ethHash, with 0x len 66, 64 without 0x, both inputs are fine 20 | * @return {CID} cid representing the input. 21 | * */ 22 | static createCID(ethHash) { 23 | try { 24 | const h = CIDUtil.parseHashStr(ethHash); 25 | const buffHash = Buffer.from(h, "hex"); 26 | const mh = multihash.encode(buffHash, "keccak-256"); 27 | return new CID(1, "eth-block", mh, "base58btc"); 28 | } catch (err) { 29 | // console.log('[-] err creating cid {%s}', err); 30 | return null; 31 | } 32 | } 33 | 34 | static createCIDFromB58(b58CID) { 35 | try { 36 | return new CID(b58CID); 37 | } catch (err) { 38 | return null; 39 | } 40 | } 41 | 42 | /** remove 0x from the hash if existing 43 | * @param {String} h, keccack256 hash 44 | * @return {String} hash without 0x or the same 45 | * */ 46 | static parseHashStr(h) { 47 | let final = null; 48 | if (h.length == 64) { 49 | final = h; 50 | } else if (h.length == 66) { 51 | final = h.substring(2, h.length); 52 | } 53 | return final; 54 | } 55 | 56 | static getKeccak256FromCID(cid) { 57 | try { 58 | return multihash.toHexString(cid.multihash); 59 | } catch (err) { 60 | return null; 61 | } 62 | } 63 | 64 | static isValidCID(cid) { 65 | return CID.isCID(cid); 66 | } 67 | } 68 | 69 | module.exports = CIDUtil; 70 | -------------------------------------------------------------------------------- /src/common/DbUtils.js: -------------------------------------------------------------------------------- 1 | const Web3 = require("web3"); 2 | 3 | class DbUtils { 4 | static toHexString(byteArray) { 5 | return Array.from(byteArray, function(byte) { 6 | return ("0" + (byte & 0xff).toString(16)).slice(-2); 7 | }).join(""); 8 | } 9 | 10 | static hexToBytes(hex) { 11 | if (hex.slice(0, 2) === "0x") { 12 | hex = hex.slice(2, hex.length); 13 | } 14 | const b = Buffer.from(hex, "hex"); 15 | return [...b]; 16 | } 17 | 18 | static intTo4BytesArr(num) { 19 | if (num > 4294967295) { 20 | throw new Error("integer overflow"); 21 | } 22 | const arr = new Uint8Array([ 23 | (num & 0xff000000) >> 24, 24 | (num & 0x00ff0000) >> 16, 25 | (num & 0x0000ff00) >> 8, 26 | num & 0x000000ff 27 | ]); 28 | return Array.from(arr); 29 | } 30 | 31 | static bytesArrToInt(bytesArr) { 32 | const buf = Buffer.from(bytesArr); 33 | return buf.readUInt32BE(0); 34 | } 35 | 36 | static deltaKeyBytesToTuple(byteKey) { 37 | let addr = byteKey.slice(0, byteKey.length - 4); 38 | addr = DbUtils.toHexString(addr); 39 | let index = byteKey.slice(byteKey.length - 4, byteKey.length); 40 | index = DbUtils.bytesArrToInt(index); 41 | return { address: addr, index: index }; 42 | } 43 | static toBytesKey(contractByteAddr, index) { 44 | const res = []; 45 | contractByteAddr.forEach(c => { 46 | res.push(c); 47 | }); 48 | if (index >= 0) { 49 | const indexBytes = DbUtils.intTo4BytesArr(index); 50 | indexBytes.forEach(c => { 51 | res.push(c); 52 | }); 53 | } 54 | return res; 55 | } 56 | static isValidEthereumAddress(address) { 57 | let web3 = new Web3(); 58 | return web3.utils.isAddress(address); 59 | } 60 | } 61 | 62 | module.exports = DbUtils; 63 | -------------------------------------------------------------------------------- /src/common/EncoderUtil.js: -------------------------------------------------------------------------------- 1 | const msgpack = require("msgpack-lite"); 2 | 3 | class EncoderUtil { 4 | static encode(rawObject) { 5 | try { 6 | return msgpack.encode(rawObject); 7 | } catch (e) { 8 | return null; 9 | } 10 | } 11 | static encodeToNetwork(rawObject) { 12 | const encodedBuffer = EncoderUtil.encode(rawObject); 13 | if (encodedBuffer) { 14 | const encodedByteArray = [...encodedBuffer]; 15 | return encodedByteArray; 16 | } 17 | return null; 18 | } 19 | static decode(encodedBuffer) { 20 | try { 21 | return msgpack.decode(encodedBuffer); 22 | } catch (e) { 23 | return null; 24 | } 25 | } 26 | static decodeFromNetwork(encodedBytes) { 27 | const encodedBuffer = Buffer.from(encodedBytes); 28 | const decoded = EncoderUtil.decode(encodedBuffer); 29 | return decoded; 30 | } 31 | static hexToAscii(hex) { 32 | if (!(typeof hex === "number" || typeof hex == "string")) { 33 | return ""; 34 | } 35 | hex = hex.toString().replace(/\s+/gi, ""); 36 | const stack = []; 37 | for (let n = 0; n < hex.length; n += 2) { 38 | const code = parseInt(hex.substr(n, 2), 16); 39 | if (!isNaN(code) && code !== 0) { 40 | stack.push(String.fromCharCode(code)); 41 | } 42 | } 43 | return stack.join(""); 44 | } 45 | } 46 | 47 | module.exports = EncoderUtil; 48 | 49 | /** * mini tests */ 50 | // // take input from Core (encoded) and compare the output 51 | // 52 | // let fromCore = [146, 129, 164, 110, 97, 109, 101, 166, 65, 110, 100, 114, 101, 53 | // 119, 129, 164, 110, 97, 109, 101, 165, 77, 97, 120, 105, 109]; 54 | // 55 | // let j = EncoderUtil.decodeFromNetwork(fromCore); 56 | // 57 | // console.log(j); 58 | // // take output obj from network and decode compare with core decoded 59 | // 60 | // let toCore = [{"name":"Andrew"},{"name":"Maxim"}]; 61 | // 62 | // let encoded= EncoderUtil.encodeToNetwork(toCore); 63 | // 64 | // console.log(encoded); 65 | -------------------------------------------------------------------------------- /src/common/EngCID.js: -------------------------------------------------------------------------------- 1 | const CIDUtil = require("./CIDUtil"); 2 | const EncoderUtil = require("./EncoderUtil"); 3 | 4 | /** 5 | * Abstraction - wrapper to libp2p-cid 6 | * */ 7 | class EngCID { 8 | constructor(encoder = EncoderUtil) { 9 | this._encoder = encoder; 10 | this._cid = null; 11 | this._address = null; 12 | } 13 | static createFromByteArray(bytes) { 14 | const h = CIDUtil.hashByteArray(bytes); 15 | return this.createFromKeccack256(h); 16 | } 17 | static createFromSCAddress(scAddr) { 18 | const cid = CIDUtil.createCID(scAddr); 19 | if (cid) { 20 | const engCid = new EngCID(); 21 | engCid._setCID(cid); 22 | engCid._setScAddress(scAddr); 23 | return engCid; 24 | } 25 | return null; 26 | } 27 | static createFromKeccack256(keccack256Hash) { 28 | const cid = CIDUtil.createCID(keccack256Hash); 29 | if (cid) { 30 | const engCid = new EngCID(); 31 | engCid._setCID(cid); 32 | return engCid; 33 | } 34 | return null; 35 | } 36 | static createFromNetwork(encodedB58byteArray) { 37 | const b58 = EncoderUtil.decodeFromNetwork(encodedB58byteArray); 38 | const cid = CIDUtil.createCIDFromB58(b58); 39 | if (cid) { 40 | const engCID = new EngCID(); 41 | engCID._setCID(cid); 42 | return engCID; 43 | } 44 | return null; 45 | } 46 | 47 | getCID() { 48 | return this._cid; 49 | } 50 | 51 | /** get the keccack256 hash of a CID 52 | * @param {Boolean} with0x , if true then add 0x to the result 53 | * @return {String} h, a keccak hash representation 54 | * */ 55 | getKeccack256(with0x = false) { 56 | const h = CIDUtil.getKeccak256FromCID(this._cid); 57 | if (with0x) { 58 | return "0x" + h; 59 | } 60 | return h; 61 | } 62 | 63 | toBuffer() { 64 | return this._cid.buffer; 65 | } 66 | 67 | toB58String() { 68 | return this._cid.toBaseEncodedString(); 69 | } 70 | 71 | /** Compare if this and other are equal 72 | * @param {CID} cid - other cid to test 73 | * @return {Boolean} true - this.cid == cid , false otherwise*/ 74 | equalCID(cid) { 75 | return this._cid.equals(cid); 76 | } 77 | 78 | equalKeccack256(keccackHash) { 79 | const cid = CIDUtil.createCID(keccackHash); 80 | if (cid) { 81 | return this.equalCID(cid); 82 | } 83 | return false; 84 | } 85 | 86 | equalEngCID(engCID) { 87 | if (engCID.constructor.name === "EngCID") { 88 | return this.equalCID(engCID.getCID()); 89 | } 90 | return false; 91 | } 92 | 93 | /** Encode the CID into a network stream. 94 | * Steps: 95 | * 1) b58Str = this.cid 96 | * 2) e = encode(b58Str), encode with encoder util, currently msgpack 97 | * 3) b = toBytes(e) 98 | * 4) return b 99 | * @return {Array} 100 | * */ 101 | encodeToNetwork() { 102 | return this._encoder.encodeToNetwork(this.toB58String()); 103 | } 104 | 105 | _setCID(cid) { 106 | this._cid = cid; 107 | } 108 | _setScAddress(scAddr) { 109 | this._address = scAddr; 110 | } 111 | getScAddress() { 112 | return this._address; 113 | } 114 | } 115 | 116 | module.exports = EngCID; 117 | 118 | // /** examples */ 119 | // 120 | // let eth = '0xe8a5770e2c3fa1406d8554a6539335f5d4b82ed50f442a6834149d9122e7f8af'; 121 | // let eng = EngCID.createFromKeccack256(eth); 122 | // 123 | // let eth2 = 'e8a5770e2c3fa1406d8554a6539335f5d4b82ed50f442a6834149d9122e7f8af'; 124 | // let eng2 = EngCID.createFromKeccack256(eth2); 125 | // let otherCid = new CID(eng2.toB58String()); 126 | // console.log("generated " + eng2.equalCID(otherCid)); 127 | // console.log(eng.toB58String()); 128 | // console.log(eng.toBuffer()); 129 | // console.log(eng.getKeccack256()); 130 | // 131 | // console.log(eng.equalCID(eng2.getCID())); 132 | // console.log(eng.equalKeccack256(eth2)); 133 | // console.log(eng.equalEngCID(eng2)); 134 | // //network encoding this 135 | // let fromNetwork = eng.encodeToNetwork(); 136 | // let newCID = EngCID.createFromNetwork(fromNetwork); 137 | // console.log(eng.equalEngCID(newCID)); 138 | -------------------------------------------------------------------------------- /src/common/StoppableTask.js: -------------------------------------------------------------------------------- 1 | class StoppableTask { 2 | constructor(options, task, onFinish) { 3 | this._timeout = null; 4 | this._maxRetry = null; 5 | this._taskInput = null; 6 | this._delay = 0; 7 | 8 | this._onFinish = onFinish; 9 | 10 | if (options.timeout) { 11 | this._timeout = options.timeout; 12 | } 13 | if (options.maxRetry) { 14 | this._maxRetry = options.maxRetry; 15 | } 16 | if (options.taskInput) { 17 | this._taskInput = options.taskInput; 18 | } 19 | 20 | if (options.delay) { 21 | this._delay = options.delay; 22 | } 23 | 24 | this._tryCount = -1; 25 | this._stop = false; 26 | this._task = task; 27 | this._timerId = null; 28 | } 29 | 30 | start() { 31 | if (this._timeout) { 32 | this._timerId = setTimeout(() => { 33 | this._stop = true; 34 | }, this._timeout); 35 | } 36 | 37 | if (this._tryCount > -1) { 38 | this._tryCount = 0; 39 | } 40 | 41 | this._execute(); 42 | } 43 | _execute() { 44 | setTimeout(() => { 45 | if (this._taskInput) { 46 | this._task(this._taskInput, this); 47 | } else { 48 | this._task(this); 49 | } 50 | }, this._delay); 51 | } 52 | done(status, result) { 53 | if (status.success) { 54 | this._finish(status, result); 55 | } else { 56 | if (this._shouldStop()) { 57 | this._finish(status, result); 58 | } else { 59 | this._tryCount += 1; 60 | this._execute(); 61 | } 62 | } 63 | } 64 | _finish(status, result) { 65 | clearTimeout(this._timerId); 66 | this._onFinish(status, result); 67 | } 68 | 69 | _shouldStop() { 70 | let shouldStop = false; 71 | 72 | if (this._maxRetry != null) { 73 | if (this._tryCount >= this._maxRetry) { 74 | shouldStop = true; 75 | } 76 | } 77 | 78 | return this._stop || shouldStop; 79 | } 80 | } 81 | 82 | module.exports = StoppableTask; 83 | 84 | // let job = (params, stopper)=>{ 85 | // console.log("i run " + params.val); 86 | // stopper.done({"success" : false} , 42); 87 | // }; 88 | // 89 | // let onFinish = (status, result)=>{ 90 | // if(status.success){ 91 | // console.log("yay success " + result); 92 | // }else{ 93 | // console.log("failed!"); 94 | // } 95 | // }; 96 | // 97 | // let task = new StoppableTask({ 98 | // "maxRetry" : 2, 99 | // "timeout" : 2000, 100 | // "taskInput" : {"val" : 11}, 101 | // "delay" : 500, 102 | // },job,onFinish); 103 | // 104 | // task.start(); 105 | -------------------------------------------------------------------------------- /src/common/cryptography.js: -------------------------------------------------------------------------------- 1 | const web3Utils = require("web3-utils"); 2 | const errors = require("./errors"); 3 | const JSBI = require("jsbi"); 4 | const utils = require("../common/utils"); 5 | 6 | /** 7 | * hash parameters in order 8 | * @param {Array} value of byte arrrays 9 | * @param {boolean} with0x optional defaults to true 10 | * @return {string} hash 11 | * */ 12 | module.exports.hash = (value, with0x = true) => { 13 | let h = _keccack256Hash(value); 14 | if (!with0x && h.length > 2 && h.slice(0, 2) === "0x") { 15 | h = h.substr(2); 16 | } 17 | return h; 18 | }; 19 | 20 | /** 21 | * Hash parameters in order, mimicking the way solidity is doing that 22 | * The function receives any number of parameters 23 | * @return {string} hash 24 | * */ 25 | module.exports.soliditySha3 = function() { 26 | return web3Utils.soliditySha3.apply(null, arguments); 27 | }; 28 | 29 | /** 30 | * Convert any given value to JSBI instance for handling big numbers 31 | * @param {String/Number/HEX} value to convert to BigNumber 32 | * @return {JSBI} converted value 33 | * */ 34 | module.exports.toBN = value => { 35 | return JSBI.BigInt(value); 36 | }; 37 | 38 | /** 39 | * Generate a hash of all inputs 40 | * The Enigma contract uses the same logic to generate a matching taskId 41 | * 42 | * @param {array} inputsArray 43 | * @return {string} hash of inputs 44 | */ 45 | module.exports.hashArray = inputsArray => { 46 | let hexStr = ""; 47 | for (let e of inputsArray) { 48 | e = utils.remove0x(e); 49 | // since the inputs are in hex string, they are twice as long as their bytes 50 | hexStr += 51 | JSBI.BigInt(e.length / 2) 52 | .toString(16) 53 | .padStart(16, "0") + e; 54 | } 55 | return web3Utils.soliditySha3({ t: "bytes", v: hexStr }); 56 | }; 57 | 58 | /** 59 | * internal 60 | * */ 61 | const _keccack256Hash = value => { 62 | return web3Utils.keccak256(Buffer.from(value, "hex")); 63 | }; 64 | -------------------------------------------------------------------------------- /src/common/logger.js: -------------------------------------------------------------------------------- 1 | const log4js = require("log4js"); 2 | const constants = require("./constants"); 3 | const LOG_CONFIG = constants.LOG_CONFIG; 4 | const format = require("date-format"); 5 | 6 | class Logger { 7 | constructor(options = {}) { 8 | const logName = options.hasOwnProperty("name") ? options.name : "Logger"; 9 | const logLevel = options.hasOwnProperty("level") ? options.level : LOG_CONFIG.level; 10 | 11 | log4js.configure({ 12 | appenders: { 13 | file: { 14 | type: "file", 15 | filename: LOG_CONFIG.file, 16 | maxLogSize: 10 * 1024 * 1024, // = 10Mb 17 | backups: 5, // keep five backup files 18 | compress: true, // compress the backups 19 | encoding: "utf-8", 20 | mode: 0o0640, 21 | flags: "w+" 22 | }, 23 | out: { 24 | type: "stdout", 25 | layout: { 26 | type: "pattern", 27 | pattern: "%x{getTime}Z %[%p%] [P2P-%c] - %m", 28 | tokens: { 29 | getTime: function(logEvent) { 30 | return format.asString("yyyy-MM-ddThh:mm:ss", new Date(new Date().toUTCString().slice(0, -4))); 31 | } 32 | } 33 | } 34 | }, 35 | err: { 36 | type: "stderr", 37 | layout: { 38 | type: "pattern", 39 | pattern: "%x{getTime}Z %[%p%] [P2P-%c] - %m", 40 | tokens: { 41 | getTime: function(logEvent) { 42 | return format.asString("yyyy-MM-ddThh:mm:ss", new Date(new Date().toUTCString().slice(0, -4))); 43 | } 44 | } 45 | } 46 | }, 47 | cli: { 48 | type: "stdout", 49 | layout: { 50 | type: "pattern", 51 | pattern: "%x{getTime}Z [CLI] %m", 52 | tokens: { 53 | getTime: function(logEvent) { 54 | return format.asString("yyyy-MM-ddThh:mm:ss", new Date(new Date().toUTCString().slice(0, -4))); 55 | } 56 | } 57 | } 58 | } 59 | }, 60 | categories: { 61 | [logName]: { appenders: ["file", "out"], level: logLevel, enableCallStack: true }, 62 | cli: { appenders: ["cli"], level: "info" }, 63 | default: { appenders: ["err"], level: "info" } 64 | } 65 | }); 66 | this.logger = log4js.getLogger(logName); 67 | } 68 | debug(content) { 69 | this.logger.debug(content); 70 | } 71 | info(content) { 72 | this.logger.info(content); 73 | } 74 | error(content) { 75 | this.logger.error(content); 76 | } 77 | warning(content) { 78 | this.logger.warn(content); 79 | } 80 | fatal(content) { 81 | this.logger.fatal(content); 82 | } 83 | trace(content) { 84 | this.logger.trace(content); 85 | } 86 | } 87 | 88 | module.exports = Logger; 89 | -------------------------------------------------------------------------------- /src/core/actions/DbRead/GetDbAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../../common/utils"); 3 | const Msg = require("../../../common/constants").CORE_REQUESTS; 4 | 5 | class GetDbAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | 10 | execute(envelop) { 11 | const request = { 12 | id: envelop.content().id, 13 | type: envelop.content().type, 14 | input: envelop.content().input 15 | }; 16 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 17 | envelop: envelop, 18 | sendMsg: request 19 | }); 20 | // let client = this._coreRuntime.getIpcClient(); 21 | // client.sendJsonAndReceive(,(responseMsg)=>{ 22 | // const resEnv = new Envelop(envelop.id(),responseMsg, envelop.type()); 23 | // this._coreRuntime.getCommunicator() 24 | // .send(resEnv); 25 | // }); 26 | } 27 | } 28 | module.exports = GetDbAction; 29 | -------------------------------------------------------------------------------- /src/core/actions/DbWrite/UpdateDbAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const constants = require("../../../common/constants"); 3 | const nodeUtils = require("../../../common/utils"); 4 | 5 | class UpdateDbAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | static _buildRequest(msgObj) { 10 | let request = { 11 | id: nodeUtils.randId(), 12 | type: null 13 | }; 14 | // from topic published - already parsed 15 | if (msgObj.type && typeof msgObj.type !== "function") { 16 | return msgObj; 17 | } 18 | // from sync process 19 | if (msgObj.type() === constants.P2P_MESSAGES.SYNC_STATE_RES) { 20 | request.type = constants.CORE_REQUESTS.UpdateDeltas; 21 | request.deltas = msgObj.deltas(); 22 | } else if (msgObj.type() === constants.P2P_MESSAGES.SYNC_BCODE_RES) { 23 | request.type = constants.CORE_REQUESTS.UpdateNewContract; 24 | request.address = msgObj.address(); 25 | request.bytecode = msgObj.bytecode(); 26 | } 27 | if (request.type) return request; 28 | return null; 29 | } 30 | execute(envelop) { 31 | /***/ 32 | let request = UpdateDbAction._buildRequest(envelop.content().input); 33 | this._coreRuntime.execCmd(constants.CORE_REQUESTS.CORE_DB_ACTION, { 34 | envelop: envelop, 35 | sendMsg: request 36 | }); 37 | /***/ 38 | } 39 | } 40 | module.exports = UpdateDbAction; 41 | -------------------------------------------------------------------------------- /src/core/actions/PreParseAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../common/utils"); 3 | const Msg = require("../../common/constants").CORE_REQUESTS; 4 | 5 | class PreParseAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | execute(envelop) { 10 | let request = envelop.content(); 11 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 12 | envelop: envelop, 13 | sendMsg: request 14 | }); 15 | } 16 | } 17 | module.exports = PreParseAction; 18 | -------------------------------------------------------------------------------- /src/core/actions/SendToCoreAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../main_controller/channels/Envelop"); 2 | 3 | class SendToCoreAction { 4 | constructor(coreRuntime) { 5 | this._coreRuntime = coreRuntime; 6 | } 7 | execute(params) { 8 | const sendMsg = params.sendMsg; 9 | const envelop = params.envelop; 10 | const client = this._coreRuntime.getIpcClient(); 11 | if (!sendMsg.id) { 12 | sendMsg.id = envelop.id(); 13 | } 14 | client 15 | .sendJsonAndReceive(sendMsg, (err, responseMsg) => { 16 | if (err) { 17 | console.error(`[Error] Failed in Send JSON And Receive: ${err}`); 18 | responseMsg = { error: err }; 19 | } 20 | const resEnv = new Envelop(envelop.id(), responseMsg, envelop.type()); 21 | this._coreRuntime.getCommunicator().send(resEnv); 22 | }) 23 | .catch(console.error); 24 | } 25 | } 26 | module.exports = SendToCoreAction; 27 | -------------------------------------------------------------------------------- /src/core/actions/deprecated/GetAllAddrsAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../../common/utils"); 3 | const Msg = require("../../../common/constants").CORE_REQUESTS; 4 | 5 | class GetAllAddrsAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | execute(envelop) { 10 | /***/ 11 | let request = { 12 | id: nodeUtils.randId(), 13 | type: Msg.GetAllAddrs 14 | }; 15 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 16 | envelop: envelop, 17 | sendMsg: request 18 | }); 19 | /***/ 20 | } 21 | } 22 | module.exports = GetAllAddrsAction; 23 | -------------------------------------------------------------------------------- /src/core/actions/deprecated/GetContractCodeAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../../common/utils"); 3 | const Msg = require("../../../common/constants").CORE_REQUESTS; 4 | 5 | class GetContractCodeAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | execute(envelop) { 10 | /***/ 11 | let request = { 12 | id: nodeUtils.randId(), 13 | type: Msg.GetContract, 14 | input: envelop.content().input 15 | }; 16 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 17 | envelop: envelop, 18 | sendMsg: request 19 | }); 20 | /***/ 21 | } 22 | } 23 | module.exports = GetContractCodeAction; 24 | -------------------------------------------------------------------------------- /src/core/actions/deprecated/GetDeltasAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../../common/utils"); 3 | const Msg = require("../../../common/constants").CORE_REQUESTS; 4 | 5 | class GetDeltasAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | execute(envelop) { 10 | /***/ 11 | let request = { 12 | id: nodeUtils.randId(), 13 | type: Msg.GetDeltas, 14 | input: envelop.content().input 15 | }; 16 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 17 | envelop: envelop, 18 | sendMsg: request 19 | }); 20 | /***/ 21 | } 22 | } 23 | module.exports = GetDeltasAction; 24 | -------------------------------------------------------------------------------- /src/core/actions/deprecated/NewTaskEncryptionKeyAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../../../main_controller/channels/Envelop"); 2 | const nodeUtils = require("../../../common/utils"); 3 | const Msg = require("../../../common/constants").CORE_REQUESTS; 4 | 5 | class NewTaskEncryptionKeyAction { 6 | constructor(coreRuntime) { 7 | this._coreRuntime = coreRuntime; 8 | } 9 | execute(envelop) { 10 | let request = envelop.content(); 11 | this._coreRuntime.execCmd(Msg.CORE_DB_ACTION, { 12 | envelop: envelop, 13 | sendMsg: request 14 | }); 15 | } 16 | } 17 | module.exports = NewTaskEncryptionKeyAction; 18 | -------------------------------------------------------------------------------- /src/core/core_server_mock/dataGenerator.js: -------------------------------------------------------------------------------- 1 | // DB = { 2 | // 'address' : { 3 | // bytecode : [0,0,0,0,0,...] 4 | // delta : [{ 5 | // index : 11, 6 | // data : [243,56,66758,657876] 7 | // }] 8 | // } 9 | // } 10 | 11 | const DbUtil = require("../../common/DbUtils"); 12 | const ADDR_SIZE = 32; 13 | const BCODE_SIZE = 1500; 14 | const DELTA_SIZE = 450; 15 | 16 | const fs = require("fs"); 17 | 18 | module.exports.appendToFile = (path, file) => { 19 | return new Promise((res, rej) => { 20 | _appendToFile(path, file, err => { 21 | if (err) { 22 | rej(err); 23 | } else { 24 | res(); 25 | } 26 | }); 27 | }); 28 | }; 29 | function _appendToFile(path, file, callback) { 30 | fs.appendFile(path, file, function(err) { 31 | callback(err); 32 | }); 33 | } 34 | function getRandomInt(max) { 35 | return Math.floor(Math.random() * Math.floor(max)); 36 | } 37 | function generateHexAddress() { 38 | let byteAddr = []; 39 | for (let i = 0; i < ADDR_SIZE; ++i) { 40 | byteAddr.push(getRandomInt(255)); 41 | } 42 | return DbUtil.toHexString(byteAddr); 43 | } 44 | function generateByteCode() { 45 | let byteCode = []; 46 | for (let i = 0; i < BCODE_SIZE; ++i) { 47 | byteCode.push(getRandomInt(255)); 48 | } 49 | return byteCode; 50 | } 51 | function generateDelta() { 52 | let byteDelta = []; 53 | for (let i = 0; i < DELTA_SIZE; ++i) { 54 | byteDelta.push(getRandomInt(255)); 55 | } 56 | return byteDelta; 57 | } 58 | // generate a database 59 | function generateData(contractsNum, deltasNum) { 60 | let db = {}; 61 | for (let i = 0; i < contractsNum; ++i) { 62 | let contract = {}; 63 | contract.address = generateHexAddress(); 64 | contract.bytecode = generateByteCode(); 65 | contract.deltas = []; 66 | for (let j = 0; j < deltasNum; ++j) { 67 | let delta = { 68 | index: j, 69 | data: generateDelta() 70 | }; 71 | contract.deltas.push(delta); 72 | } 73 | db[contract.address] = contract; 74 | } 75 | return db; 76 | } 77 | // generate partial database from a given database 78 | function generatePartialData(db, contractsNum, deltasNum) { 79 | let newDb = {}; 80 | let addresses = Object.keys(db); 81 | for (let i = 0; i < contractsNum; i++) { 82 | let contract = db[addresses[i]]; 83 | let newDeltas = []; 84 | for (let j = 0; j < Math.min(deltasNum, contract.deltas.length); ++j) { 85 | newDeltas.push(contract.deltas[j]); 86 | } 87 | newDb[contract.address] = contract; 88 | newDb[contract.address].deltas = newDeltas; 89 | } 90 | return newDb; 91 | } 92 | 93 | /** how to generate data && save to file */ 94 | // let db = generateData(3, 3); 95 | //let file = 'module.exports.DB_PROVIDER=' + JSON.stringify(db); 96 | // const fs = require('fs'); 97 | // 98 | // fs.writeFile('./here.js', file, function(err) { 99 | // if (err) { 100 | // return console.log(err); 101 | // } 102 | // console.log('The file was saved!'); 103 | // }); 104 | /** how to load the db*/ 105 | // let db2 = require('./here'); 106 | // console.log(db2.DB_PROVIDER); 107 | 108 | /** how to generate a partial db from a given db */ 109 | // let db = generateData(3,3); 110 | // let newDb = generatePartialData(db,2,1); 111 | -------------------------------------------------------------------------------- /src/db/DbKey.js: -------------------------------------------------------------------------------- 1 | const DbUtils = require("../common/DbUtils"); 2 | 3 | class DbKey { 4 | constructor(byteKey, hexAddr, idx) { 5 | this._byteKey = byteKey; 6 | this._hexAddr = hexAddr; 7 | this._idx = idx; 8 | this._isContract = true; 9 | if (idx) { 10 | this._isContract = false; 11 | } 12 | if (this._hexAddr && this._hexAddr.slice(0, 2) === "0x") { 13 | this._hexAddr = this._hexAddr.slice(2); 14 | } 15 | } 16 | /** key builder 17 | * create a key pointing to bytecode 18 | * represented by addr only 19 | * @param {String} addr, secret-contract address 20 | * @return {DbKey} dbKey 21 | */ 22 | static fromContractAddr(addr) { 23 | const byteAddr = DbUtils.hexToBytes(addr); 24 | return new DbKey(byteAddr, addr); 25 | } 26 | /** key builder 27 | * create a key pointing to bytecode 28 | * represented by addr only 29 | * @param {Array} byteAddr, secret contract address 30 | * @return {DbKey} dbKey 31 | */ 32 | static fromContractBytes(byteAddr) { 33 | const addr = DbUtils.toHexString(byteAddr); 34 | return new DbKey(byteAddr, addr); 35 | } 36 | /** key builder 37 | * @param {String} addr, secret-contract address 38 | * @param {Integer} idx , state delta id 39 | * @return {DbKey} dbKey 40 | */ 41 | static fromDeltaTouple(addr, idx) { 42 | const byteAddr = DbUtils.hexToBytes(addr); 43 | const byteKey = DbUtils.toBytesKey(byteAddr, idx); 44 | return new DbKey(byteKey, addr, idx); 45 | } 46 | /** key builder 47 | * @param {Array} byteKey 48 | * @return {DbKey} 49 | */ 50 | static fromDeltaBytes(byteKey) { 51 | const tuple = DbUtils.deltaKeyBytesToTuple(byteKey); 52 | return new DbKey(byteKey, tuple.address, tuple.index); 53 | } 54 | isContract() { 55 | return this._isContract; 56 | } 57 | isDelta() { 58 | return !this.isContract(); 59 | } 60 | getIndex() { 61 | return this._idx; 62 | } 63 | getAddress() { 64 | return this._hexAddr; 65 | } 66 | getBytesKey() { 67 | return this._byteKey; 68 | } 69 | equals(otherDbKey) { 70 | if (this.isContract() && otherDbKey.isContract()) { 71 | const equal = this.getAddress() === otherDbKey.getAddress(); 72 | return equal; 73 | } else if (this.isDelta() && otherDbKey.isDelta()) { 74 | const equal = this.getAddress() === otherDbKey.getAddress(); 75 | return equal && this.getIndex() === otherDbKey.getIndex(); 76 | } else { 77 | return false; 78 | } 79 | } 80 | } 81 | 82 | module.exports = DbKey; 83 | -------------------------------------------------------------------------------- /src/ethereum/EthereumAPI.js: -------------------------------------------------------------------------------- 1 | const EthereumServices = require("./EthereumServices"); 2 | const EthereumVerifier = require("./EthereumVerifier"); 3 | const EnigmaContractAPIBuilder = require("./EnigmaContractAPIBuilder"); 4 | const utils = require("../common/utils"); 5 | 6 | class EthereumAPI { 7 | constructor(logger) { 8 | this._logger = logger; 9 | this._environment = null; 10 | this._url = null; 11 | this._enigmaContractAddress = null; 12 | this._api = null; 13 | this._verifier = null; 14 | this._services = null; 15 | } 16 | 17 | /** 18 | * check the connectivity to the Ethereum node 19 | * @param {JSON} config 20 | * {ethereumAddress - wallet address, 21 | * enigmaContractAddress - the contract address 22 | * ethereumUrlProvider - the network url 23 | * } 24 | * */ 25 | async init(config) { 26 | let builder = new EnigmaContractAPIBuilder(this._logger); 27 | let res = await builder.setConfigAndBuild(config); 28 | 29 | this._api = res.api; 30 | this._environment = res.environment; 31 | this._url = res.url; 32 | this._enigmaContractAddress = res.enigmaContractAddress; 33 | 34 | this._services = new EthereumServices(this._api); 35 | this._services.initServices(null); 36 | 37 | this._verifier = new EthereumVerifier(this._api, this._services, this._logger); 38 | await this._verifier.init(); 39 | } 40 | 41 | async destroy() { 42 | await this._environment.destroy(); 43 | } 44 | 45 | /** 46 | * check the connectivity to the Ethereum node 47 | * @return {JSON} 48 | * {isConnected - a flag describing the connectivity state, 49 | * blockNumber - Ethereum block number 50 | * url - the network url 51 | * enigmaContractAddress - the contract address 52 | * } 53 | * */ 54 | async healthCheck() { 55 | let connected = null; 56 | let blockNumber = null; 57 | 58 | try { 59 | blockNumber = await utils.getEthereumBlockNumber(this._api.w3()); 60 | connected = true; 61 | } catch (e) { 62 | this._logger.debug("Error received while trying to read Ethereum block number: " + e); 63 | connected = false; 64 | } 65 | 66 | return { 67 | isConnected: connected, 68 | blockNumber: blockNumber, 69 | url: this._url, 70 | enigmaContractAddress: this._enigmaContractAddress 71 | }; 72 | } 73 | 74 | api() { 75 | return this._api; 76 | } 77 | 78 | verifier() { 79 | return this._verifier; 80 | } 81 | 82 | services() { 83 | return this._services; 84 | } 85 | } 86 | 87 | module.exports = EthereumAPI; 88 | -------------------------------------------------------------------------------- /src/ethereum/EthereumServices.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require("events"); 2 | 3 | const constants = require("../common/constants"); 4 | 5 | let servicesMap = {}; 6 | 7 | servicesMap[constants.ETHEREUM_EVENTS.NewEpoch] = [constants.RAW_ETHEREUM_EVENTS.WorkersParameterized]; 8 | servicesMap[constants.ETHEREUM_EVENTS.TaskCreation] = [constants.RAW_ETHEREUM_EVENTS.TaskRecordCreated]; 9 | servicesMap[constants.ETHEREUM_EVENTS.TaskSuccessSubmission] = [constants.RAW_ETHEREUM_EVENTS.ReceiptVerified]; 10 | servicesMap[constants.ETHEREUM_EVENTS.TaskFailureSubmission] = [constants.RAW_ETHEREUM_EVENTS.ReceiptFailed]; 11 | servicesMap[constants.ETHEREUM_EVENTS.TaskFailureDueToEthereumCB] = [constants.RAW_ETHEREUM_EVENTS.ReceiptFailedETH]; 12 | servicesMap[constants.ETHEREUM_EVENTS.TaskCancelled] = [constants.RAW_ETHEREUM_EVENTS.TaskFeeReturned]; 13 | servicesMap[constants.ETHEREUM_EVENTS.SecretContractDeployment] = [ 14 | constants.RAW_ETHEREUM_EVENTS.SecretContractDeployed 15 | ]; 16 | 17 | class EthereumServices extends EventEmitter { 18 | /** 19 | * {EnigmaContractReaderAPI} enigmaContractAPI 20 | * 21 | * */ 22 | constructor(enigmaContractAPI) { 23 | super(); 24 | this._api = enigmaContractAPI; 25 | this._servicesMap = servicesMap; 26 | } 27 | 28 | /** 29 | * init services 30 | * @param {Array} desiredServices 31 | * */ 32 | initServices(desiredServices) { 33 | if (desiredServices !== undefined && desiredServices !== null) { 34 | desiredServices.forEach(service => { 35 | this._initService(service); 36 | }); 37 | } else { 38 | Object.keys(this._servicesMap).forEach(service => { 39 | this._initService(service); 40 | }); 41 | } 42 | } 43 | 44 | _initService(serviceName) { 45 | this._servicesMap[serviceName].forEach(eventName => { 46 | this._api.subscribe(eventName, {}, (err, event) => { 47 | if (err) { 48 | this.emit(serviceName, err); 49 | } else { 50 | event.type = serviceName; 51 | this.emit(serviceName, null, event); 52 | } 53 | }); 54 | }); 55 | } 56 | } 57 | 58 | module.exports = EthereumServices; 59 | -------------------------------------------------------------------------------- /src/ethereum/StateSync.js: -------------------------------------------------------------------------------- 1 | const DbUtils = require("../common/DbUtils"); 2 | 3 | /** 4 | * Queries the Enigma contract and returns the comparison between the local tips and the consensus states 5 | * @param {EnigmaContractReaderAPI} api 6 | * @param {Array} localTips [{address,key,delta},...}] 7 | * @return {Promise} returning a JSON: missingList - missing states [{address, deltas : [deltaHash, index]}]. 8 | * In case the entire contract is missing, the bytecodeHash is returned as well: 9 | * [{address, bytecodeHash , deltas : [deltaHash, index]}] 10 | * excessList - excessive states [{address, remoteTip, localTip]. 11 | * In case the entire contract is excessive, the remoteTip field is set to -1 12 | * */ 13 | 14 | async function compareLocalStateToRemote(api, localTips) { 15 | // create a hashmap from the localTips array 16 | return new Promise(async (resolve, reject) => { 17 | const tipsMap = localTips.reduce((obj, item) => { 18 | let address = item.address; 19 | if (typeof address !== "string") { 20 | address = DbUtils.toHexString(address); 21 | } 22 | obj[address] = item.key; 23 | return obj; 24 | }, {}); 25 | 26 | let missingList = []; 27 | let excessList = []; 28 | 29 | try { 30 | const remoteSecretContractsAddresses = await api.getAllSecretContractAddresses(); 31 | // First go over the remote secret contract tp compare state 32 | for (let secretContractAddress of remoteSecretContractsAddresses) { 33 | const contractData = await api.getContractParams(secretContractAddress); 34 | let missingAddress = false; 35 | let firstMissingIndex; 36 | let missingCodeHash; 37 | let missingDeltas = []; 38 | // get the local tip index, if exists; otherwise 0 39 | if (secretContractAddress in tipsMap) { 40 | firstMissingIndex = tipsMap[secretContractAddress] + 1; 41 | // we delete the secret contract from the tipsHash in order to check if there are excessive contracts locally (in the end of the loop) 42 | delete tipsMap[secretContractAddress]; 43 | } 44 | // the address does not exist at the local db, set the firstMissingIndex to 0 and request the codehash 45 | else { 46 | firstMissingIndex = 0; 47 | missingAddress = true; 48 | missingCodeHash = contractData.codeHash; 49 | } 50 | // check if the local state has left over deltas 51 | if (firstMissingIndex > contractData.deltaHashes.length) { 52 | excessList.push({ 53 | address: secretContractAddress, 54 | remoteTip: contractData.deltaHashes.length - 1, 55 | localTip: firstMissingIndex - 1 56 | }); 57 | } 58 | for (let i = firstMissingIndex; i < contractData.deltaHashes.length; i++) { 59 | missingDeltas.push({ 60 | deltaHash: contractData.deltaHashes[i], 61 | index: i 62 | }); 63 | } 64 | if (missingDeltas.length) { 65 | if (missingAddress === true) { 66 | missingList.push({ 67 | address: secretContractAddress, 68 | deltas: missingDeltas, 69 | bytecodeHash: missingCodeHash 70 | }); 71 | } else { 72 | missingList.push({ 73 | address: secretContractAddress, 74 | deltas: missingDeltas 75 | }); 76 | } 77 | } 78 | } 79 | // Now check that there are no excessive contracts locally 80 | for (let secretContractAddress of Object.keys(tipsMap)) { 81 | excessList.push({ address: secretContractAddress, remoteTip: -1, localTip: tipsMap[secretContractAddress] }); 82 | } 83 | } catch (err) { 84 | return reject(err); 85 | } 86 | resolve({ missingList, excessList }); 87 | }); 88 | } 89 | 90 | module.exports = { compareLocalStateToRemote: compareLocalStateToRemote }; 91 | -------------------------------------------------------------------------------- /src/ethereum/config.json: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "gas": 3000000 4 | }, 5 | "valid": { 6 | "gasMin": 21000, 7 | "gasMax": 8000000, 8 | "gasPriceMin": 3, 9 | "gasPriceMax": 100 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | module.exports.Builder = require("./main_controller/EnvironmentBuilder"); 2 | module.exports.cryptography = require("./common/cryptography"); 3 | module.exports.Utils = { 4 | nodeUtils: require("./common/utils"), 5 | dbUtils: require("./common/DbUtils") 6 | }; 7 | -------------------------------------------------------------------------------- /src/main_controller/DummyRuntime.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("./channels/Envelop"); 2 | 3 | class DummyRuntime { 4 | constructor() { 5 | this._communicator = null; 6 | } 7 | setChannel(communicator) { 8 | this._communicator = communicator; 9 | this._communicator.setOnMessage(envelop => { 10 | console.log("DummyRuntime: got msg : " + JSON.stringify(envelop.content())); 11 | }); 12 | } 13 | sendMsg(content) { 14 | const envelop = new Envelop(true, content, "dummy"); 15 | console.log("sending id -> " + envelop.id()); 16 | this._communicator.sendAndReceive(envelop).then(resEnv => { 17 | console.log("got response id -> " + resEnv.id()); 18 | console.log("response content -> " + JSON.stringify(resEnv.content())); 19 | }); 20 | } 21 | type() { 22 | return "dummy"; 23 | } 24 | } 25 | 26 | module.exports = DummyRuntime; 27 | -------------------------------------------------------------------------------- /src/main_controller/FacadeController.js: -------------------------------------------------------------------------------- 1 | const MainController = require("./MainController"); 2 | const constants = require("../common/constants"); 3 | const runtimesTypes = constants.RUNTIME_TYPE; 4 | 5 | /** 6 | * Exposes a concrete API to all the components 7 | * Should be instantiated instead of MainController (general implementation) 8 | * This exposes an API that a CLI can interface with, for example. 9 | * TODO:: implement concrete methods 10 | * TODO:: for now can use getNode(), getIpcClient() etc... 11 | * */ 12 | class FacadeController extends MainController { 13 | constructor(runtimes) { 14 | super(runtimes); 15 | this._runtimesMap = {}; 16 | try { 17 | runtimes.forEach(rt => { 18 | this._runtimesMap[rt.type()] = rt; 19 | }); 20 | } catch (e) { 21 | throw new Error("Runtime does not implement type()"); 22 | } 23 | } 24 | 25 | getNode() { 26 | return this._runtimesMap[runtimesTypes.Node]; 27 | } 28 | 29 | getIpcClient() { 30 | return this._runtimesMap[runtimesTypes.Core]; 31 | } 32 | 33 | getJsonRpcServer() { 34 | return this._runtimesMap[runtimesTypes.JsonRpc]; 35 | } 36 | 37 | async shutdownSystem() { 38 | if (this.getJsonRpcServer()) { 39 | this.getJsonRpcServer().close(); 40 | } 41 | this.getIpcClient().disconnect(); 42 | await this.getNode().stop(); 43 | } 44 | } 45 | 46 | module.exports = FacadeController; 47 | -------------------------------------------------------------------------------- /src/main_controller/actions/DbAction.js: -------------------------------------------------------------------------------- 1 | const constatnts = require("../../common/constants"); 2 | 3 | class DbAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | execute(reqCommunicator, envelop) { 8 | //TODO:: go to db and get all tips 9 | if (envelop.id()) { 10 | // pass to core 11 | //TODO:: rethink this "return" cuz if happens its not normal. comment out and run jsonrpc_test #6 12 | if (!this._controller.getCommunicator(constatnts.RUNTIME_TYPE.Core)) { 13 | return; 14 | } 15 | let dbCommunicator = this._controller.getCommunicator(constatnts.RUNTIME_TYPE.Core).thisCommunicator; 16 | dbCommunicator.sendAndReceive(envelop).then(resEnv => { 17 | reqCommunicator.send(resEnv); 18 | }); 19 | } 20 | } 21 | } 22 | module.exports = DbAction; 23 | -------------------------------------------------------------------------------- /src/main_controller/actions/DummyAction.js: -------------------------------------------------------------------------------- 1 | const Envelop = require("../channels/Envelop"); 2 | 3 | class DummyAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | execute(communicator, envelop) { 8 | if (envelop.id()) { 9 | console.log("Action: got " + envelop.type() + " " + JSON.stringify(envelop.content())); 10 | console.log("Action: sending back envelop"); 11 | const type = "dummy"; 12 | // if we need another runtime communicator 13 | // let dbCommunicator = this._controller.getCommunicator("db"); 14 | // now send messages to db for example 15 | // 16 | const resEnv = new Envelop(envelop.id(), { response: "some response data" }, type); 17 | communicator.send(resEnv); 18 | } 19 | } 20 | } 21 | 22 | module.exports = DummyAction; 23 | -------------------------------------------------------------------------------- /src/main_controller/actions/ProxyAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../common/constants"); 2 | 3 | class ProxyAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | execute(reqCommunicator, envelop) { 8 | if (envelop.id()) { 9 | this._controller 10 | .getCommunicator(constants.RUNTIME_TYPE.Node) 11 | .thisCommunicator.sendAndReceive(envelop) 12 | .then(resEnv => { 13 | reqCommunicator.send(resEnv); 14 | }); 15 | } 16 | } 17 | } 18 | module.exports = ProxyAction; 19 | -------------------------------------------------------------------------------- /src/main_controller/channels/Channel.js: -------------------------------------------------------------------------------- 1 | const Communicator = require("./Communicator"); 2 | 3 | class Channel { 4 | /** 5 | * Creates a bi-directional channel 6 | * @return {Object} {channel1, channel2} 7 | * */ 8 | static biDirectChannel() { 9 | const c1 = new Communicator(); 10 | const c2 = new Communicator(c1); 11 | c1._setCommunicator(c2); 12 | return { channel1: c1, channel2: c2 }; 13 | } 14 | } 15 | 16 | module.exports = Channel; 17 | -------------------------------------------------------------------------------- /src/main_controller/channels/Communicator.js: -------------------------------------------------------------------------------- 1 | const EventEmitter = require("events").EventEmitter; 2 | 3 | class Communicator extends EventEmitter { 4 | constructor(otherCommunicator) { 5 | super(); 6 | this._M = "m"; 7 | this._other = otherCommunicator; 8 | } 9 | _setCommunicator(other) { 10 | this._other = other; 11 | } 12 | send(envelop) { 13 | if (envelop.id()) { 14 | this.emit(envelop.id(), envelop); 15 | } else { 16 | this.emit(this._M, envelop); 17 | } 18 | } 19 | sendAndReceive(envelop) { 20 | return new Promise((res, rej) => { 21 | // assumption: the responder will emit id() event 22 | this._other.on(envelop.id(), responseEnvelop => { 23 | res(responseEnvelop); 24 | }); 25 | // emit the message 26 | this.emit(this._M, envelop); 27 | }); 28 | } 29 | setOnMessage(callback) { 30 | this._other.on(this._M, envelop => { 31 | callback(envelop); 32 | }); 33 | } 34 | } 35 | 36 | module.exports = Communicator; 37 | // 38 | // async function test(){ 39 | // 40 | // let c1 = new Communicator(); 41 | // let c2 = new Communicator(c1); 42 | // c1._setCommunicator(c2); 43 | // 44 | // let e = new Envelop(true,{"req":"123"}); 45 | // 46 | // c2.setOnMessage((envelop)=>{ 47 | // 48 | // if(envelop.id()){ 49 | // 50 | // c2.send(new Envelop(envelop.id(),{"res":"456"})); 51 | // }else{ 52 | // console.log("w/e stateless"); 53 | // } 54 | // 55 | // }); 56 | // let eRes = await c1.sendAndReceive(e); 57 | // console.log("got response => " + JSON.stringify(eRes.content())); 58 | // c1.send(new Envelop(false,{"stateless" : [1,2,3,4]})); 59 | // } 60 | // 61 | // test(); 62 | -------------------------------------------------------------------------------- /src/main_controller/channels/Envelop.js: -------------------------------------------------------------------------------- 1 | const nodeUtils = require("../../common/utils"); 2 | 3 | class Envelop { 4 | /** @param {Boolean/string} sequenceOrId 5 | * -- if sequenceOrId is True generate new random id, if sequenceOrId is a string=> it is already id. (hence the name ;-) ) 6 | * @param {Object} obj, the data being passed 7 | * @param {string} msgType , used by the MainController to identify which runtime should be called */ 8 | constructor(sequenceOrId, obj, msgType) { 9 | this._validEnvelop = true; 10 | //TODO:: this does not actually THROW it just hangs in there without any signal 11 | if (!sequenceOrId || !obj || !msgType) { 12 | console.log("[-] error initializing envelop sequenceOrId,obj,msgType must be specified!"); 13 | this._validEnvelop = false; 14 | } 15 | this._msgType = msgType; 16 | this._obj = obj; 17 | this._id = false; 18 | // for response envelop we reuse the id from the original request 19 | if (sequenceOrId && nodeUtils.isString(sequenceOrId)) { 20 | this._id = sequenceOrId; 21 | } else if (sequenceOrId === true) { 22 | // initialize a request with id for response 23 | this._id = nodeUtils.randId(); 24 | } else { 25 | console.log( 26 | "[-] error initializing envelop sequenceOrId must be either a string ID or a `true` to generate one randomally!" 27 | ); 28 | this._validEnvelop = false; 29 | } 30 | // attach id to msg if missing 31 | if (!("id" in this._obj) && this._id !== false) { 32 | this._obj.id = nodeUtils.randId(); 33 | } 34 | } 35 | isValidEnvelop() { 36 | return this._validEnvelop; 37 | } 38 | type() { 39 | return this._msgType; 40 | } 41 | id() { 42 | return this._id; 43 | } 44 | content() { 45 | return this._obj; 46 | } 47 | } 48 | 49 | module.exports = Envelop; 50 | -------------------------------------------------------------------------------- /src/main_controller/mock_tests/IdentifyMissingContentTest.js: -------------------------------------------------------------------------------- 1 | // const EnvironmentBuilder = require('../EnvironmentBuilder'); 2 | // const utils = require('../../common/utils'); 3 | // const CoreServer = require('../../core/core_server_mock/core_server'); 4 | // 5 | // const peerConfig = { 6 | // 'bootstrapNodes': [], 7 | // 'port': '0', 8 | // 'nickname': 'peer', 9 | // 'idPath': null, 10 | // }; 11 | // 12 | // async function test(){ 13 | // const uri = 'tcp://127.0.0.1:5555'; 14 | // // start the server (core) 15 | // let coreServer = new CoreServer(); 16 | // coreServer.runServer(uri); 17 | // await utils.sleep(1500); 18 | // // start the client (enigma-p2p) 19 | // let builder = new EnvironmentBuilder(); 20 | // let mainController = await builder 21 | // .setNodeConfig(peerConfig) 22 | // .setIpcConfig({uri : uri}) 23 | // .build(); 24 | // await utils.sleep(5000); 25 | // let fromCache = false; 26 | // mainController.getNode().identifyMissingStates(fromCache,(missingStates)=>{ 27 | // console.log("got the missing states. success"); 28 | // console.log(JSON.stringify(missingStates)); 29 | // }); 30 | // } 31 | // // test(); 32 | -------------------------------------------------------------------------------- /src/policy/p2p_messages/principal_messages.js: -------------------------------------------------------------------------------- 1 | class MsgPrincipal { 2 | constructor(request, sig, addresses, blockNumber) { 3 | this._request = request; 4 | this._sig = sig; 5 | this._blockNumber = blockNumber; 6 | this._addresses = addresses; 7 | Object.freeze(this); 8 | } 9 | 10 | static build(jsonObj) { 11 | if (jsonObj && jsonObj.request && jsonObj.sig) { 12 | const blockNumber = jsonObj.blockNumber ? jsonObj.blockNumber : null; 13 | const addresses = jsonObj.addresses ? jsonObj.addresses : null; 14 | return new MsgPrincipal(jsonObj.request, jsonObj.sig, addresses, blockNumber); 15 | } else { 16 | return null; 17 | } 18 | } 19 | 20 | getRequest() { 21 | return this._request; 22 | } 23 | 24 | getSig() { 25 | return this._sig; 26 | } 27 | 28 | getBlockNumber() { 29 | return this._blockNumber; 30 | } 31 | 32 | getAddresses() { 33 | return this._addresses; 34 | } 35 | 36 | toJson() { 37 | let dict = { 38 | data: this._request, 39 | sig: this._sig 40 | }; 41 | if (this._blockNumber) { 42 | dict.block_number = this._blockNumber.toString(); 43 | } 44 | if (this._addresses) { 45 | dict.addresses = this._addresses; 46 | } 47 | return dict; 48 | } 49 | } 50 | 51 | module.exports = MsgPrincipal; 52 | -------------------------------------------------------------------------------- /src/policy/p2p_messages/schemes/state_sync_scheme.json: -------------------------------------------------------------------------------- 1 | { 2 | "SYNC_STATE_REQ": { 3 | "id": "/SYNC_STATE_REQ", 4 | "type": "object", 5 | "properties": { 6 | "msgType": { "type": "string" }, 7 | "contractAddress": { "type": "string" }, 8 | "fromIndex": { "type": "number" }, 9 | "toIndex": { "type": "number" }, 10 | "fromHash": { "type": "string" }, 11 | "toHash": { "type": "string" } 12 | }, 13 | "required": ["msgType", "contractAddress", "fromIndex", "toIndex", "fromHash", "toHash"] 14 | }, 15 | "SYNC_STATE_RES": { 16 | "id": "/SYNC_STATE_RES", 17 | "type": "object", 18 | "properties": { 19 | "msgType": { "type": "string" }, 20 | "contractAddress": { "type": "string" }, 21 | "states": { "type": "list" } 22 | }, 23 | "required": ["msgType", "contractAddress", "states"] 24 | }, 25 | "SYNC_BCODE_REQ": { 26 | "id": "/SYNC_BCODE_REQ", 27 | "type": "object", 28 | "properties": { 29 | "msgType": { "type": "string" }, 30 | "contractAddress": { "type": "string" } 31 | }, 32 | "required": ["msgType", "contractAddress"] 33 | }, 34 | "SYNC_BCODE_RES": { 35 | "id": "/SYNC_BCODE_RES", 36 | "type": "object", 37 | "properties": { 38 | "msgType": { "type": "string" }, 39 | "contractAddress": { "type": "string" }, 40 | "deployedByteCode": { "type": "list" } 41 | }, 42 | "required": ["msgType", "contractAddress", "deployedByteCode"] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/policy/policy.js: -------------------------------------------------------------------------------- 1 | /** Policy class that handles messages policy*/ 2 | const constants = require("../common/constants"); 3 | const PROTOCOLS = constants.PROTOCOLS; 4 | const PUBSUB_TOPICS = constants.PUBSUB_TOPICS; 5 | 6 | class Policy { 7 | constructor() {} 8 | /** is a valid procol name 9 | * @param {String} protocolName, 10 | * @return {Boolean}, is valid protocol 11 | */ 12 | isValidProtocol(protocolName) { 13 | for (const key in PROTOCOLS) { 14 | if (PROTOCOLS[key] === protocolName) { 15 | return true; 16 | } 17 | } 18 | return false; 19 | } 20 | /** 21 | * is a valid topic name 22 | * @param {String} topicName 23 | * @return {Boolean} , is valid topic 24 | */ 25 | isValidTopic(topicName) { 26 | for (const key in PUBSUB_TOPICS) { 27 | if (PUBSUB_TOPICS[key] === topicName) { 28 | return true; 29 | } 30 | } 31 | return false; 32 | } 33 | /** Validate JSON RPC message type 34 | * @param {Json} msg, some json 35 | * @return {Boolean} isValid 36 | */ 37 | validJsonRpc(msg) { 38 | return "jsonrpc" in msg && (("method" in msg && "params") || "result" in msg) && "id" in msg; 39 | } 40 | } 41 | 42 | module.exports = Policy; 43 | -------------------------------------------------------------------------------- /src/worker/builder/WorkerBuilder.js: -------------------------------------------------------------------------------- 1 | /** 2 | * isDiscover : true/false 3 | * Config: 4 | * bootstrapNodes : [], 5 | * port : 0, otherwise number 6 | * nickname : "nick1" optional, 7 | * multiAddrs : ['/ip4/0.0.0.0/tcp/'] 8 | * namespace : 'ipfs', 9 | * idPath : '/path/to/id' or null, 10 | */ 11 | 12 | const constants = require("../../common/constants"); 13 | const EnigmaNode = require("../EnigmaNode"); 14 | const ProtocolHandler = require("../handlers/ProtocolHandler"); 15 | 16 | /** WIP - load the node configration 17 | * @param {String} path, path to config or default in /config/debug.json 18 | * @return {Json} configObj 19 | * */ 20 | module.exports.loadConfig = function(path) { 21 | return _loadConfig(path); 22 | }; 23 | /** WIP - build the Node given a config object 24 | * @param {Json} config 25 | * @param {Logger} logger 26 | * @return {EnigmaNode} engNode 27 | * */ 28 | module.exports.build = function(config, logger) { 29 | return _buildNode(config, logger); 30 | }; 31 | 32 | function _loadConfig(path) { 33 | let config = null; 34 | if (path) { 35 | config = require(path); 36 | } else { 37 | config = require(constants.configPath); 38 | } 39 | return Object.assign({}, config, {}); 40 | } 41 | 42 | function _buildNode(config, logger) { 43 | const options = {}; 44 | options.isDiscover = config.isDiscover; 45 | const maAddrs = config.multiAddrs; 46 | options.multiAddrs = []; 47 | options.dnsNodes = config.bootstrapNodes; 48 | options.namespace = config.namespace; 49 | options.port = config.port; 50 | options.nickname = config.nickname; 51 | options.pathPeerId = config.idPath; 52 | // parsed multi-addrs with port 53 | maAddrs.forEach(ma => { 54 | options.multiAddrs.push(ma + options.port); 55 | }); 56 | 57 | return new EnigmaNode(options, new ProtocolHandler(logger), logger); 58 | } 59 | -------------------------------------------------------------------------------- /src/worker/controller/actions/GetRegistrationParamsAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | const Envelop = require("../../../main_controller/channels/Envelop"); 3 | class GetRegistrationParamsAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | execute(params) { 8 | const onResponse = params.onResponse; 9 | const requestEnvelop = new Envelop( 10 | true, 11 | { type: constants.CORE_REQUESTS.GetRegistrationParams }, 12 | constants.MAIN_CONTROLLER_NOTIFICATIONS.DbRequest 13 | ); 14 | 15 | this._controller 16 | .communicator() 17 | .sendAndReceive(requestEnvelop) 18 | .then(responseEnvelop => { 19 | onResponse(null, responseEnvelop.content()); 20 | }); 21 | } 22 | } 23 | module.exports = GetRegistrationParamsAction; 24 | -------------------------------------------------------------------------------- /src/worker/controller/actions/GetStateKeysAction.js: -------------------------------------------------------------------------------- 1 | const MsgPrincipal = require("../../../policy/p2p_messages/principal_messages"); 2 | const constants = require("../../../common/constants"); 3 | 4 | class GetStateKeysAction { 5 | constructor(controller) { 6 | this._controller = controller; 7 | } 8 | 9 | asyncExecute(params) { 10 | const action = this; 11 | return new Promise((resolve, reject) => { 12 | if (!params) { 13 | params = {}; 14 | } 15 | params.onResponse = function(err, data) { 16 | if (err) reject(err); 17 | else resolve(data); 18 | }; 19 | action.execute(params); 20 | }); 21 | } 22 | 23 | execute(params) { 24 | let onResponse; 25 | if (params && params.onResponse) { 26 | onResponse = params.onResponse; 27 | } else { 28 | onResponse = () => {}; 29 | } 30 | 31 | // First, set PTT flag (and validate that no PTT is in progress now) 32 | if (!this._controller.principal().startPTT()) { 33 | const err = "PTT in progress.. aborting GetStateKeysAction"; 34 | this._controller.logger().error(err); 35 | return onResponse(err); 36 | } 37 | this._controller.logger().info(`Starting PTT`); 38 | const onPTTRequestResponse = async (err, coreResponse) => { 39 | if (err || coreResponse.type === "Error") { 40 | if (coreResponse && coreResponse.type === "Error") { 41 | err = coreResponse.msg; 42 | } 43 | return this._handleError( 44 | `Failed Core connection: err: ${JSON.stringify(err)}, coreResponse: ${JSON.stringify(coreResponse)}`, 45 | err, 46 | onResponse 47 | ); 48 | } 49 | 50 | let principalResponse; 51 | try { 52 | principalResponse = await this._controller 53 | .principal() 54 | .getStateKeys(this._buildRequestMsg(coreResponse, params)); 55 | } catch (err) { 56 | // TODO: Errors. 57 | return this._handleError(`Failed Principal node connection: ${err.code} - ${err.message}`, err, onResponse); 58 | } 59 | this._pttResponse({ response: principalResponse.data, sig: principalResponse.sig }, (err, response) => { 60 | if (err || response.type === "Error" || response.result.errors.length > 0) { 61 | if (response && coreResponse.type === "Error") { 62 | err = coreResponse.msg; 63 | } else if (response && response.result && response.result.errors.length > 0) { 64 | err = response.result; 65 | } 66 | return this._handleError( 67 | `Failed Core connection: err: ${JSON.stringify(err)}, coreResponse: ${JSON.stringify(response)}`, 68 | err, 69 | onResponse 70 | ); 71 | } 72 | this._controller.principal().onPTTEnd(); 73 | return onResponse(null); 74 | }); 75 | }; 76 | 77 | let dbRequestParams = { 78 | dbQueryType: constants.CORE_REQUESTS.GetPTTRequest, 79 | onResponse: onPTTRequestResponse 80 | }; 81 | 82 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, dbRequestParams); 83 | } 84 | 85 | _pttResponse(params, cb) { 86 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 87 | dbQueryType: constants.CORE_REQUESTS.PTTResponse, 88 | input: params, 89 | onResponse: cb 90 | }); 91 | } 92 | 93 | _buildRequestMsg(coreResponse, params) { 94 | let msg = { 95 | request: coreResponse.result.request, 96 | sig: coreResponse.result.workerSig 97 | }; 98 | if (params) { 99 | if (params.addresses) { 100 | msg.addresses = params.addresses; 101 | } 102 | if (params.blockNumber) { 103 | msg.blockNumber = params.blockNumber; 104 | } 105 | } 106 | return MsgPrincipal.build(msg); 107 | } 108 | 109 | _handleError(errMsg, err, onResponse) { 110 | this._controller.logger().error(errMsg); 111 | this._controller.principal().onPTTEnd(); 112 | onResponse(err); 113 | } 114 | } 115 | 116 | module.exports = GetStateKeysAction; 117 | -------------------------------------------------------------------------------- /src/worker/controller/actions/GetStatusAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | 3 | class GetStatusAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | 8 | /** 9 | * @param {Function} callback (err)=>{} 10 | * */ 11 | async execute(params) { 12 | const callback = params.callback; 13 | const C = constants.NODE_NOTIFICATIONS; 14 | let status = null; 15 | let error = null; 16 | 17 | if (this._controller.isWorkerInitInProgress()) { 18 | status = constants.WORKER_STATUS.INITIALIZING; 19 | } else { 20 | try { 21 | const workerParams = await this._controller.asyncExecCmd(C.GET_ETH_WORKER_PARAM); 22 | switch (workerParams.status) { 23 | case constants.ETHEREUM_WORKER_STATUS.UNREGISTERED: 24 | status = constants.WORKER_STATUS.UNREGISTERED; 25 | break; 26 | case constants.ETHEREUM_WORKER_STATUS.LOGGEDIN: 27 | status = constants.WORKER_STATUS.LOGGEDIN; 28 | break; 29 | case constants.ETHEREUM_WORKER_STATUS.LOGGEDOUT: 30 | status = constants.WORKER_STATUS.REGISTERED; 31 | break; 32 | } 33 | } catch (err) { 34 | this._controller.logger().warning("reading worker params from ethereum failed: " + err); 35 | error = err; 36 | } 37 | } 38 | callback(error, status); 39 | } 40 | 41 | asyncExecute(params) { 42 | const action = this; 43 | return new Promise((resolve, reject) => { 44 | params.callback = function(err, res) { 45 | if (err) { 46 | reject(err); 47 | } else { 48 | resolve(res); 49 | } 50 | }; 51 | action.execute(params); 52 | }); 53 | } 54 | } 55 | 56 | module.exports = GetStatusAction; 57 | -------------------------------------------------------------------------------- /src/worker/controller/actions/HealthCheckAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | 3 | class HealthCheckAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | 8 | /** 9 | * @param {Function} callback (err)=>{} 10 | * */ 11 | execute(params) { 12 | const callback = params.callback; 13 | const C = constants.NODE_NOTIFICATIONS; 14 | 15 | let healthCheckResult = { 16 | status: false, 17 | core: { 18 | status: false, 19 | registrationParams: { 20 | signKey: null 21 | } 22 | }, 23 | ethereum: { 24 | status: false, 25 | uri: null, 26 | contract_addr: null 27 | }, 28 | connectivity: { 29 | status: false, 30 | connections: null 31 | } 32 | // TODO: consider adding a periodic, once there 33 | /*state: { 34 | status: false, 35 | missing: null 36 | }*/ 37 | }; 38 | 39 | this._controller.execCmd(C.REGISTRATION_PARAMS, { 40 | onResponse: async (err, regParams) => { 41 | if (!err) { 42 | // core 43 | healthCheckResult.core.registrationParams.signKey = regParams.result.signingKey; 44 | healthCheckResult.core.status = healthCheckResult.core.registrationParams.signKey != null; 45 | 46 | // ethereum 47 | if (this._controller.hasEthereum()) { 48 | try { 49 | let eth = await this._controller.ethereum().healthCheck(); 50 | healthCheckResult.ethereum.uri = eth.url; 51 | healthCheckResult.ethereum.contract_addr = eth.enigmaContractAddress; 52 | healthCheckResult.ethereum.status = eth.isConnected; 53 | } catch (err) { 54 | healthCheckResult.ethereum.status = false; 55 | } 56 | } 57 | 58 | // connectivity 59 | healthCheckResult.connectivity.connections = this._controller.engNode().getConnectedPeers().length; 60 | healthCheckResult.connectivity.status = healthCheckResult.connectivity.connections >= 1; 61 | 62 | healthCheckResult.status = 63 | healthCheckResult.core.status && healthCheckResult.ethereum.status && healthCheckResult.connectivity.status; // && healthCheckResult.state.status; 64 | callback(null, healthCheckResult); 65 | } 66 | } 67 | }); 68 | } 69 | 70 | asyncExecute(params) { 71 | const action = this; 72 | return new Promise((resolve, reject) => { 73 | params.callback = function(err, res) { 74 | if (err) { 75 | reject(err); 76 | } else { 77 | resolve(res); 78 | } 79 | }; 80 | action.execute(params); 81 | }); 82 | } 83 | } 84 | 85 | module.exports = HealthCheckAction; 86 | -------------------------------------------------------------------------------- /src/worker/controller/actions/NewTaskEncryptionKeyAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | const Envelop = require("../../../main_controller/channels/Envelop"); 3 | class NewTaskEncryptionKeyAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | execute(params) { 8 | const onResponse = params.onResponse; 9 | const requestEnvelop = new Envelop( 10 | params.request.id, 11 | params.request, 12 | constants.MAIN_CONTROLLER_NOTIFICATIONS.DbRequest 13 | ); 14 | let err = null; 15 | if (!requestEnvelop.isValidEnvelop()) { 16 | err = "invalid envelop"; 17 | } 18 | this._controller 19 | .communicator() 20 | .sendAndReceive(requestEnvelop) 21 | .then(responseEnvelop => { 22 | onResponse(err, responseEnvelop.content()); 23 | }); 24 | } 25 | } 26 | module.exports = NewTaskEncryptionKeyAction; 27 | -------------------------------------------------------------------------------- /src/worker/controller/actions/PubSubUnsubscribeAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | const TOPICS = constants.PUBSUB_TOPICS; 3 | 4 | // TODO:: after pr https://github.com/ipfs/interface-js-ipfs-core/pull/437 5 | class PubSubUnsubscribeAction { 6 | constructor(controller) { 7 | this._controller = controller; 8 | } 9 | /** subscribe to to self ethereum signing key topic - useful for jsonrpc api 10 | * @param {string} topic , topic name 11 | * @param {Function} onPublish , (msg)=>{} 12 | * @param {Function} onSubscribed, ()=>{} 13 | * */ 14 | execute(params) { 15 | // let topic = params.topic; 16 | // this._controller.engNode().unsubscribe() 17 | } 18 | } 19 | module.exports = PubSubUnsubscribeAction; 20 | -------------------------------------------------------------------------------- /src/worker/controller/actions/PubsubPublishAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | const nodeUtils = require("../../../common/utils"); 3 | 4 | class PubsubPublishAction { 5 | constructor(controller) { 6 | this._controller = controller; 7 | } 8 | 9 | execute(params) { 10 | const topic = params.topic; 11 | const msgBuffer = Buffer.from(params.message); 12 | this._controller.engNode().broadcast(topic, msgBuffer, () => { 13 | this._controller.logger().debug(`published [${topic}]`); 14 | }); 15 | } 16 | } 17 | module.exports = PubsubPublishAction; 18 | -------------------------------------------------------------------------------- /src/worker/controller/actions/PubsubSubscribeAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../common/constants"); 2 | const TOPICS = constants.PUBSUB_TOPICS; 3 | 4 | class PubsubSubscribeAction { 5 | constructor(controller) { 6 | this._controller = controller; 7 | } 8 | /** subscribe to to self ethereum signing key topic - useful for jsonrpc api 9 | * @param {string} topic , topic name 10 | * @param {Function} onPublish , (msg)=>{} 11 | * @param {Function} onSubscribed, ()=>{} 12 | * */ 13 | execute(params) { 14 | const topic = params.topic; 15 | const topicHandler = params.onPublish; 16 | const finalHandler = params.onSubscribed; 17 | this._controller.engNode().subscribe([ 18 | { 19 | topic: topic, 20 | topic_handler: msg => { 21 | topicHandler(msg); 22 | }, 23 | final_handler: () => { 24 | finalHandler(); 25 | } 26 | } 27 | ]); 28 | } 29 | } 30 | module.exports = PubsubSubscribeAction; 31 | -------------------------------------------------------------------------------- /src/worker/controller/actions/connectivity/BootstrapDiscoveredAction.js: -------------------------------------------------------------------------------- 1 | class BootstrapDiscoveredAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | 6 | async execute(params) { 7 | params = params.params; 8 | const otherPeer = params.peer; 9 | 10 | // Connect to a bootstrap only if there are no active connections 11 | if (this._controller.engNode().arePeersConnected()) { 12 | return; 13 | } 14 | 15 | this._controller.logger().info(`trying to connect to discovered bootstrap ${otherPeer.id.toB58String()}`); 16 | const success = await this._controller.engNode().connectToBootstrap(otherPeer); 17 | this._controller.logger().info(`connection to bootstrap succeeded=${success}`); 18 | } 19 | } 20 | module.exports = BootstrapDiscoveredAction; 21 | -------------------------------------------------------------------------------- /src/worker/controller/actions/connectivity/GetPeers.js: -------------------------------------------------------------------------------- 1 | class GetPeersAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | 6 | execute(params) { 7 | const callback = params.callback; 8 | let length = 0; 9 | let error = null; 10 | try { 11 | length = this._controller.engNode().getConnectedPeers().length; 12 | } catch (err) { 13 | error = err; 14 | } 15 | callback(error, length); 16 | } 17 | } 18 | module.exports = GetPeersAction; 19 | -------------------------------------------------------------------------------- /src/worker/controller/actions/connectivity/NewPeerAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | 3 | class NewPeerAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | 8 | execute(params) { 9 | params = params.params; 10 | const callback = params.callback; 11 | const autoInit = this._controller.isAutoInit(); 12 | const initRequired = this._controller.canInitWorker(); 13 | 14 | // Check if auto init is set and initialization has not done yet 15 | if (autoInit && initRequired) { 16 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.INIT_WORKER, { 17 | callback: callback 18 | }); 19 | } 20 | } 21 | } 22 | module.exports = NewPeerAction; 23 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/DbRequestAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | const errs = require("../../../../common/errors"); 3 | const Envelop = require("../../../../main_controller/channels/Envelop"); 4 | 5 | class DbRequestAction { 6 | constructor(controller) { 7 | this._controller = controller; 8 | } 9 | execute(params) { 10 | const onResponse = params.onResponse; 11 | const queryType = params.dbQueryType; 12 | const input = params.input; 13 | if (!this._validateRequest(queryType)) { 14 | onResponse(new errs.TypeErr(`"invalid queryType ${queryType}`)); 15 | return; 16 | } 17 | const requestEnvelop = new Envelop( 18 | true, 19 | { type: queryType, input: input }, 20 | constants.MAIN_CONTROLLER_NOTIFICATIONS.DbRequest 21 | ); 22 | this._controller 23 | .communicator() 24 | .sendAndReceive(requestEnvelop) 25 | .then(responseEnvelop => { 26 | const parsedResponse = responseEnvelop.content(); 27 | onResponse(parsedResponse.error, parsedResponse); 28 | }); 29 | } 30 | _validateRequest(reqType) { 31 | return reqType in constants.CORE_REQUESTS; 32 | } 33 | } 34 | module.exports = DbRequestAction; 35 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/read/GetAllAddrsAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../../common/constants"); 2 | 3 | /** 4 | * Get all addresses either from core 5 | * params: 6 | * - onResponse : (err,result)=>{} 7 | * */ 8 | class GetAllAddrsAction { 9 | constructor(controller) { 10 | this._controller = controller; 11 | } 12 | execute(params) { 13 | const onResponse = params.onResponse; 14 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 15 | dbQueryType: constants.CORE_REQUESTS.GetAllAddrs, 16 | onResponse: (err, result) => { 17 | onResponse(err, result); 18 | } 19 | }); 20 | } 21 | } 22 | module.exports = GetAllAddrsAction; 23 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/read/GetAllTipsAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../../common/constants"); 2 | 3 | /** 4 | This action returns all the tips from core. 5 | * */ 6 | class GetAllTipsAction { 7 | constructor(controller) { 8 | this._controller = controller; 9 | } 10 | execute(params) { 11 | const onResult = params.onResponse; 12 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 13 | dbQueryType: constants.CORE_REQUESTS.GetAllTips, 14 | onResponse: (err, result) => { 15 | let tips; 16 | if (result && result.result && result.result.tips) { 17 | tips = result.result.tips; 18 | } else { 19 | tips = []; 20 | } 21 | return onResult(err, tips); 22 | } 23 | }); 24 | } 25 | asyncExecute(params) { 26 | const action = this; 27 | return new Promise((res, rej) => { 28 | params.onResponse = function(err, tips) { 29 | if (err) rej(err); 30 | else res(tips); 31 | }; 32 | action.execute(params); 33 | }); 34 | } 35 | } 36 | module.exports = GetAllTipsAction; 37 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/read/GetContractCodeAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../../common/constants"); 2 | /** 3 | This action returns all the requested deltas from core. 4 | * */ 5 | class GetContractCodeAction { 6 | constructor(controller) { 7 | this._controller = controller; 8 | } 9 | execute(params) { 10 | const onResult = params.onResponse; 11 | const queryMsg = params.requestMsg; 12 | // make query 13 | const addr = queryMsg.contractAddress(); 14 | const input = addr; 15 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 16 | dbQueryType: constants.CORE_REQUESTS.GetContract, 17 | input: input, 18 | onResponse: (err, result) => { 19 | return onResult(err, result); 20 | } 21 | }); 22 | } 23 | } 24 | module.exports = GetContractCodeAction; 25 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/read/GetDeltasAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../../common/constants"); 2 | 3 | /** 4 | This action returns all the requested deltas from core. 5 | * */ 6 | class GetDeltasAction { 7 | constructor(controller) { 8 | this._controller = controller; 9 | } 10 | execute(params) { 11 | const onResult = params.onResponse; 12 | const queryMsg = params.requestMsg; 13 | // make query 14 | const addr = queryMsg.contractAddress(); 15 | const range = queryMsg.getRange(); 16 | const from = range.fromIndex; 17 | const to = range.toIndex; 18 | const input = [{ address: addr, from: from, to: to }]; 19 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 20 | dbQueryType: constants.CORE_REQUESTS.GetDeltas, 21 | input: input, 22 | onResponse: (err, result) => { 23 | return onResult(err, result); 24 | } 25 | }); 26 | } 27 | } 28 | module.exports = GetDeltasAction; 29 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/read/GetTipsAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../../common/constants"); 2 | 3 | /** 4 | This action returns tips for the requested secret contracts array from core. 5 | * */ 6 | class GetTipsAction { 7 | constructor(controller) { 8 | this._controller = controller; 9 | } 10 | execute(params) { 11 | const contractAddresses = params.contractAddresses; 12 | const onResult = params.onResponse; 13 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 14 | dbQueryType: constants.CORE_REQUESTS.GetTips, 15 | input: [contractAddresses], 16 | onResponse: (err, result) => { 17 | let tips = []; 18 | if (result && result.result && result.result.tips) { 19 | tips = result.result.tips; 20 | } 21 | return onResult(err, tips); 22 | } 23 | }); 24 | } 25 | asyncExecute(params) { 26 | const action = this; 27 | return new Promise((res, rej) => { 28 | params.onResponse = function(err, tips) { 29 | if (err) rej(err); 30 | else res(tips); 31 | }; 32 | action.execute(params); 33 | }); 34 | } 35 | } 36 | module.exports = GetTipsAction; 37 | -------------------------------------------------------------------------------- /src/worker/controller/actions/db/write/UpdateDbAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This action is responsible for writting to the db of core. 3 | * not directly of course. 4 | * this will write: 5 | * - new contract bytecode 6 | * - new deltas to an existing contract. 7 | * */ 8 | const constants = require("../../../../../common/constants"); 9 | 10 | class UpdateDbAction { 11 | constructor(controller) { 12 | this._controller = controller; 13 | } 14 | execute(params) { 15 | const msgRes = params.data; 16 | const onFinish = params.callback; 17 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.DB_REQUEST, { 18 | input: msgRes, 19 | dbQueryType: constants.CORE_REQUESTS.UpdateDb, 20 | onResponse: (err, result) => { 21 | let error = err; 22 | if (!error) { 23 | if (result.status !== constants.CORE_RESPONSE_STATUS_CODES.OK) { 24 | if (result.errors) { 25 | error = result.errors; 26 | } else { 27 | error = result.status; 28 | } 29 | } 30 | } 31 | onFinish(error, result); 32 | } 33 | }); 34 | } 35 | asyncExecute(params) { 36 | const action = this; 37 | return new Promise((res, rej) => { 38 | if (!params) params = {}; 39 | params.callback = function(err, result) { 40 | if (err) rej(err); 41 | else res(result); 42 | }; 43 | action.execute(params); 44 | }); 45 | } 46 | } 47 | module.exports = UpdateDbAction; 48 | -------------------------------------------------------------------------------- /src/worker/controller/actions/ethereum/GetWorkerParamsAction.js: -------------------------------------------------------------------------------- 1 | class GetWorkerParamsAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | async execute(params) { 6 | const onResult = params.onResponse; 7 | let err = null; 8 | let workerParams = null; 9 | 10 | try { 11 | workerParams = await this._controller 12 | .ethereum() 13 | .api() 14 | .getSelfWorker(); 15 | } catch (e) { 16 | this._controller.logger().error(`[GET_ETH_WORKER_PARAM] error = ${e}`); 17 | err = e; 18 | } 19 | if (onResult) { 20 | onResult(err, workerParams); 21 | } 22 | } 23 | asyncExecute(params) { 24 | const action = this; 25 | return new Promise((res, rej) => { 26 | if (!params) params = {}; 27 | params.onResponse = function(err, verificationResult) { 28 | if (err) rej(err); 29 | else res(verificationResult); 30 | }; 31 | action.execute(params); 32 | }); 33 | } 34 | } 35 | module.exports = GetWorkerParamsAction; 36 | -------------------------------------------------------------------------------- /src/worker/controller/actions/ethereum/LoginAction.js: -------------------------------------------------------------------------------- 1 | class LoginAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | async execute(params) { 6 | const onResult = params.onResponse; 7 | let loginSuccess = false; 8 | let err = null; 9 | try { 10 | await this._controller 11 | .ethereum() 12 | .api() 13 | .login(); 14 | this._controller.logger().info(`[LOGIN] successful login`); 15 | loginSuccess = true; 16 | } catch (e) { 17 | this._controller.logger().error(`[LOGIN] error in login error= ${e}`); 18 | err = e; 19 | } 20 | if (onResult) { 21 | onResult(err, loginSuccess); 22 | } 23 | } 24 | asyncExecute(params) { 25 | const action = this; 26 | return new Promise((res, rej) => { 27 | if (!params) params = {}; 28 | params.onResponse = function(err, result) { 29 | if (err) rej(err); 30 | else res(result); 31 | }; 32 | action.execute(params); 33 | }); 34 | } 35 | } 36 | module.exports = LoginAction; 37 | -------------------------------------------------------------------------------- /src/worker/controller/actions/ethereum/LogoutAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | 3 | class LogoutAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | async execute(params) { 8 | const onResult = params.onResponse; 9 | let logoutSuccess = false; 10 | let err = null; 11 | 12 | const api = this._controller.ethereum().api(); 13 | try { 14 | const workerAddress = api.getWorkerAddress(); 15 | const { status } = await api.getWorker(workerAddress); 16 | if (status === constants.ETHEREUM_WORKER_STATUS.LOGGEDOUT) { 17 | this._controller.logger().info(`[LOGOUT] already logged out`); 18 | } else { 19 | await api.logout(); 20 | this._controller.logger().info(`[LOGOUT] successful logout`); 21 | } 22 | logoutSuccess = true; 23 | } catch (e) { 24 | this._controller.logger().error(`[LOGOUT] error in logout error= ${e}`); 25 | err = e; 26 | } 27 | if (onResult) { 28 | onResult(err, logoutSuccess); 29 | } 30 | } 31 | asyncExecute(params) { 32 | const action = this; 33 | return new Promise((res, rej) => { 34 | if (!params) params = {}; 35 | params.onResponse = function(err, result) { 36 | if (err) rej(err); 37 | else res(result); 38 | }; 39 | action.execute(params); 40 | }); 41 | } 42 | } 43 | module.exports = LogoutAction; 44 | -------------------------------------------------------------------------------- /src/worker/controller/actions/ethereum/RegisterAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | 3 | class RegisterAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | async execute(params) { 8 | const onResult = params.onResponse; 9 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.REGISTRATION_PARAMS, { 10 | onResponse: async (err, regParams) => { 11 | let success = false; 12 | if (err) { 13 | this._controller.logger().error(`[REGISTER] error= ${err}`); 14 | } else { 15 | const signerAddress = regParams.result.signingKey; 16 | const report = regParams.result.report; 17 | const signature = regParams.result.signature; 18 | try { 19 | await this._controller 20 | .ethereum() 21 | .api() 22 | .register(signerAddress, report, signature); 23 | this._controller.logger().info("[REGISTER] successful registration"); 24 | success = true; 25 | } catch (e) { 26 | this._controller.logger().error(`[REGISTER] error= ${e}`); 27 | err = e; 28 | } 29 | } 30 | if (onResult) { 31 | onResult(err, success); 32 | } 33 | } 34 | }); 35 | } 36 | asyncExecute(params) { 37 | const action = this; 38 | return new Promise((res, rej) => { 39 | if (!params) params = {}; 40 | params.onResponse = function(err, result) { 41 | if (err) rej(err); 42 | else res(result); 43 | }; 44 | action.execute(params); 45 | }); 46 | } 47 | } 48 | module.exports = RegisterAction; 49 | -------------------------------------------------------------------------------- /src/worker/controller/actions/ethereum/UnregisterAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | 3 | class UnregisterAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | async execute(params) { 8 | const onResult = params.onResponse; 9 | let success = false; 10 | let err = null; 11 | 12 | const api = this._controller.ethereum().api(); 13 | 14 | try { 15 | const workerAddress = api.getWorkerAddress(); 16 | const { status } = await api.getWorker(workerAddress); 17 | if (status === constants.ETHEREUM_WORKER_STATUS.UNREGISTERED) { 18 | this._controller.logger().info(`[UNREGISTER] already unregistered`); 19 | } else { 20 | await api.unregister(); 21 | this._controller.logger().info(`[UNREGISTER] successful unregister`); 22 | } 23 | 24 | success = true; 25 | } catch (e) { 26 | this._controller.logger().error(`[UNREGISTER] error in unregister error= ${e}`); 27 | err = e; 28 | } 29 | if (onResult) { 30 | onResult(err, success); 31 | } 32 | } 33 | asyncExecute(params) { 34 | const action = this; 35 | return new Promise((res, rej) => { 36 | if (!params) params = {}; 37 | params.onResponse = function(err, result) { 38 | if (err) rej(err); 39 | else res(result); 40 | }; 41 | action.execute(params); 42 | }); 43 | } 44 | } 45 | module.exports = UnregisterAction; 46 | -------------------------------------------------------------------------------- /src/worker/controller/actions/proxy/GetStatusProxyAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | const Envelop = require("../../../../main_controller/channels/Envelop"); 3 | /** 4 | * This action takes a taskId and checks if the result + status exists locally. 5 | * if true: returns back the envelop 6 | * else: 7 | * ROUTE_BLOCKING_RPC (i.e goes to the network and looks for the worker) 8 | * */ 9 | class GetStatusProxyAction { 10 | constructor(controller) { 11 | this._controller = controller; 12 | } 13 | async execute(requestEnvelop) { 14 | let taskId = requestEnvelop.content().taskId; 15 | try { 16 | let result = await this._controller.asyncExecCmd(constants.NODE_NOTIFICATIONS.GET_TASK_RESULT, { 17 | taskId: taskId 18 | }); 19 | const responseEnvelop = new Envelop( 20 | requestEnvelop.id(), 21 | { result: result.getStatus(), output: result.getOutput() }, 22 | requestEnvelop.type() 23 | ); 24 | this._controller.communicator().send(responseEnvelop); 25 | } catch (e) { 26 | this._controller.logger().error(e); 27 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.ROUTE_BLOCKING_RPC, requestEnvelop); 28 | } 29 | } 30 | } 31 | module.exports = GetStatusProxyAction; 32 | -------------------------------------------------------------------------------- /src/worker/controller/actions/proxy/ProxyDispatcherAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * performed by the gateway side. 3 | * this action dispatches 4 | * */ 5 | 6 | const constants = require("../../../../common/constants"); 7 | const utils = require("../../../../common/utils"); 8 | const Envelop = require("../../../../main_controller/channels/Envelop"); 9 | 10 | class ProxyDispatcherAction { 11 | constructor(controller) { 12 | this._controller = controller; 13 | } 14 | async execute(requestEnvelop) { 15 | const type = requestEnvelop.content().type; 16 | let theAction = null; 17 | switch (type) { 18 | case constants.CORE_REQUESTS.NewTaskEncryptionKey: 19 | theAction = constants.NODE_NOTIFICATIONS.ROUTE_BLOCKING_RPC; 20 | const workerSignKey = requestEnvelop.content().workerSignKey; 21 | const sequence = requestEnvelop.content().id; 22 | const selfId = this._controller.engNode().getSelfIdB58Str(); 23 | requestEnvelop.content().targetTopic = selfId + workerSignKey + sequence; 24 | break; 25 | case constants.NODE_NOTIFICATIONS.GET_TASK_STATUS: 26 | theAction = constants.NODE_NOTIFICATIONS.DISPATCH_STATUS_REQ_RPC; 27 | const taskId = requestEnvelop.content().taskId; 28 | const workerAddr = requestEnvelop.content().workerAddress; 29 | requestEnvelop.content().targetTopic = taskId + workerAddr; 30 | requestEnvelop.content().workerSignKey = workerAddr; 31 | if (!requestEnvelop.content().id) { 32 | requestEnvelop.content().id = taskId; 33 | } 34 | break; 35 | case constants.CORE_REQUESTS.DeploySecretContract: 36 | try { 37 | // translate from base64 to byte array 38 | const preCodeBufferGzip = Buffer.from(requestEnvelop.content().request.preCode, "base64"); 39 | // unzip the preCode 40 | const preCodeBuffer = await utils.gunzip(preCodeBufferGzip); 41 | const preCodeByteArray = [...preCodeBuffer]; 42 | requestEnvelop.content().request.preCode = preCodeByteArray; 43 | } catch (e) { 44 | this._controller 45 | .logger() 46 | .info(`[PROXY_DISPATCH] an exception occurred while trying to unpack DeploySecretContract RPC ${e}`); 47 | return; 48 | } 49 | case constants.CORE_REQUESTS.ComputeTask: 50 | theAction = constants.NODE_NOTIFICATIONS.ROUTE_NON_BLOCK_RPC; 51 | break; 52 | case constants.NODE_NOTIFICATIONS.GET_TASK_RESULT: 53 | this._getTaskResult(type, requestEnvelop); 54 | break; 55 | } 56 | if (theAction) { 57 | this._controller.logger().debug("[PROXY_DISPATCH] sending dispatched rpc request"); 58 | this._controller.execCmd(theAction, requestEnvelop); 59 | } 60 | } 61 | async _getTaskResult(type, requestEnvelop) { 62 | let result = null; 63 | try { 64 | result = await this._controller.asyncExecCmd(type, { 65 | taskId: requestEnvelop.content().taskId 66 | }); 67 | if (result) { 68 | result = result.toDbJson(); 69 | } else { 70 | this._controller.logger().info(`[PROXY_DISPATCH] received an empty result`); 71 | } 72 | } catch (e) { 73 | this._controller.logger().info(`[PROXY_DISPATCH] problem in getting result ${e}`); 74 | } finally { 75 | const responseEnvelop = new Envelop(requestEnvelop.id(), { result: result }, requestEnvelop.type()); 76 | this._controller.communicator().send(responseEnvelop); 77 | } 78 | } 79 | } 80 | module.exports = ProxyDispatcherAction; 81 | -------------------------------------------------------------------------------- /src/worker/controller/actions/proxy/RouteRpcBlockingAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | const EncoderUtil = require("../../../../common/EncoderUtil"); 3 | const Envelop = require("../../../../main_controller/channels/Envelop"); 4 | 5 | class RouteRpcBlockingAction { 6 | constructor(controller) { 7 | this._controller = controller; 8 | } 9 | /** 10 | * expects a targetTopic and id in the envelop.content() 11 | * targetTopic : the result topic will be published to 12 | * workerSignKey : the target topic for the request 13 | * */ 14 | execute(requestEnvelop) { 15 | const request = requestEnvelop.content(); 16 | const sequence = requestEnvelop.content().id; 17 | const targetTopic = requestEnvelop.content().targetTopic; 18 | const workerSignKey = requestEnvelop.content().workerSignKey; 19 | const reqType = requestEnvelop.content().type; 20 | 21 | if (!targetTopic || !sequence || !workerSignKey) { 22 | this._sendResponseEnvelope(requestEnvelop, false, "error no sequence/targetTopic/signKey"); 23 | return; 24 | } 25 | 26 | const routedMessage = EncoderUtil.encode({ 27 | type: reqType, 28 | request: request, 29 | sequence: sequence, 30 | targetTopic: targetTopic 31 | }); 32 | if (!routedMessage) { 33 | this._sendResponseEnvelope(requestEnvelop, false, "error in encoding routed message"); 34 | return; 35 | } 36 | 37 | // onPublish callback 38 | const onPublish = msg => { 39 | // once the result from the worker arrives 40 | const data = EncoderUtil.decode(msg.data); 41 | if (!data) { 42 | this._sendResponseEnvelope(requestEnvelop, false, "error in decoding response message"); 43 | } else { 44 | this._sendResponseEnvelope(requestEnvelop, data.result, null); 45 | } 46 | // TODO:: possible unsubscribe depends what the reqs are it might not be default maybe reuse the topic 47 | }; 48 | // onSubscribed callback 49 | const onSubscribed = () => { 50 | console.log("[rpc] subscribed to target topic = " + targetTopic); 51 | // publish the actual request 52 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.PUBSUB_PUB, { 53 | topic: workerSignKey, 54 | message: routedMessage 55 | }); 56 | }; 57 | 58 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.PUBSUB_SUB, { 59 | topic: targetTopic, 60 | onPublish: onPublish, 61 | onSubscribed: onSubscribed 62 | }); 63 | } 64 | 65 | _sendResponseEnvelope(requestEnvelop, result, error) { 66 | const env = new Envelop(requestEnvelop.id(), { result: result, error: error }, requestEnvelop.type()); 67 | this._controller.communicator().send(env); 68 | } 69 | } 70 | module.exports = RouteRpcBlockingAction; 71 | -------------------------------------------------------------------------------- /src/worker/controller/actions/proxy/RouteRpcNonBlockingAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | const Envelop = require("../../../../main_controller/channels/Envelop"); 3 | const EncoderUtil = require("../../../../common/EncoderUtil"); 4 | 5 | class RouteRpcNonBlockingAction { 6 | constructor(controller) { 7 | this._controller = controller; 8 | } 9 | execute(requestEnvelop) { 10 | const targetTopic = requestEnvelop.content().request.workerAddress; 11 | const request = requestEnvelop.content().request; 12 | const type = requestEnvelop.content().type; 13 | 14 | // validate topic indicated 15 | if (!targetTopic || !request) { 16 | this._sendResponseEnvelope(requestEnvelop, { sent: false }, "error no targetTopic/request"); 17 | return; 18 | } 19 | // encode routed msg 20 | const routedMessage = EncoderUtil.encode({ 21 | type: type, 22 | request: request 23 | }); 24 | if (!routedMessage) { 25 | this._sendResponseEnvelope(requestEnvelop, { sent: false }, "error in encoding routed message"); 26 | return; 27 | } 28 | 29 | // send the request 30 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.PUBSUB_PUB, { 31 | topic: targetTopic, 32 | message: routedMessage 33 | }); 34 | // return jsonrpc response ack 35 | this._sendResponseEnvelope(requestEnvelop, { sent: true }, null); 36 | } 37 | 38 | _sendResponseEnvelope(requestEnvelop, result, error) { 39 | const env = new Envelop(requestEnvelop.id(), { result: result, error: error }, requestEnvelop.type()); 40 | this._controller.communicator().send(env); 41 | } 42 | } 43 | module.exports = RouteRpcNonBlockingAction; 44 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/AnnounceContentAction.js: -------------------------------------------------------------------------------- 1 | const errors = require("../../../../common/errors"); 2 | 3 | class AnnounceContentAction { 4 | constructor(controller) { 5 | this._controller = controller; 6 | } 7 | async execute(params) { 8 | const onResponse = params.onResponse; 9 | let engCids = params.engCids; 10 | if (!engCids || !engCids.length) { 11 | const msg = `[AnnounceContent] ${engCids} is not list of EngCid's`; 12 | this._controller.logger().error(msg); 13 | return onResponse(new errors.TypeErr(msg)); 14 | } 15 | try { 16 | let failedCids = await this._controller.provider().asyncProvideContentsBatch(engCids); 17 | if (Array.isArray(failedCids) && failedCids.length) { 18 | if (failedCids.length === engCids.length) { 19 | const error = `[AnnounceContent] content announce failed`; 20 | this._controller.logger().error(error); 21 | return onResponse(error); 22 | } 23 | this._controller 24 | .logger() 25 | .debug(`[AnnounceContent] announced = ${engCids.length - failedCids.length} out of ${engCids.length}`); 26 | } else { 27 | this._controller.logger().debug(`[AnnounceContent] success announcing content`); 28 | } 29 | return onResponse(null, failedCids); 30 | } catch (e) { 31 | this._controller.logger().error(`[AnnounceContent] can't announce: ${e}`); 32 | return onResponse(e); 33 | } 34 | } 35 | asyncExecute(params) { 36 | const action = this; 37 | return new Promise((res, rej) => { 38 | params.onResponse = function(err, failedCids) { 39 | if (err) rej(err); 40 | else res(failedCids); 41 | }; 42 | action.execute(params); 43 | }); 44 | } 45 | } 46 | module.exports = AnnounceContentAction; 47 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/AnnounceLocalStateAction.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../../../common/constants"); 2 | const EngCid = require("../../../../common/EngCID"); 3 | /** 4 | * This Action announces to the network about it's local state 5 | * This should be called once the node is synched with the network and has all the deltas and contracts. 6 | * //TODO:: add flag to check if and only if NODE_IS_FULLY_SYNCED then allow otherwise dismiss the request 7 | * */ 8 | class AnnounceLocalStateAction { 9 | constructor(controller) { 10 | this._controller = controller; 11 | } 12 | execute(params) { 13 | const onResponse = params.onResponse; 14 | let isEngCid = params.isEngCid; 15 | 16 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.GET_ALL_ADDRS, { 17 | onResponse: (err, allAddrsResponse) => { 18 | /** 19 | * do the announcement 20 | * */ 21 | let parsedEngCids = []; 22 | for (const address of allAddrsResponse.result.addresses) { 23 | const ecid = EngCid.createFromSCAddress(address); 24 | if (ecid) { 25 | parsedEngCids.push(ecid); 26 | } else { 27 | this._controller.logger().error(`error converting address ${address} to ecid !`); 28 | } 29 | } 30 | isEngCid = true; 31 | this._controller.provider().provideContentsBatch(parsedEngCids, isEngCid, failedCids => { 32 | return onResponse(null, parsedEngCids); 33 | }); 34 | } 35 | }); 36 | } 37 | 38 | asyncExecute(params) { 39 | const action = this; 40 | return new Promise((resolve, reject) => { 41 | params.callback = function(status, result) { 42 | resolve({ status: status, result: result }); 43 | }; 44 | action.execute(params); 45 | }); 46 | } 47 | } 48 | module.exports = AnnounceLocalStateAction; 49 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/FindContentProviderAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Find content providers to provide data 3 | * Takes list of hashes -> turns them into cid's 4 | * calls next(result) 5 | * */ 6 | class FindContentProviderAction { 7 | constructor(controller) { 8 | this._controller = controller; 9 | } 10 | execute(params) { 11 | const descriptorsList = params.descriptorsList; 12 | const next = params.next; 13 | const isEngCid = params.isEngCid; 14 | this._controller.receiver().findProvidersBatch(descriptorsList, isEngCid, findProviderResult => { 15 | // TODO:: add error param to the callback. 16 | next(null, findProviderResult); 17 | }); 18 | } 19 | asyncExecute(params) { 20 | const action = this; 21 | return new Promise((resolve, reject) => { 22 | if (!params) { 23 | params = {}; 24 | } 25 | params.next = function(err, data) { 26 | if (err) reject(err); 27 | else resolve(data); 28 | }; 29 | action.execute(params); 30 | }); 31 | } 32 | } 33 | module.exports = FindContentProviderAction; 34 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/GetLocalTipsOfRemote.js: -------------------------------------------------------------------------------- 1 | const errors = require("../../../../common/errors"); 2 | /** 3 | * Find content providers to provide data 4 | * Takes list of hashes -> turns them into cid's 5 | * calls next(result) 6 | * */ 7 | class GetLocalTipsOfRemote { 8 | constructor(controller) { 9 | this._controller = controller; 10 | } 11 | async execute(params) { 12 | let peerB58Id = params.peerB58Id; 13 | if (!peerB58Id) { 14 | return null; 15 | } 16 | try { 17 | let peerInfo = await this._controller.engNode().lookUpPeer(peerB58Id); 18 | if (!peerInfo) { 19 | throw new errors.P2PErr(`no such peer ${b58Id}`); 20 | } 21 | let remoteTips = await this._controller.engNode().getLocalStateOfRemote(peerInfo); 22 | return remoteTips; 23 | } catch (e) { 24 | this._controller.logger().error(`GetLocalTipsOfRemote Action ${e}`); 25 | return null; 26 | } 27 | } 28 | } 29 | module.exports = GetLocalTipsOfRemote; 30 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/IdentifyMissingStatesAction.js: -------------------------------------------------------------------------------- 1 | const StateSync = require("../../../../ethereum/StateSync"); 2 | const constants = require("../../../../common/constants"); 3 | const errs = require("../../../../common/errors"); 4 | const NODE_NOTIY = constants.NODE_NOTIFICATIONS; 5 | 6 | /** 7 | * This action is the first step to sync 8 | * this identifies the missing state the worker needs. 9 | * - it will read from core. 10 | * - get the local tips 11 | * - get remote tips 12 | * - parse them into a format class "MissingStatesMap" 13 | * - and return the result to the caller. 14 | * @return {JSON} res: 15 | * missingList - missing states [{address, deltas : [deltaHash, index]}]. 16 | * In case the entire contract is missing, the bytecodeHash is returned as well: 17 | * [{address, bytecodeHash , deltas : [deltaHash, index]}] 18 | * excessList - excessive states [{address, remoteTip]. 19 | * In case the entire contract is excessive, the remoteTip field is set to -1 20 | * */ 21 | class IdentifyMissingStatesAction { 22 | constructor(controller) { 23 | this._controller = controller; 24 | } 25 | async execute(params) { 26 | const callback = params.onResponse; 27 | try { 28 | // LOCAL TIPS : {type,id,tips: [{address,key,delta},...]} 29 | const localTips = await this._controller.asyncExecCmd(NODE_NOTIY.GET_ALL_TIPS, {}); 30 | if (!this._controller.hasEthereum()) { 31 | const error = new errs.EthereumErr(`[IDENTIFY_MISSING_STATES] failure, no ethereum!`); 32 | return callback(error); 33 | } 34 | StateSync.compareLocalStateToRemote(this._controller.ethereum().api(), localTips) 35 | .then(res => { 36 | callback(null, res); 37 | }) 38 | .catch(err => callback(err)); 39 | } catch (err) { 40 | return callback(err); 41 | } 42 | } 43 | 44 | asyncExecute(params) { 45 | const action = this; 46 | return new Promise((resolve, reject) => { 47 | if (!params) { 48 | params = {}; 49 | } 50 | params.onResponse = function(err, data) { 51 | if (err) reject(err); 52 | else resolve(data); 53 | }; 54 | action.execute(params); 55 | }); 56 | } 57 | } 58 | module.exports = IdentifyMissingStatesAction; 59 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/ProvideSyncStateAction.js: -------------------------------------------------------------------------------- 1 | class ProvideSyncStateAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | execute(params) { 6 | const provider = this._controller.provider(); 7 | const connectionStream = params.params.connection; 8 | provider.startStateSyncResponse(connectionStream); 9 | } 10 | } 11 | module.exports = ProvideSyncStateAction; 12 | -------------------------------------------------------------------------------- /src/worker/controller/actions/sync/TryReceiveAllAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This can fail. 3 | * Given an input: 4 | * - Missing deltas 5 | * - CID's and ProviderList for EACH CID (FindProviderResult.js) 6 | * Fetch from the providers all the bytecode/deltas 7 | * */ 8 | const waterfall = require("async/waterfall"); 9 | const errors = require("../../../../common/errors"); 10 | class TryReceiveAllAction { 11 | constructor(controller) { 12 | this._controller = controller; 13 | } 14 | /** 15 | * @param {JSON} params : { 16 | * - findProvidersResult 17 | * - missingStates 18 | * - onFinish 19 | * } 20 | * */ 21 | execute(params) { 22 | const allMissingDataList = params.allMissingDataList; 23 | const remoteMissingStatesMap = params.remoteMissingStatesMap; 24 | const onFinish = params.onFinish; 25 | const receiver = this._controller.receiver(); 26 | 27 | if (allMissingDataList.length === 0) { 28 | this._controller.logger().info(`[TRY_RECEIVE_ALL] No missing data`); 29 | return onFinish(null); 30 | } 31 | 32 | const jobs = []; 33 | const firstJob = allMissingDataList[0]; 34 | // pass the missingStateList to the receiver 35 | receiver.setRemoteMissingStatesMap(remoteMissingStatesMap); 36 | // init the first job 37 | jobs.push(cb => { 38 | receiver.trySyncReceive(firstJob.providers, firstJob.requestMessages, (err, isDone, resultList) => { 39 | if (err) { 40 | return cb(err); 41 | } else { 42 | const allResults = []; 43 | allResults.push({ 44 | success: isDone, 45 | resultList: resultList, 46 | error: err 47 | }); 48 | return cb(null, allResults); 49 | } 50 | }); 51 | }); 52 | // init the rest of the jobs 53 | for (let i = 1; i < allMissingDataList.length; ++i) { 54 | const providers = allMissingDataList[i].providers; 55 | const requestMessages = allMissingDataList[i].requestMessages; 56 | jobs.push((allResults, cb) => { 57 | receiver.trySyncReceive(providers, requestMessages, (err, isDone, resultList) => { 58 | if (err) { 59 | return cb(err); 60 | } else { 61 | allResults.push({ 62 | success: isDone, 63 | resultList: resultList, 64 | error: err 65 | }); 66 | return cb(null, allResults); 67 | } 68 | }); 69 | }); 70 | } 71 | // execute all the jobs 72 | waterfall(jobs, (err, allResults) => { 73 | onFinish(err, allResults); 74 | }); 75 | } 76 | asyncExecute(params) { 77 | const action = this; 78 | return new Promise((resolve, reject) => { 79 | if (!params) { 80 | params = {}; 81 | } 82 | params.onFinish = function(err, data) { 83 | if (err) reject(err); 84 | else resolve(data); 85 | }; 86 | action.execute(params); 87 | }); 88 | } 89 | } 90 | module.exports = TryReceiveAllAction; 91 | -------------------------------------------------------------------------------- /src/worker/controller/actions/tasks/GetResultAction.js: -------------------------------------------------------------------------------- 1 | class GetResultAction { 2 | constructor(controller) { 3 | this._controller = controller; 4 | } 5 | async execute(params) { 6 | let taskId = params.taskId; 7 | let task = await this._controller.taskManager().asyncGetTask(taskId); 8 | return task.getResult(); 9 | } 10 | asyncExecute(params) { 11 | return this.execute(params); 12 | } 13 | } 14 | module.exports = GetResultAction; 15 | -------------------------------------------------------------------------------- /src/worker/controller/actions/tasks/HandleVerifiedTaskAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * this action is called once a task is verified and now needs to be executed. 3 | * it checks whether PTT in progress, if so the task execution is delayed. If not, the task execution starts immediately. 4 | * */ 5 | const constants = require("../../../../common/constants"); 6 | 7 | class HandleVerifiedTaskAction { 8 | constructor(controller) { 9 | this._controller = controller; 10 | } 11 | async execute(params) { 12 | const task = params.task; 13 | 14 | const executeTaskCallback = () => { 15 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.EXEC_TASK, { 16 | task: task 17 | }); 18 | }; 19 | 20 | // First check if we are in the middle of PTT 21 | // if so, schedule the task execution to after it is done 22 | if (this._controller.principal().isInPTT()) { 23 | this._controller.principal().once(constants.PTT_END_EVENT, executeTaskCallback); 24 | } 25 | // otherwise, execute task now 26 | else { 27 | executeTaskCallback(); 28 | } 29 | } 30 | } 31 | module.exports = HandleVerifiedTaskAction; 32 | -------------------------------------------------------------------------------- /src/worker/controller/actions/tasks/PublishTaskResultAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Once the TaskManager emits FINISH_TASK 3 | * this action: 4 | * - publish the result to the results topic 5 | * - publish the result back to etherum 6 | * */ 7 | const constants = require("../../../../common/constants"); 8 | const DeployTask = require("../../../tasks/DeployTask"); 9 | const EngCid = require("../../../../common/EngCID"); 10 | 11 | class PublishTaskResultAction { 12 | constructor(controller) { 13 | this._controller = controller; 14 | } 15 | async execute(params) { 16 | const task = params.task; 17 | let taskType; 18 | let err = null; 19 | 20 | if (task.isSuccess()) { 21 | taskType = task.getTaskType(); 22 | } else { 23 | taskType = constants.CORE_REQUESTS.FailedTask; 24 | } 25 | 26 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.PUBSUB_PUB, { 27 | topic: constants.PUBSUB_TOPICS.TASK_RESULTS, 28 | message: JSON.stringify({ 29 | contractAddress: task.getContractAddr(), 30 | result: task.getResult().toDbJson(), 31 | type: taskType 32 | }) 33 | }); 34 | 35 | // commit to Ethereum 36 | if (this._controller.hasEthereum()) { 37 | err = await this._controller.asyncExecCmd(constants.NODE_NOTIFICATIONS.COMMIT_RECEIPT, { 38 | task: task 39 | }); 40 | if (err) { 41 | this._controller.logger().info(`[PUBLISH_ANNOUNCE_TASK] error occurred in task ${task.getTaskId()} commit`); 42 | } 43 | } 44 | 45 | // announce as provider if its deployment and successful and no error occurred in task's commit 46 | if (task instanceof DeployTask && task.getResult().isSuccess() && !err) { 47 | // 48 | let ecid = EngCid.createFromSCAddress(task.getContractAddr()); 49 | if (ecid) { 50 | try { 51 | await this._controller.asyncExecCmd(constants.NODE_NOTIFICATIONS.ANNOUNCE_ENG_CIDS, { engCids: [ecid] }); 52 | } catch (e) { 53 | this._controller.logger().debug(`[PUBLISH_ANNOUNCE_TASK] cant publish ecid ${e}`); 54 | } 55 | } else { 56 | this._controller 57 | .logger() 58 | .error(`[PUBLISH_ANNOUNCE_TASK] cant publish ${task.getContractAddr()} - ecid is null`); 59 | } 60 | } 61 | } 62 | } 63 | module.exports = PublishTaskResultAction; 64 | -------------------------------------------------------------------------------- /src/worker/controller/actions/tasks/StartTaskExecutionAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This action takes a raw message (usually jsonrpc or self compute) 3 | * - turns it into a Task object 4 | * - sends to TaskManager 5 | * */ 6 | const ComputeTask = require("../../../tasks/ComputeTask"); 7 | const DeployTask = require("../../../tasks/DeployTask"); 8 | const taskTypes = require("../../../../common/constants").CORE_REQUESTS; 9 | 10 | class StartTaskExecutionAction { 11 | constructor(controller) { 12 | this._controller = controller; 13 | } 14 | execute(params) { 15 | const type = params.type; 16 | const request = params.request; 17 | const onResponse = params.onResponse; 18 | let task = null; 19 | 20 | // The following parameters are being overwritten in the VerifyNewTaskAction 21 | request.gasLimit = 0; 22 | request.blockNumber = 0; 23 | 24 | switch (type) { 25 | case taskTypes.DeploySecretContract: 26 | request.taskId = request.contractAddress; 27 | task = DeployTask.buildTask(request); 28 | break; 29 | case taskTypes.ComputeTask: 30 | task = ComputeTask.buildTask(request); 31 | break; 32 | } 33 | if (task) { 34 | this._controller.taskManager().addTaskUnverified(task); 35 | } 36 | if (onResponse) { 37 | onResponse(null); 38 | } 39 | } 40 | asyncExecute(params) { 41 | const action = this; 42 | return new Promise((res, rej) => { 43 | params.onResponse = function(err, verificationResult) { 44 | if (err) rej(err); 45 | else res(verificationResult); 46 | }; 47 | action.execute(params); 48 | }); 49 | } 50 | } 51 | module.exports = StartTaskExecutionAction; 52 | -------------------------------------------------------------------------------- /src/worker/controller/actions/tasks/VerifyNewTaskAction.js: -------------------------------------------------------------------------------- 1 | /** 2 | * verify a task 3 | * */ 4 | const constants = require("../../../../common/constants"); 5 | const ethUtils = require("../../../../common/utils"); 6 | 7 | class VerifyNewTaskAction { 8 | constructor(controller) { 9 | this._controller = controller; 10 | } 11 | async execute(params) { 12 | let onResult = params.onResponse; 13 | const unverifiedTask = params.task; 14 | this._controller.execCmd(constants.NODE_NOTIFICATIONS.REGISTRATION_PARAMS, { 15 | onResponse: async (err, regParams) => { 16 | // TODO: remove this default!!!! 17 | let isVerified = true; 18 | if (this._controller.hasEthereum()) { 19 | isVerified = false; 20 | try { 21 | const currentBlockNumber = await ethUtils.getEthereumBlockNumber( 22 | this._controller 23 | .ethereum() 24 | .api() 25 | .w3() 26 | ); 27 | let res = await this._controller 28 | .ethereum() 29 | .verifier() 30 | .verifyTaskCreation(unverifiedTask, currentBlockNumber, regParams.result.signingKey); 31 | if (res.error) { 32 | this._controller 33 | .logger() 34 | .info(`[VERIFY_NEW_TASK] error in verification of task ${unverifiedTask.getTaskId()}: ${res.error}`); 35 | } else if (res.isVerified) { 36 | unverifiedTask.setGasLimit(res.gasLimit); 37 | unverifiedTask.setBlockNumber(res.blockNumber); 38 | this._controller 39 | .logger() 40 | .debug(`[VERIFY_NEW_TASK] successful verification of task ${unverifiedTask.getTaskId()}`); 41 | isVerified = true; 42 | } 43 | } catch (err) { 44 | this._controller 45 | .logger() 46 | .error( 47 | `[VERIFY_NEW_TASK] an exception occurred while trying to verify task ${unverifiedTask.getTaskId()} = ${err}` 48 | ); 49 | } 50 | } 51 | await this._controller.taskManager().asyncOnVerifyTask(unverifiedTask.getTaskId(), isVerified); 52 | if (onResult) { 53 | onResult(null, isVerified); 54 | } 55 | } 56 | }); 57 | } 58 | 59 | asyncExecute(params) { 60 | const action = this; 61 | return new Promise((res, rej) => { 62 | params.onResponse = function(err, verificationResult) { 63 | if (err) rej(err); 64 | else res(verificationResult); 65 | }; 66 | action.execute(params); 67 | }); 68 | } 69 | } 70 | module.exports = VerifyNewTaskAction; 71 | 72 | // let c = nodeController; 73 | // let isVerified= await c.asyncExecCmd('verify aeubesiuhf', {task: Task}); 74 | -------------------------------------------------------------------------------- /src/worker/handlers/ManagementServer.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const http = require("http"); 3 | const EventEmitter = require("events").EventEmitter; 4 | const constants = require("../../common/constants"); 5 | const WEB_SERVER_CONSTANTS = constants.WEB_SERVER_CONSTANTS; 6 | const GET_PEERS_CONST = constants.NODE_NOTIFICATIONS.GET_PEERS; 7 | 8 | const mgmtActions = [ 9 | constants.NODE_NOTIFICATIONS.REGISTER, 10 | constants.NODE_NOTIFICATIONS.LOGIN, 11 | constants.NODE_NOTIFICATIONS.LOGOUT, 12 | constants.NODE_NOTIFICATIONS.UNREGISTER 13 | ]; 14 | 15 | class ManagementServer extends EventEmitter { 16 | constructor(config, logger) { 17 | super(); 18 | if (!config.hasOwnProperty("mgmtBase")) { 19 | throw new Error("Webserver config doesn't contain 'mgmtBase' option required"); 20 | } 21 | this._mgmtPort = Object.prototype.hasOwnProperty.call(config["mgmtBase"], "port") 22 | ? config["mgmtBase"].port 23 | : WEB_SERVER_CONSTANTS.MGMT.port; 24 | this._mgmtUrl = Object.prototype.hasOwnProperty.call(config["mgmtBase"], "url") 25 | ? config["mgmtBase"].url 26 | : WEB_SERVER_CONSTANTS.MGMT.url; 27 | 28 | this._logger = logger; 29 | this._app = express(); 30 | this._server = null; 31 | } 32 | 33 | start() { 34 | this._server = http.createServer(this._app); 35 | this._server.listen(this._mgmtPort); 36 | mgmtActions.forEach(item => { 37 | this._app.get(`${this._mgmtUrl}/${item}`, this.performAction.bind(this, item)); 38 | }); 39 | this._app.get(`${this._mgmtUrl}/connections`, this.getPeers.bind(this)); 40 | this._logger.debug(`listening on port ${this._mgmtPort} for management on URL ${this._mgmtUrl}`); 41 | } 42 | 43 | stop() { 44 | if (this._server) { 45 | this._server.close(); 46 | } 47 | } 48 | /** 49 | * Notify observer (Some controller subscribed) 50 | * @param {JSON} params, MUST CONTAIN notification field 51 | */ 52 | notify(params) { 53 | this.emit("notify", params); 54 | } 55 | async getPeers(req, res, next) { 56 | this.notify({ 57 | notification: GET_PEERS_CONST, 58 | callback: (err, result) => { 59 | if (Number.isInteger(result)) { 60 | res.json(result.toString()); 61 | } else { 62 | next(WEB_SERVER_CONSTANTS.error_code); 63 | } 64 | } 65 | }); 66 | } 67 | 68 | async performAction(notification, req, res, next) { 69 | this._logger.info(`Management Server: Got notification for ${notification}`); 70 | this.notify({ 71 | notification: notification, 72 | onResponse: (err, result) => { 73 | if (result) { 74 | res.send(result); 75 | } else { 76 | next(WEB_SERVER_CONSTANTS.error_code); 77 | } 78 | } 79 | }); 80 | } 81 | } 82 | 83 | module.exports = ManagementServer; 84 | -------------------------------------------------------------------------------- /src/worker/handlers/PrincipalNode.js: -------------------------------------------------------------------------------- 1 | const jayson = require("jayson"); 2 | const retry = require("retry"); 3 | const EventEmitter = require("events").EventEmitter; 4 | 5 | const constants = require("../../common/constants"); 6 | const MsgPrincipal = require("../../policy/p2p_messages/principal_messages"); 7 | const PRINCIPAL_CONSTANTS = constants.PRINCIPAL_NODE; 8 | 9 | class PrincipalNode extends EventEmitter { 10 | constructor(config, logger) { 11 | super(); 12 | 13 | if (config && config.uri) { 14 | this._uri = config.uri; 15 | } else { 16 | this._uri = PRINCIPAL_CONSTANTS.uri; 17 | } 18 | 19 | this._logger = logger; 20 | this._client = jayson.client.http(this._uri); 21 | this._pttInProgress = false; 22 | } 23 | 24 | startPTT() { 25 | if (this._pttInProgress) { 26 | this._logger.error("PTT is already in progress, cannot initiate a new one"); 27 | return false; 28 | } 29 | this._pttInProgress = true; 30 | return true; 31 | } 32 | 33 | onPTTEnd() { 34 | this._pttInProgress = false; 35 | this.emit(constants.PTT_END_EVENT); 36 | } 37 | 38 | isInPTT() { 39 | return this._pttInProgress; 40 | } 41 | 42 | async getStateKeys(msg) { 43 | return new Promise((resolve, reject) => { 44 | if (!(msg instanceof MsgPrincipal)) { 45 | // TODO: Changed to type error from common/errors. 46 | reject(new Error("getStateKeys accepts only object of type MsgPrincipal")); 47 | } 48 | 49 | // TODO: adjust config params and not use defaults 50 | let operation = retry.operation(PRINCIPAL_CONSTANTS.retryOptions); 51 | operation.attempt(currentAttempt => { 52 | this._client.request("getStateKeys", msg.toJson(), (err, response) => { 53 | if (this._logger) { 54 | this._logger.debug("Connecting to principal node: " + this._uri); 55 | } 56 | // Check if there was an error and the operation can be retried 57 | if ( 58 | (err || 59 | (response.error && 60 | response.error.code && 61 | response.error.code === PRINCIPAL_CONSTANTS.EPOCH_STATE_TRANSITION_ERROR_CODE)) && 62 | operation.retry(true) 63 | ) { 64 | const error = err || PRINCIPAL_CONSTANTS.EPOCH_STATE_TRANSITION_ERROR_CODE; 65 | this._logger.debug("Error received from KM, will retry. Error code =" + error); 66 | return; 67 | } 68 | 69 | // Check if there was an error (after the retries have done) and reject 70 | if (err) return reject(err); 71 | 72 | // Check the response and reject/resolve accordingly 73 | if (response.error) return reject(response.error); 74 | resolve(response.result); 75 | }); 76 | }); 77 | }); 78 | } 79 | } 80 | 81 | module.exports = PrincipalNode; 82 | -------------------------------------------------------------------------------- /src/worker/handlers/WebServer.js: -------------------------------------------------------------------------------- 1 | const express = require("express"); 2 | const http = require("http"); 3 | const EventEmitter = require("events").EventEmitter; 4 | const constants = require("../../common/constants"); 5 | const WEB_SERVER_CONSTANTS = constants.WEB_SERVER_CONSTANTS; 6 | 7 | class WebServer extends EventEmitter { 8 | constructor(config, logger) { 9 | super(); 10 | 11 | if (config && config.port) { 12 | this._port = config.port; 13 | } else { 14 | this._port = WEB_SERVER_CONSTANTS.port; 15 | } 16 | if (config.healthCheck && config.healthCheck.url) { 17 | this._healthCheckUrl = config.healthCheck.url; 18 | } else { 19 | this._healthCheckUrl = WEB_SERVER_CONSTANTS.health.url; 20 | } 21 | if (config.status && config.status.url) { 22 | this._statusUrl = config.status.url; 23 | } else { 24 | this._statusUrl = WEB_SERVER_CONSTANTS.status.url; 25 | } 26 | this._logger = logger; 27 | this._app = express(); 28 | this._server = null; 29 | } 30 | 31 | start() { 32 | this._server = http.createServer(this._app); 33 | this._server.listen(this._port); 34 | this._app.get(this._healthCheckUrl, this.performHealthCheck.bind(this)); 35 | this._app.get(this._statusUrl, this.getStatus.bind(this)); 36 | this._logger.debug( 37 | `listening on port ${this._port} for health check on URL ${this._healthCheckUrl} and status queries on URL ${this._statusUrl}` 38 | ); 39 | } 40 | 41 | stop() { 42 | if (this._server) { 43 | this._server.close(); 44 | } 45 | } 46 | /** 47 | * Notify observer (Some controller subscribed) 48 | * @param {JSON} params, MUST CONTAIN notification field 49 | */ 50 | notify(params) { 51 | this.emit("notify", params); 52 | } 53 | 54 | async performHealthCheck(req, res, next) { 55 | this.notify({ 56 | notification: constants.NODE_NOTIFICATIONS.HEALTH_CHECK, 57 | callback: (err, result) => { 58 | if (result.status) { 59 | res.send(result); 60 | } else { 61 | next(WEB_SERVER_CONSTANTS.error_code); 62 | } 63 | } 64 | }); 65 | } 66 | 67 | async getStatus(req, res, next) { 68 | this.notify({ 69 | notification: constants.NODE_NOTIFICATIONS.GET_WORKER_STATUS, 70 | callback: (err, status) => { 71 | if (err) { 72 | next(WEB_SERVER_CONSTANTS.error_code); 73 | } else { 74 | res.send(status); 75 | } 76 | } 77 | }); 78 | } 79 | } 80 | 81 | module.exports = WebServer; 82 | -------------------------------------------------------------------------------- /src/worker/libp2p-bundle.js: -------------------------------------------------------------------------------- 1 | const libp2p = require("libp2p"); 2 | const TCP = require("libp2p-tcp"); 3 | const Mplex = require("libp2p-mplex"); 4 | const SECIO = require("libp2p-secio"); 5 | const KadDHT = require("libp2p-kad-dht"); 6 | const defaultsDeep = require("@nodeutils/defaults-deep"); 7 | const Bootstrap = require("libp2p-bootstrap"); 8 | const SPDY = require("libp2p-spdy"); 9 | const WS = require("libp2p-websockets"); 10 | // const MulticastDNS = require('libp2p-mdns'); 11 | 12 | class PeerBundle extends libp2p { 13 | constructor(_options) { 14 | const defaults = { 15 | modules: { 16 | transport: [TCP, WS], 17 | streamMuxer: [Mplex, SPDY], 18 | connEncryption: [SECIO], 19 | peerDiscovery: [Bootstrap], 20 | dht: KadDHT 21 | }, 22 | config: { 23 | dht: { 24 | kBucketSize: 20 25 | }, 26 | EXPERIMENTAL: { 27 | dht: true, 28 | pubsub: true 29 | }, 30 | peerDiscovery: { 31 | bootstrap: { 32 | interval: 2000, 33 | enabled: false, 34 | list: [] 35 | } 36 | } 37 | } 38 | }; 39 | const finalConfigurations = defaultsDeep(_options, defaults); 40 | super(finalConfigurations); 41 | } 42 | } 43 | 44 | module.exports = PeerBundle; 45 | -------------------------------------------------------------------------------- /src/worker/state_sync/receiver/FindProviderResult.js: -------------------------------------------------------------------------------- 1 | class FindProviderResult { 2 | constructor() { 3 | this._map = {}; 4 | this._errored = {}; 5 | this._completeError = false; 6 | } 7 | setCompleteError() { 8 | this._completeError = true; 9 | } 10 | /** 11 | * add a new mapping 12 | * @param {EngCID} engCid 13 | * @param {Array} providerList 14 | */ 15 | addProviderResult(engCid, providerList) { 16 | const key = engCid.getKeccack256(); 17 | this._map[key] = { providers: providerList, ecid: engCid }; 18 | } 19 | addErroredProviderResult(engCid, error) { 20 | const key = engCid.getKeccack256(); 21 | this._errored[key] = { ecid: engCid, error: error }; 22 | } 23 | /** 24 | * indicates if there was a general error in the process - the _map will be empty in that case 25 | * @return {boolean} 26 | */ 27 | isCompleteError() { 28 | return this._completeError; 29 | } 30 | /** 31 | * indicates if some of the registries has error 32 | * @return {boolean} 33 | */ 34 | isErrors() { 35 | if (Object.keys(this._errored).length > 0) { 36 | return true; 37 | } else { 38 | return false; 39 | } 40 | } 41 | getProvidersMap() { 42 | return this._map; 43 | } 44 | getProvidersFor(ecid) { 45 | const key = ecid.getKeccack256(); 46 | if (this._map[key]) { 47 | return this._map[key].providers; 48 | } 49 | return null; 50 | } 51 | /** the keys are the keccack hash of each ecid 52 | * @return {Array}*/ 53 | getKeysList() { 54 | return Object.keys(this._map); 55 | } 56 | } 57 | 58 | module.exports = FindProviderResult; 59 | -------------------------------------------------------------------------------- /src/worker/state_sync/receiver/StateSyncReqVerifier.js: -------------------------------------------------------------------------------- 1 | const crypto = require("../../../common/cryptography"); 2 | const constants = require("../../../common/constants"); 3 | const MSG_TYPES = constants.P2P_MESSAGES; 4 | 5 | class StateSyncReqVerifier { 6 | /** 7 | * Verifies the syncMessage contents with the remoteMissingStates that was requested from Ethereum 8 | * @param {JSON} remoteMissingStates {address : {deltas: {index: deltaHash}, bytecodeHash: hash}} 9 | * @param {SyncMsg} syncMessage 10 | * @param {Function} callback (err, isOk)=>{} - isOk is a flag indicating whether the syncMessage is corresponding to the missing information 11 | * */ 12 | static verify(remoteMissingStates, syncMessage, callback) { 13 | let res = true; 14 | let err = null; 15 | 16 | const msgType = syncMessage.type(); 17 | 18 | if (msgType === MSG_TYPES.SYNC_STATE_RES) { 19 | const deltas = syncMessage.deltas(); 20 | for (let i = 0; i < deltas.length; i++) { 21 | const address = deltas[i].address; 22 | const data = deltas[i].data; 23 | const index = deltas[i].key; 24 | if (!(address in remoteMissingStates)) { 25 | err = "received an unknown address " + address + " in SyncStateRes"; 26 | res = false; 27 | break; 28 | } 29 | if (!(index in remoteMissingStates[address].deltas)) { 30 | err = "received an unknown index " + index + " for address " + address; 31 | res = false; 32 | break; 33 | } 34 | if (remoteMissingStates[address].deltas[index] != crypto.hash(data)) { 35 | err = "delta received for address " + address + " in index " + index + " does not match remote hash"; 36 | res = false; 37 | break; 38 | } 39 | } 40 | } else { 41 | if (msgType === MSG_TYPES.SYNC_BCODE_RES) { 42 | const address = syncMessage.address(); 43 | const bytecodeHash = crypto.hash(syncMessage.bytecode()); 44 | if (!(address in remoteMissingStates)) { 45 | err = "received an unknown address " + address + " in SyncBcodeRes"; 46 | res = false; 47 | } else if (!("bytecodeHash" in remoteMissingStates[address])) { 48 | err = "received a bytecodeHash for unknown address " + address; 49 | res = false; 50 | } else if (remoteMissingStates[address].bytecodeHash != bytecodeHash) { 51 | err = "bytecodeHash received for address " + address + " does not match remote hash"; 52 | res = false; 53 | } 54 | } else { 55 | err = "received an unknown msgType " + msgType; 56 | res = false; 57 | } 58 | } 59 | callback(err, res); 60 | } 61 | } 62 | module.exports = StateSyncReqVerifier; 63 | -------------------------------------------------------------------------------- /src/worker/tasks/ComputeTask.js: -------------------------------------------------------------------------------- 1 | const Task = require("./Task"); 2 | const Result = require("./Result"); 3 | const constants = require("../../common/constants"); 4 | class ComputeTask extends Task { 5 | /** 6 | * @param {JSON} computeReqMsg , all fields specified in the `expected` list in the func 7 | * @return {ComputeTask} task 8 | * */ 9 | static buildTask(computeReqMsg) { 10 | const expected = [ 11 | "taskId", 12 | "encryptedArgs", 13 | "encryptedFn", 14 | "userDHKey", 15 | "gasLimit", 16 | "contractAddress", 17 | "blockNumber" 18 | ]; 19 | const isMissing = expected.some(attr => { 20 | return !(attr in computeReqMsg); 21 | }); 22 | // TODO:: check more stuff in each field when building the task 23 | if (isMissing) { 24 | return null; 25 | } else { 26 | return new ComputeTask( 27 | computeReqMsg.taskId, 28 | computeReqMsg.encryptedArgs, 29 | computeReqMsg.encryptedFn, 30 | computeReqMsg.userDHKey, 31 | computeReqMsg.gasLimit, 32 | computeReqMsg.contractAddress, 33 | computeReqMsg.blockNumber 34 | ); 35 | } 36 | } 37 | constructor(taskId, encryptedArgs, encryptedFn, userDHKey, gasLimit, contractAddr, blockNumber) { 38 | super(taskId, constants.CORE_REQUESTS.ComputeTask, contractAddr, gasLimit, blockNumber); 39 | this._encryptedArgs = encryptedArgs; 40 | this._encryptedFn = encryptedFn; 41 | this._userDHKey = userDHKey; 42 | } 43 | getEncryptedArgs() { 44 | return this._encryptedArgs; 45 | } 46 | getEncryptedFn() { 47 | return this._encryptedFn; 48 | } 49 | getUserDHKey() { 50 | return this._userDHKey; 51 | } 52 | toDbJson() { 53 | const output = { 54 | status: this.getStatus(), 55 | taskId: this.getTaskId(), 56 | encryptedArgs: this.getEncryptedArgs(), 57 | encryptedFn: this.getEncryptedFn(), 58 | userDHKey: this.getUserDHKey(), 59 | gasLimit: this.getGasLimit(), 60 | contractAddress: this.getContractAddr(), 61 | blockNumber: this.getBlockNumber() 62 | }; 63 | if (this.isFinished()) { 64 | output.result = this._result.toDbJson(); 65 | } 66 | return output; 67 | } 68 | toCoreJson() { 69 | return { 70 | encryptedArgs: this.getEncryptedArgs(), 71 | encryptedFn: this.getEncryptedFn(), 72 | userDHKey: this.getUserDHKey(), 73 | gasLimit: this.getGasLimit(), 74 | contractAddress: this.getContractAddr() 75 | }; 76 | } 77 | static fromDbJson(taskObj) { 78 | if (taskObj.status) { 79 | const task = ComputeTask.buildTask(taskObj); 80 | task._setStatus(taskObj.status); 81 | if (taskObj.result && taskObj.result.status === constants.TASK_STATUS.SUCCESS) { 82 | const result = Result.ComputeResult.buildComputeResult(taskObj.result); 83 | task.setResult(result); 84 | } else if (taskObj.result) { 85 | const result = Result.FailedResult.buildFailedResult(taskObj.result); 86 | task.setResult(result); 87 | } 88 | return task; 89 | } 90 | return null; 91 | } 92 | } 93 | module.exports = ComputeTask; 94 | -------------------------------------------------------------------------------- /src/worker/tasks/DeployTask.js: -------------------------------------------------------------------------------- 1 | const Task = require("./Task"); 2 | const Result = require("./Result"); 3 | const constants = require("../../common/constants"); 4 | class DeployTask extends Task { 5 | /** 6 | * @param {JSON} deployReqMsg , all fields specified in the `expected` list in the func 7 | * @return {DeployTask} task 8 | * */ 9 | static buildTask(deployReqMsg) { 10 | const expected = [ 11 | "taskId", 12 | "preCode", 13 | "encryptedArgs", 14 | "encryptedFn", 15 | "userDHKey", 16 | "gasLimit", 17 | "contractAddress", 18 | "blockNumber" 19 | ]; 20 | const isMissing = expected.some(attr => { 21 | return !(attr in deployReqMsg); 22 | }); 23 | // TODO:: check more stuff in each field when building the task 24 | if (isMissing) { 25 | return null; 26 | } else { 27 | return new DeployTask( 28 | deployReqMsg.taskId, 29 | deployReqMsg.preCode, 30 | deployReqMsg.encryptedArgs, 31 | deployReqMsg.encryptedFn, 32 | deployReqMsg.userDHKey, 33 | deployReqMsg.gasLimit, 34 | deployReqMsg.contractAddress, 35 | deployReqMsg.blockNumber 36 | ); 37 | } 38 | } 39 | constructor(taskId, preCode, encryptedArgs, encryptedFn, userDHKey, gasLimit, contractAddr, blockNumber) { 40 | super(taskId, constants.CORE_REQUESTS.DeploySecretContract, contractAddr, gasLimit, blockNumber); 41 | this._preCode = preCode; 42 | this._encryptedArgs = encryptedArgs; 43 | this._encryptedFn = encryptedFn; 44 | this._userDHKey = userDHKey; 45 | } 46 | getPreCode() { 47 | return this._preCode; 48 | } 49 | getEncryptedArgs() { 50 | return this._encryptedArgs; 51 | } 52 | getEncryptedFn() { 53 | return this._encryptedFn; 54 | } 55 | getUserDHKey() { 56 | return this._userDHKey; 57 | } 58 | toDbJson() { 59 | const output = { 60 | status: this.getStatus(), 61 | taskId: this.getTaskId(), 62 | preCode: this.getPreCode(), 63 | encryptedArgs: this.getEncryptedArgs(), 64 | encryptedFn: this.getEncryptedFn(), 65 | userDHKey: this.getUserDHKey(), 66 | gasLimit: this.getGasLimit(), 67 | contractAddress: this.getContractAddr(), 68 | blockNumber: this.getBlockNumber() 69 | }; 70 | if (this.isFinished()) { 71 | output.result = this._result.toDbJson(); 72 | } 73 | return output; 74 | } 75 | toCoreJson() { 76 | return { 77 | preCode: this.getPreCode(), 78 | encryptedArgs: this.getEncryptedArgs(), 79 | encryptedFn: this.getEncryptedFn(), 80 | userDHKey: this.getUserDHKey(), 81 | gasLimit: this.getGasLimit(), 82 | contractAddress: this.getContractAddr() 83 | }; 84 | } 85 | static fromDbJson(taskObj) { 86 | if (taskObj.status) { 87 | const task = DeployTask.buildTask(taskObj); 88 | task._setStatus(taskObj.status); 89 | if (taskObj.result && taskObj.result.status !== constants.TASK_STATUS.FAILED) { 90 | // here is string 91 | const result = Result.DeployResult.buildDeployResult(taskObj.result); 92 | task.setResult(result); 93 | } else if (taskObj.result) { 94 | const result = Result.FailedResult.buildFailedResult(taskObj.result); 95 | task.setResult(result); 96 | } 97 | return task; 98 | } 99 | return null; 100 | } 101 | } 102 | module.exports = DeployTask; 103 | -------------------------------------------------------------------------------- /src/worker/tasks/OutsideTask.js: -------------------------------------------------------------------------------- 1 | const Task = require("./Task"); 2 | const Result = require("./Result").Result; 3 | 4 | class OutsideTask extends Task { 5 | constructor(taskId, type, result) { 6 | super(taskId, type); 7 | // set task status 8 | this.setResult(result); 9 | } 10 | static buildTask(type, rawResult) { 11 | let result = Result.buildFromRaw(type, rawResult); 12 | if (result) { 13 | return new OutsideTask(result.getTaskId(), type, result); 14 | } 15 | return null; 16 | } 17 | toDbJson() { 18 | let output = { 19 | outsideTask: true, 20 | status: this.getResult().getStatus(), 21 | type: this.getTaskType(), 22 | taskId: this.getTaskId(), 23 | result: this.getResult().toDbJson() 24 | }; 25 | return output; 26 | } 27 | static fromDbJson(taskObj) { 28 | if (taskObj.status) { 29 | const task = OutsideTask.buildTask(taskObj.type, taskObj.result); 30 | return task; 31 | } 32 | return null; 33 | } 34 | } 35 | 36 | module.exports = OutsideTask; 37 | -------------------------------------------------------------------------------- /src/worker/tasks/Task.js: -------------------------------------------------------------------------------- 1 | const constants = require("../../common/constants"); 2 | const utils = require("../../common/utils"); 3 | const EventEmitter = require("events").EventEmitter; 4 | const Result = require("./Result").Result; 5 | 6 | class Task extends EventEmitter { 7 | constructor(taskId, type, contractAddress, gasLimit, blockNumber) { 8 | super(); 9 | this._taskId = utils.remove0x(taskId); 10 | this._status = constants.TASK_STATUS.UNVERIFIED; 11 | this._result = null; 12 | this._type = type; 13 | this._contractAddr = utils.remove0x(contractAddress); 14 | this._gasLimit = gasLimit; 15 | this._blockNumber = blockNumber; 16 | } 17 | /** 18 | * set the task result 19 | * @param {Result} result 20 | * */ 21 | setResult(result) { 22 | if (result instanceof Result && result.getTaskId() === this.getTaskId()) { 23 | this._result = result; 24 | if (result.isSuccess()) { 25 | this.setSuccessStatus(); 26 | } else { 27 | this.setFailedStatus(); 28 | } 29 | } 30 | } 31 | /** 32 | * get the task result 33 | * @return {Result} result or null 34 | * */ 35 | getResult() { 36 | return this._result; 37 | } 38 | _setStatus(status) { 39 | this._status = status; 40 | this.emit("status", { taskId: this._taskId, status: status }); 41 | } 42 | setInProgressStatus() { 43 | this._setStatus(constants.TASK_STATUS.IN_PROGRESS); 44 | return this; 45 | } 46 | setSuccessStatus() { 47 | this._setStatus(constants.TASK_STATUS.SUCCESS); 48 | return this; 49 | } 50 | setFailedStatus() { 51 | this._setStatus(constants.TASK_STATUS.FAILED); 52 | return this; 53 | } 54 | setGasLimit(gasLimit) { 55 | this._gasLimit = gasLimit; 56 | } 57 | setBlockNumber(blockNumber) { 58 | this._blockNumber = blockNumber; 59 | } 60 | getStatus() { 61 | return this._status; 62 | } 63 | getTaskId() { 64 | return this._taskId; 65 | } 66 | getTaskType() { 67 | return this._type; 68 | } 69 | getGasLimit() { 70 | return this._gasLimit; 71 | } 72 | getContractAddr() { 73 | return this._contractAddr; 74 | } 75 | getBlockNumber() { 76 | return this._blockNumber; 77 | } 78 | isUnverified() { 79 | return this._status === constants.TASK_STATUS.UNVERIFIED; 80 | } 81 | isSuccess() { 82 | return this._status === constants.TASK_STATUS.SUCCESS; 83 | } 84 | isFailed() { 85 | return this._status === constants.TASK_STATUS.FAILED; 86 | } 87 | isFinished() { 88 | return this.isSuccess() || this.isFailed(); 89 | } 90 | } 91 | module.exports = Task; 92 | -------------------------------------------------------------------------------- /test/basic_test.js: -------------------------------------------------------------------------------- 1 | const path = require("path"); 2 | const testUtils = require("./testUtils/utils"); 3 | const assert = require("assert"); 4 | const TEST_TREE = require("./test_tree").TEST_TREE; 5 | const WorkerBuilder = require("../src/worker/builder/WorkerBuilder"); 6 | const NodeController = require("../src/worker/controller/NodeController"); 7 | 8 | const B1Path = path.join(__dirname, "testUtils", "id-l.json"); 9 | const B1Port = "10300"; 10 | 11 | it("#1 Should test the worker builder", async function() { 12 | let tree = TEST_TREE["basic"]; 13 | if (!tree["all"] || !tree["#1"]) { 14 | this.skip(); 15 | } 16 | 17 | return new Promise(async resolve => { 18 | // load configs 19 | let c = WorkerBuilder.loadConfig(); 20 | // change defaults 21 | c.nickname = "worker"; 22 | c.idPath = B1Path; 23 | // build the worker 24 | let worker = WorkerBuilder.build(c); 25 | // start the worker 26 | await worker.syncRun(); 27 | 28 | await testUtils.sleep(1000); 29 | assert.strictEqual(0, worker.getAllPeersInfo().length, "peer info don't match "); 30 | // stop the worker 31 | await worker.syncStop(); 32 | resolve(); 33 | }); 34 | }); 35 | 36 | it("#2 Should test dialing to a bootstrap", async function() { 37 | let tree = TEST_TREE["basic"]; 38 | if (!tree["all"] || !tree["#2"]) { 39 | this.skip(); 40 | } 41 | return new Promise(async resolve => { 42 | let bootstrapNodes = ["/ip4/0.0.0.0/tcp/10300/ipfs/QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm"]; 43 | let bootstrapController = NodeController.initDefaultTemplate({ 44 | port: B1Port, 45 | idPath: B1Path, 46 | nickname: "bootstrap", 47 | bootstrapNodes: bootstrapNodes, 48 | extraConfig: {} 49 | }); 50 | let peerController = NodeController.initDefaultTemplate({ 51 | nickname: "peer", 52 | bootstrapNodes: bootstrapNodes, 53 | extraConfig: {} 54 | }); 55 | 56 | await bootstrapController.engNode().syncRun(); 57 | await peerController.engNode().syncRun(); 58 | await testUtils.sleep(3000); 59 | 60 | assert.strictEqual(bootstrapController.isConnected(peerController.getSelfB58Id()), true); 61 | assert.strictEqual(bootstrapController.getConnectedPeers().length, 1); 62 | assert.strictEqual(peerController.isConnected(bootstrapController.getSelfB58Id()), true); 63 | assert.strictEqual(peerController.getConnectedPeers().length, 1); 64 | 65 | await bootstrapController.engNode().syncStop(); 66 | await peerController.engNode().syncStop(); 67 | resolve(); 68 | }); 69 | }); 70 | 71 | it("#3 Should test libp2p discovery", async function() { 72 | let tree = TEST_TREE["basic"]; 73 | if (!tree["all"] || !tree["#3"]) { 74 | this.skip(); 75 | } 76 | 77 | return new Promise(async (resolve, reject) => { 78 | let nodesNum = 10; 79 | let bootstrapNodes = ["/ip4/0.0.0.0/tcp/10300/ipfs/QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm"]; 80 | let bNode = NodeController.initDefaultTemplate({ 81 | port: B1Port, 82 | idPath: B1Path, 83 | nickname: "bootstrap", 84 | bootstrapNodes: bootstrapNodes, 85 | extraConfig: {} 86 | }); 87 | 88 | let peers = []; 89 | 90 | for (let i = 0; i < nodesNum; i++) { 91 | let p = NodeController.initDefaultTemplate({ 92 | nickname: "peer" + i, 93 | bootstrapNodes: bootstrapNodes, 94 | extraConfig: {} 95 | }); 96 | peers.push(p); 97 | } 98 | 99 | // init bootstrap nodes 100 | await bNode.engNode().syncRun(); 101 | 102 | // init peer nodes 103 | for (let i = 0; i < nodesNum; i++) { 104 | await peers[i].engNode().syncRun(); 105 | } 106 | 107 | await testUtils.sleep(3000); 108 | 109 | assert.strictEqual(bNode.getConnectedPeers().length, nodesNum); 110 | for (let i = 0; i < nodesNum; ++i) { 111 | assert.strictEqual(bNode.getConnectedPeers().length, nodesNum); 112 | } 113 | 114 | for (let i = 0; i < nodesNum; ++i) { 115 | await peers[i].engNode().syncStop(); 116 | } 117 | await bNode.engNode().syncStop(); 118 | resolve(); 119 | }); 120 | }); 121 | -------------------------------------------------------------------------------- /test/ethereum/EnigmaContractMock.js: -------------------------------------------------------------------------------- 1 | class EnigmaContractMock { 2 | constructor() { 3 | this._taskRecords = {}; 4 | this._contracts = {}; 5 | this._epochSize = null; 6 | this._taskTimeout = 0; 7 | this._ethereumBlockNumber = 0; 8 | this._eventListeners = {}; 9 | this._workersParams = []; 10 | this._except = false; 11 | } 12 | 13 | setTaskParams(taskId, blockNumber, status, gasLimit, inputsHash, outputHash) { 14 | this._taskRecords[taskId] = { 15 | taskId: taskId, 16 | blockNumber: blockNumber, 17 | status: status, 18 | gasLimit: gasLimit, 19 | inputsHash: inputsHash, 20 | outputHash: outputHash 21 | }; 22 | } 23 | 24 | setContractParams(contractAddress, codeHash, deltas) { 25 | this._contracts[contractAddress] = { 26 | codeHash: codeHash, 27 | deltaHashes: deltas 28 | }; 29 | } 30 | 31 | setEpochSize(size) { 32 | this._epochSize = size; 33 | } 34 | 35 | setWorkerParams(workerParams) { 36 | this._workersParams = workerParams; 37 | } 38 | 39 | setTaskTimeout(blocks) { 40 | this._taskTimeout = blocks; 41 | } 42 | 43 | setEthereumBlockNumber(number) { 44 | this._ethereumBlockNumber = number; 45 | } 46 | 47 | getTaskParams(taskId) { 48 | if (this._except) { 49 | throw Error("Ethereum Mock exception"); 50 | } 51 | return this._taskRecords[taskId]; 52 | } 53 | 54 | getEpochSize() { 55 | return this._epochSize; 56 | } 57 | 58 | getWorkersParams() { 59 | return this._workersParams; 60 | } 61 | 62 | getContractParams(contractAddress) { 63 | return this._contracts[contractAddress]; 64 | } 65 | 66 | getTaskTimeout() { 67 | return this._taskTimeout; 68 | } 69 | 70 | getEthereumBlockNumber() { 71 | return this._ethereumBlockNumber; 72 | } 73 | 74 | getEthereumBlockNumberAsync(cb) { 75 | cb(null, this._ethereumBlockNumber); 76 | } 77 | 78 | subscribe(eventName, filter, callback) { 79 | this._eventListeners[eventName] = callback; 80 | } 81 | 82 | triggerEvent(eventName, event) { 83 | this._eventListeners[eventName](null, event); 84 | } 85 | 86 | triggerException() { 87 | this._except = true; 88 | } 89 | 90 | w3() { 91 | return { 92 | eth: { getBlockNumber: this.getEthereumBlockNumberAsync.bind(this) } 93 | }; 94 | } 95 | } 96 | 97 | module.exports = EnigmaContractMock; 98 | -------------------------------------------------------------------------------- /test/ethereum/EthereumAPIMock.js: -------------------------------------------------------------------------------- 1 | const EnigmaContractMock = require("./EnigmaContractMock"); 2 | const EthereumAPI = require("../../src/ethereum/EthereumAPI"); 3 | const EthereumServices = require("../../src/ethereum/EthereumServices"); 4 | const EthereumVerifier = require("../../src/ethereum/EthereumVerifier"); 5 | 6 | class EthereumAPIMock extends EthereumAPI { 7 | constructor(logger) { 8 | super(logger); 9 | this._api = new EnigmaContractMock(); 10 | this._services = new EthereumServices(this._api); 11 | this._verifier = new EthereumVerifier(this._api, this._services, logger); 12 | } 13 | 14 | async init() { 15 | this._services.initServices(); 16 | await this._verifier.init(); 17 | } 18 | 19 | async destroy() {} 20 | } 21 | 22 | module.exports = EthereumAPIMock; 23 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/EnigmaToken.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol"; 3 | import "openzeppelin-solidity/contracts/token/ERC20/ERC20Detailed.sol"; 4 | 5 | /** 6 | * @title Enigma Token 7 | * @dev ERC20 Enigma Token (ENG) 8 | * 9 | * ENG Tokens are divisible by 1e8 (100,000,000) base 10 | * units referred to as 'Grains'. 11 | * 12 | * ENG are displayed using 8 decimal places of precision. 13 | * 14 | * 1 ENG is equivalent to: 15 | * 100000000 == 1 * 10**8 == 1e8 == One Hundred Million Grains 16 | * 17 | * 150 million ENG (total supply) is equivalent to: 18 | * 15000000000000000 == 150000000 * 10**8 == 1e17 19 | * 20 | * All initial ENG Grains are assigned to the creator of 21 | * this contract. 22 | * 23 | */ 24 | contract EnigmaToken is ERC20, ERC20Detailed { 25 | 26 | uint256 public constant INITIAL_SUPPLY = 150000000 * 10**8; // 150 million ENG specified in Grains 27 | 28 | /** 29 | * @dev EnigmaToken Constructor 30 | * Runs only on initial contract creation. 31 | */ 32 | constructor() public ERC20Detailed("Enigma", "ENG", 8) { 33 | _mint(msg.sender, INITIAL_SUPPLY); 34 | } 35 | 36 | /** 37 | * @dev Transfer token for a specified address when not paused 38 | * @param _to The address to transfer to. 39 | * @param _value The amount to be transferred. 40 | */ 41 | function transfer(address _to, uint256 _value) public returns (bool) { 42 | return super.transfer(_to, _value); 43 | } 44 | 45 | /** 46 | * @dev Transfer tokens from one address to another when not paused 47 | * @param _from address The address which you want to send tokens from 48 | * @param _to address The address which you want to transfer to 49 | * @param _value uint256 the amount of tokens to be transferred 50 | */ 51 | function transferFrom(address _from, address _to, uint256 _value) public returns (bool) { 52 | return super.transferFrom(_from, _to, _value); 53 | } 54 | 55 | /** 56 | * @dev Approve the passed address to spend the specified amount of tokens on behalf of msg.sender when not paused. 57 | * @param _spender The address which will spend the funds. 58 | * @param _value The amount of tokens to be spent. 59 | */ 60 | function approve(address _spender, uint256 _value) public returns (bool) { 61 | return super.approve(_spender, _value); 62 | } 63 | 64 | function allowance(address _owner, address _spender) public view returns (uint256) { 65 | return super.allowance(_owner,_spender); 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/ExchangeRate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | contract ExchangeRate { 5 | 6 | constructor() public { 7 | 8 | } 9 | 10 | function getExchangeRate() public view returns (uint256) { 11 | return 164518;// 0.00164518 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/Migrations.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | contract Migrations { 4 | address public owner; 5 | uint public last_completed_migration; 6 | 7 | modifier restricted() { 8 | if (msg.sender == owner) { 9 | _; 10 | } 11 | } 12 | 13 | constructor() public { 14 | owner = msg.sender; 15 | } 16 | 17 | function setCompleted(uint completed) public restricted { 18 | last_completed_migration = completed; 19 | } 20 | 21 | function upgrade(address new_address) public restricted { 22 | Migrations upgraded = Migrations(new_address); 23 | upgraded.setCompleted(last_completed_migration); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/impl/EnigmaEvents.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | /** 5 | * @author Enigma 6 | * 7 | * Registers events to be emitted by the various functionalities of the Enigma codebase (need to be detailed here 8 | * as well as in the individual library as well 9 | */ 10 | contract EnigmaEvents { 11 | event Registered(address custodian, address signer); 12 | event Unregistered(address custodian); 13 | event WorkersParameterized(uint seed, uint256 firstBlockNumber, uint256 inclusionBlockNumber, address[] workers, 14 | uint[] stakes, uint nonce); 15 | event TaskRecordCreated(bytes32 indexed taskId, bytes32 inputsHash, uint64 gasLimit, uint64 gasPx, address sender, 16 | uint blockNumber); 17 | // ReceiptVerified => bytes32s [scAddr, taskId, stateDeltaHash, outputHash] 18 | event ReceiptVerified(bytes32 indexed taskId, uint64 gasUsed, address optionalEthereumContractAddress, 19 | bytes32[4] bytes32s, uint deltaHashIndex, uint gasUsedTotal, bytes optionalEthereumData, address workerAddress); 20 | event ReceiptFailed(bytes32 indexed taskId, bytes32 scAddr, bytes32 outputHash, 21 | uint gasUsed, address workerAddress); 22 | event ReceiptFailedETH(bytes32 indexed taskId, bytes32 scAddr, uint gasUsed, uint gasUsedTotal, 23 | address workerAddress); 24 | event TaskFeeReturned(bytes32 indexed taskId); 25 | event DepositSuccessful(address from, uint value); 26 | event WithdrawSuccessful(address to, uint value); 27 | // SecretContractDeployed => bytes32s [taskId, preCodeHash, codeHash, initStateDeltaHash] 28 | event SecretContractDeployed(bytes32 indexed taskId, uint64 gasUsed, address optionalEthereumContractAddress, 29 | bytes32[4] bytes32s, uint gasUsedTotal, bytes optionalEthereumData, address workerAddress); 30 | event LoggedIn(address workerAddress); 31 | event LoggedOut(address workerAddress); 32 | } 33 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/impl/EnigmaState.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import { EnigmaCommon } from "./EnigmaCommon.sol"; 5 | import { ERC20 } from "../interfaces/ERC20.sol"; 6 | 7 | /** 8 | * @author Enigma 9 | * 10 | * Maintains the state of the Enigma contract (and the associated libraries) 11 | */ 12 | library EnigmaState { 13 | struct State { 14 | // The interface of the deployed ENG ERC20 token contract 15 | ERC20 engToken; 16 | 17 | // Epoch size in number of blocks 18 | uint epochSize; 19 | 20 | // Task timeout size in number of blocks 21 | uint taskTimeoutSize; 22 | 23 | /** 24 | * The signer address of the principal node 25 | * This must be set when deploying the contract and remains immutable 26 | * Since the signer address is derived from the public key of an 27 | * SGX enclave, this ensures that the principal node cannot be tempered 28 | * with or replaced. 29 | */ 30 | address principal; 31 | 32 | address exchangeRate; 33 | address updatedEnigmaContractAddress; 34 | 35 | address oldEnigmaContractAddress; 36 | 37 | /** 38 | * The last 5 worker parameters 39 | * We keep a collection of worker parameters to account for latency issues. 40 | * A computation task might be conceivably given out at a certain block number 41 | * but executed at a later block in a different epoch. It follows that 42 | * the contract must have access to the worker parameters effective when giving 43 | * out the task, otherwise the selected worker would not match. We calculated 44 | * that keeping the last 5 items should be more than enough to account for 45 | * all latent tasks. Tasks results will be rejected past this limit. 46 | */ 47 | EnigmaCommon.WorkersParams[5] workersParams; 48 | 49 | // An address-based index of all registered worker 50 | address[] workerAddresses; 51 | // An address-based index of all secret contracts 52 | bytes32[] scAddresses; 53 | 54 | // A registry of all registered workers with their attributes 55 | mapping(address => EnigmaCommon.Worker) workers; 56 | 57 | mapping(address => address) stakingToOperatingAddresses; 58 | 59 | // A registry of all tasks with their attributes 60 | mapping(bytes32 => EnigmaCommon.TaskRecord) tasks; 61 | 62 | // An array of all task IDs 63 | bytes32[] taskIds; 64 | 65 | // A registry of all deployed secret contracts with their attributes 66 | mapping(bytes32 => EnigmaCommon.SecretContract) contracts; 67 | 68 | // A mapping of number of tasks deployed for each address 69 | mapping(address => uint) userTaskDeployments; 70 | 71 | // TODO: do we keep tasks forever? if not, when do we delete them? 72 | uint stakingThreshold; 73 | uint workerGroupSize; 74 | 75 | bool debug; 76 | bytes mrSigner; 77 | bytes isvSvn; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/impl/EnigmaStorage.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import { EnigmaState } from "./EnigmaState.sol"; 5 | 6 | /** 7 | * @author Enigma 8 | * 9 | * Storage for the Enigma state 10 | */ 11 | contract EnigmaStorage { 12 | EnigmaState.State state; 13 | } 14 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/impl/SecretContractImpl.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; 6 | 7 | import { EnigmaCommon } from "./EnigmaCommon.sol"; 8 | import { EnigmaState } from "./EnigmaState.sol"; 9 | import "../utils/SolRsaVerify.sol"; 10 | 11 | /** 12 | * @author Enigma 13 | * 14 | * Library that maintains functionality associated with secret contracts 15 | */ 16 | library SecretContractImpl { 17 | using SafeMath for uint256; 18 | using ECDSA for bytes32; 19 | 20 | function countStateDeltasImpl(EnigmaState.State storage state, bytes32 _scAddr) 21 | public 22 | view 23 | returns (uint) 24 | { 25 | return state.contracts[_scAddr].stateDeltaHashes.length; 26 | } 27 | 28 | function countSecretContractsImpl(EnigmaState.State storage state) 29 | public 30 | view 31 | returns (uint) 32 | { 33 | return state.scAddresses.length; 34 | } 35 | 36 | function getSecretContractAddressesImpl(EnigmaState.State storage state, uint _start, uint _stop) 37 | public 38 | view 39 | returns (bytes32[] memory) 40 | { 41 | if (_stop == 0) { 42 | _stop = state.scAddresses.length; 43 | } 44 | bytes32[] memory addresses = new bytes32[](_stop.sub(_start)); 45 | uint pos = 0; 46 | for (uint i = _start; i < _stop; i++) { 47 | addresses[pos] = state.scAddresses[i]; 48 | pos++; 49 | } 50 | return addresses; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/impl/UpgradeImpl.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.12; 2 | pragma experimental ABIEncoderV2; 3 | 4 | import "openzeppelin-solidity/contracts/math/SafeMath.sol"; 5 | import "openzeppelin-solidity/contracts/cryptography/ECDSA.sol"; 6 | 7 | import { EnigmaCommon } from "./EnigmaCommon.sol"; 8 | import { EnigmaState } from "./EnigmaState.sol"; 9 | import { Bytes } from "../utils/Bytes.sol"; 10 | 11 | /** 12 | * @author Enigma 13 | * 14 | * Library that maintains functionality associated with tasks 15 | */ 16 | library UpgradeImpl { 17 | using SafeMath for uint256; 18 | using SafeMath for uint64; 19 | using ECDSA for bytes32; 20 | using Bytes for address; 21 | 22 | function upgradeEnigmaContractImpl( 23 | EnigmaState.State storage state, 24 | address _updatedEnigmaContractAddress 25 | ) 26 | public 27 | { 28 | state.updatedEnigmaContractAddress = _updatedEnigmaContractAddress; 29 | 30 | for (uint i = 0; i < state.taskIds.length; i++) { 31 | if (state.tasks[state.taskIds[i]].status == EnigmaCommon.TaskStatus.RecordCreated) { 32 | EnigmaCommon.TaskRecord storage task = state.tasks[state.taskIds[i]]; 33 | 34 | // Return the full fee to the task sender 35 | require(state.engToken.transfer(task.sender, task.gasLimit.mul(task.gasPx)), "Token transfer failed"); 36 | 37 | // Set task's status to ReceiptFailed and emit event 38 | task.status = EnigmaCommon.TaskStatus.ReceiptFailedReturn; 39 | } 40 | } 41 | } 42 | 43 | function transferWorkerStakePostUpgradeImpl( 44 | EnigmaState.State storage state, 45 | address _operatingAddress, 46 | address _stakingAddress, 47 | bytes memory _sig 48 | ) 49 | public 50 | returns (uint256) 51 | { 52 | require(state.workers[_operatingAddress].stakingAddress == _stakingAddress, 53 | "Invalid staking address for registration balance transfer"); 54 | // Verify the worker's signature 55 | bytes memory message; 56 | message = EnigmaCommon.appendMessage(message, state.updatedEnigmaContractAddress.toBytes()); 57 | bytes32 msgHash = keccak256(message); 58 | require(msgHash.toEthSignedMessageHash().recover(_sig) == _operatingAddress, "Invalid signature"); 59 | EnigmaCommon.Worker storage worker = state.workers[_operatingAddress]; 60 | uint256 oldWorkerBalance = worker.balance; 61 | worker.balance = 0; 62 | require(state.engToken.transfer(state.updatedEnigmaContractAddress, oldWorkerBalance), 63 | "Token transfer failed"); 64 | return oldWorkerBalance; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/interfaces/ERC20.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | /** 5 | * @author Enigma 6 | * 7 | * ERC20 interface to wrap the EnigmaToken contract 8 | */ 9 | interface ERC20 { 10 | function allowance(address owner, address spender) external view returns (uint256); 11 | 12 | function transferFrom(address from, address to, uint256 value) external returns (bool); 13 | 14 | function approve(address spender, uint256 value) external returns (bool); 15 | 16 | function totalSupply() external view returns (uint256); 17 | 18 | function balanceOf(address who) external view returns (uint256); 19 | 20 | function transfer(address to, uint256 value) external returns (bool); 21 | 22 | event Transfer(address indexed from, address indexed to, uint256 value); 23 | event Approval(address indexed owner, address indexed spender, uint256 value); 24 | } 25 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/interfaces/IEnigma.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | interface IEnigma { 5 | function register(address _signer, bytes calldata _report, bytes calldata _signature) external; 6 | function getActiveWorkers(uint _blockNumber) external view returns (address[] memory, uint[] memory); 7 | function setWorkersParams(uint _blockNumber, uint _seed, bytes calldata _sig) external; 8 | function countSecretContracts() external view returns (uint); 9 | function getSecretContractAddresses(uint _start, uint _stop) external view returns (bytes32[] memory); 10 | function getSigningAddress() external view returns (address); 11 | } 12 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/interfaces/IExchangeRate.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | pragma experimental ABIEncoderV2; 3 | 4 | interface IExchangeRate { 5 | function getExchangeRate() external view returns (uint256); 6 | } 7 | -------------------------------------------------------------------------------- /test/ethereum/scripts/contracts/utils/GetCode2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.5.0; 2 | 3 | library GetCode2 { 4 | function at(address _addr) internal view returns (bytes memory o_code) { 5 | assembly { 6 | // retrieve the size of the code, this needs assembly 7 | let size := extcodesize(_addr) 8 | // allocate output byte array - this could also be done without assembly 9 | // by using o_code = new bytes(size) 10 | o_code := mload(0x40) 11 | // new "memory end" including padding 12 | mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f)))) 13 | // store length in memory 14 | mstore(o_code, size) 15 | // actually retrieve the code, this needs assembly 16 | extcodecopy(_addr, add(o_code, 0x20), 0, size) 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /test/ethereum/scripts/env_initializer.js: -------------------------------------------------------------------------------- 1 | const { exec, spawn } = require("child_process"); 2 | const Web3 = require("web3"); 3 | 4 | const testUtils = require("../../testUtils/utils"); 5 | const path = require("path"); 6 | 7 | let subprocess; // Global `trufffle develop` "child process" object 8 | 9 | function buildEnv(truffleDirectory) { 10 | return new Promise((resolve, reject) => { 11 | const command = "cd " + truffleDirectory + " && npx truffle compile && cd " + process.cwd(); 12 | exec(command, (err, stdout, stderr) => { 13 | if (err) { 14 | reject("env_initializer.buildEnv " + err); 15 | } 16 | //console.log(stdout); 17 | resolve(stderr, stdout); 18 | }); 19 | }); 20 | } 21 | 22 | function resetEnv(truffleDirectory) { 23 | return new Promise((resolve, reject) => { 24 | const command = "cd " + truffleDirectory + " && npx truffle migrate --reset && cd " + process.cwd(); 25 | exec(command, (err, stdout, stderr) => { 26 | if (err) { 27 | reject("env_initalizer.resetEnv " + err); 28 | } 29 | //console.log(stdout); 30 | resolve(stderr, stdout); 31 | }); 32 | }); 33 | } 34 | 35 | async function init(truffleDirectory) { 36 | await buildEnv(truffleDirectory); 37 | await resetEnv(truffleDirectory); 38 | 39 | //await testUtils.sleep(3000); 40 | 41 | const truffleConfig = require(path.join(truffleDirectory, "truffle")); 42 | const EnigmaContractJson = require("./build/contracts/Enigma.json"); //require('./build/contracts/EnigmaMock.json'); 43 | //const EnigmaTokenContractJson = require('./build/contracts/EnigmaToken.json'); 44 | 45 | const websocketProvider = "ws://127.0.0.1:9545"; 46 | const provider = new Web3.providers.WebsocketProvider(websocketProvider); 47 | 48 | // from https://github.com/ethereum/web3.js/issues/1354 49 | // provider.on('error', (e) => console.error('WS Error: ', e)); // provider.on('error', e => console.error('WS Error', e)); 50 | // provider.on('end', (e) => console.log('WS End')); // provider.on('end', e => console.error('WS End', e)); 51 | 52 | const web3 = new Web3(provider); 53 | 54 | // const accounts = await web3.eth.getAccounts(); 55 | // 56 | // const sender1 = accounts[0]; 57 | // const sender2 = accounts[1]; 58 | // const principal = accounts[2];// '0x627306090abab3a6e1400e9345bc60c78a8bef57'; 59 | // 60 | // const enigmaTokenContract = new web3.eth.Contract(EnigmaTokenContractJson.abi); 61 | // 62 | // const enigmaTokenContractInstance = await enigmaTokenContract.deploy({data: EnigmaTokenContractJson.bytecode, arguments: []}) 63 | // .send({ 64 | // from: sender1, 65 | // gas: 1500000, 66 | // // gasPrice: '100000000000' 67 | // }); 68 | // 69 | // // console.log('using account', principal, 'as principal signer'); 70 | // 71 | // const enigmaContract = new web3.eth.Contract(EnigmaContractJson.abi); 72 | // const enigmaContractInstance = await enigmaContract.deploy({ 73 | // data: EnigmaContractJson.bytecode, 74 | // arguments: [enigmaTokenContractInstance.options.address, principal], 75 | // }).send({ 76 | // from: sender2, 77 | // gas: 6500000, // 4500000, 78 | // // gasPrice: '100000000000' 79 | // }); 80 | // 81 | // await testUtils.sleep(1000); 82 | 83 | const networkId = truffleConfig.networks.development.network_id; 84 | 85 | return { 86 | contractAddress: EnigmaContractJson.networks[networkId].address, 87 | contractABI: EnigmaContractJson.abi, 88 | web3: web3 89 | }; 90 | } 91 | 92 | async function startNetwork(truffleDirectory) { 93 | const command = "cd " + truffleDirectory + " && npx truffle develop"; 94 | subprocess = spawn(command, { 95 | shell: true, 96 | detached: true 97 | }); 98 | 99 | subprocess.unref(); 100 | 101 | await testUtils.sleep(3000); 102 | } 103 | 104 | async function start(truffleDirectory) { 105 | await startNetwork(truffleDirectory); 106 | } 107 | 108 | function stop() { 109 | subprocess.kill(); 110 | } 111 | 112 | function disconnect(web3) { 113 | web3.currentProvider.disconnect(); 114 | } 115 | 116 | module.exports = { 117 | start: start, 118 | stop: stop, 119 | init: init, 120 | disconnect: disconnect 121 | }; 122 | -------------------------------------------------------------------------------- /test/ethereum/scripts/migrations/1_initial_migration.js: -------------------------------------------------------------------------------- 1 | var Migrations = artifacts.require("./Migrations.sol"); 2 | 3 | module.exports = function(deployer) { 4 | deployer.deploy(Migrations); 5 | }; 6 | 7 | // const EnigmaToken = artifacts.require('EnigmaToken.sol'); 8 | // const Enigma = artifacts.require('Enigma.sol'); 9 | 10 | // module.exports = function(deployer) { 11 | // return deployer.then(() => { 12 | // return deployer.deploy(EnigmaToken); 13 | // }).then(() => { 14 | // const principal = '0x627306090abab3a6e1400e9345bc60c78a8bef57'; 15 | // console.log('using account', principal, 'as principal signer'); 16 | // return deployer.deploy(Enigma, EnigmaToken.address, principal); 17 | // }); 18 | // }; 19 | -------------------------------------------------------------------------------- /test/ethereum/scripts/migrations/2_deploy_contracts.js: -------------------------------------------------------------------------------- 1 | const EnigmaToken = artifacts.require("EnigmaToken.sol"); 2 | const SolRsaVerify = artifacts.require("./utils/SolRsaVerify.sol"); 3 | const SecretContractImpl = artifacts.require("./impl/SecretContractImpl.sol"); 4 | const ExchangeRate = artifacts.require("ExchangeRate.sol"); 5 | const UpgradeImpl = artifacts.require("./impl/UpgradeImpl.sol"); 6 | 7 | const PRINCIPAL_SIGNING_ADDRESS = "0x3078356633353161633136306365333763653066"; 8 | const ISVSVN = "0x0000"; 9 | const MRSIGNER = "0x83d719e77deaca1470f6baf62a4d774303c899db69020f9c70ee1dfc08c7ce9e"; 10 | const EPOCH_SIZE = 10; 11 | const TIMEOUT_THRESHOLD = 2; 12 | const DEBUG = false; 13 | 14 | const Enigma = artifacts.require("Enigma.sol"); 15 | const WorkersImpl = artifacts.require("./impl/WorkersImpl.sol"); 16 | const PrincipalImpl = artifacts.require("./impl/PrincipalImpl.sol"); 17 | const TaskImpl = artifacts.require("./impl/TaskImpl.sol"); 18 | 19 | async function deployProtocol(deployer) { 20 | await Promise.all([ 21 | deployer.deploy(EnigmaToken), 22 | deployer.deploy(SolRsaVerify), 23 | deployer.deploy(WorkersImpl), 24 | deployer.deploy(SecretContractImpl), 25 | deployer.deploy(UpgradeImpl) 26 | ]); 27 | 28 | await Promise.all([ 29 | TaskImpl.link("WorkersImpl", WorkersImpl.address), 30 | PrincipalImpl.link("WorkersImpl", WorkersImpl.address) 31 | ]); 32 | 33 | await Promise.all([deployer.deploy(TaskImpl), deployer.deploy(PrincipalImpl)]); 34 | 35 | await Promise.all([ 36 | Enigma.link("WorkersImpl", WorkersImpl.address), 37 | Enigma.link("PrincipalImpl", PrincipalImpl.address), 38 | Enigma.link("TaskImpl", TaskImpl.address), 39 | Enigma.link("UpgradeImpl", UpgradeImpl.address), 40 | Enigma.link("SecretContractImpl", SecretContractImpl.address) 41 | ]); 42 | 43 | let principal = PRINCIPAL_SIGNING_ADDRESS; 44 | console.log("using account", principal, "as principal signer"); 45 | await deployer.deploy(ExchangeRate); 46 | await deployer.deploy( 47 | Enigma, 48 | EnigmaToken.address, 49 | principal, 50 | ExchangeRate.address, 51 | EPOCH_SIZE, 52 | TIMEOUT_THRESHOLD, 53 | DEBUG, 54 | MRSIGNER, 55 | ISVSVN 56 | ); 57 | } 58 | 59 | async function doMigration(deployer) { 60 | await deployProtocol(deployer); 61 | } 62 | 63 | module.exports = function(deployer) { 64 | deployer.then(() => doMigration(deployer)); 65 | }; 66 | -------------------------------------------------------------------------------- /test/ethereum/scripts/truffle.js: -------------------------------------------------------------------------------- 1 | // See 2 | // to customize your Truffle configuration! 3 | module.exports = { 4 | networks: { 5 | development: { 6 | host: "localhost", 7 | port: 9545, 8 | network_id: "5777" // Match any network id 9 | } 10 | }, 11 | solc: { 12 | // Turns on the Solidity optimizer. For development the optimizer's 13 | // quite helpful, just remember to be careful, and potentially turn it 14 | // off, for live deployment and/or audit time. For more information, 15 | // see the Truffle 4.0.0 release notes. 16 | // 17 | // https://github.com/trufflesuite/truffle/releases/tag/v4.0.0 18 | optimizer: { 19 | enabled: true, 20 | runs: 200 21 | } 22 | } 23 | }; 24 | -------------------------------------------------------------------------------- /test/msg_test.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const syncMsgs = require("../src/policy/p2p_messages/sync_messages"); 3 | const schemeValidator = require("../src/policy/p2p_messages/schemes/SchemeValidator"); 4 | const constants = require("../src/common/constants"); 5 | const MsgTypes = constants.P2P_MESSAGES; 6 | 7 | it("should scheme validate state sync req", function(done) { 8 | let state_sync_req_obj = { 9 | msgType: "SYNC_STATE_REQ", 10 | contractAddress: "0x...", 11 | fromIndex: 1, 12 | toIndex: 101, 13 | fromHash: "0x...", 14 | toHash: "0x..." 15 | }; 16 | 17 | schemeValidator.validateScheme(state_sync_req_obj, MsgTypes.SYNC_STATE_REQ, (err, isValid) => { 18 | if (err) { 19 | assert.strictEqual(false, true, err); 20 | } else { 21 | assert.strictEqual(true, isValid, "invalid scheme"); 22 | } 23 | done(); 24 | }); 25 | }); 26 | 27 | it("should scheme validate state sync res", function(done) { 28 | let state_sync_res_obj = { 29 | msgType: "SYNC_STATE_RES", 30 | contractAddress: "0x...", 31 | states: [ 32 | { index: 1, hash: "0x1", data: [11, 12, 13] }, 33 | { index: 2, hash: "0x2", data: [311, 122, 133] }, 34 | { index: 3, hash: "0x3", data: [151, 152, 143] } 35 | ] 36 | }; 37 | 38 | schemeValidator.validateScheme(state_sync_res_obj, MsgTypes.SYNC_STATE_RES, (err, isValid) => { 39 | if (err) { 40 | assert.strictEqual(false, true, err); 41 | } else { 42 | assert.strictEqual(true, isValid, "invalid scheme"); 43 | } 44 | done(); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /test/singleConfig/config_1.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | logger: { 3 | level: "debug", // log level 4 | cli: true, // output to std 5 | file: false // output to file, if set path then will save to file else none. 6 | }, 7 | node: { 8 | network: { 9 | port: "0", // if 0 then chose random port, 10 | multiAddrs: ["/ip4/0.0.0.0/tcp/"], 11 | // TODO:: ignored because of constants/namespace 12 | namespace: "ipfs", 13 | bootstrapNodes: ["/ip4/0.0.0.0/tcp/10300/ipfs/QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm"] 14 | }, 15 | idPath: null, // load PeerId, if null-> create one 16 | // TODO:: ignored currently cuz of implementation 17 | id: null, // either idPath or id -> actuall object here are acceptable if both are set, idPath is the default 18 | // TODO:: ignored libp2p-bundle 19 | isDiscover: true, // should do discovery ? 20 | // the inner task manager of the node controller 21 | taskManager: { 22 | dbPath: null // the db path for storage, if null saves in default 23 | }, 24 | // epoch related config 25 | principalNode: { 26 | uri: null //principal node url, default if null 27 | } 28 | }, 29 | // IPC 30 | core: { 31 | uri: "tcp://127.0.0.1:5522" // ipc uri 32 | }, 33 | // JsonRpc config 34 | proxy: { 35 | withProxy: true, // default serve as a proxy node 36 | port: null // integer or null will default in constants 37 | }, 38 | // Ethereum related configuration 39 | ethereum: { 40 | //default use ethereum or not 41 | withEthereum: false, 42 | // websocket provider 43 | ethereumWebsocketProvider: "", 44 | // enigma contract address 45 | enigmaContractAddress: "" 46 | }, 47 | // TODO:: CURRENTLY IGNORED 48 | "dev:": { 49 | truffleDir: "" 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /test/singleConfig/config_2_bootstrap.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | logger: { 3 | level: "debug", // log level 4 | cli: true, // output to std 5 | file: false // output to file, if set path then will save to file else none. 6 | }, 7 | node: { 8 | network: { 9 | port: "10300", // if 0 then chose random port, 10 | multiAddrs: ["/ip4/0.0.0.0/tcp/"], 11 | // TODO:: ignored because of constants/namespace 12 | namespace: "ipfs", 13 | bootstrapNodes: ["/ip4/0.0.0.0/tcp/10300/ipfs/QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm"] 14 | }, 15 | idPath: "./id-j.json", // load PeerId, if null-> create one 16 | // TODO:: ignored currently cuz of implementation 17 | id: null, // either idPath or id -> actuall object here are acceptable if both are set, idPath is the default 18 | // TODO:: ignored libp2p-bundle 19 | isDiscover: true, // should do discovery ? 20 | // the inner task manager of the node controller 21 | taskManager: { 22 | dbPath: null // the db path for storage, if null saves in default 23 | }, 24 | // epoch related config 25 | principalNode: { 26 | uri: null //principal node url, default if null 27 | } 28 | }, 29 | // IPC 30 | core: { 31 | uri: "tcp://127.0.0.1:5533" // ipc uri 32 | }, 33 | // JsonRpc config 34 | proxy: { 35 | withProxy: true, // default serve as a proxy node 36 | port: 3001 // integer or null will default in constants 37 | }, 38 | // Ethereum related configuration 39 | ethereum: { 40 | //default use ethereum or not 41 | withEthereum: false, 42 | // websocket provider 43 | ethereumWebsocketProvider: "", 44 | // enigma contract address 45 | enigmaContractAddress: "" 46 | }, 47 | // TODO:: CURRENTLY IGNORED 48 | "dev:": { 49 | truffleDir: "" 50 | } 51 | }; 52 | -------------------------------------------------------------------------------- /test/singleConfig/id-l.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm", 3 | "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6", 4 | "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE=" 5 | } 6 | -------------------------------------------------------------------------------- /test/singleConfig/single_config_test.js: -------------------------------------------------------------------------------- 1 | const EnvironmentBuilder = require("../../src/main_controller/EnvironmentBuilder"); 2 | const CoreServer = require("../../src/core/core_server_mock/core_server"); 3 | const tree = require("../test_tree").TEST_TREE.single_config; 4 | const expect = require("expect"); 5 | const assert = require("assert"); 6 | const MainController = require("../../src/main_controller/FacadeController"); 7 | const testUtils = require("../testUtils/utils"); 8 | const path = require("path"); 9 | const ID_B_PATH = path.join(__dirname, "id-l.json"); 10 | const jayson = require("jayson"); 11 | 12 | function getRpcClient(port) { 13 | return jayson.client.http("http://localhost:" + port); 14 | } 15 | function getConfig() { 16 | return require("./config_1"); 17 | } 18 | function getBootsrapConfig() { 19 | let c = require("./config_2_bootstrap"); 20 | c.node.idPath = ID_B_PATH; 21 | return c; 22 | } 23 | 24 | function getCoreServer(uri) { 25 | coreServer = new CoreServer(); 26 | coreServer.runServer(uri); 27 | return coreServer; 28 | } 29 | 30 | describe("single_config_tests", () => { 31 | it("#1 Should create node and shutdown", async function() { 32 | if (!tree["all"] || !tree["#1"]) { 33 | this.skip(); 34 | } 35 | const c = getConfig(); 36 | return new Promise(async resolve => { 37 | let coreServer = getCoreServer(c.core.uri); 38 | let mainController = await EnvironmentBuilder.buildFromSingle(c); 39 | expect(mainController).toEqual(expect.anything()); 40 | assert(mainController instanceof MainController, "not main controller"); 41 | await mainController.shutdownSystem(); 42 | coreServer.disconnect(); 43 | resolve(); 44 | }); 45 | }); 46 | it("#2 Should test with proxy and shutdown", async function() { 47 | if (!tree["all"] || !tree["#2"]) { 48 | this.skip(); 49 | } 50 | return new Promise(async (resolve, reject) => { 51 | await testUtils.sleep(2000); 52 | const c = getConfig(); 53 | const bc = getBootsrapConfig(); 54 | let bCoreServer = getCoreServer(bc.core.uri); 55 | let pCoreServer = getCoreServer(c.core.uri); 56 | let bMainController = await EnvironmentBuilder.buildFromSingle(bc); 57 | let pMainController = await EnvironmentBuilder.buildFromSingle(c); 58 | let client = getRpcClient(bc.proxy.port); 59 | // verify connectivity 60 | expect(pMainController).toEqual(expect.anything()); 61 | assert(pMainController instanceof MainController, "not main controller"); 62 | assert(bMainController instanceof MainController, "not main controller"); 63 | await testUtils.sleep(5000); 64 | // rpc 65 | let signKey = await pMainController.getNode().selfSubscribeAction(); 66 | await testUtils.sleep(1000); 67 | const userPubKey = 68 | "5587fbc96b01bfe6482bf9361a08e84810afcc0b1af72a8e4520f9" + 69 | "8771ea1080681e8a2f9546e5924e18c047fa948591dba098bffaced50f97a41b0050bdab99"; 70 | client.request("getWorkerEncryptionKey", { workerAddress: signKey, userPubKey: userPubKey }, async (err, res) => { 71 | if (err) { 72 | reject(err); 73 | } 74 | assert.strictEqual( 75 | "worker-signature-with-signed-by-the-private-key-of-the-sender-key", 76 | res.result.result.workerSig, 77 | "workersig dont match" 78 | ); 79 | await pMainController.shutdownSystem(); 80 | await bMainController.shutdownSystem(); 81 | pCoreServer.disconnect(); 82 | bCoreServer.disconnect(); 83 | resolve(); 84 | }); 85 | }); 86 | }); 87 | }); 88 | -------------------------------------------------------------------------------- /test/tasks/utils.js: -------------------------------------------------------------------------------- 1 | const ComputeTask = require("../../src/worker/tasks/ComputeTask"); 2 | const DeployTask = require("../../src/worker/tasks/DeployTask"); 3 | const Result = require("../../src/worker/tasks/Result"); 4 | const testUtils = require("../testUtils/utils"); 5 | const constants = require("../../src/common/constants"); 6 | const deployTasks = require("./random_deploy_tasks.json"); 7 | const computeTasks = require("./random_compute_tasks.json"); 8 | 9 | function generateTasks(num, type) { 10 | const tasks = []; 11 | const taskType = type === "deploy" ? DeployTask : ComputeTask; 12 | const taskList = type === "deploy" ? deployTasks : computeTasks; 13 | for (let i = 0; i < num; i++) { 14 | const task = taskList[i]; 15 | task.taskId = testUtils.randLenStr(64); 16 | tasks.push(taskType.fromDbJson(task)); 17 | } 18 | return tasks; 19 | } 20 | 21 | generateComputeTasks = num => generateTasks(num, "compute"); 22 | 23 | generateDeployTasks = num => generateTasks(num, "deploy"); 24 | 25 | function generateBundle(num, isSuccess, type) { 26 | const tasks = type === "deploy" ? generateDeployTasks(num) : generateComputeTasks(num); 27 | const taskResult = 28 | type === "deploy" ? Result.DeployResult.buildDeployResult : Result.ComputeResult.buildComputeResult; 29 | return tasks.map(task => { 30 | let resObj = { 31 | taskId: task.getTaskId(), 32 | status: isSuccess ? constants.TASK_STATUS.SUCCESS : constants.TASK_STATUS.FAILED, 33 | output: testUtils.randLenStr(200), 34 | delta: { key: 0, data: testUtils.getRandomByteArray(20) }, 35 | usedGas: testUtils.getRandomInt(10000), 36 | ethereumPayload: testUtils.getRandomByteArray(100), 37 | ethereumAddress: testUtils.randLenStr(40), 38 | signature: testUtils.getRandomByteArray(120), 39 | preCodeHash: testUtils.randLenStr(64), 40 | blockNumber: testUtils.getRandomInt(100) 41 | }; 42 | return { 43 | task, 44 | result: isSuccess ? taskResult(resObj) : Result.FailedResult.buildFailedResult(resObj) 45 | }; 46 | }); 47 | } 48 | 49 | module.exports.generateComputeTasks = generateComputeTasks; 50 | 51 | module.exports.generateDeployTasks = generateDeployTasks; 52 | 53 | module.exports.generateDeployBundle = (num, isSuccess) => generateBundle(num, isSuccess, "deploy"); 54 | 55 | module.exports.generateComputeBundle = (num, isSuccess) => generateBundle(num, isSuccess, "compute"); 56 | -------------------------------------------------------------------------------- /test/testUtils/id-d.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "Qma3GsJmB47xYuyahPZPSadh1avvxfyYQwk8R3UnFrQ6aP", 3 | "privKey": "CAASpwkwggSjAgEAAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAECggEAZnrCJ6IYiLyyRdr9SbKXCNDb4YByGYPEi/HT1aHgIJfFE1PSMjxcdytxfyjP4JJpVtPjiT9JFVU2ddoYu5qJN6tGwjVwgJEWg1UXmPaAw1T/drjS94kVsAs82qICtFmwp52Apg3dBZ0Qwq/8qE1XbG7lLyohIbfCBiL0tiPYMfkcsN9gnFT/kFCX0LVs2pa9fHCRMY9rqCc4/rWJa1w8sMuQ23y4lDaxKF9OZVvOHFQkbBDrkquWHE4r55fchCz/rJklkPJUNENuncBRu0/2X+p4IKFD1DnttXNwb8j4LPiSlLro1T0hiUr5gO2QmdYwXFF63Q3mjQy0+5I4eNbjjQKBgQDZvZy3gUKS/nQNkYfq9za80uLbIj/cWbO+ZZjXCsj0fNIcQFJcKMBoA7DjJvu2S/lf86/41YHkPdmrLAEQAkJ+5BBNOycjYK9minTEjIMMmZDTXXugZ62wnU6F46uLkgEChTqEP57Y6xwwV+JaEDFEsW5N1eE9lEVX9nGIr4phMwKBgQC1TazLuEt1WBx/iUT83ita7obXqoKNzwsS/MWfY2innzYZKDOqeSYZzLtt9uTtp4X4uLyPbYs0qFYhXLsUYMoGHNN8+NdjoyxCjQRJRBkMtaNR0lc5lVDWl3bTuJovjFCgAr9uqJrmI5OHcCIk/cDpdWb3nWaMihVlePmiTcTy9wKBgQCU0u7c1jKkudqks4XM6a+2HAYGdUBk4cLjLhnrUWnNAcuyl5wzdX8dGPi8KZb+IKuQE8WBNJ2VXVj7kBYh1QmSJVunDflQSvNYCOaKuOeRoxzD+y9Wkca74qkbBmPn/6FFEb7PSZTO+tPHjyodGNgz9XpJJRjQuBk1aDJtlF3m1QKBgE5SAr5ym65SZOU3UGUIOKRsfDW4Q/OsqDUImvpywCgBICaX9lHDShFFHwau7FA52ScL7vDquoMB4UtCOtLfyQYA9995w9oYCCurrVlVIJkb8jSLcADBHw3EmqF1kq3NqJqm9TmBfoDCh52vdCCUufxgKh33kfBOSlXuf7B8dgMbAoGAZ3r0/mBQX6S+s5+xCETMTSNv7TQzxgtURIpVs+ZVr2cMhWhiv+n0Omab9X9Z50se8cWl5lkvx8vn3D/XHHIPrMF6qk7RAXtvReb+PeitNvm0odqjFv0J2qki6fDs0HKwq4kojAXI1Md8Th0eobNjsy21fEEJT7uKMJdovI/SErI=", 4 | "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCaNSDOjPz6T8HZsf7LDpxiQRiN2OjeyIHUS05p8QWOr3EFUCFsC31R4moihE5HN+FxNalUyyFZU//yjf1pdnlMJqrVByJSMa+y2y4x2FucpoCAO97Tx+iWzwlZ2UXEUXM1Y81mhPbeWXy+wP2xElTgIER0Tsn/thoA0SD2u9wJuVvM7dB7cBcHYmqV6JH+KWCedRTum6O1BssqP/4Lbm2+rkrbZ4+oVRoU2DRLoFhKqwqLtylrbuj4XOI3XykMXV5+uQXz1JzubNOB9lsc6K+eRC+w8hhhDuFMgzkZ4qomCnx3uhO67KaICd8yqqBa6PJ/+fBM5Xk4hjyR40bwcf41AgMBAAE=" 5 | } 6 | -------------------------------------------------------------------------------- /test/testUtils/id-l.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "QmcrQZ6RJdpYuGvZqD5QEHAv6qX4BrQLJLQPQUrTrzdcgm", 3 | "privKey": "CAASqAkwggSkAgEAAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAECggEAQj0obPnVyjxLFZFnsFLgMHDCv9Fk5V5bOYtmxfvcm50us6ye+T8HEYWGUa9RrGmYiLweuJD34gLgwyzE1RwptHPj3tdNsr4NubefOtXwixlWqdNIjKSgPlaGULQ8YF2tm/kaC2rnfifwz0w1qVqhPReO5fypL+0ShyANVD3WN0Fo2ugzrniCXHUpR2sHXSg6K+2+qWdveyjNWog34b7CgpV73Ln96BWae6ElU8PR5AWdMnRaA9ucA+/HWWJIWB3Fb4+6uwlxhu2L50Ckq1gwYZCtGw63q5L4CglmXMfIKnQAuEzazq9T4YxEkp+XDnVZAOgnQGUBYpetlgMmkkh9qQKBgQDvsEs0ThzFLgnhtC2Jy//ZOrOvIAKAZZf/mS08AqWH3L0/Rjm8ZYbLsRcoWU78sl8UFFwAQhMRDBP9G+RPojWVahBL/B7emdKKnFR1NfwKjFdDVaoX5uNvZEKSl9UubbC4WZJ65u/cd5jEnj+w3ir9G8n+P1gp/0yBz02nZXFgSwKBgQDZPQr4HBxZL7Kx7D49ormIlB7CCn2i7mT11Cppn5ifUTrp7DbFJ2t9e8UNk6tgvbENgCKXvXWsmflSo9gmMxeEOD40AgAkO8Pn2R4OYhrwd89dECiKM34HrVNBzGoB5+YsAno6zGvOzLKbNwMG++2iuNXqXTk4uV9GcI8OnU5ZPQKBgCZUGrKSiyc85XeiSGXwqUkjifhHNh8yH8xPwlwGUFIZimnD4RevZI7OEtXw8iCWpX2gg9XGuyXOuKORAkF5vvfVriV4e7c9Ad4Igbj8mQFWz92EpV6NHXGCpuKqRPzXrZrNOA9PPqwSs+s9IxI1dMpk1zhBCOguWx2m+NP79NVhAoGBAI6WSoTfrpu7ewbdkVzTWgQTdLzYNe6jmxDf2ZbKclrf7lNr/+cYIK2Ud5qZunsdBwFdgVcnu/02czeS42TvVBgs8mcgiQc/Uy7yi4/VROlhOnJTEMjlU2umkGc3zLzDgYiRd7jwRDLQmMrYKNyEr02HFKFn3w8kXSzW5I8rISnhAoGBANhchHVtJd3VMYvxNcQb909FiwTnT9kl9pkjhwivx+f8/K8pDfYCjYSBYCfPTM5Pskv5dXzOdnNuCj6Y2H/9m2SsObukBwF0z5Qijgu1DsxvADVIKZ4rzrGb4uSEmM6200qjJ/9U98fVM7rvOraakrhcf9gRwuspguJQnSO9cLj6", 4 | "pubKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDLZZcGcbe4urMBVlcHgN0fpBymY+xcr14ewvamG70QZODJ1h9sljlExZ7byLiqRB3SjGbfpZ1FweznwNxWtWpjHkQjTVXeoM4EEgDSNO/Cg7KNlU0EJvgPJXeEPycAZX9qASbVJ6EECQ40VR/7+SuSqsdL1hrmG1phpIju+D64gLyWpw9WEALfzMpH5I/KvdYDW3N4g6zOD2mZNp5y1gHeXINHWzMF596O72/6cxwyiXV1eJ000k1NVnUyrPjXtqWdVLRk5IU1LFpoQoXZU5X1hKj1a2qt/lZfH5eOrF/ramHcwhrYYw1txf8JHXWO/bbNnyemTHAvutZpTNrsWATfAgMBAAE=" 5 | } 6 | -------------------------------------------------------------------------------- /test/testUtils/principal_mock.js: -------------------------------------------------------------------------------- 1 | const jayson = require("jayson"); 2 | 3 | /** 4 | * Creates a principal mock server 5 | * @param {Integer} getStateKeysCb 6 | * @return {Object} principal mock server 7 | * */ 8 | module.exports.create = getStateKeysCb => { 9 | const server = jayson 10 | .server({ 11 | getStateKeys: getStateKeysCb 12 | }) 13 | .http() 14 | .setTimeout(500000); 15 | server.listen(0, "127.0.0.1"); 16 | return server; 17 | }; 18 | 19 | module.exports.destroy = server => { 20 | server.close(); 21 | }; 22 | 23 | module.exports.getPort = server => { 24 | return server.address().port; 25 | }; 26 | -------------------------------------------------------------------------------- /test/testUtils/utils.js: -------------------------------------------------------------------------------- 1 | var rimraf = require("rimraf"); 2 | const randomize = require("randomatic"); 3 | 4 | /** 5 | * Generate variable size random string from Aa0 6 | * @param {Integer} size 7 | * @return {string} result 8 | * */ 9 | module.exports.randLenStr = function(size) { 10 | return randomize("Aa0", size); 11 | }; 12 | /** 13 | * generate random integer with max 14 | * @param {Integer} max 15 | */ 16 | module.exports.getRandomInt = function(max) { 17 | return _randomInt(max); 18 | }; 19 | 20 | function _randomInt(max) { 21 | return Math.floor(Math.random() * Math.floor(max)); 22 | } 23 | 24 | module.exports.getRandomByteArray = function(size) { 25 | let output = []; 26 | for (let i = 0; i < size; ++i) { 27 | output.push(_randomInt(256)); 28 | } 29 | return output; 30 | }; 31 | 32 | module.exports.sleep = function(ms) { 33 | return _sleep(ms); 34 | }; 35 | 36 | function _sleep(ms) { 37 | return new Promise(resolve => setTimeout(resolve, ms)); 38 | } 39 | 40 | module.exports.rm_Minus_Rf = async path => { 41 | return new Promise((resolve, reject) => { 42 | _deleteFolderFromOSRecursive(path, err => { 43 | if (err) reject(err); 44 | else resolve(); 45 | }); 46 | }); 47 | }; 48 | 49 | module.exports.deleteFolderFromOSRecursive = function(path, callback) { 50 | _deleteFolderFromOSRecursive(path, callback); 51 | }; 52 | 53 | /** 54 | * same as rm -rf 55 | * @param {string} path 56 | * @param {function} callback 57 | */ 58 | function _deleteFolderFromOSRecursive(path, callback) { 59 | rimraf(path, callback); 60 | } 61 | -------------------------------------------------------------------------------- /test/test_tree.js: -------------------------------------------------------------------------------- 1 | module.exports.TEST_TREE = { 2 | basic: { 3 | all: true, 4 | "#1": true, 5 | "#2": true, 6 | "#3": true 7 | }, 8 | ipc: { 9 | all: true, 10 | "#1": true, 11 | "#2": true, 12 | "#3": true, 13 | "#4": true, 14 | "#5": true, 15 | "#6": true 16 | }, 17 | ethereum: { 18 | all: true, 19 | "#1": true, 20 | "#2": true, 21 | "#3": true, 22 | "#4": true, 23 | "#5": true, 24 | "#6": true, 25 | "#7": true, 26 | "#8": true, 27 | "#9": true, 28 | "#10": true, 29 | "#11": true, 30 | "#12": true, 31 | "#13": true, 32 | "#14": true, 33 | "#15": true, 34 | "#16": true, 35 | "#17": true, 36 | "#18": true, 37 | "#19": true 38 | }, 39 | ethereum_advanced: { 40 | all: true, 41 | "#1": true, 42 | "#2": true, 43 | "#3": true, 44 | "#4": true 45 | }, 46 | task_manager: { 47 | all: true, 48 | "#1": true, 49 | "#2": true, 50 | "#3": true, 51 | "#4": true, 52 | "#5": true, 53 | "#6": true 54 | }, 55 | task_flow: { 56 | all: true, 57 | "#1": true, 58 | "#2": true, 59 | "#3": true, 60 | "#4": true 61 | }, 62 | sync_basic: { 63 | all: true, 64 | "#1": true, 65 | "#2": true, 66 | "#3": true, 67 | "#4": true, 68 | "#5": true 69 | }, 70 | sync_network: { 71 | all: true, 72 | "#1": true 73 | }, 74 | jsonrpc_basic: { 75 | all: true, 76 | "#1": true, 77 | "#2": true, 78 | "#3": true, 79 | "#4": true, 80 | "#5": true, 81 | "#6": true 82 | }, 83 | jsonrpc_advanced: { 84 | all: true, 85 | "#1": true, 86 | "#2": true, 87 | "#3": true, 88 | "#4": true 89 | }, 90 | actions_tests: { 91 | all: true, 92 | "#1": true, 93 | "#2": true, 94 | "#3": true 95 | }, 96 | principal: { 97 | all: true, 98 | "#1": true, 99 | "#2": true, 100 | "#3": true 101 | }, 102 | verifier: { 103 | all: true, 104 | "#1": true, 105 | "#2": true, 106 | "#3": true, 107 | "#4": true, 108 | "#5": true, 109 | "#6": true, 110 | "#7": true, 111 | "#8": true, 112 | "#9": true, 113 | "#10": true, 114 | "#11": true, 115 | "#12": true, 116 | "#13": true, 117 | "#14": true, 118 | "#15": true, 119 | "#16": true, 120 | "#17": true, 121 | "#18": true, 122 | "#19": true, 123 | "#20": true, 124 | "#21": true, 125 | "#22": true, 126 | "#23": true, 127 | "#24": true, 128 | "#25": true, 129 | "#26": true, 130 | "#27": true, 131 | "#28": true, 132 | "#29": true, 133 | "#30": true, 134 | "#31": true, 135 | "#32": true, 136 | "#33": true, 137 | "#34": true, 138 | "#35": true, 139 | "#36": true, 140 | "#37": true, 141 | "#38": true, 142 | "#39": true, 143 | "#40": true, 144 | "#41": true, 145 | "#42": true, 146 | "#43": true, 147 | "#44": true, 148 | "#45": true, 149 | "#46": true, 150 | "#47": true, 151 | "#48": true 152 | }, 153 | ethereum_integration: { 154 | all: true, 155 | "#1": true, 156 | "#2": true, 157 | "#3": true, 158 | "#4": true, 159 | "#5": true, 160 | "#6": true, 161 | "#7": true, 162 | "#8": true 163 | }, 164 | single_config: { 165 | all: true, 166 | "#1": true, 167 | "#2": true 168 | }, 169 | healthcheck: { 170 | all: true, 171 | "#1": true 172 | }, 173 | init_worker: { 174 | all: true, 175 | "#1": true 176 | } 177 | }; 178 | -------------------------------------------------------------------------------- /test/webserver_test.js: -------------------------------------------------------------------------------- 1 | const assert = require("assert"); 2 | const axios = require("axios"); 3 | const testBuilder = require("./testUtils/quickBuilderUtil"); 4 | const testUtils = require("./testUtils/utils"); 5 | const constants = require("../src/common/constants"); 6 | const tree = require("./test_tree").TEST_TREE.healthcheck; 7 | 8 | const noLoggerOpts = { 9 | bOpts: { 10 | withLogger: true 11 | }, 12 | pOpts: { 13 | withLogger: true 14 | } 15 | }; 16 | 17 | const stopTest = async (peers, bNodeController, bNodeCoreServer, resolve) => { 18 | const pPaths = peers.map(p => { 19 | return p.tasksDbPath; 20 | }); 21 | try { 22 | for (let i = 0; i < pPaths.length; ++i) { 23 | await peers[i].mainController.shutdownSystem(); 24 | peers[i].coreServer.disconnect(); 25 | } 26 | await bNodeController.shutdownSystem(); 27 | bNodeCoreServer.disconnect(); 28 | } catch (e) { 29 | console.log("ERROR while trying to stop the nodes=" + JSON.stringify(e)); 30 | } 31 | resolve(); 32 | }; 33 | 34 | it("#1 Query healthCheck + status", async function() { 35 | if (!tree["all"] || !tree["#1"]) { 36 | this.skip(); 37 | } 38 | return new Promise(async resolve => { 39 | const peersNum = 5; 40 | const port = 9898; 41 | const hcUrl = "/hc"; 42 | const statusUrl = "/st"; 43 | // init nodes 44 | const { peers, bNode } = await testBuilder.createN(peersNum, noLoggerOpts); 45 | await testUtils.sleep(4000); 46 | const bNodeController = bNode.mainController; 47 | const bNodeCoreServer = bNode.coreServer; 48 | // start the tested node 49 | const testPeer = await testBuilder.createNode({ 50 | withEth: true, 51 | ethWorkerAddress: "0xb9A219631Aed55eBC3D998f17C3840B7eC39C0cc", 52 | webserver: { port: port, healthCheck: { url: hcUrl }, status: { url: statusUrl } } 53 | }); 54 | 55 | let connectedPeers = 0; 56 | testPeer.mainController 57 | .getNode() 58 | .engNode() 59 | .node.on(constants.PROTOCOLS.PEER_CONNECT, async peer => { 60 | connectedPeers += 1; 61 | 62 | // start test only after all connections established 63 | if (connectedPeers === peersNum + 1) { 64 | // request health check 65 | let url = "http://localhost:" + port + hcUrl; 66 | let response = await axios.get(url); 67 | assert.strictEqual(response.status, 200); 68 | assert.strictEqual(response.data.core.status, true); 69 | assert.strictEqual(response.data.core.registrationParams.signKey.length, 42); 70 | assert.strictEqual(response.data.ethereum.status, true); 71 | assert.strictEqual(response.data.connectivity.status, true); 72 | assert.strictEqual(response.data.connectivity.connections, peersNum + 1); 73 | 74 | // request status 75 | url = "http://localhost:" + port + statusUrl; 76 | response = await axios.get(url); 77 | assert.strictEqual(response.status, 200); 78 | assert.strictEqual(response.data, constants.WORKER_STATUS.UNREGISTERED); 79 | 80 | // STOP EVERYTHING 81 | peers.push(testPeer); 82 | await stopTest(peers, bNodeController, bNodeCoreServer, resolve); 83 | } 84 | }); 85 | }); 86 | }); 87 | --------------------------------------------------------------------------------